From: Stefan Eissing Date: Fri, 29 Jul 2016 15:12:38 +0000 (+0000) Subject: mod_http2: complete implemenmtation of draft-http2-debug-state X-Git-Tag: 2.5.0-alpha~1366 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d6cd77eb9530744faa778c6d83c2ddeb58d335f1;p=apache mod_http2: complete implemenmtation of draft-http2-debug-state git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1754534 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/http2/h2_filter.c b/modules/http2/h2_filter.c index a48ae2e307..68082a6ac3 100644 --- a/modules/http2/h2_filter.c +++ b/modules/http2/h2_filter.c @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -32,6 +33,7 @@ #include "h2_stream.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" @@ -223,13 +225,68 @@ static void add_settings(apr_bucket_brigade *bb, h2_session *s, int last) 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; + + 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, " \"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, " }"); + + ++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"); + bbout(bb, " \"push\": {\n"); diary = s->push_diary; if (diary) { const char *data; @@ -241,51 +298,50 @@ static void add_push(apr_bucket_brigade *bb, h2_session *s, &data, &len); if (status == APR_SUCCESS) { base64_digest = h2_util_base64url_encode(data, len, bb->p); - bbout(bb, " \"cacheDigest\": \"%s\",\n", base64_digest); + bbout(bb, " \"cacheDigest\": \"%s\",\n", base64_digest); } } - 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? "" : ","); + 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 void add_in(apr_bucket_brigade *bb, h2_session *s, int last) { - 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? "" : ","); + 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 void add_out(apr_bucket_brigade *bb, h2_session *s, int last) { - 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? "" : ","); + 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? "" : ","); } -/* -int32_t nghttp2_session_get_effective_local_window_size(session); -int32_t -nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, - int32_t stream_id); - int32_t -nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, - int32_t stream_id); -int32_t -nghttp2_session_get_remote_window_size(nghttp2_session *session); +static void add_stats(apr_bucket_brigade *bb, h2_session *s, + h2_stream *stream, int last) +{ + 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_status_t h2_status_stream_filter(h2_stream *stream) { h2_session *s = stream->session; conn_rec *c = s->c; apr_bucket_brigade *bb; + int32_t connFlowIn, connFlowOut; if (!stream->response) { return APR_EINVAL; @@ -299,20 +355,25 @@ static apr_status_t h2_status_stream_filter(h2_stream *stream) 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"); add_settings(bb, s, 0); - bbout(bb, " \"connFlowIn\": %d,\n", - nghttp2_session_get_effective_local_window_size(s->ngh2)); - bbout(bb, " \"connFlowOut\": %d,\n", - nghttp2_session_get_remote_window_size(s->ngh2)); + add_peer_settings(bb, s, 0); + bbout(bb, " \"connFlowIn\": %d,\n", connFlowIn); + bbout(bb, " \"connFlowOut\": %d,\n", connFlowOut); bbout(bb, " \"sentGoAway\": %d,\n", (s->state == H2_SESSION_ST_LOCAL_SHUTDOWN || s->state == H2_SESSION_ST_DONE)); - add_in(bb, s, 0); - add_out(bb, s, 0); - - add_push(bb, s, stream, 1); + add_streams(bb, s, 0); + + add_stats(bb, s, stream, 1); bbout(bb, "}\n"); return APR_SUCCESS; diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c index fd6bf6ba29..4313b5946f 100644 --- a/modules/http2/h2_mplx.c +++ b/modules/http2/h2_mplx.c @@ -467,6 +467,33 @@ static int stream_done_iter(void *ctx, void *val) return 0; } +typedef struct { + h2_mplx_stream_cb *cb; + void *ctx; +} stream_iter_ctx_t; + +static int stream_iter_wrap(void *ctx, void *stream) +{ + stream_iter_ctx_t *x = ctx; + return x->cb(stream, x->ctx); +} + +apr_status_t h2_mplx_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx) +{ + apr_status_t status; + int acquired; + + if ((status = enter_mutex(m, &acquired)) == APR_SUCCESS) { + stream_iter_ctx_t x; + x.cb = cb; + x.ctx = ctx; + h2_ihash_iter(m->streams, stream_iter_wrap, &x); + + leave_mutex(m, acquired); + } + return status; +} + static int task_print(void *ctx, void *val) { h2_mplx *m = ctx; diff --git a/modules/http2/h2_mplx.h b/modules/http2/h2_mplx.h index 821e6d65df..4af0ba3c13 100644 --- a/modules/http2/h2_mplx.h +++ b/modules/http2/h2_mplx.h @@ -230,6 +230,11 @@ apr_status_t h2_mplx_dispatch_master_events(h2_mplx *m, apr_status_t h2_mplx_suspend_stream(h2_mplx *m, int stream_id); + +typedef int h2_mplx_stream_cb(struct h2_stream *s, void *ctx); + +apr_status_t h2_mplx_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx); + /******************************************************************************* * Output handling of streams. ******************************************************************************/ diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c index f32c79c984..1a905eab53 100644 --- a/modules/http2/h2_session.c +++ b/modules/http2/h2_session.c @@ -628,7 +628,8 @@ static int on_send_data_cb(nghttp2_session *ngh2, apr_brigade_cleanup(session->bbtmp); if (status == APR_SUCCESS) { - stream->data_frames_sent++; + stream->out_data_frames++; + stream->out_data_octets += length; return 0; } else { diff --git a/modules/http2/h2_stream.c b/modules/http2/h2_stream.c index 3a7f1a94a1..fe5e560f01 100644 --- a/modules/http2/h2_stream.c +++ b/modules/http2/h2_stream.c @@ -462,6 +462,9 @@ apr_status_t h2_stream_write_data(h2_stream *stream, status = h2_beam_send(stream->input, stream->tmp, APR_BLOCK_READ); apr_brigade_cleanup(stream->tmp); + stream->in_data_frames++; + stream->in_data_octets += len; + return status; } @@ -695,3 +698,26 @@ const h2_priority *h2_stream_get_priority(h2_stream *stream) return NULL; } +const char *h2_stream_state_str(h2_stream *stream) +{ + switch (stream->state) { + case H2_STREAM_ST_IDLE: + return "IDLE"; + case H2_STREAM_ST_OPEN: + return "OPEN"; + case H2_STREAM_ST_RESV_LOCAL: + return "RESERVED_LOCAL"; + case H2_STREAM_ST_RESV_REMOTE: + return "RESERVED_REMOTE"; + case H2_STREAM_ST_CLOSED_INPUT: + return "HALF_CLOSED_REMOTE"; + case H2_STREAM_ST_CLOSED_OUTPUT: + return "HALF_CLOSED_LOCAL"; + case H2_STREAM_ST_CLOSED: + return "CLOSED"; + default: + return "UNKNOWN"; + + } +} + diff --git a/modules/http2/h2_stream.h b/modules/http2/h2_stream.h index f80f8115e7..4ffa7c32b1 100644 --- a/modules/http2/h2_stream.h +++ b/modules/http2/h2_stream.h @@ -65,7 +65,10 @@ struct h2_stream { unsigned int submitted : 1; /* response HEADER has been sent */ apr_off_t input_remaining; /* remaining bytes on input as advertised via content-length */ - apr_off_t data_frames_sent; /* # of DATA frames sent out for this stream */ + apr_off_t out_data_frames; /* # of DATA frames sent */ + apr_off_t out_data_octets; /* # of DATA octets (payload) sent */ + apr_off_t in_data_frames; /* # of DATA frames received */ + apr_off_t in_data_octets; /* # of DATA octets (payload) received */ }; @@ -277,4 +280,10 @@ apr_status_t h2_stream_submit_pushes(h2_stream *stream); */ const struct h2_priority *h2_stream_get_priority(h2_stream *stream); +/** + * Return a textual representation of the stream state as in RFC 7540 + * nomenclator, all caps, underscores. + */ +const char *h2_stream_state_str(h2_stream *stream); + #endif /* defined(__mod_h2__h2_stream__) */