]> granicus.if.org Git - apache/blobdiff - modules/http2/h2_filter.c
mod_http2: latest h2/state debug draft, fixes in 100-continue response generation
[apache] / modules / http2 / h2_filter.c
index 9bc550004b9a04ca4f961ffd696cd1f27bd7125f..ce94b52ed6e0ca229e0d932ca62b8ab863937793 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <assert.h>
 
+#include <apr_strings.h>
 #include <httpd.h>
 #include <http_core.h>
 #include <http_log.h>
 #include <scoreboard.h>
 
 #include "h2_private.h"
+#include "h2.h"
+#include "h2_config.h"
 #include "h2_conn_io.h"
 #include "h2_ctx.h"
 #include "h2_mplx.h"
 #include "h2_push.h"
 #include "h2_task.h"
 #include "h2_stream.h"
-#include "h2_stream_set.h"
 #include "h2_request.h"
 #include "h2_response.h"
+#include "h2_stream.h"
 #include "h2_session.h"
 #include "h2_util.h"
 #include "h2_version.h"
@@ -92,9 +95,9 @@ h2_filter_cin *h2_filter_cin_create(apr_pool_t *p, h2_filter_cin_cb *cb, void *c
     return cin;
 }
 
-void h2_filter_cin_timeout_set(h2_filter_cin *cin, int timeout_secs)
+void h2_filter_cin_timeout_set(h2_filter_cin *cin, apr_interval_time_t timeout)
 {
-    cin->timeout_secs = timeout_secs;
+    cin->timeout = timeout;
 }
 
 apr_status_t h2_filter_core_input(ap_filter_t* f,
@@ -105,12 +108,12 @@ apr_status_t h2_filter_core_input(ap_filter_t* f,
 {
     h2_filter_cin *cin = f->ctx;
     apr_status_t status = APR_SUCCESS;
-    apr_time_t saved_timeout = UNSET;
+    apr_interval_time_t saved_timeout = UNSET;
     
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
-                  "core_input(%ld): read, %s, mode=%d, readbytes=%ld, timeout=%d", 
+                  "core_input(%ld): read, %s, mode=%d, readbytes=%ld", 
                   (long)f->c->id, (block == APR_BLOCK_READ)? "BLOCK_READ" : "NONBLOCK_READ", 
-                  mode, (long)readbytes, cin->timeout_secs);
+                  mode, (long)readbytes);
     
     if (mode == AP_MODE_INIT || mode == AP_MODE_SPECULATIVE) {
         return ap_get_brigade(f->next, brigade, mode, block, readbytes);
@@ -137,10 +140,9 @@ apr_status_t h2_filter_core_input(ap_filter_t* f,
          * in the scoreboard is preserved.
          */
         if (block == APR_BLOCK_READ) {
-            if (cin->timeout_secs > 0) {
-                apr_time_t t = apr_time_from_sec(cin->timeout_secs);
+            if (cin->timeout > 0) {
                 apr_socket_timeout_get(cin->socket, &saved_timeout);
-                apr_socket_timeout_set(cin->socket, t);
+                apr_socket_timeout_set(cin->socket, cin->timeout);
             }
         }
         status = ap_get_brigade(f->next, cin->bb, AP_MODE_READBYTES,
@@ -148,8 +150,6 @@ apr_status_t h2_filter_core_input(ap_filter_t* f,
         if (saved_timeout != UNSET) {
             apr_socket_timeout_set(cin->socket, saved_timeout);
         }
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
-                      "core_input(%ld): got_brigade", (long)f->c->id);
     }
     
     switch (status) {
@@ -159,6 +159,8 @@ apr_status_t h2_filter_core_input(ap_filter_t* f,
         case APR_EOF:
         case APR_EAGAIN:
         case APR_TIMEUP:
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
+                          "core_input(%ld): read", (long)f->c->id);
             break;
         default:
             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, f->c, APLOGNO(03046)
@@ -198,116 +200,191 @@ int h2_filter_h2_status_handler(request_rec *r)
     return DECLINED;
 }
 
-#define bbout(...)   apr_brigade_printf(bb, NULL, NULL, __VA_ARGS__)
-static apr_status_t h2_sos_h2_status_buffer(h2_sos *sos, apr_bucket_brigade *bb)
+static apr_status_t bbout(apr_bucket_brigade *bb, const char *fmt, ...)
 {
-    h2_stream *stream = sos->stream;
-    h2_session *session = stream->session;
-    h2_mplx *mplx = session->mplx;
-    h2_push_diary *diary;
-    apr_status_t status;
+    va_list args;
+    apr_status_t rv;
+
+    va_start(args, fmt);
+    rv = apr_brigade_vprintf(bb, NULL, NULL, fmt, args);
+    va_end(args);
+
+    return rv;
+}
+
+static void add_settings(apr_bucket_brigade *bb, h2_session *s, int last) 
+{
+    h2_mplx *m = s->mplx;
     
-    if (!bb) {
-        bb = apr_brigade_create(stream->pool, session->c->bucket_alloc);
-    }
+    bbout(bb, "  \"settings\": {\n");
+    bbout(bb, "    \"SETTINGS_MAX_CONCURRENT_STREAMS\": %d,\n", m->max_streams); 
+    bbout(bb, "    \"SETTINGS_MAX_FRAME_SIZE\": %d,\n", 16*1024); 
+    bbout(bb, "    \"SETTINGS_INITIAL_WINDOW_SIZE\": %d,\n",
+          h2_config_geti(s->config, H2_CONF_WIN_SIZE));
+    bbout(bb, "    \"SETTINGS_ENABLE_PUSH\": %d\n", h2_session_push_enabled(s)); 
+    bbout(bb, "  }%s\n", last? "" : ",");
+}
+
+static void add_peer_settings(apr_bucket_brigade *bb, h2_session *s, int last) 
+{
+    bbout(bb, "  \"peerSettings\": {\n");
+    bbout(bb, "    \"SETTINGS_MAX_CONCURRENT_STREAMS\": %d,\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS)); 
+    bbout(bb, "    \"SETTINGS_MAX_FRAME_SIZE\": %d,\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_MAX_FRAME_SIZE)); 
+    bbout(bb, "    \"SETTINGS_INITIAL_WINDOW_SIZE\": %d,\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE)); 
+    bbout(bb, "    \"SETTINGS_ENABLE_PUSH\": %d,\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_ENABLE_PUSH)); 
+    bbout(bb, "    \"SETTINGS_HEADER_TABLE_SIZE\": %d,\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_HEADER_TABLE_SIZE)); 
+    bbout(bb, "    \"SETTINGS_MAX_HEADER_LIST_SIZE\": %d\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE)); 
+    bbout(bb, "  }%s\n", last? "" : ",");
+}
+
+typedef struct {
+    apr_bucket_brigade *bb;
+    h2_session *s;
+    int idx;
+} stream_ctx_t;
+
+static int add_stream(h2_stream *stream, void *ctx)
+{
+    stream_ctx_t *x = ctx;
+    int32_t flowIn, flowOut;
     
-    bbout("{\n");
-    bbout("  \"HTTP2\": \"on\",\n");
-    bbout("  \"H2PUSH\": \"%s\",\n", h2_session_push_enabled(session)? "on" : "off");
-    bbout("  \"mod_http2_version\": \"%s\",\n", MOD_HTTP2_VERSION);
-    bbout("  \"session_id\": %ld,\n", (long)session->id);
-    bbout("  \"streams_max\": %d,\n", (int)session->max_stream_count);
-    bbout("  \"this_stream\": %d,\n", stream->id);
-    bbout("  \"streams_open\": %d,\n", (int)h2_stream_set_size(session->streams));
-    bbout("  \"max_stream_started\": %d,\n", mplx->max_stream_started);
-    bbout("  \"requests_received\": %d,\n", session->requests_received);
-    bbout("  \"responses_submitted\": %d,\n", session->responses_submitted);
-    bbout("  \"streams_reset\": %d, \n", session->streams_reset);
-    bbout("  \"pushes_promised\": %d,\n", session->pushes_promised);
-    bbout("  \"pushes_submitted\": %d,\n", session->pushes_submitted);
-    bbout("  \"pushes_reset\": %d,\n", session->pushes_reset);
+    flowIn = nghttp2_session_get_stream_effective_local_window_size(x->s->ngh2, stream->id); 
+    flowOut = nghttp2_session_get_stream_remote_window_size(x->s->ngh2, stream->id);
+    bbout(x->bb, "%s\n    \"%d\": {\n", (x->idx? "," : ""), stream->id);
+    bbout(x->bb, "    \"state\": \"%s\",\n", h2_stream_state_str(stream));
+    bbout(x->bb, "    \"created\": %f,\n", ((double)stream->created)/APR_USEC_PER_SEC);
+    bbout(x->bb, "    \"flowIn\": %d,\n", flowIn);
+    bbout(x->bb, "    \"flowOut\": %d,\n", flowOut);
+    bbout(x->bb, "    \"dataIn\": %"APR_UINT64_T_FMT",\n", stream->in_data_octets);  
+    bbout(x->bb, "    \"dataOut\": %"APR_UINT64_T_FMT"\n", stream->out_data_octets);  
+    bbout(x->bb, "    }");
     
-    diary = session->push_diary;
+    ++x->idx;
+    return 1;
+} 
+
+static void add_streams(apr_bucket_brigade *bb, h2_session *s, int last) 
+{
+    stream_ctx_t x;
+    
+    x.bb = bb;
+    x.s = s;
+    x.idx = 0;
+    bbout(bb, "  \"streams\": {");
+    h2_mplx_stream_do(s->mplx, add_stream, &x);
+    bbout(bb, "\n  }%s\n", last? "" : ",");
+}
+
+static void add_push(apr_bucket_brigade *bb, h2_session *s, 
+                     h2_stream *stream, int last) 
+{
+    h2_push_diary *diary;
+    apr_status_t status;
+    
+    bbout(bb, "    \"push\": {\n");
+    diary = s->push_diary;
     if (diary) {
         const char *data;
         const char *base64_digest;
         apr_size_t len;
         
-        status = h2_push_diary_digest_get(diary, stream->pool, 256, 
-                                          stream->request->authority, &data, &len);
+        status = h2_push_diary_digest_get(diary, bb->p, 256, 
+                                          stream->request->authority, 
+                                          &data, &len);
         if (status == APR_SUCCESS) {
-            base64_digest = h2_util_base64url_encode(data, len, stream->pool);
-            bbout("  \"cache_digest\": \"%s\",\n", base64_digest);
-        }
-        
-        /* try the reverse for testing purposes */
-        status = h2_push_diary_digest_set(diary, stream->request->authority, data, len);
-        if (status == APR_SUCCESS) {
-            status = h2_push_diary_digest_get(diary, stream->pool, 256, 
-                                              stream->request->authority, &data, &len);
-            if (status == APR_SUCCESS) {
-                base64_digest = h2_util_base64url_encode(data, len, stream->pool);
-                bbout("  \"cache_digest^2\": \"%s\",\n", base64_digest);
-            }
+            base64_digest = h2_util_base64url_encode(data, len, bb->p);
+            bbout(bb, "      \"cacheDigest\": \"%s\",\n", base64_digest);
         }
     }
-    bbout("  \"frames_received\": %ld,\n", (long)session->frames_received);
-    bbout("  \"frames_sent\": %ld,\n", (long)session->frames_sent);
-    bbout("  \"bytes_received\": %"APR_UINT64_T_FMT",\n", session->io.bytes_read);
-    bbout("  \"bytes_sent\": %"APR_UINT64_T_FMT"\n", session->io.bytes_written);
-    bbout("}\n");
-    
-    return sos->prev->buffer(sos->prev, bb);
+    bbout(bb, "      \"promises\": %d,\n", s->pushes_promised);
+    bbout(bb, "      \"submits\": %d,\n", s->pushes_submitted);
+    bbout(bb, "      \"resets\": %d\n", s->pushes_reset);
+    bbout(bb, "    }%s\n", last? "" : ",");
 }
 
-static apr_status_t h2_sos_h2_status_read_to(h2_sos *sos, apr_bucket_brigade *bb, 
-                                             apr_off_t *plen, int *peos)
+static void add_in(apr_bucket_brigade *bb, h2_session *s, int last) 
 {
-    return sos->prev->read_to(sos->prev, bb, plen, peos);
+    bbout(bb, "    \"in\": {\n");
+    bbout(bb, "      \"requests\": %d,\n", s->remote.emitted_count);
+    bbout(bb, "      \"resets\": %d, \n", s->streams_reset);
+    bbout(bb, "      \"frames\": %ld,\n", (long)s->frames_received);
+    bbout(bb, "      \"octets\": %"APR_UINT64_T_FMT"\n", s->io.bytes_read);
+    bbout(bb, "    }%s\n", last? "" : ",");
 }
 
-static apr_status_t h2_sos_h2_status_prep_read(h2_sos *sos, apr_off_t *plen, int *peos)
+static void add_out(apr_bucket_brigade *bb, h2_session *s, int last) 
 {
-    return sos->prev->prep_read(sos->prev, plen, peos);
+    bbout(bb, "    \"out\": {\n");
+    bbout(bb, "      \"responses\": %d,\n", s->responses_submitted);
+    bbout(bb, "      \"frames\": %ld,\n", (long)s->frames_sent);
+    bbout(bb, "      \"octets\": %"APR_UINT64_T_FMT"\n", s->io.bytes_written);
+    bbout(bb, "    }%s\n", last? "" : ",");
 }
 
-static apr_status_t h2_sos_h2_status_readx(h2_sos *sos, h2_io_data_cb *cb, void *ctx,
-                                           apr_off_t *plen, int *peos)
+static void add_stats(apr_bucket_brigade *bb, h2_session *s, 
+                     h2_stream *stream, int last) 
 {
-    return sos->prev->readx(sos->prev, cb, ctx, plen, peos);
+    bbout(bb, "  \"stats\": {\n");
+    add_in(bb, s, 0);
+    add_out(bb, s, 0);
+    add_push(bb, s, stream, 1);
+    bbout(bb, "  }%s\n", last? "" : ",");
 }
 
-static apr_table_t *h2_sos_h2_status_get_trailers(h2_sos *sos)
+static apr_status_t h2_status_stream_filter(h2_stream *stream)
 {
-    return sos->prev->get_trailers(sos->prev);
-}
-
-static h2_sos *h2_sos_h2_status_create(h2_sos *prev) 
-{
-    h2_sos *sos;
-    h2_response *response = prev->response;
+    h2_session *s = stream->session;
+    conn_rec *c = s->c;
+    apr_bucket_brigade *bb;
+    int32_t connFlowIn, connFlowOut;
+    
+    if (!stream->response) {
+        return APR_EINVAL;
+    }
     
-    apr_table_unset(response->headers, "Content-Length");
-    response->content_length = -1;
+    if (!stream->buffer) {
+        stream->buffer = apr_brigade_create(stream->pool, c->bucket_alloc);
+    }
+    bb = stream->buffer;
+    
+    apr_table_unset(stream->response->headers, "Content-Length");
+    stream->response->content_length = -1;
+    
+    connFlowIn = nghttp2_session_get_effective_local_window_size(s->ngh2); 
+    connFlowOut = nghttp2_session_get_remote_window_size(s->ngh2);
+    apr_table_setn(stream->response->headers, "conn-flow-in", 
+                   apr_itoa(stream->pool, connFlowIn));
+    apr_table_setn(stream->response->headers, "conn-flow-out", 
+                   apr_itoa(stream->pool, connFlowOut));
+     
+    bbout(bb, "{\n");
+    bbout(bb, "  \"version\": \"draft-01\",\n");
+    add_settings(bb, s, 0);
+    add_peer_settings(bb, s, 0);
+    bbout(bb, "  \"connFlowIn\": %d,\n", connFlowIn);
+    bbout(bb, "  \"connFlowOut\": %d,\n", connFlowOut);
+    bbout(bb, "  \"sentGoAway\": %d,\n", s->local.shutdown);
 
-    sos = apr_pcalloc(prev->stream->pool, sizeof(*sos));
-    sos->prev         = prev;
-    sos->response     = response;
-    sos->stream       = prev->stream;
-    sos->buffer       = h2_sos_h2_status_buffer;
-    sos->prep_read    = h2_sos_h2_status_prep_read;
-    sos->readx        = h2_sos_h2_status_readx;
-    sos->read_to      = h2_sos_h2_status_read_to;
-    sos->get_trailers = h2_sos_h2_status_get_trailers;
+    add_streams(bb, s, 0);
+    
+    add_stats(bb, s, stream, 1);
+    bbout(bb, "}\n");
     
-    return sos;
+    return APR_SUCCESS;
 }
 
-h2_sos *h2_filter_sos_create(const char *name, struct h2_sos *prev)
+apr_status_t h2_stream_filter(h2_stream *stream)
 {
-    if (!strcmp(H2_SOS_H2_STATUS, name)) {
-        return h2_sos_h2_status_create(prev);
+    const char *fname = stream->response? stream->response->sos_filter : NULL; 
+    if (fname && !strcmp(H2_SOS_H2_STATUS, fname)) {
+        return h2_status_stream_filter(stream);
     }
-    return prev;
+    return APR_SUCCESS;
 }