#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"
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;
}