From d0dafa03039c761bd2da4b04ef6dfebe89994aee Mon Sep 17 00:00:00 2001
From: Frederik Deweerdt
Date: Mon, 11 Apr 2016 14:45:21 -0700
Subject: [PATCH 1/8] Improvements to reported stats
- Add a structure to track internally generated errors
- Structure the stats a pluggable modules. New status reporting code can
use the `h2o_config_register_status_handler` (for queries that need to
access per thread data) or `h2o_config_register_simple_status_handler`
(for queries that can be run in the request context) APIs in order to
add new statuses
- requests statuses and error reporting is moved under
`lib/handler/status/`
---
.travis.yml | 1 +
CMakeLists.txt | 2 +
include/h2o.h | 78 +++++++-
include/h2o/http2_internal.h | 2 +-
lib/core/config.c | 17 ++
lib/core/context.c | 65 +++++++
lib/core/request.c | 2 +
lib/handler/status.c | 265 +++++++++++++++-----------
lib/handler/status/errors.c | 113 +++++++++++
lib/handler/status/requests.c | 177 +++++++++++++++++
lib/http2/connection.c | 4 +
src/main.c | 2 +-
srcdoc/configure/status_directives.mt | 3 +
t/50status.t | 47 +++++
14 files changed, 665 insertions(+), 113 deletions(-)
create mode 100644 lib/handler/status/errors.c
create mode 100644 lib/handler/status/requests.c
create mode 100644 t/50status.t
diff --git a/.travis.yml b/.travis.yml
index af7d3f6285..055ee8654d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -23,6 +23,7 @@ before_script:
- misc/install-perl-module.pl Net::EmptyPort
- misc/install-perl-module.pl Scope::Guard
- misc/install-perl-module.pl Starlet
+ - misc/install-perl-module.pl JSON
# install the `ab` command (a.k.a. ApacheBench; optionally required for running some of the tests)
- sudo apt-get install -qq apache2-utils
# install nghttp2 with `--enable-app` (optionally required for running HTTP/2 tests)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ac9985f61b..7e0cf97d49 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -239,6 +239,8 @@ SET(LIB_SOURCE_FILES
lib/handler/redirect.c
lib/handler/reproxy.c
lib/handler/status.c
+ lib/handler/status/errors.c
+ lib/handler/status/requests.c
lib/handler/configurator/access_log.c
lib/handler/configurator/compress.c
lib/handler/configurator/errordoc.c
diff --git a/include/h2o.h b/include/h2o.h
index e5014ea30c..0c5cbe067d 100644
--- a/include/h2o.h
+++ b/include/h2o.h
@@ -275,6 +275,28 @@ typedef struct st_h2o_protocol_callbacks_t {
int (*foreach_request)(h2o_context_t *ctx, int (*cb)(h2o_req_t *req, void *cbdata), void *cbdata);
} h2o_protocol_callbacks_t;
+enum h2o_status_handler_type {
+ H2O_STATUS_HANDLER_SIMPLE,
+ H2O_STATUS_HANDLER_PER_THREAD,
+};
+typedef h2o_iovec_t (*simple_status_handler_cb)(h2o_globalconf_t *gconf, h2o_mem_pool_t *pool);
+typedef struct h2o_status_handler {
+ enum h2o_status_handler_type type;
+ h2o_iovec_t name;
+ union {
+ struct {
+ simple_status_handler_cb status_cb;
+ } simple;
+ struct {
+ void *(*alloc_context_cb)(h2o_req_t *src_req);
+ void (*per_thread_cb)(void *priv, h2o_context_t *ctx);
+ h2o_iovec_t (*assemble_cb)(void *ctx);
+ void (*done_cb)(void *ctx);
+ } per_thread;
+ };
+} h2o_status_handler_t;
+
+typedef H2O_VECTOR(h2o_status_handler_t) h2o_status_callbacks_t;
struct st_h2o_globalconf_t {
/**
* a NULL-terminated list of host contexts (h2o_hostconf_t)
@@ -371,10 +393,7 @@ struct st_h2o_globalconf_t {
} filecache;
/* status */
- struct {
- /* optional callback set by the user of libh2o to feed additional json to the status handler */
- h2o_iovec_t (*extra_status)(h2o_globalconf_t *conf, h2o_mem_pool_t *pool);
- } status;
+ h2o_status_callbacks_t statuses;
size_t _num_config_slots;
};
@@ -411,6 +430,42 @@ typedef struct st_h2o_mimemap_type_t {
} data;
} h2o_mimemap_type_t;
+/* errors emanating from h2o */
+enum {
+ /* http */
+ E_HTTP_400 = 0,
+ E_HTTP_403,
+ E_HTTP_404,
+ E_HTTP_405,
+ E_HTTP_416,
+ E_HTTP_417,
+ E_HTTP_500,
+ E_HTTP_502,
+ E_HTTP_503,
+ E_HTTP_4XX,
+ E_HTTP_5XX,
+ E_HTTP_XXX,
+ /* http2 */
+ E_HTTP2_PROTOCOL,
+ E_HTTP2_INTERNAL,
+ E_HTTP2_FLOW_CONTROL,
+ E_HTTP2_SETTINGS_TIMEOUT,
+ E_HTTP2_STREAM_CLOSED,
+ E_HTTP2_FRAME_SIZE,
+ E_HTTP2_REFUSED_STREAM,
+ E_HTTP2_CANCEL,
+ E_HTTP2_COMPRESSION,
+ E_HTTP2_CONNECT,
+ E_HTTP2_ENHANCE_YOUR_CALM,
+ E_HTTP2_INADEQUATE_SECURITY,
+ E_HTTP2_OTHER,
+ EMITTED_ERRORS_MAX,
+};
+
+typedef struct st_h2o_emitted_errors_cnt {
+ unsigned long emitted_errors_cnt[EMITTED_ERRORS_MAX];
+} h2o_emitted_errors_t;
+
/**
* context of the http server.
*/
@@ -503,6 +558,11 @@ struct st_h2o_context_t {
h2o_timestamp_string_t *value;
} _timestamp_cache;
+ /**
+ * counter for errors internally emitted by h2o
+ */
+ h2o_emitted_errors_t emitted_errors;
+
H2O_VECTOR(h2o_pathconf_t *) _pathconfs_inited;
};
@@ -1143,6 +1203,11 @@ h2o_hostconf_t *h2o_config_register_host(h2o_globalconf_t *config, h2o_iovec_t h
* * configuration path does not end with a `/`, and the request path begins with the configuration path followed by a `/`
*/
h2o_pathconf_t *h2o_config_register_path(h2o_hostconf_t *hostconf, const char *path, int flags);
+/**
+ * registers an extra status handler
+ */
+void h2o_config_register_status_handler(h2o_globalconf_t *config, h2o_status_handler_t);
+void h2o_config_register_simple_status_handler(h2o_globalconf_t *config, h2o_iovec_t name, simple_status_handler_cb status_handler);
/**
* disposes of the resources allocated for the global configuration
*/
@@ -1211,7 +1276,10 @@ static void h2o_context_set_filter_context(h2o_context_t *ctx, h2o_filter_t *fil
* returns per-module context set by the on_context_init callback
*/
static void *h2o_context_get_logger_context(h2o_context_t *ctx, h2o_logger_t *logger);
-
+/**
+ * internally reports an error emitted by h2o, so that this can in turn be reported in statuses
+ */
+void h2o_context_report_emitted_error(h2o_context_t *ctx, int status, int http);
/* built-in generators */
enum {
diff --git a/include/h2o/http2_internal.h b/include/h2o/http2_internal.h
index 2abcaf1156..c17502e716 100644
--- a/include/h2o/http2_internal.h
+++ b/include/h2o/http2_internal.h
@@ -48,7 +48,7 @@ typedef struct st_h2o_http2_stream_t h2o_http2_stream_t;
#define H2O_HTTP2_ERROR_COMPRESSION -9
#define H2O_HTTP2_ERROR_CONNECT -10
#define H2O_HTTP2_ERROR_ENHANCE_YOUR_CALM -11
-#define H2O_HTTP2_ERROR_INADEUATE_SECURITY -12
+#define H2O_HTTP2_ERROR_INADEQUATE_SECURITY -12
#define H2O_HTTP2_ERROR_INCOMPLETE -255 /* an internal value indicating that all data is not ready */
#define H2O_HTTP2_ERROR_PROTOCOL_CLOSE_IMMEDIATELY -256
diff --git a/lib/core/config.c b/lib/core/config.c
index 6c28207ce2..6dab5e95f0 100644
--- a/lib/core/config.c
+++ b/lib/core/config.c
@@ -198,6 +198,23 @@ h2o_pathconf_t *h2o_config_register_path(h2o_hostconf_t *hostconf, const char *p
return pathconf;
}
+void h2o_config_register_status_handler(h2o_globalconf_t *config, h2o_status_handler_t status_handler)
+{
+ h2o_vector_reserve(NULL, &config->statuses, config->statuses.size + 1);
+ config->statuses.entries[config->statuses.size++] = status_handler;
+}
+
+void h2o_config_register_simple_status_handler(h2o_globalconf_t *config, h2o_iovec_t name, simple_status_handler_cb status_handler)
+{
+ h2o_status_handler_t *sh;
+
+ h2o_vector_reserve(NULL, &config->statuses, config->statuses.size + 1);
+ sh = &config->statuses.entries[config->statuses.size++];
+ sh->type = H2O_STATUS_HANDLER_SIMPLE;
+ sh->name = h2o_strdup(NULL, name.base, name.len);
+ sh->simple.status_cb = status_handler;
+}
+
h2o_hostconf_t *h2o_config_register_host(h2o_globalconf_t *config, h2o_iovec_t host, uint16_t port)
{
h2o_hostconf_t *hostconf = NULL;
diff --git a/lib/core/context.c b/lib/core/context.c
index 7fcc47370f..797f60ea41 100644
--- a/lib/core/context.c
+++ b/lib/core/context.c
@@ -183,3 +183,68 @@ void h2o_context_update_timestamp_cache(h2o_context_t *ctx)
h2o_time2str_log(ctx->_timestamp_cache.value->log, ctx->_timestamp_cache.tv_at.tv_sec);
}
}
+
+void h2o_context_report_emitted_error(h2o_context_t *ctx, int status, int http)
+{
+ int s100;
+
+#define INC_PREDEF_ERROR(x) \
+ do { \
+ if (status == x) { \
+ ctx->emitted_errors.emitted_errors_cnt[E_HTTP_ ## x]++; \
+ return; \
+ } \
+ } while(0)
+
+ if (http == 1) {
+ INC_PREDEF_ERROR(400);
+ INC_PREDEF_ERROR(403);
+ INC_PREDEF_ERROR(404);
+ INC_PREDEF_ERROR(405);
+ INC_PREDEF_ERROR(416);
+ INC_PREDEF_ERROR(417);
+ INC_PREDEF_ERROR(500);
+ INC_PREDEF_ERROR(502);
+ INC_PREDEF_ERROR(503);
+
+#undef INC_PREDEF_ERROR
+
+ s100 = status / 100;
+ switch(s100) {
+ case 4:
+ ctx->emitted_errors.emitted_errors_cnt[E_HTTP_4XX]++;
+ return;
+ case 5:
+ ctx->emitted_errors.emitted_errors_cnt[E_HTTP_5XX]++;
+ return;
+ default:
+ ctx->emitted_errors.emitted_errors_cnt[E_HTTP_XXX]++;
+ return;
+ }
+ } else {
+#define INC_PREDEF_ERROR2(name, x) \
+ do { \
+ if (status == x) { \
+ ctx->emitted_errors.emitted_errors_cnt[E_HTTP2_ ## name]++; \
+ return; \
+ } \
+ } while(0)
+
+ INC_PREDEF_ERROR2(PROTOCOL, -1);
+ INC_PREDEF_ERROR2(INTERNAL, -2);
+ INC_PREDEF_ERROR2(FLOW_CONTROL, -3);
+ INC_PREDEF_ERROR2(SETTINGS_TIMEOUT, -4);
+ INC_PREDEF_ERROR2(STREAM_CLOSED, -5);
+ INC_PREDEF_ERROR2(FRAME_SIZE, -6);
+ INC_PREDEF_ERROR2(REFUSED_STREAM, -7);
+ INC_PREDEF_ERROR2(CANCEL, -8);
+ INC_PREDEF_ERROR2(COMPRESSION, -9);
+ INC_PREDEF_ERROR2(CONNECT, -10);
+ INC_PREDEF_ERROR2(ENHANCE_YOUR_CALM, -11);
+ INC_PREDEF_ERROR2(INADEQUATE_SECURITY, -12);
+
+ ctx->emitted_errors.emitted_errors_cnt[E_HTTP2_OTHER]++;
+ return;
+#undef INC_PREDEF_ERROR
+ }
+}
diff --git a/lib/core/request.c b/lib/core/request.c
index d3760a6ee6..feb26271aa 100644
--- a/lib/core/request.c
+++ b/lib/core/request.c
@@ -458,6 +458,8 @@ void h2o_send_inline(h2o_req_t *req, const char *body, size_t len)
void h2o_send_error(h2o_req_t *req, int status, const char *reason, const char *body, int flags)
{
+ h2o_context_report_emitted_error(req->conn->ctx, status, 1);
+
if (req->pathconf == NULL) {
h2o_hostconf_t *hostconf = setup_before_processing(req);
h2o_req_bind_conf(req, hostconf, &hostconf->fallback_path);
diff --git a/lib/handler/status.c b/lib/handler/status.c
index ac68cca8a0..f00e8d0280 100644
--- a/lib/handler/status.c
+++ b/lib/handler/status.c
@@ -21,6 +21,13 @@
*/
#include "h2o.h"
+extern h2o_status_handler_t errors_status_handler;
+extern h2o_status_handler_t requests_status_handler;
+
+struct st_h2o_status_logger_t {
+ h2o_logger_t super;
+};
+
struct st_h2o_status_handler_t {
h2o_handler_t super;
H2O_VECTOR(h2o_multithread_receiver_t *) receivers;
@@ -31,15 +38,19 @@ struct st_h2o_status_context_t {
h2o_multithread_receiver_t receiver;
};
+struct status_ctx {
+ int active;
+ void *ctx;
+};
struct st_h2o_status_collector_t {
struct {
h2o_req_t *req;
h2o_multithread_receiver_t *receiver;
} src;
- h2o_logconf_t *logconf;
pthread_mutex_t mutex;
- h2o_iovec_t data;
size_t num_remaining_threads;
+ h2o_globalconf_t *gconf;
+ H2O_VECTOR(struct status_ctx) status_ctx;
};
struct st_h2o_status_message_t {
@@ -47,55 +58,28 @@ struct st_h2o_status_message_t {
struct st_h2o_status_collector_t *collector;
};
-struct st_collect_req_status_cbdata_t {
- h2o_logconf_t *logconf;
- h2o_buffer_t *buffer;
-};
-
-static int collect_req_status(h2o_req_t *req, void *_cbdata)
-{
- struct st_collect_req_status_cbdata_t *cbdata = _cbdata;
-
- /* collect log */
- char buf[4096];
- size_t len = sizeof(buf);
- char *logline = h2o_log_request(cbdata->logconf, req, &len, buf);
-
- /* append to buffer */
- assert(len != 0);
- --len; /* omit trailing LF */
- h2o_buffer_reserve(&cbdata->buffer, len + 4);
- memcpy(cbdata->buffer->bytes + cbdata->buffer->size, ",\n ", 4);
- cbdata->buffer->size += 4;
- memcpy(cbdata->buffer->bytes + cbdata->buffer->size, logline, len);
- cbdata->buffer->size += len;
-
- if (logline != buf)
- free(logline);
-
- return 0;
-}
-
static void collect_reqs_of_context(struct st_h2o_status_collector_t *collector, h2o_context_t *ctx)
{
- struct st_collect_req_status_cbdata_t cbdata = {collector->logconf};
int was_last_thread;
-
- h2o_buffer_init(&cbdata.buffer, &h2o_socket_buffer_prototype);
- ctx->globalconf->http1.callbacks.foreach_request(ctx, collect_req_status, &cbdata);
- ctx->globalconf->http2.callbacks.foreach_request(ctx, collect_req_status, &cbdata);
+ int i;
pthread_mutex_lock(&collector->mutex);
- if (cbdata.buffer->size != 0) {
- collector->data.base = h2o_mem_realloc(collector->data.base, collector->data.len + cbdata.buffer->size);
- memcpy(collector->data.base + collector->data.len, cbdata.buffer->bytes, cbdata.buffer->size);
- collector->data.len += cbdata.buffer->size;
+
+ for (i = 0; i < collector->gconf->statuses.size; i++) {
+ h2o_status_handler_t *sh;
+ sh = &collector->gconf->statuses.entries[i];
+ if (!collector->status_ctx.entries[i].active) {
+ continue;
+ }
+ if (sh->type == H2O_STATUS_HANDLER_PER_THREAD) {
+ sh->per_thread.per_thread_cb(collector->status_ctx.entries[i].ctx, ctx);
+ }
}
+
+
was_last_thread = --collector->num_remaining_threads == 0;
pthread_mutex_unlock(&collector->mutex);
- h2o_buffer_dispose(&cbdata.buffer);
-
if (was_last_thread) {
struct st_h2o_status_message_t *message = h2o_mem_alloc(sizeof(*message));
message->super = (h2o_multithread_message_t){};
@@ -108,19 +92,57 @@ static void send_response(struct st_h2o_status_collector_t *collector)
{
static h2o_generator_t generator = {NULL, NULL};
h2o_req_t *req;
+ size_t nr_statuses;
+ int i;
+ int cur_resp = 0;
+
+ req = collector->src.req;
+ if (!req) {
+ h2o_mem_release_shared(collector);
+ return;
+ }
- if ((req = collector->src.req) != NULL) {
- h2o_iovec_t resp[] = {{H2O_STRLIT("{\n \"requests\": ")}, collector->data, {H2O_STRLIT("\n ]")}, {}, {H2O_STRLIT("\n}\n")}};
- resp[1].base[0] = '[';
- if (req->conn->ctx->globalconf->status.extra_status != NULL)
- resp[3] = req->conn->ctx->globalconf->status.extra_status(req->conn->ctx->globalconf, &req->pool);
- req->res.status = 200;
- h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8"));
- h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CACHE_CONTROL, H2O_STRLIT("no-cache, no-store"));
- h2o_start_response(req, &generator);
- h2o_send(req, resp,
- h2o_memis(req->input.method.base, req->input.method.len, H2O_STRLIT("HEAD")) ? 0 : sizeof(resp) / sizeof(resp[0]),
- 1);
+ nr_statuses = req->conn->ctx->globalconf->statuses.size;
+ size_t nr_resp = nr_statuses + 2; // 2 for the footer and header
+ h2o_iovec_t resp[nr_resp];
+
+ memset(resp, 0, sizeof(resp[0]) * nr_resp);
+ resp[cur_resp++] = (h2o_iovec_t){ H2O_STRLIT("{\n") };
+
+ int coma_removed = 0;
+ for (i = 0; i < req->conn->ctx->globalconf->statuses.size; i++) {
+ h2o_status_handler_t *sh = &req->conn->ctx->globalconf->statuses.entries[i];
+ if (!collector->status_ctx.entries[i].active) {
+ continue;
+ }
+ if (sh->type == H2O_STATUS_HANDLER_SIMPLE) {
+ resp[cur_resp++] = sh->simple.status_cb(req->conn->ctx->globalconf, &req->pool);
+ } else {
+ resp[cur_resp++] = sh->per_thread.assemble_cb(collector->status_ctx.entries[i].ctx);
+ }
+ if (resp[cur_resp - 1].len > 0 && !coma_removed) {
+ /* requests come in with a leading coma, replace if with a space */
+ resp[cur_resp - 1].base[0] = ' ';
+ coma_removed = 1;
+ }
+ }
+ resp[cur_resp++] = (h2o_iovec_t){ H2O_STRLIT("\n}\n") };
+
+ req->res.status = 200;
+ h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8"));
+ h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CACHE_CONTROL, H2O_STRLIT("no-cache, no-store"));
+ h2o_start_response(req, &generator);
+ h2o_send(req, resp,
+ h2o_memis(req->input.method.base, req->input.method.len, H2O_STRLIT("HEAD")) ? 0 : nr_resp,
+ 1);
+ for (i = 0; i < req->conn->ctx->globalconf->statuses.size; i++) {
+ h2o_status_handler_t *sh = &req->conn->ctx->globalconf->statuses.entries[i];
+ if (!collector->status_ctx.entries[i].active) {
+ continue;
+ }
+ if (sh->type == H2O_STATUS_HANDLER_PER_THREAD) {
+ sh->per_thread.done_cb(collector->status_ctx.entries[i].ctx);
+ }
}
h2o_mem_release_shared(collector);
}
@@ -154,9 +176,7 @@ static void on_collector_dispose(void *_collector)
{
struct st_h2o_status_collector_t *collector = _collector;
- h2o_logconf_dispose(collector->logconf);
pthread_mutex_destroy(&collector->mutex);
- free(collector->data.base);
}
static void on_req_close(void *p)
@@ -166,60 +186,44 @@ static void on_req_close(void *p)
h2o_mem_release_shared(collector);
}
-static int on_req_json(struct st_h2o_status_handler_t *self, h2o_req_t *req)
+static int on_req_json(struct st_h2o_status_handler_t *self, h2o_req_t *req, h2o_iovec_t *status_list)
{
- h2o_logconf_t *logconf;
-
-#define ELEMENT(key, expr) "\"" key "\": \"" expr "\""
-#define X_ELEMENT(id) ELEMENT(id, "%{" id "}x")
-#define SEPARATOR ", "
- const char *fmt = "{"
- /* combined_log */
- ELEMENT("host", "%h") SEPARATOR ELEMENT("user", "%u") SEPARATOR ELEMENT("at", "%{%Y%m%dT%H%M%S}t.%{usec_frac}t%{%z}t")
- SEPARATOR ELEMENT("method", "%m") SEPARATOR ELEMENT("path", "%U") SEPARATOR ELEMENT("query", "%q")
- SEPARATOR ELEMENT("protocol", "%H") SEPARATOR ELEMENT("referer", "%{Referer}i")
- SEPARATOR ELEMENT("user-agent", "%{User-agent}i") SEPARATOR
- /* time */
- X_ELEMENT("connect-time") SEPARATOR X_ELEMENT("request-header-time") SEPARATOR X_ELEMENT("request-body-time")
- SEPARATOR X_ELEMENT("request-total-time") SEPARATOR X_ELEMENT("process-time") SEPARATOR X_ELEMENT("response-time")
- SEPARATOR
- /* connection */
- X_ELEMENT("connection-id") SEPARATOR X_ELEMENT("ssl.protocol-version") SEPARATOR X_ELEMENT("ssl.session-reused")
- SEPARATOR X_ELEMENT("ssl.cipher") SEPARATOR X_ELEMENT("ssl.cipher-bits") SEPARATOR
- /* http1 */
- X_ELEMENT("http1.request-index") SEPARATOR
- /* http2 */
- X_ELEMENT("http2.stream-id") SEPARATOR X_ELEMENT("http2.priority.received.exclusive")
- SEPARATOR X_ELEMENT("http2.priority.received.parent") SEPARATOR X_ELEMENT("http2.priority.received.weight")
- SEPARATOR X_ELEMENT("http2.priority.actual.parent") SEPARATOR X_ELEMENT("http2.priority.actual.weight") SEPARATOR
- /* misc */
- ELEMENT("authority", "%V")
- /* end */
- "}";
-#undef ELEMENT
-#undef X_ELEMENT
-#undef SEPARATOR
-
- { /* compile logconf */
- char errbuf[256];
- if ((logconf = h2o_logconf_compile(fmt, H2O_LOGCONF_ESCAPE_JSON, errbuf)) == NULL) {
- h2o_iovec_t resp = h2o_concat(&req->pool, h2o_iovec_init(H2O_STRLIT("failed to compile log format:")),
- h2o_iovec_init(errbuf, strlen(errbuf)));
- h2o_send_error(req, 400, "Invalid Request", resp.base, 0);
- return 0;
- }
- }
-
{ /* construct collector and send request to every thread */
struct st_h2o_status_context_t *status_ctx = h2o_context_get_handler_context(req->conn->ctx, &self->super);
struct st_h2o_status_collector_t *collector = h2o_mem_alloc_shared(NULL, sizeof(*collector), on_collector_dispose);
- size_t i;
-
+ size_t i, j;
+
+ memset(collector, 0, sizeof(*collector));
+ for (i = 0; i < req->conn->ctx->globalconf->statuses.size; i++) {
+ h2o_status_handler_t *sh;
+
+ h2o_vector_reserve(&req->pool, &collector->status_ctx, collector->status_ctx.size + 1);
+ sh = &req->conn->ctx->globalconf->statuses.entries[i];
+
+ if (status_list) {
+ int found = 0;
+ for (j = 0; status_list[j].base; j++) {
+ if (h2o_memis(status_list[j].base, status_list[j].len, sh->name.base, sh->name.len)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ collector->status_ctx.entries[collector->status_ctx.size].active = 0;
+ goto Skip;
+ }
+ }
+ if (sh->type == H2O_STATUS_HANDLER_PER_THREAD && sh->per_thread.alloc_context_cb) {
+ collector->status_ctx.entries[collector->status_ctx.size].ctx = sh->per_thread.alloc_context_cb(req);
+ }
+ collector->status_ctx.entries[collector->status_ctx.size].active = 1;
+Skip:
+ collector->status_ctx.size++;
+ }
+ collector->gconf = req->conn->ctx->globalconf;
collector->src.req = req;
collector->src.receiver = &status_ctx->receiver;
- collector->logconf = logconf;
pthread_mutex_init(&collector->mutex, NULL);
- collector->data = h2o_iovec_init(NULL, 0);
collector->num_remaining_threads = self->receivers.size;
for (i = 0; i != self->receivers.size; ++i) {
@@ -236,6 +240,44 @@ static int on_req_json(struct st_h2o_status_handler_t *self, h2o_req_t *req)
return 0;
}
+static h2o_iovec_t *build_status_list(const char *param, size_t len)
+{
+ int i, cur, start;
+ int nr_list = 1;
+ h2o_iovec_t *ret;
+ for (i = 0; i < len; i++) {
+ if (param[i] == '|') {
+ nr_list++;
+ }
+ }
+ ret = h2o_mem_alloc(sizeof(*ret) * (nr_list + 1));
+ cur = 0;
+ start = 0;
+ for (i = 0; i < len; i++) {
+ if (param[i] == '|') {
+ ret[cur++] = h2o_strdup(NULL, ¶m[start], i - start);
+ start = i + 1;
+ }
+ }
+ if (start != i) {
+ ret[cur++] = h2o_strdup(NULL, ¶m[start], i - start);
+ }
+ ret[cur] = h2o_iovec_init(NULL, 0);
+
+ return ret;
+}
+
+static void free_status_list(h2o_iovec_t *status_list)
+{
+ int i;
+ if (!status_list)
+ return;
+ for (i = 0; status_list[i].base; i++) {
+ free(status_list[i].base);
+ }
+ free(status_list);
+}
+
static int on_req(h2o_handler_t *_self, h2o_req_t *req)
{
struct st_h2o_status_handler_t *self = (void *)_self;
@@ -252,8 +294,17 @@ static int on_req(h2o_handler_t *_self, h2o_req_t *req)
h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CACHE_CONTROL, H2O_STRLIT("no-cache"));
return h2o_file_send(req, 200, "OK", fn.base, h2o_iovec_init(H2O_STRLIT("text/html; charset=utf-8")), 0);
} else if (h2o_memis(local_path.base, local_path.len, H2O_STRLIT("/json"))) {
+ int ret;
/* "/json" maps to the JSON API */
- return on_req_json(self, req);
+ h2o_iovec_t *status_list = NULL; /* NULL means all */
+ if (req->query_at != SIZE_MAX && (req->path.len - req->query_at > 6)) {
+ if (h2o_memis(&req->path.base[req->query_at], 6, "?show=", 6)) {
+ status_list = build_status_list(&req->path.base[req->query_at + 6], req->path.len - req->query_at - 6);
+ }
+ }
+ ret = on_req_json(self, req, status_list);
+ free_status_list(status_list);
+ return ret;
}
return -1;
@@ -291,10 +342,12 @@ static void on_context_dispose(h2o_handler_t *_self, h2o_context_t *ctx)
free(status_ctx);
}
-void h2o_status_register(h2o_pathconf_t *pathconf)
+void h2o_status_register(h2o_pathconf_t *conf)
{
- struct st_h2o_status_handler_t *self = (void *)h2o_create_handler(pathconf, sizeof(*self));
+ struct st_h2o_status_handler_t *self = (void *)h2o_create_handler(conf, sizeof(*self));
self->super.on_context_init = on_context_init;
self->super.on_context_dispose = on_context_dispose;
self->super.on_req = on_req;
+ h2o_config_register_status_handler(conf->global, requests_status_handler);
+ h2o_config_register_status_handler(conf->global, errors_status_handler);
}
diff --git a/lib/handler/status/errors.c b/lib/handler/status/errors.c
new file mode 100644
index 0000000000..92afa3fa25
--- /dev/null
+++ b/lib/handler/status/errors.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016 Fastly
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "h2o.h"
+
+struct errors_status_ctx {
+ h2o_mem_pool_t *pool;
+ unsigned long long agg_errors[EMITTED_ERRORS_MAX];
+};
+
+static void errors_status_per_thread(void *priv, h2o_context_t *ctx)
+{
+ size_t i;
+ struct errors_status_ctx *esc = priv;
+ for (i = 0; i < EMITTED_ERRORS_MAX; i++) {
+ esc->agg_errors[i] += ctx->emitted_errors.emitted_errors_cnt[i];
+ }
+}
+
+static void *errors_status_alloc_context(h2o_req_t *req)
+{
+ struct errors_status_ctx *ret;
+
+ ret = h2o_mem_alloc_pool(&req->pool, sizeof(*ret));
+ memset(ret, 0, sizeof(*ret));
+ ret->pool = &req->pool;
+
+ return ret;
+}
+
+static h2o_iovec_t errors_status_assemble(void *priv)
+{
+ struct errors_status_ctx *esc = priv;
+ h2o_iovec_t ret;
+
+#define BUFSIZE (2*1024)
+ ret.base = h2o_mem_alloc_pool(esc->pool, BUFSIZE);
+ ret.len = snprintf(ret.base, BUFSIZE, ",\n"
+ " \"http1-errors-400\": %llu,\n"
+ " \"http1-errors-403\": %llu,\n"
+ " \"http1-errors-404\": %llu,\n"
+ " \"http1-errors-405\": %llu,\n"
+ " \"http1-errors-416\": %llu,\n"
+ " \"http1-errors-417\": %llu,\n"
+ " \"http1-errors-500\": %llu,\n"
+ " \"http1-errors-502\": %llu,\n"
+ " \"http1-errors-503\": %llu,\n"
+ " \"http1-errors-4xx-others\": %llu,\n"
+ " \"http1-errors-5xx-others\": %llu,\n"
+ " \"http1-errors-others\": %llu,\n"
+ " \"http2-errors-protocol\": %llu, \n"
+ " \"http2-errors-internal\": %llu, \n"
+ " \"http2-errors-flow_control\": %llu, \n"
+ " \"http2-errors-settings_timeout\": %llu, \n"
+ " \"http2-errors-stream_closed\": %llu, \n"
+ " \"http2-errors-frame_size\": %llu, \n"
+ " \"http2-errors-refused_stream\": %llu, \n"
+ " \"http2-errors-cancel\": %llu, \n"
+ " \"http2-errors-compression\": %llu, \n"
+ " \"http2-errors-connect\": %llu, \n"
+ " \"http2-errors-enhance_your_calm\": %llu, \n"
+ " \"http2-errors-inadequate_security\": %llu, \n"
+ " \"http2-errors-other\": %llu",
+ esc->agg_errors[E_HTTP_400], esc->agg_errors[E_HTTP_403], esc->agg_errors[E_HTTP_404],
+ esc->agg_errors[E_HTTP_405], esc->agg_errors[E_HTTP_416], esc->agg_errors[E_HTTP_417],
+ esc->agg_errors[E_HTTP_500], esc->agg_errors[E_HTTP_502], esc->agg_errors[E_HTTP_503],
+ esc->agg_errors[E_HTTP_4XX], esc->agg_errors[E_HTTP_5XX], esc->agg_errors[E_HTTP_XXX],
+ esc->agg_errors[E_HTTP2_PROTOCOL], esc->agg_errors[E_HTTP2_INTERNAL], esc->agg_errors[E_HTTP2_FLOW_CONTROL],
+ esc->agg_errors[E_HTTP2_SETTINGS_TIMEOUT], esc->agg_errors[E_HTTP2_STREAM_CLOSED], esc->agg_errors[E_HTTP2_FRAME_SIZE],
+ esc->agg_errors[E_HTTP2_REFUSED_STREAM], esc->agg_errors[E_HTTP2_CANCEL], esc->agg_errors[E_HTTP2_COMPRESSION],
+ esc->agg_errors[E_HTTP2_CONNECT], esc->agg_errors[E_HTTP2_ENHANCE_YOUR_CALM], esc->agg_errors[E_HTTP2_INADEQUATE_SECURITY],
+ esc->agg_errors[E_HTTP2_OTHER]);
+ return ret;
+#undef BUFSIZE
+}
+
+static void errors_status_done(void *priv)
+{
+ struct errors_status_ctx *esc = priv;
+ if (!esc->pool) {
+ free(esc);
+ }
+}
+
+h2o_status_handler_t errors_status_handler = {
+ .type = H2O_STATUS_HANDLER_PER_THREAD,
+ .name = { H2O_STRLIT("errors") },
+ .per_thread = {
+ .alloc_context_cb = errors_status_alloc_context,
+ .per_thread_cb = errors_status_per_thread,
+ .assemble_cb = errors_status_assemble,
+ .done_cb = errors_status_done,
+ }
+};
diff --git a/lib/handler/status/requests.c b/lib/handler/status/requests.c
new file mode 100644
index 0000000000..697cad338e
--- /dev/null
+++ b/lib/handler/status/requests.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "h2o.h"
+
+struct requests_status_ctx {
+ h2o_logconf_t *logconf;
+ h2o_mem_pool_t *pool;
+ h2o_iovec_t req_data;
+ int first; /* used to know if a thread is the first one, we skip the leading
+ * coma in that case. */
+};
+
+struct st_collect_req_status_cbdata_t {
+ h2o_logconf_t *logconf;
+ h2o_buffer_t *buffer;
+ int first; /* see requests_status_ctx */
+};
+
+static int collect_req_status(h2o_req_t *req, void *_cbdata)
+{
+ struct st_collect_req_status_cbdata_t *cbdata = _cbdata;
+
+ /* collect log */
+ char buf[4096];
+ size_t len = sizeof(buf);
+ char *logline = h2o_log_request(cbdata->logconf, req, &len, buf);
+
+ /* append to buffer */
+ assert(len != 0);
+ --len; /* omit trailing LF */
+ if (cbdata->first) {
+ h2o_buffer_reserve(&cbdata->buffer, len + 3);
+ memcpy(cbdata->buffer->bytes + cbdata->buffer->size, "\n ", 3);
+ cbdata->buffer->size += 3;
+ } else {
+ h2o_buffer_reserve(&cbdata->buffer, len + 4);
+ memcpy(cbdata->buffer->bytes + cbdata->buffer->size, ",\n ", 4);
+ cbdata->buffer->size += 4;
+ }
+ memcpy(cbdata->buffer->bytes + cbdata->buffer->size, logline, len);
+ cbdata->buffer->size += len;
+
+ if (logline != buf)
+ free(logline);
+
+ return 0;
+}
+
+static void requests_status_per_thread(void *priv, h2o_context_t *ctx)
+{
+ struct requests_status_ctx *rsc = priv;
+ struct st_collect_req_status_cbdata_t cbdata = {rsc->logconf};
+
+ cbdata.first = rsc->first;
+ if (rsc->first) {
+ rsc->first = 0;
+ }
+ h2o_buffer_init(&cbdata.buffer, &h2o_socket_buffer_prototype);
+ ctx->globalconf->http1.callbacks.foreach_request(ctx, collect_req_status, &cbdata);
+ ctx->globalconf->http2.callbacks.foreach_request(ctx, collect_req_status, &cbdata);
+
+ if (cbdata.buffer->size != 0) {
+ rsc->req_data.base = h2o_mem_realloc(rsc->req_data.base, rsc->req_data.len + cbdata.buffer->size);
+ memcpy(rsc->req_data.base + rsc->req_data.len, cbdata.buffer->bytes, cbdata.buffer->size);
+ rsc->req_data.len += cbdata.buffer->size;
+ }
+
+ h2o_buffer_dispose(&cbdata.buffer);
+}
+
+static void *requests_status_alloc_context(h2o_req_t *req)
+{
+ struct requests_status_ctx *rsc;
+ rsc = h2o_mem_alloc_pool(&req->pool, sizeof(*rsc));
+ rsc->pool = &req->pool;
+ rsc->first = 1;
+
+#define ELEMENT(key, expr) "\"" key "\": \"" expr "\""
+#define X_ELEMENT(id) ELEMENT(id, "%{" id "}x")
+#define SEPARATOR ", "
+ const char *fmt = "{"
+ /* combined_log */
+ ELEMENT("host", "%h") SEPARATOR ELEMENT("user", "%u") SEPARATOR ELEMENT("at", "%{%Y%m%dT%H%M%S}t.%{usec_frac}t%{%z}t")
+ SEPARATOR ELEMENT("method", "%m") SEPARATOR ELEMENT("path", "%U") SEPARATOR ELEMENT("query", "%q")
+ SEPARATOR ELEMENT("protocol", "%H") SEPARATOR ELEMENT("referer", "%{Referer}i")
+ SEPARATOR ELEMENT("user-agent", "%{User-agent}i") SEPARATOR
+ /* time */
+ X_ELEMENT("connect-time") SEPARATOR X_ELEMENT("request-header-time") SEPARATOR X_ELEMENT("request-body-time")
+ SEPARATOR X_ELEMENT("request-total-time") SEPARATOR X_ELEMENT("process-time") SEPARATOR X_ELEMENT("response-time")
+ SEPARATOR
+ /* connection */
+ X_ELEMENT("connection-id") SEPARATOR X_ELEMENT("ssl.protocol-version") SEPARATOR X_ELEMENT("ssl.session-reused")
+ SEPARATOR X_ELEMENT("ssl.cipher") SEPARATOR X_ELEMENT("ssl.cipher-bits") SEPARATOR
+ /* http1 */
+ X_ELEMENT("http1.request-index") SEPARATOR
+ /* http2 */
+ X_ELEMENT("http2.stream-id") SEPARATOR X_ELEMENT("http2.priority.received.exclusive")
+ SEPARATOR X_ELEMENT("http2.priority.received.parent") SEPARATOR X_ELEMENT("http2.priority.received.weight")
+ SEPARATOR X_ELEMENT("http2.priority.actual.parent") SEPARATOR X_ELEMENT("http2.priority.actual.weight") SEPARATOR
+ /* misc */
+ ELEMENT("authority", "%V")
+ /* end */
+ "}";
+#undef ELEMENT
+#undef X_ELEMENT
+#undef SEPARATOR
+
+ { /* compile logconf */
+ char errbuf[256];
+ if ((rsc->logconf = h2o_logconf_compile(fmt, H2O_LOGCONF_ESCAPE_JSON, errbuf)) == NULL) {
+ h2o_iovec_t resp = h2o_concat(&req->pool, h2o_iovec_init(H2O_STRLIT("failed to compile log format:")),
+ h2o_iovec_init(errbuf, strlen(errbuf)));
+ h2o_send_error(req, 400, "Invalid Request", resp.base, 0);
+ return NULL;
+ }
+ }
+
+ rsc->req_data = h2o_strdup(NULL, ",\n \"requests\": [", SIZE_MAX);
+ return rsc;
+}
+
+static h2o_iovec_t requests_status_assemble(void *priv)
+{
+ struct requests_status_ctx *rsc = priv;
+
+#define JSON_FOOTER "\n ]"
+#define JSON_FOOTER_LEN 3
+ rsc->req_data.base = h2o_mem_realloc(rsc->req_data.base, rsc->req_data.len + JSON_FOOTER_LEN);
+ memcpy(rsc->req_data.base + rsc->req_data.len, JSON_FOOTER, JSON_FOOTER_LEN);
+ rsc->req_data.len += JSON_FOOTER_LEN;
+#undef JSON_FOOTER
+#undef JSON_FOOTER_LEN
+
+ return rsc->req_data;
+}
+
+static void requests_status_done(void *priv)
+{
+ struct requests_status_ctx *rsc = priv;
+
+ h2o_logconf_dispose(rsc->logconf);
+ free(rsc->req_data.base);
+
+ if (!rsc->pool) {
+ free(rsc);
+ }
+}
+h2o_status_handler_t requests_status_handler = {
+ .type = H2O_STATUS_HANDLER_PER_THREAD,
+ .name = { H2O_STRLIT("requests") },
+ .per_thread = {
+ .alloc_context_cb = requests_status_alloc_context,
+ .per_thread_cb = requests_status_per_thread,
+ .assemble_cb = requests_status_assemble,
+ .done_cb = requests_status_done,
+ }
+};
diff --git a/lib/http2/connection.c b/lib/http2/connection.c
index 2f891740af..9a3b86a592 100644
--- a/lib/http2/connection.c
+++ b/lib/http2/connection.c
@@ -281,6 +281,8 @@ void send_stream_error(h2o_http2_conn_t *conn, uint32_t stream_id, int errnum)
assert(stream_id != 0);
assert(conn->state < H2O_HTTP2_CONN_STATE_IS_CLOSING);
+ h2o_context_report_emitted_error(conn->super.ctx, errnum, 2);
+
h2o_http2_encode_rst_stream_frame(&conn->_write.buf, stream_id, -errnum);
h2o_http2_conn_request_write(conn);
}
@@ -859,6 +861,7 @@ static void on_read(h2o_socket_t *sock, const char *err)
h2o_http2_conn_t *conn = sock->data;
if (err != NULL) {
+ h2o_context_report_emitted_error(conn->super.ctx, 0, 2);
h2o_socket_read_stop(conn->sock);
close_connection(conn);
return;
@@ -929,6 +932,7 @@ static void on_write_complete(h2o_socket_t *sock, const char *err)
/* close by error if necessary */
if (err != NULL) {
+ h2o_context_report_emitted_error(conn->super.ctx, 0, 2);
close_connection_now(conn);
return;
}
diff --git a/src/main.c b/src/main.c
index 8f3d9e5fc6..a40576403e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1511,7 +1511,7 @@ static void setup_configurators(void)
h2o_mruby_register_configurator(&conf.globalconf);
#endif
- conf.globalconf.status.extra_status = on_extra_status;
+ h2o_config_register_simple_status_handler(&conf.globalconf, (h2o_iovec_t){H2O_STRLIT("main")}, on_extra_status);
}
int main(int argc, char **argv)
diff --git a/srcdoc/configure/status_directives.mt b/srcdoc/configure/status_directives.mt
index b8f7a81f9a..68fb499afb 100644
--- a/srcdoc/configure/status_directives.mt
+++ b/srcdoc/configure/status_directives.mt
@@ -19,6 +19,9 @@ EOT
Access to the handler should be restricted, considering the fact that the status includes the details of in-flight HTTP requests.
The example below uses Basic authentication.
+Displaying in-flight HTTP requests in the json query
+(/path-to-handler/json
) can optionally be disabled by
+passing ?noreqs
as a parameter to the url.
= $ctx->{example}->("Exposing status with Basic authentication", <<'EOT');
paths:
diff --git a/t/50status.t b/t/50status.t
new file mode 100644
index 0000000000..65ff190919
--- /dev/null
+++ b/t/50status.t
@@ -0,0 +1,47 @@
+use strict;
+use warnings;
+use Test::More;
+use t::Util;
+use JSON;
+
+plan skip_all => 'curl not found'
+ unless prog_exists('curl');
+
+subtest "default json handler" => sub {
+ my $server = spawn_h2o(<< "EOT");
+hosts:
+ default:
+ paths:
+ /:
+ file.dir: @{[ DOC_ROOT ]}
+ /s:
+ status: ON
+EOT
+
+ my $resp = `curl --silent -o /dev/stderr http://127.0.0.1:$server->{port}/s/json 2>&1 > /dev/null`;
+ my $jresp = decode_json("$resp");
+ my @requests = @{$jresp->{'requests'}};
+ is $jresp->{'connections'}, 1, "One connection";
+ is scalar @requests, 1, "One request";
+};
+
+subtest "json hander noreqs" => sub {
+ my $server = spawn_h2o(<< "EOT");
+hosts:
+ default:
+ paths:
+ /:
+ file.dir: @{[ DOC_ROOT ]}
+ /s:
+ status: ON
+EOT
+
+ my $resp = `curl --silent -o /dev/stderr 'http://127.0.0.1:$server->{port}/s/json?show=main|errors' 2>&1 > /dev/null`;
+ my $jresp = decode_json("$resp");
+ is $jresp->{'connections'}, 1, "One connection";
+ is $jresp->{'requests'}, undef, "Zero request";
+ is $jresp->{'http1-errors-404'}, 0, "Internal errors monitoring";
+};
+
+
+done_testing();
From 99b9acf2609784ff8b4c569736ac90f36f1d4872 Mon Sep 17 00:00:00 2001
From: Frederik Deweerdt
Date: Mon, 16 May 2016 09:55:56 -0700
Subject: [PATCH 2/8] - Simplify `h2o_status_handler_t` by removing the type,
and making the `init` and `per_thread` callbacks optional. Only `final`
will be mandatory. - Remove GNUisms - Fix the doc to reflect the current
implementation.
---
include/h2o.h | 25 ++++----------
lib/core/config.c | 6 ++--
lib/handler/status.c | 42 ++++++++++--------------
lib/handler/status/errors.c | 31 +++++-------------
lib/handler/status/requests.c | 47 ++++++++++-----------------
src/main.c | 4 +--
srcdoc/configure/status_directives.mt | 14 +++++---
7 files changed, 66 insertions(+), 103 deletions(-)
diff --git a/include/h2o.h b/include/h2o.h
index 0c5cbe067d..54d5ad1eee 100644
--- a/include/h2o.h
+++ b/include/h2o.h
@@ -275,25 +275,12 @@ typedef struct st_h2o_protocol_callbacks_t {
int (*foreach_request)(h2o_context_t *ctx, int (*cb)(h2o_req_t *req, void *cbdata), void *cbdata);
} h2o_protocol_callbacks_t;
-enum h2o_status_handler_type {
- H2O_STATUS_HANDLER_SIMPLE,
- H2O_STATUS_HANDLER_PER_THREAD,
-};
-typedef h2o_iovec_t (*simple_status_handler_cb)(h2o_globalconf_t *gconf, h2o_mem_pool_t *pool);
-typedef struct h2o_status_handler {
- enum h2o_status_handler_type type;
+typedef h2o_iovec_t (*final_status_handler_cb)(void *ctx, h2o_globalconf_t *gconf, h2o_req_t *req);
+typedef struct st_h2o_status_handler_t {
h2o_iovec_t name;
- union {
- struct {
- simple_status_handler_cb status_cb;
- } simple;
- struct {
- void *(*alloc_context_cb)(h2o_req_t *src_req);
- void (*per_thread_cb)(void *priv, h2o_context_t *ctx);
- h2o_iovec_t (*assemble_cb)(void *ctx);
- void (*done_cb)(void *ctx);
- } per_thread;
- };
+ void *(*init)(h2o_iovec_t *error); /* optional call back, allocates a context that will be passed to per_thread() */
+ void (*per_thread)(void *priv, h2o_context_t *ctx); /* optional callback, will be called for each thread */
+ h2o_iovec_t (*final)(void *ctx, h2o_globalconf_t *gconf, h2o_req_t *req); /* mandatory, will be passed the optional context */
} h2o_status_handler_t;
typedef H2O_VECTOR(h2o_status_handler_t) h2o_status_callbacks_t;
@@ -1207,7 +1194,7 @@ h2o_pathconf_t *h2o_config_register_path(h2o_hostconf_t *hostconf, const char *p
* registers an extra status handler
*/
void h2o_config_register_status_handler(h2o_globalconf_t *config, h2o_status_handler_t);
-void h2o_config_register_simple_status_handler(h2o_globalconf_t *config, h2o_iovec_t name, simple_status_handler_cb status_handler);
+void h2o_config_register_simple_status_handler(h2o_globalconf_t *config, h2o_iovec_t name, final_status_handler_cb status_handler);
/**
* disposes of the resources allocated for the global configuration
*/
diff --git a/lib/core/config.c b/lib/core/config.c
index 6dab5e95f0..474958b653 100644
--- a/lib/core/config.c
+++ b/lib/core/config.c
@@ -204,15 +204,15 @@ void h2o_config_register_status_handler(h2o_globalconf_t *config, h2o_status_han
config->statuses.entries[config->statuses.size++] = status_handler;
}
-void h2o_config_register_simple_status_handler(h2o_globalconf_t *config, h2o_iovec_t name, simple_status_handler_cb status_handler)
+void h2o_config_register_simple_status_handler(h2o_globalconf_t *config, h2o_iovec_t name, final_status_handler_cb status_handler)
{
h2o_status_handler_t *sh;
h2o_vector_reserve(NULL, &config->statuses, config->statuses.size + 1);
sh = &config->statuses.entries[config->statuses.size++];
- sh->type = H2O_STATUS_HANDLER_SIMPLE;
+ memset(sh, 0, sizeof(*sh));
sh->name = h2o_strdup(NULL, name.base, name.len);
- sh->simple.status_cb = status_handler;
+ sh->final = status_handler;
}
h2o_hostconf_t *h2o_config_register_host(h2o_globalconf_t *config, h2o_iovec_t host, uint16_t port)
diff --git a/lib/handler/status.c b/lib/handler/status.c
index f00e8d0280..3fe9035705 100644
--- a/lib/handler/status.c
+++ b/lib/handler/status.c
@@ -28,7 +28,7 @@ struct st_h2o_status_logger_t {
h2o_logger_t super;
};
-struct st_h2o_status_handler_t {
+struct st_h2o_root_status_handler_t {
h2o_handler_t super;
H2O_VECTOR(h2o_multithread_receiver_t *) receivers;
};
@@ -71,8 +71,8 @@ static void collect_reqs_of_context(struct st_h2o_status_collector_t *collector,
if (!collector->status_ctx.entries[i].active) {
continue;
}
- if (sh->type == H2O_STATUS_HANDLER_PER_THREAD) {
- sh->per_thread.per_thread_cb(collector->status_ctx.entries[i].ctx, ctx);
+ if (sh->per_thread) {
+ sh->per_thread(collector->status_ctx.entries[i].ctx, ctx);
}
}
@@ -115,11 +115,7 @@ static void send_response(struct st_h2o_status_collector_t *collector)
if (!collector->status_ctx.entries[i].active) {
continue;
}
- if (sh->type == H2O_STATUS_HANDLER_SIMPLE) {
- resp[cur_resp++] = sh->simple.status_cb(req->conn->ctx->globalconf, &req->pool);
- } else {
- resp[cur_resp++] = sh->per_thread.assemble_cb(collector->status_ctx.entries[i].ctx);
- }
+ resp[cur_resp++] = sh->final(collector->status_ctx.entries[i].ctx, req->conn->ctx->globalconf, req);
if (resp[cur_resp - 1].len > 0 && !coma_removed) {
/* requests come in with a leading coma, replace if with a space */
resp[cur_resp - 1].base[0] = ' ';
@@ -135,15 +131,6 @@ static void send_response(struct st_h2o_status_collector_t *collector)
h2o_send(req, resp,
h2o_memis(req->input.method.base, req->input.method.len, H2O_STRLIT("HEAD")) ? 0 : nr_resp,
1);
- for (i = 0; i < req->conn->ctx->globalconf->statuses.size; i++) {
- h2o_status_handler_t *sh = &req->conn->ctx->globalconf->statuses.entries[i];
- if (!collector->status_ctx.entries[i].active) {
- continue;
- }
- if (sh->type == H2O_STATUS_HANDLER_PER_THREAD) {
- sh->per_thread.done_cb(collector->status_ctx.entries[i].ctx);
- }
- }
h2o_mem_release_shared(collector);
}
@@ -186,7 +173,7 @@ static void on_req_close(void *p)
h2o_mem_release_shared(collector);
}
-static int on_req_json(struct st_h2o_status_handler_t *self, h2o_req_t *req, h2o_iovec_t *status_list)
+static int on_req_json(struct st_h2o_root_status_handler_t *self, h2o_req_t *req, h2o_iovec_t *status_list)
{
{ /* construct collector and send request to every thread */
struct st_h2o_status_context_t *status_ctx = h2o_context_get_handler_context(req->conn->ctx, &self->super);
@@ -213,8 +200,15 @@ static int on_req_json(struct st_h2o_status_handler_t *self, h2o_req_t *req, h2o
goto Skip;
}
}
- if (sh->type == H2O_STATUS_HANDLER_PER_THREAD && sh->per_thread.alloc_context_cb) {
- collector->status_ctx.entries[collector->status_ctx.size].ctx = sh->per_thread.alloc_context_cb(req);
+ if (sh->init) {
+ h2o_iovec_t err = {NULL, 0};
+ void *p;
+ p = sh->init(&err);
+ if (!p) {
+ h2o_send_error(req, 400, "Invalid Request", err.base, 0);
+ return 0;
+ }
+ collector->status_ctx.entries[collector->status_ctx.size].ctx = p;
}
collector->status_ctx.entries[collector->status_ctx.size].active = 1;
Skip:
@@ -280,7 +274,7 @@ static void free_status_list(h2o_iovec_t *status_list)
static int on_req(h2o_handler_t *_self, h2o_req_t *req)
{
- struct st_h2o_status_handler_t *self = (void *)_self;
+ struct st_h2o_root_status_handler_t *self = (void *)_self;
size_t prefix_len = req->pathconf->path.len - (req->pathconf->path.base[req->pathconf->path.len - 1] == '/');
h2o_iovec_t local_path = h2o_iovec_init(req->path_normalized.base + prefix_len, req->path_normalized.len - prefix_len);
@@ -312,7 +306,7 @@ static int on_req(h2o_handler_t *_self, h2o_req_t *req)
static void on_context_init(h2o_handler_t *_self, h2o_context_t *ctx)
{
- struct st_h2o_status_handler_t *self = (void *)_self;
+ struct st_h2o_root_status_handler_t *self = (void *)_self;
struct st_h2o_status_context_t *status_ctx = h2o_mem_alloc(sizeof(*status_ctx));
status_ctx->ctx = ctx;
@@ -326,7 +320,7 @@ static void on_context_init(h2o_handler_t *_self, h2o_context_t *ctx)
static void on_context_dispose(h2o_handler_t *_self, h2o_context_t *ctx)
{
- struct st_h2o_status_handler_t *self = (void *)_self;
+ struct st_h2o_root_status_handler_t *self = (void *)_self;
struct st_h2o_status_context_t *status_ctx = h2o_context_get_handler_context(ctx, &self->super);
size_t i;
@@ -344,7 +338,7 @@ static void on_context_dispose(h2o_handler_t *_self, h2o_context_t *ctx)
void h2o_status_register(h2o_pathconf_t *conf)
{
- struct st_h2o_status_handler_t *self = (void *)h2o_create_handler(conf, sizeof(*self));
+ struct st_h2o_root_status_handler_t *self = (void *)h2o_create_handler(conf, sizeof(*self));
self->super.on_context_init = on_context_init;
self->super.on_context_dispose = on_context_dispose;
self->super.on_req = on_req;
diff --git a/lib/handler/status/errors.c b/lib/handler/status/errors.c
index 92afa3fa25..4f5365d14b 100644
--- a/lib/handler/status/errors.c
+++ b/lib/handler/status/errors.c
@@ -23,7 +23,6 @@
#include "h2o.h"
struct errors_status_ctx {
- h2o_mem_pool_t *pool;
unsigned long long agg_errors[EMITTED_ERRORS_MAX];
};
@@ -36,24 +35,23 @@ static void errors_status_per_thread(void *priv, h2o_context_t *ctx)
}
}
-static void *errors_status_alloc_context(h2o_req_t *req)
+static void *errors_status_init(h2o_iovec_t *err)
{
struct errors_status_ctx *ret;
- ret = h2o_mem_alloc_pool(&req->pool, sizeof(*ret));
+ ret = h2o_mem_alloc(sizeof(*ret));
memset(ret, 0, sizeof(*ret));
- ret->pool = &req->pool;
return ret;
}
-static h2o_iovec_t errors_status_assemble(void *priv)
+static h2o_iovec_t errors_status_final(void *priv, h2o_globalconf_t *gconf, h2o_req_t *req)
{
struct errors_status_ctx *esc = priv;
h2o_iovec_t ret;
#define BUFSIZE (2*1024)
- ret.base = h2o_mem_alloc_pool(esc->pool, BUFSIZE);
+ ret.base = h2o_mem_alloc_pool(&req->pool, BUFSIZE);
ret.len = snprintf(ret.base, BUFSIZE, ",\n"
" \"http1-errors-400\": %llu,\n"
" \"http1-errors-403\": %llu,\n"
@@ -89,25 +87,14 @@ static h2o_iovec_t errors_status_assemble(void *priv)
esc->agg_errors[E_HTTP2_REFUSED_STREAM], esc->agg_errors[E_HTTP2_CANCEL], esc->agg_errors[E_HTTP2_COMPRESSION],
esc->agg_errors[E_HTTP2_CONNECT], esc->agg_errors[E_HTTP2_ENHANCE_YOUR_CALM], esc->agg_errors[E_HTTP2_INADEQUATE_SECURITY],
esc->agg_errors[E_HTTP2_OTHER]);
+ free(esc);
return ret;
#undef BUFSIZE
}
-static void errors_status_done(void *priv)
-{
- struct errors_status_ctx *esc = priv;
- if (!esc->pool) {
- free(esc);
- }
-}
-
h2o_status_handler_t errors_status_handler = {
- .type = H2O_STATUS_HANDLER_PER_THREAD,
- .name = { H2O_STRLIT("errors") },
- .per_thread = {
- .alloc_context_cb = errors_status_alloc_context,
- .per_thread_cb = errors_status_per_thread,
- .assemble_cb = errors_status_assemble,
- .done_cb = errors_status_done,
- }
+ { H2O_STRLIT("errors") },
+ errors_status_init,
+ errors_status_per_thread,
+ errors_status_final,
};
diff --git a/lib/handler/status/requests.c b/lib/handler/status/requests.c
index 697cad338e..c67e89b1b5 100644
--- a/lib/handler/status/requests.c
+++ b/lib/handler/status/requests.c
@@ -24,7 +24,6 @@
struct requests_status_ctx {
h2o_logconf_t *logconf;
- h2o_mem_pool_t *pool;
h2o_iovec_t req_data;
int first; /* used to know if a thread is the first one, we skip the leading
* coma in that case. */
@@ -88,11 +87,10 @@ static void requests_status_per_thread(void *priv, h2o_context_t *ctx)
h2o_buffer_dispose(&cbdata.buffer);
}
-static void *requests_status_alloc_context(h2o_req_t *req)
+static void *requests_status_init(h2o_iovec_t *error)
{
struct requests_status_ctx *rsc;
- rsc = h2o_mem_alloc_pool(&req->pool, sizeof(*rsc));
- rsc->pool = &req->pool;
+ rsc = h2o_mem_alloc(sizeof(*rsc));
rsc->first = 1;
#define ELEMENT(key, expr) "\"" key "\": \"" expr "\""
@@ -128,9 +126,10 @@ static void *requests_status_alloc_context(h2o_req_t *req)
{ /* compile logconf */
char errbuf[256];
if ((rsc->logconf = h2o_logconf_compile(fmt, H2O_LOGCONF_ESCAPE_JSON, errbuf)) == NULL) {
- h2o_iovec_t resp = h2o_concat(&req->pool, h2o_iovec_init(H2O_STRLIT("failed to compile log format:")),
+ h2o_iovec_t resp = h2o_concat(NULL, h2o_iovec_init(H2O_STRLIT("failed to compile log format:")),
h2o_iovec_init(errbuf, strlen(errbuf)));
- h2o_send_error(req, 400, "Invalid Request", resp.base, 0);
+ *error = resp;
+ free(rsc);
return NULL;
}
}
@@ -139,39 +138,29 @@ static void *requests_status_alloc_context(h2o_req_t *req)
return rsc;
}
-static h2o_iovec_t requests_status_assemble(void *priv)
+static h2o_iovec_t requests_status_final(void *priv, h2o_globalconf_t *gconf, h2o_req_t *req)
{
+ h2o_iovec_t ret;
struct requests_status_ctx *rsc = priv;
#define JSON_FOOTER "\n ]"
#define JSON_FOOTER_LEN 3
- rsc->req_data.base = h2o_mem_realloc(rsc->req_data.base, rsc->req_data.len + JSON_FOOTER_LEN);
- memcpy(rsc->req_data.base + rsc->req_data.len, JSON_FOOTER, JSON_FOOTER_LEN);
- rsc->req_data.len += JSON_FOOTER_LEN;
+ ret.base = h2o_mem_alloc_pool(&req->pool, rsc->req_data.len + JSON_FOOTER_LEN);
+ memcpy(ret.base, rsc->req_data.base, rsc->req_data.len);
+ memcpy(ret.base + rsc->req_data.len, JSON_FOOTER, JSON_FOOTER_LEN);
+ ret.len = rsc->req_data.len + JSON_FOOTER_LEN;
#undef JSON_FOOTER
#undef JSON_FOOTER_LEN
- return rsc->req_data;
-}
-
-static void requests_status_done(void *priv)
-{
- struct requests_status_ctx *rsc = priv;
-
h2o_logconf_dispose(rsc->logconf);
free(rsc->req_data.base);
-
- if (!rsc->pool) {
- free(rsc);
- }
+ free(rsc);
+ return ret;
}
+
h2o_status_handler_t requests_status_handler = {
- .type = H2O_STATUS_HANDLER_PER_THREAD,
- .name = { H2O_STRLIT("requests") },
- .per_thread = {
- .alloc_context_cb = requests_status_alloc_context,
- .per_thread_cb = requests_status_per_thread,
- .assemble_cb = requests_status_assemble,
- .done_cb = requests_status_done,
- }
+ { H2O_STRLIT("requests") },
+ requests_status_init,
+ requests_status_per_thread,
+ requests_status_final,
};
diff --git a/src/main.c b/src/main.c
index a40576403e..5238f1c10c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1428,7 +1428,7 @@ static int run_using_server_starter(const char *h2o_cmd, const char *config_file
return EX_CONFIG;
}
-static h2o_iovec_t on_extra_status(h2o_globalconf_t *_conf, h2o_mem_pool_t *pool)
+static h2o_iovec_t on_extra_status(void *unused, h2o_globalconf_t *_conf, h2o_req_t *req)
{
#define BUFSIZE 1024
h2o_iovec_t ret;
@@ -1441,7 +1441,7 @@ static h2o_iovec_t on_extra_status(h2o_globalconf_t *_conf, h2o_mem_pool_t *pool
if ((generation = getenv("SERVER_STARTER_GENERATION")) == NULL)
generation = "null";
- ret.base = h2o_mem_alloc_pool(pool, BUFSIZE);
+ ret.base = h2o_mem_alloc_pool(&req->pool, BUFSIZE);
ret.len = snprintf(ret.base, BUFSIZE, ",\n"
" \"server-version\": \"" H2O_VERSION "\",\n"
" \"openssl-version\": \"%s\",\n"
diff --git a/srcdoc/configure/status_directives.mt b/srcdoc/configure/status_directives.mt
index 68fb499afb..6e56691190 100644
--- a/srcdoc/configure/status_directives.mt
+++ b/srcdoc/configure/status_directives.mt
@@ -18,10 +18,16 @@ EOT
?>
Access to the handler should be restricted, considering the fact that the status includes the details of in-flight HTTP requests.
-The example below uses Basic authentication.
-Displaying in-flight HTTP requests in the json query
-(/path-to-handler/json
) can optionally be disabled by
-passing ?noreqs
as a parameter to the url.
+The example below uses Basic authentication.
+The information returned by the /json
handler can be filtered out using
+the optional show=module1|module2
parameter.
+
+There are currently three modules defined:
+
+requests
: displays the requests currently in-flight.
+errors
: displays counters for internally generated errors.
+main
: displays general daemon-wide stats.
+
= $ctx->{example}->("Exposing status with Basic authentication", <<'EOT');
paths:
From 7ee8e64787f621e536c5fc450e36bb0d492600c9 Mon Sep 17 00:00:00 2001
From: Frederik Deweerdt
Date: Mon, 16 May 2016 13:30:57 -0700
Subject: [PATCH 3/8] - Split errors in http1/http2 - Use predefined functions
for the callsites where the status is known, avoiding one indirection level
---
include/h2o.h | 64 ++++++++++++++++++++++++-------------
lib/core/context.c | 37 +++------------------
lib/core/proxy.c | 8 ++---
lib/core/request.c | 13 ++++----
lib/handler/fastcgi.c | 2 +-
lib/handler/file.c | 14 ++++----
lib/handler/mruby.c | 4 +--
lib/handler/redirect.c | 2 +-
lib/handler/status.c | 2 +-
lib/handler/status/errors.c | 32 ++++++++++++-------
lib/http1.c | 4 +--
lib/http2/connection.c | 11 +++++--
12 files changed, 99 insertions(+), 94 deletions(-)
diff --git a/include/h2o.h b/include/h2o.h
index 54d5ad1eee..f5340dfe79 100644
--- a/include/h2o.h
+++ b/include/h2o.h
@@ -419,19 +419,6 @@ typedef struct st_h2o_mimemap_type_t {
/* errors emanating from h2o */
enum {
- /* http */
- E_HTTP_400 = 0,
- E_HTTP_403,
- E_HTTP_404,
- E_HTTP_405,
- E_HTTP_416,
- E_HTTP_417,
- E_HTTP_500,
- E_HTTP_502,
- E_HTTP_503,
- E_HTTP_4XX,
- E_HTTP_5XX,
- E_HTTP_XXX,
/* http2 */
E_HTTP2_PROTOCOL,
E_HTTP2_INTERNAL,
@@ -446,12 +433,24 @@ enum {
E_HTTP2_ENHANCE_YOUR_CALM,
E_HTTP2_INADEQUATE_SECURITY,
E_HTTP2_OTHER,
- EMITTED_ERRORS_MAX,
+ E_HTTP2_MAX,
+};
+enum {
+ /* http1 */
+ E_HTTP_400 = 0,
+ E_HTTP_403,
+ E_HTTP_404,
+ E_HTTP_405,
+ E_HTTP_416,
+ E_HTTP_417,
+ E_HTTP_500,
+ E_HTTP_502,
+ E_HTTP_503,
+ E_HTTP_4XX,
+ E_HTTP_5XX,
+ E_HTTP_XXX,
+ E_HTTP_MAX,
};
-
-typedef struct st_h2o_emitted_errors_cnt {
- unsigned long emitted_errors_cnt[EMITTED_ERRORS_MAX];
-} h2o_emitted_errors_t;
/**
* context of the http server.
@@ -521,6 +520,10 @@ struct st_h2o_context_t {
* timeout entry used for graceful shutdown
*/
h2o_timeout_entry_t _graceful_shutdown_timeout;
+ /**
+ * counter for http2 errors internally emitted by h2o
+ */
+ unsigned long emitted_errors[E_HTTP2_MAX];
} http2;
struct {
@@ -546,9 +549,9 @@ struct st_h2o_context_t {
} _timestamp_cache;
/**
- * counter for errors internally emitted by h2o
+ * counter for http1 errors internally emitted by h2o
*/
- h2o_emitted_errors_t emitted_errors;
+ unsigned long emitted_errors[E_HTTP_MAX];
H2O_VECTOR(h2o_pathconf_t *) _pathconfs_inited;
};
@@ -1266,7 +1269,7 @@ static void *h2o_context_get_logger_context(h2o_context_t *ctx, h2o_logger_t *lo
/**
* internally reports an error emitted by h2o, so that this can in turn be reported in statuses
*/
-void h2o_context_report_emitted_error(h2o_context_t *ctx, int status, int http);
+void h2o_context_report_http1_error(h2o_context_t *ctx, int status);
/* built-in generators */
enum {
@@ -1287,7 +1290,24 @@ void h2o_send_inline(h2o_req_t *req, const char *body, size_t len);
/**
* sends the given information as an error response to the client
*/
-void h2o_send_error(h2o_req_t *req, int status, const char *reason, const char *body, int flags);
+void h2o_send_error_generic(h2o_req_t *req, int status, const char *reason, const char *body, int flags);
+#define H2O_SEND_ERROR_XXX(status) \
+ static inline void h2o_send_error_ ## status(h2o_req_t *req, const char *reason, const char *body, int flags) \
+ { \
+ req->conn->ctx->emitted_errors[E_HTTP_ ## status]++; \
+ h2o_send_error_generic(req, status, reason, body, flags); \
+ }
+
+H2O_SEND_ERROR_XXX(400)
+H2O_SEND_ERROR_XXX(403)
+H2O_SEND_ERROR_XXX(404)
+H2O_SEND_ERROR_XXX(405)
+H2O_SEND_ERROR_XXX(416)
+H2O_SEND_ERROR_XXX(417)
+H2O_SEND_ERROR_XXX(500)
+H2O_SEND_ERROR_XXX(502)
+H2O_SEND_ERROR_XXX(503)
+
/**
* sends error response using zero timeout; can be called by output filters while processing the headers
*/
diff --git a/lib/core/context.c b/lib/core/context.c
index 797f60ea41..b3afc48c1e 100644
--- a/lib/core/context.c
+++ b/lib/core/context.c
@@ -184,19 +184,18 @@ void h2o_context_update_timestamp_cache(h2o_context_t *ctx)
}
}
-void h2o_context_report_emitted_error(h2o_context_t *ctx, int status, int http)
+void h2o_context_report_http1_error(h2o_context_t *ctx, int status)
{
int s100;
#define INC_PREDEF_ERROR(x) \
do { \
if (status == x) { \
- ctx->emitted_errors.emitted_errors_cnt[E_HTTP_ ## x]++; \
+ ctx->emitted_errors[E_HTTP_ ## x]++; \
return; \
} \
} while(0)
- if (http == 1) {
INC_PREDEF_ERROR(400);
INC_PREDEF_ERROR(403);
INC_PREDEF_ERROR(404);
@@ -212,39 +211,13 @@ void h2o_context_report_emitted_error(h2o_context_t *ctx, int status, int http)
s100 = status / 100;
switch(s100) {
case 4:
- ctx->emitted_errors.emitted_errors_cnt[E_HTTP_4XX]++;
+ ctx->emitted_errors[E_HTTP_4XX]++;
return;
case 5:
- ctx->emitted_errors.emitted_errors_cnt[E_HTTP_5XX]++;
+ ctx->emitted_errors[E_HTTP_5XX]++;
return;
default:
- ctx->emitted_errors.emitted_errors_cnt[E_HTTP_XXX]++;
+ ctx->emitted_errors[E_HTTP_XXX]++;
return;
}
- } else {
-#define INC_PREDEF_ERROR2(name, x) \
- do { \
- if (status == x) { \
- ctx->emitted_errors.emitted_errors_cnt[E_HTTP2_ ## name]++; \
- return; \
- } \
- } while(0)
-
- INC_PREDEF_ERROR2(PROTOCOL, -1);
- INC_PREDEF_ERROR2(INTERNAL, -2);
- INC_PREDEF_ERROR2(FLOW_CONTROL, -3);
- INC_PREDEF_ERROR2(SETTINGS_TIMEOUT, -4);
- INC_PREDEF_ERROR2(STREAM_CLOSED, -5);
- INC_PREDEF_ERROR2(FRAME_SIZE, -6);
- INC_PREDEF_ERROR2(REFUSED_STREAM, -7);
- INC_PREDEF_ERROR2(CANCEL, -8);
- INC_PREDEF_ERROR2(COMPRESSION, -9);
- INC_PREDEF_ERROR2(CONNECT, -10);
- INC_PREDEF_ERROR2(ENHANCE_YOUR_CALM, -11);
- INC_PREDEF_ERROR2(INADEQUATE_SECURITY, -12);
-
- ctx->emitted_errors.emitted_errors_cnt[E_HTTP2_OTHER]++;
- return;
-#undef INC_PREDEF_ERROR
- }
}
diff --git a/lib/core/proxy.c b/lib/core/proxy.c
index 040be5d588..72d096a853 100644
--- a/lib/core/proxy.c
+++ b/lib/core/proxy.c
@@ -336,7 +336,7 @@ static h2o_http1client_body_cb on_head(h2o_http1client_t *client, const char *er
if (errstr != NULL && errstr != h2o_http1client_error_is_eos) {
self->client = NULL;
h2o_req_log_error(req, "lib/core/proxy.c", "%s", errstr);
- h2o_send_error(req, 502, "Gateway Error", errstr, 0);
+ h2o_send_error_502(req, "Gateway Error", errstr, 0);
return NULL;
}
@@ -355,7 +355,7 @@ static h2o_http1client_body_cb on_head(h2o_http1client_t *client, const char *er
(req->res.content_length = h2o_strtosize(headers[i].value, headers[i].value_len)) == SIZE_MAX) {
self->client = NULL;
h2o_req_log_error(req, "lib/core/proxy.c", "%s", "invalid response from upstream (malformed content-length)");
- h2o_send_error(req, 502, "Gateway Error", "invalid response from upstream", 0);
+ h2o_send_error_502(req, "Gateway Error", "invalid response from upstream", 0);
return NULL;
}
goto Skip;
@@ -419,7 +419,7 @@ static h2o_http1client_head_cb on_connect(h2o_http1client_t *client, const char
if (errstr != NULL) {
self->client = NULL;
h2o_req_log_error(self->src_req, "lib/core/proxy.c", "%s", errstr);
- h2o_send_error(self->src_req, 502, "Gateway Error", errstr, 0);
+ h2o_send_error_502(self->src_req, "Gateway Error", errstr, 0);
return NULL;
}
@@ -488,7 +488,7 @@ void h2o__proxy_process_request(h2o_req_t *req)
h2o_req_log_error(req, "lib/core/proxy.c", "invalid URL supplied for internal redirection:%s://%.*s%.*s",
req->scheme->name.base, (int)req->authority.len, req->authority.base, (int)req->path.len,
req->path.base);
- h2o_send_error(req, 502, "Gateway Error", "internal error", 0);
+ h2o_send_error_502(req, "Gateway Error", "internal error", 0);
return;
}
if (port == 65535)
diff --git a/lib/core/request.c b/lib/core/request.c
index feb26271aa..d80d37843d 100644
--- a/lib/core/request.c
+++ b/lib/core/request.c
@@ -128,7 +128,7 @@ static void call_handlers(h2o_req_t *req, h2o_handler_t **handler)
if ((*handler)->on_req(*handler, req) == 0)
return;
- h2o_send_error(req, 404, "File Not Found", "not found", 0);
+ h2o_send_error_404(req, "File Not Found", "not found", 0);
}
static void process_hosted_request(h2o_req_t *req, h2o_hostconf_t *hostconf)
@@ -304,14 +304,14 @@ void h2o_reprocess_request(h2o_req_t *req, h2o_iovec_t method, const h2o_url_sch
if (req->res_is_delegated) {
if (req->num_delegated == req->conn->ctx->globalconf->max_delegations) {
/* TODO log */
- h2o_send_error(req, 502, "Gateway Error", "too many internal delegations", 0);
+ h2o_send_error_502(req, "Gateway Error", "too many internal delegations", 0);
return;
}
++req->num_delegated;
} else {
if (req->num_reprocessed >= 5) {
/* TODO log */
- h2o_send_error(req, 502, "Gateway Error", "too many internal reprocesses", 0);
+ h2o_send_error_502(req, "Gateway Error", "too many internal reprocesses", 0);
return;
}
++req->num_reprocessed;
@@ -456,10 +456,8 @@ void h2o_send_inline(h2o_req_t *req, const char *body, size_t len)
h2o_send(req, &buf, 1, 1);
}
-void h2o_send_error(h2o_req_t *req, int status, const char *reason, const char *body, int flags)
+void h2o_send_error_generic(h2o_req_t *req, int status, const char *reason, const char *body, int flags)
{
- h2o_context_report_emitted_error(req->conn->ctx, status, 1);
-
if (req->pathconf == NULL) {
h2o_hostconf_t *hostconf = setup_before_processing(req);
h2o_req_bind_conf(req, hostconf, &hostconf->fallback_path);
@@ -484,7 +482,8 @@ static void send_error_deferred_cb(h2o_timeout_entry_t *entry)
{
struct st_send_error_deferred_t *args = H2O_STRUCT_FROM_MEMBER(struct st_send_error_deferred_t, _timeout, entry);
reset_response(args->req);
- h2o_send_error(args->req, args->status, args->reason, args->body, args->flags);
+ h2o_context_report_http1_error(args->req->conn->ctx, args->status);
+ h2o_send_error_generic(args->req, args->status, args->reason, args->body, args->flags);
}
void h2o_send_error_deferred(h2o_req_t *req, int status, const char *reason, const char *body, int flags)
diff --git a/lib/handler/fastcgi.c b/lib/handler/fastcgi.c
index 56242ca8aa..87011c88c7 100644
--- a/lib/handler/fastcgi.c
+++ b/lib/handler/fastcgi.c
@@ -461,7 +461,7 @@ static void errorclose(struct st_fcgi_generator_t *generator)
} else {
h2o_req_t *req = generator->req;
close_generator(generator);
- h2o_send_error(req, 503, "Internal Server Error", "Internal Server Error", 0);
+ h2o_send_error_503(req, "Internal Server Error", "Internal Server Error", 0);
}
}
diff --git a/lib/handler/file.c b/lib/handler/file.c
index 8e0b35b3d0..aea70a75ce 100644
--- a/lib/handler/file.c
+++ b/lib/handler/file.c
@@ -562,7 +562,7 @@ static int try_dynamic_request(h2o_file_handler_t *self, h2o_req_t *req, char *r
static void send_method_not_allowed(h2o_req_t *req)
{
h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ALLOW, H2O_STRLIT("GET, HEAD"));
- h2o_send_error(req, 405, "Method Not Allowed", "method not allowed", H2O_SEND_ERROR_KEEP_HEADERS);
+ h2o_send_error_405(req, "Method Not Allowed", "method not allowed", H2O_SEND_ERROR_KEEP_HEADERS);
}
static int serve_with_generator(struct st_h2o_sendfile_generator_t *generator, h2o_req_t *req, const char *rpath, size_t rpath_len,
@@ -622,7 +622,7 @@ static int serve_with_generator(struct st_h2o_sendfile_generator_t *generator, h
content_range.base = h2o_mem_alloc_pool(&req->pool, 32);
content_range.len = sprintf(content_range.base, "bytes */%zu", generator->bytesleft);
h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_RANGE, content_range.base, content_range.len);
- h2o_send_error(req, 416, "Request Range Not Satisfiable", "requested range not satisfiable",
+ h2o_send_error_416(req, "Request Range Not Satisfiable", "requested range not satisfiable",
H2O_SEND_ERROR_KEEP_HEADERS);
goto Close;
}
@@ -767,14 +767,14 @@ static int on_req(h2o_handler_t *_self, h2o_req_t *req)
/* failed to open */
if (errno == ENFILE || errno == EMFILE) {
- h2o_send_error(req, 503, "Service Unavailable", "please try again later", 0);
+ h2o_send_error_503(req, "Service Unavailable", "please try again later", 0);
} else {
if (h2o_mimemap_has_dynamic_type(self->mimemap) && try_dynamic_request(self, req, rpath, rpath_len) == 0)
return 0;
if (errno == ENOENT || errno == ENOTDIR) {
return -1;
} else {
- h2o_send_error(req, 403, "Access Forbidden", "access forbidden", 0);
+ h2o_send_error_403(req, "Access Forbidden", "access forbidden", 0);
}
}
return 0;
@@ -888,13 +888,13 @@ static int specific_handler_on_req(h2o_handler_t *_self, h2o_req_t *req)
/* open file (or send error or return -1) */
if ((generator = create_generator(req, self->real_path.base, self->real_path.len, &is_dir, self->flags)) == NULL) {
if (is_dir) {
- h2o_send_error(req, 403, "Access Forbidden", "access forbidden", 0);
+ h2o_send_error_403(req, "Access Forbidden", "access forbidden", 0);
} else if (errno == ENOENT) {
return -1;
} else if (errno == ENFILE || errno == EMFILE) {
- h2o_send_error(req, 503, "Service Unavailable", "please try again later", 0);
+ h2o_send_error_503(req, "Service Unavailable", "please try again later", 0);
} else {
- h2o_send_error(req, 403, "Access Forbidden", "access forbidden", 0);
+ h2o_send_error_403(req, "Access Forbidden", "access forbidden", 0);
}
return 0;
}
diff --git a/lib/handler/mruby.c b/lib/handler/mruby.c
index 6814888ac4..a8f1c7643e 100644
--- a/lib/handler/mruby.c
+++ b/lib/handler/mruby.c
@@ -681,7 +681,7 @@ static void send_response(h2o_mruby_generator_t *generator, mrb_int status, mrb_
GotException:
report_exception(generator->req, mrb);
- h2o_send_error(generator->req, 500, "Internal Server Error", "Internal Server Error", 0);
+ h2o_send_error_500(generator->req, "Internal Server Error", "Internal Server Error", 0);
}
void h2o_mruby_run_fiber(h2o_mruby_generator_t *generator, mrb_value receiver, mrb_value input, int *is_delegate)
@@ -778,7 +778,7 @@ void h2o_mruby_run_fiber(h2o_mruby_generator_t *generator, mrb_value receiver, m
if (generator->req != NULL) {
report_exception(generator->req, mrb);
if (generator->req->_generator == NULL) {
- h2o_send_error(generator->req, 500, "Internal Server Error", "Internal Server Error", 0);
+ h2o_send_error_500(generator->req, "Internal Server Error", "Internal Server Error", 0);
} else {
h2o_mruby_send_chunked_close(generator);
}
diff --git a/lib/handler/redirect.c b/lib/handler/redirect.c
index f61bd0bb9e..38292b8b12 100644
--- a/lib/handler/redirect.c
+++ b/lib/handler/redirect.c
@@ -76,7 +76,7 @@ static void redirect_internally(h2o_redirect_handler_t *self, h2o_req_t *req, h2
return;
SendInternalError:
- h2o_send_error(req, 503, "Internal Server Error", "internal server error", 0);
+ h2o_send_error_503(req, "Internal Server Error", "internal server error", 0);
}
static int on_req(h2o_handler_t *_self, h2o_req_t *req)
diff --git a/lib/handler/status.c b/lib/handler/status.c
index 3fe9035705..2e25e8ccb2 100644
--- a/lib/handler/status.c
+++ b/lib/handler/status.c
@@ -205,7 +205,7 @@ static int on_req_json(struct st_h2o_root_status_handler_t *self, h2o_req_t *req
void *p;
p = sh->init(&err);
if (!p) {
- h2o_send_error(req, 400, "Invalid Request", err.base, 0);
+ h2o_send_error_400(req, "Invalid Request", err.base, 0);
return 0;
}
collector->status_ctx.entries[collector->status_ctx.size].ctx = p;
diff --git a/lib/handler/status/errors.c b/lib/handler/status/errors.c
index 4f5365d14b..a6f9b5c58d 100644
--- a/lib/handler/status/errors.c
+++ b/lib/handler/status/errors.c
@@ -23,15 +23,19 @@
#include "h2o.h"
struct errors_status_ctx {
- unsigned long long agg_errors[EMITTED_ERRORS_MAX];
+ unsigned long long agg_errors_http1[E_HTTP_MAX];
+ unsigned long long agg_errors_http2[E_HTTP2_MAX];
};
static void errors_status_per_thread(void *priv, h2o_context_t *ctx)
{
size_t i;
struct errors_status_ctx *esc = priv;
- for (i = 0; i < EMITTED_ERRORS_MAX; i++) {
- esc->agg_errors[i] += ctx->emitted_errors.emitted_errors_cnt[i];
+ for (i = 0; i < E_HTTP_MAX; i++) {
+ esc->agg_errors_http1[i] += ctx->emitted_errors[i];
+ }
+ for (i = 0; i < E_HTTP_MAX; i++) {
+ esc->agg_errors_http2[i] += ctx->http2.emitted_errors[i];
}
}
@@ -78,15 +82,19 @@ static h2o_iovec_t errors_status_final(void *priv, h2o_globalconf_t *gconf, h2o_
" \"http2-errors-enhance_your_calm\": %llu, \n"
" \"http2-errors-inadequate_security\": %llu, \n"
" \"http2-errors-other\": %llu",
- esc->agg_errors[E_HTTP_400], esc->agg_errors[E_HTTP_403], esc->agg_errors[E_HTTP_404],
- esc->agg_errors[E_HTTP_405], esc->agg_errors[E_HTTP_416], esc->agg_errors[E_HTTP_417],
- esc->agg_errors[E_HTTP_500], esc->agg_errors[E_HTTP_502], esc->agg_errors[E_HTTP_503],
- esc->agg_errors[E_HTTP_4XX], esc->agg_errors[E_HTTP_5XX], esc->agg_errors[E_HTTP_XXX],
- esc->agg_errors[E_HTTP2_PROTOCOL], esc->agg_errors[E_HTTP2_INTERNAL], esc->agg_errors[E_HTTP2_FLOW_CONTROL],
- esc->agg_errors[E_HTTP2_SETTINGS_TIMEOUT], esc->agg_errors[E_HTTP2_STREAM_CLOSED], esc->agg_errors[E_HTTP2_FRAME_SIZE],
- esc->agg_errors[E_HTTP2_REFUSED_STREAM], esc->agg_errors[E_HTTP2_CANCEL], esc->agg_errors[E_HTTP2_COMPRESSION],
- esc->agg_errors[E_HTTP2_CONNECT], esc->agg_errors[E_HTTP2_ENHANCE_YOUR_CALM], esc->agg_errors[E_HTTP2_INADEQUATE_SECURITY],
- esc->agg_errors[E_HTTP2_OTHER]);
+ esc->agg_errors_http1[E_HTTP_400], esc->agg_errors_http1[E_HTTP_403],
+ esc->agg_errors_http1[E_HTTP_404], esc->agg_errors_http1[E_HTTP_405],
+ esc->agg_errors_http1[E_HTTP_416], esc->agg_errors_http1[E_HTTP_417],
+ esc->agg_errors_http1[E_HTTP_500], esc->agg_errors_http1[E_HTTP_502],
+ esc->agg_errors_http1[E_HTTP_503], esc->agg_errors_http1[E_HTTP_4XX],
+ esc->agg_errors_http1[E_HTTP_5XX], esc->agg_errors_http1[E_HTTP_XXX],
+ esc->agg_errors_http2[E_HTTP2_PROTOCOL], esc->agg_errors_http2[E_HTTP2_INTERNAL],
+ esc->agg_errors_http2[E_HTTP2_FLOW_CONTROL], esc->agg_errors_http2[E_HTTP2_SETTINGS_TIMEOUT],
+ esc->agg_errors_http2[E_HTTP2_STREAM_CLOSED], esc->agg_errors_http2[E_HTTP2_FRAME_SIZE],
+ esc->agg_errors_http2[E_HTTP2_REFUSED_STREAM], esc->agg_errors_http2[E_HTTP2_CANCEL],
+ esc->agg_errors_http2[E_HTTP2_COMPRESSION], esc->agg_errors_http2[E_HTTP2_CONNECT],
+ esc->agg_errors_http2[E_HTTP2_ENHANCE_YOUR_CALM], esc->agg_errors_http2[E_HTTP2_INADEQUATE_SECURITY],
+ esc->agg_errors_http2[E_HTTP2_OTHER]);
free(esc);
return ret;
#undef BUFSIZE
diff --git a/lib/http1.c b/lib/http1.c
index 56cb2d195d..53f5926f4e 100644
--- a/lib/http1.c
+++ b/lib/http1.c
@@ -153,7 +153,7 @@ static void entity_read_send_error(struct st_h2o_http1_conn_t *conn, int status,
set_timeout(conn, NULL, NULL);
h2o_socket_read_stop(conn->sock);
conn->req.http1_is_persistent = 0;
- h2o_send_error(&conn->req, status, reason, body, H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION);
+ h2o_send_error_generic(&conn->req, status, reason, body, H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION);
}
static void on_entity_read_complete(struct st_h2o_http1_conn_t *conn)
@@ -409,7 +409,7 @@ static void handle_incoming_request(struct st_h2o_http1_conn_t *conn)
if (!h2o_lcstris(expect.base, expect.len, H2O_STRLIT("100-continue"))) {
set_timeout(conn, NULL, NULL);
h2o_socket_read_stop(conn->sock);
- h2o_send_error(&conn->req, 417, "Expectation Failed", "unknown expectation",
+ h2o_send_error_417(&conn->req, "Expectation Failed", "unknown expectation",
H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION);
return;
}
diff --git a/lib/http2/connection.c b/lib/http2/connection.c
index 9a3b86a592..35648e1616 100644
--- a/lib/http2/connection.c
+++ b/lib/http2/connection.c
@@ -276,12 +276,17 @@ void close_connection(h2o_http2_conn_t *conn)
}
}
+static inline void h2o_context_report_http2_error(h2o_context_t *ctx, int errnum)
+{
+ ctx->http2.emitted_errors[errnum]++;
+}
+
void send_stream_error(h2o_http2_conn_t *conn, uint32_t stream_id, int errnum)
{
assert(stream_id != 0);
assert(conn->state < H2O_HTTP2_CONN_STATE_IS_CLOSING);
- h2o_context_report_emitted_error(conn->super.ctx, errnum, 2);
+ h2o_context_report_http2_error(conn->super.ctx, -errnum);
h2o_http2_encode_rst_stream_frame(&conn->_write.buf, stream_id, -errnum);
h2o_http2_conn_request_write(conn);
@@ -861,7 +866,7 @@ static void on_read(h2o_socket_t *sock, const char *err)
h2o_http2_conn_t *conn = sock->data;
if (err != NULL) {
- h2o_context_report_emitted_error(conn->super.ctx, 0, 2);
+ h2o_context_report_http2_error(conn->super.ctx, E_HTTP2_OTHER);
h2o_socket_read_stop(conn->sock);
close_connection(conn);
return;
@@ -932,7 +937,7 @@ static void on_write_complete(h2o_socket_t *sock, const char *err)
/* close by error if necessary */
if (err != NULL) {
- h2o_context_report_emitted_error(conn->super.ctx, 0, 2);
+ h2o_context_report_http2_error(conn->super.ctx, E_HTTP2_OTHER);
close_connection_now(conn);
return;
}
From ebc247e46444a4c9849271232c39ff3afcd7fc26 Mon Sep 17 00:00:00 2001
From: Frederik Deweerdt
Date: Wed, 18 May 2016 10:45:37 -0700
Subject: [PATCH 4/8] - Add a specialized `h2o_send_error_deferred` define so
that we can update the counters directly from that function, instead of
relying on `h2o_context_report_http1_error` - Remove
`h2o_context_report_http1_error` and `E_HTTP_{4,5,X}XX` enums: Every call
side now uses a well known http return code. - Simplify the `status_list`
logic by calling `h2o_contains_token`. We also use `,` as a separator
between the modules, rather than the reserved `|` (update docs and the
test). - Add a test case verifying the increment of the 404 counter
---
include/h2o.h | 47 +++++++++----------
include/h2o/http2_internal.h | 17 -------
lib/core/context.c | 38 ---------------
lib/core/request.c | 37 ++++++++-------
lib/handler/status.c | 64 ++++---------------------
lib/handler/status/errors.c | 67 +++++++++++++--------------
lib/http2/connection.c | 6 +--
srcdoc/configure/status_directives.mt | 2 +-
t/50status.t | 25 ++++++++--
9 files changed, 107 insertions(+), 196 deletions(-)
diff --git a/include/h2o.h b/include/h2o.h
index f5340dfe79..7856f34bd8 100644
--- a/include/h2o.h
+++ b/include/h2o.h
@@ -417,24 +417,26 @@ typedef struct st_h2o_mimemap_type_t {
} data;
} h2o_mimemap_type_t;
-/* errors emanating from h2o */
-enum {
- /* http2 */
- E_HTTP2_PROTOCOL,
- E_HTTP2_INTERNAL,
- E_HTTP2_FLOW_CONTROL,
- E_HTTP2_SETTINGS_TIMEOUT,
- E_HTTP2_STREAM_CLOSED,
- E_HTTP2_FRAME_SIZE,
- E_HTTP2_REFUSED_STREAM,
- E_HTTP2_CANCEL,
- E_HTTP2_COMPRESSION,
- E_HTTP2_CONNECT,
- E_HTTP2_ENHANCE_YOUR_CALM,
- E_HTTP2_INADEQUATE_SECURITY,
- E_HTTP2_OTHER,
- E_HTTP2_MAX,
-};
+/* defined as negated form of the error codes defined in HTTP2-spec section 7 */
+#define H2O_HTTP2_ERROR_NONE 0
+#define H2O_HTTP2_ERROR_PROTOCOL -1
+#define H2O_HTTP2_ERROR_INTERNAL -2
+#define H2O_HTTP2_ERROR_FLOW_CONTROL -3
+#define H2O_HTTP2_ERROR_SETTINGS_TIMEOUT -4
+#define H2O_HTTP2_ERROR_STREAM_CLOSED -5
+#define H2O_HTTP2_ERROR_FRAME_SIZE -6
+#define H2O_HTTP2_ERROR_REFUSED_STREAM -7
+#define H2O_HTTP2_ERROR_CANCEL -8
+#define H2O_HTTP2_ERROR_COMPRESSION -9
+#define H2O_HTTP2_ERROR_CONNECT -10
+#define H2O_HTTP2_ERROR_ENHANCE_YOUR_CALM -11
+#define H2O_HTTP2_ERROR_INADEQUATE_SECURITY -12
+/* end of the HTT2-spec defined errors */
+#define H2O_HTTP2_ERROR_OTHER -13
+#define H2O_HTTP2_ERROR_MAX 14
+#define H2O_HTTP2_ERROR_INCOMPLETE -255 /* an internal value indicating that all data is not ready */
+#define H2O_HTTP2_ERROR_PROTOCOL_CLOSE_IMMEDIATELY -256
+
enum {
/* http1 */
E_HTTP_400 = 0,
@@ -446,9 +448,6 @@ enum {
E_HTTP_500,
E_HTTP_502,
E_HTTP_503,
- E_HTTP_4XX,
- E_HTTP_5XX,
- E_HTTP_XXX,
E_HTTP_MAX,
};
@@ -523,7 +522,7 @@ struct st_h2o_context_t {
/**
* counter for http2 errors internally emitted by h2o
*/
- unsigned long emitted_errors[E_HTTP2_MAX];
+ uint64_t emitted_errors[H2O_HTTP2_ERROR_MAX];
} http2;
struct {
@@ -1266,10 +1265,6 @@ static void h2o_context_set_filter_context(h2o_context_t *ctx, h2o_filter_t *fil
* returns per-module context set by the on_context_init callback
*/
static void *h2o_context_get_logger_context(h2o_context_t *ctx, h2o_logger_t *logger);
-/**
- * internally reports an error emitted by h2o, so that this can in turn be reported in statuses
- */
-void h2o_context_report_http1_error(h2o_context_t *ctx, int status);
/* built-in generators */
enum {
diff --git a/include/h2o/http2_internal.h b/include/h2o/http2_internal.h
index c17502e716..85e0dec0bb 100644
--- a/include/h2o/http2_internal.h
+++ b/include/h2o/http2_internal.h
@@ -35,23 +35,6 @@ typedef struct st_h2o_http2_stream_t h2o_http2_stream_t;
/* connection flow control window + alpha */
#define H2O_HTTP2_DEFAULT_OUTBUF_SIZE 81920
-/* defined as negated form of the error codes defined in HTTP2-spec section 7 */
-#define H2O_HTTP2_ERROR_NONE 0
-#define H2O_HTTP2_ERROR_PROTOCOL -1
-#define H2O_HTTP2_ERROR_INTERNAL -2
-#define H2O_HTTP2_ERROR_FLOW_CONTROL -3
-#define H2O_HTTP2_ERROR_SETTINGS_TIMEOUT -4
-#define H2O_HTTP2_ERROR_STREAM_CLOSED -5
-#define H2O_HTTP2_ERROR_FRAME_SIZE -6
-#define H2O_HTTP2_ERROR_REFUSED_STREAM -7
-#define H2O_HTTP2_ERROR_CANCEL -8
-#define H2O_HTTP2_ERROR_COMPRESSION -9
-#define H2O_HTTP2_ERROR_CONNECT -10
-#define H2O_HTTP2_ERROR_ENHANCE_YOUR_CALM -11
-#define H2O_HTTP2_ERROR_INADEQUATE_SECURITY -12
-#define H2O_HTTP2_ERROR_INCOMPLETE -255 /* an internal value indicating that all data is not ready */
-#define H2O_HTTP2_ERROR_PROTOCOL_CLOSE_IMMEDIATELY -256
-
/* hpack */
#define H2O_HTTP2_ENCODE_INT_MAX_LENGTH 5
diff --git a/lib/core/context.c b/lib/core/context.c
index b3afc48c1e..7fcc47370f 100644
--- a/lib/core/context.c
+++ b/lib/core/context.c
@@ -183,41 +183,3 @@ void h2o_context_update_timestamp_cache(h2o_context_t *ctx)
h2o_time2str_log(ctx->_timestamp_cache.value->log, ctx->_timestamp_cache.tv_at.tv_sec);
}
}
-
-void h2o_context_report_http1_error(h2o_context_t *ctx, int status)
-{
- int s100;
-
-#define INC_PREDEF_ERROR(x) \
- do { \
- if (status == x) { \
- ctx->emitted_errors[E_HTTP_ ## x]++; \
- return; \
- } \
- } while(0)
-
- INC_PREDEF_ERROR(400);
- INC_PREDEF_ERROR(403);
- INC_PREDEF_ERROR(404);
- INC_PREDEF_ERROR(405);
- INC_PREDEF_ERROR(416);
- INC_PREDEF_ERROR(417);
- INC_PREDEF_ERROR(500);
- INC_PREDEF_ERROR(502);
- INC_PREDEF_ERROR(503);
-
-#undef INC_PREDEF_ERROR
-
- s100 = status / 100;
- switch(s100) {
- case 4:
- ctx->emitted_errors[E_HTTP_4XX]++;
- return;
- case 5:
- ctx->emitted_errors[E_HTTP_5XX]++;
- return;
- default:
- ctx->emitted_errors[E_HTTP_XXX]++;
- return;
- }
-}
diff --git a/lib/core/request.c b/lib/core/request.c
index d80d37843d..86a9478fdd 100644
--- a/lib/core/request.c
+++ b/lib/core/request.c
@@ -478,21 +478,26 @@ void h2o_send_error_generic(h2o_req_t *req, int status, const char *reason, cons
h2o_send_inline(req, body, SIZE_MAX);
}
-static void send_error_deferred_cb(h2o_timeout_entry_t *entry)
-{
- struct st_send_error_deferred_t *args = H2O_STRUCT_FROM_MEMBER(struct st_send_error_deferred_t, _timeout, entry);
- reset_response(args->req);
- h2o_context_report_http1_error(args->req->conn->ctx, args->status);
- h2o_send_error_generic(args->req, args->status, args->reason, args->body, args->flags);
-}
-
-void h2o_send_error_deferred(h2o_req_t *req, int status, const char *reason, const char *body, int flags)
-{
- struct st_send_error_deferred_t *args = h2o_mem_alloc_pool(&req->pool, sizeof(*args));
- *args = (struct st_send_error_deferred_t){req, status, reason, body, flags};
- args->_timeout.cb = send_error_deferred_cb;
- h2o_timeout_link(req->conn->ctx->loop, &req->conn->ctx->zero_timeout, &args->_timeout);
-}
+#define SEND_ERROR_DEFERRED(status_) \
+ static void send_error_deferred_cb_ ## status_ (h2o_timeout_entry_t *entry) \
+ { \
+ struct st_send_error_deferred_t *args = H2O_STRUCT_FROM_MEMBER(struct st_send_error_deferred_t, _timeout, entry); \
+ reset_response(args->req); \
+ args->req->conn->ctx->emitted_errors[E_HTTP_ ## status_]++; \
+ h2o_send_error_generic(args->req, args->status, args->reason, args->body, args->flags); \
+ } \
+ \
+ static void h2o_send_error_deferred_ ## status_ (h2o_req_t *req, const char *reason, const char *body, int flags) \
+ { \
+ struct st_send_error_deferred_t *args = h2o_mem_alloc_pool(&req->pool, sizeof(*args)); \
+ *args = (struct st_send_error_deferred_t){req, status_, reason, body, flags}; \
+ args->_timeout.cb = send_error_deferred_cb_ ## status_; \
+ h2o_timeout_link(req->conn->ctx->loop, &req->conn->ctx->zero_timeout, &args->_timeout); \
+ }
+
+SEND_ERROR_DEFERRED(502)
+
+#undef SEND_ERROR_DEFERRED
void h2o_req_log_error(h2o_req_t *req, const char *module, const char *fmt, ...)
{
@@ -560,7 +565,7 @@ void h2o_send_redirect_internal(h2o_req_t *req, h2o_iovec_t method, const char *
/* parse the location URL */
if (h2o_url_parse_relative(url_str, url_len, &url) != 0) {
/* TODO log fprintf(stderr, "[proxy] cannot handle location header: %.*s\n", (int)url_len, url); */
- h2o_send_error_deferred(req, 502, "Gateway Error", "internal error", 0);
+ h2o_send_error_deferred_502(req, "Gateway Error", "internal error", 0);
return;
}
/* convert the location to absolute (while creating copies of the values passed to the deferred call) */
diff --git a/lib/handler/status.c b/lib/handler/status.c
index 2e25e8ccb2..bacd6c59b1 100644
--- a/lib/handler/status.c
+++ b/lib/handler/status.c
@@ -49,7 +49,6 @@ struct st_h2o_status_collector_t {
} src;
pthread_mutex_t mutex;
size_t num_remaining_threads;
- h2o_globalconf_t *gconf;
H2O_VECTOR(struct status_ctx) status_ctx;
};
@@ -65,9 +64,9 @@ static void collect_reqs_of_context(struct st_h2o_status_collector_t *collector,
pthread_mutex_lock(&collector->mutex);
- for (i = 0; i < collector->gconf->statuses.size; i++) {
+ for (i = 0; i < ctx->globalconf->statuses.size; i++) {
h2o_status_handler_t *sh;
- sh = &collector->gconf->statuses.entries[i];
+ sh = &ctx->globalconf->statuses.entries[i];
if (!collector->status_ctx.entries[i].active) {
continue;
}
@@ -173,12 +172,12 @@ static void on_req_close(void *p)
h2o_mem_release_shared(collector);
}
-static int on_req_json(struct st_h2o_root_status_handler_t *self, h2o_req_t *req, h2o_iovec_t *status_list)
+static int on_req_json(struct st_h2o_root_status_handler_t *self, h2o_req_t *req, h2o_iovec_t status_list)
{
{ /* construct collector and send request to every thread */
struct st_h2o_status_context_t *status_ctx = h2o_context_get_handler_context(req->conn->ctx, &self->super);
struct st_h2o_status_collector_t *collector = h2o_mem_alloc_shared(NULL, sizeof(*collector), on_collector_dispose);
- size_t i, j;
+ size_t i;
memset(collector, 0, sizeof(*collector));
for (i = 0; i < req->conn->ctx->globalconf->statuses.size; i++) {
@@ -187,15 +186,8 @@ static int on_req_json(struct st_h2o_root_status_handler_t *self, h2o_req_t *req
h2o_vector_reserve(&req->pool, &collector->status_ctx, collector->status_ctx.size + 1);
sh = &req->conn->ctx->globalconf->statuses.entries[i];
- if (status_list) {
- int found = 0;
- for (j = 0; status_list[j].base; j++) {
- if (h2o_memis(status_list[j].base, status_list[j].len, sh->name.base, sh->name.len)) {
- found = 1;
- break;
- }
- }
- if (!found) {
+ if (status_list.base) {
+ if (!h2o_contains_token(status_list.base, status_list.len, sh->name.base, sh->name.len, ',')) {
collector->status_ctx.entries[collector->status_ctx.size].active = 0;
goto Skip;
}
@@ -214,7 +206,6 @@ static int on_req_json(struct st_h2o_root_status_handler_t *self, h2o_req_t *req
Skip:
collector->status_ctx.size++;
}
- collector->gconf = req->conn->ctx->globalconf;
collector->src.req = req;
collector->src.receiver = &status_ctx->receiver;
pthread_mutex_init(&collector->mutex, NULL);
@@ -234,44 +225,6 @@ static int on_req_json(struct st_h2o_root_status_handler_t *self, h2o_req_t *req
return 0;
}
-static h2o_iovec_t *build_status_list(const char *param, size_t len)
-{
- int i, cur, start;
- int nr_list = 1;
- h2o_iovec_t *ret;
- for (i = 0; i < len; i++) {
- if (param[i] == '|') {
- nr_list++;
- }
- }
- ret = h2o_mem_alloc(sizeof(*ret) * (nr_list + 1));
- cur = 0;
- start = 0;
- for (i = 0; i < len; i++) {
- if (param[i] == '|') {
- ret[cur++] = h2o_strdup(NULL, ¶m[start], i - start);
- start = i + 1;
- }
- }
- if (start != i) {
- ret[cur++] = h2o_strdup(NULL, ¶m[start], i - start);
- }
- ret[cur] = h2o_iovec_init(NULL, 0);
-
- return ret;
-}
-
-static void free_status_list(h2o_iovec_t *status_list)
-{
- int i;
- if (!status_list)
- return;
- for (i = 0; status_list[i].base; i++) {
- free(status_list[i].base);
- }
- free(status_list);
-}
-
static int on_req(h2o_handler_t *_self, h2o_req_t *req)
{
struct st_h2o_root_status_handler_t *self = (void *)_self;
@@ -290,14 +243,13 @@ static int on_req(h2o_handler_t *_self, h2o_req_t *req)
} else if (h2o_memis(local_path.base, local_path.len, H2O_STRLIT("/json"))) {
int ret;
/* "/json" maps to the JSON API */
- h2o_iovec_t *status_list = NULL; /* NULL means all */
+ h2o_iovec_t status_list = {NULL, 0}; /* NULL means we'll show all statuses */
if (req->query_at != SIZE_MAX && (req->path.len - req->query_at > 6)) {
if (h2o_memis(&req->path.base[req->query_at], 6, "?show=", 6)) {
- status_list = build_status_list(&req->path.base[req->query_at + 6], req->path.len - req->query_at - 6);
+ status_list = h2o_iovec_init(&req->path.base[req->query_at + 6], req->path.len - req->query_at - 6);
}
}
ret = on_req_json(self, req, status_list);
- free_status_list(status_list);
return ret;
}
diff --git a/lib/handler/status/errors.c b/lib/handler/status/errors.c
index a6f9b5c58d..daee7cf2a1 100644
--- a/lib/handler/status/errors.c
+++ b/lib/handler/status/errors.c
@@ -21,10 +21,11 @@
*/
#include "h2o.h"
+#include
struct errors_status_ctx {
- unsigned long long agg_errors_http1[E_HTTP_MAX];
- unsigned long long agg_errors_http2[E_HTTP2_MAX];
+ uint64_t agg_errors_http1[E_HTTP_MAX];
+ uint64_t agg_errors_http2[H2O_HTTP2_ERROR_MAX];
};
static void errors_status_per_thread(void *priv, h2o_context_t *ctx)
@@ -57,44 +58,38 @@ static h2o_iovec_t errors_status_final(void *priv, h2o_globalconf_t *gconf, h2o_
#define BUFSIZE (2*1024)
ret.base = h2o_mem_alloc_pool(&req->pool, BUFSIZE);
ret.len = snprintf(ret.base, BUFSIZE, ",\n"
- " \"http1-errors-400\": %llu,\n"
- " \"http1-errors-403\": %llu,\n"
- " \"http1-errors-404\": %llu,\n"
- " \"http1-errors-405\": %llu,\n"
- " \"http1-errors-416\": %llu,\n"
- " \"http1-errors-417\": %llu,\n"
- " \"http1-errors-500\": %llu,\n"
- " \"http1-errors-502\": %llu,\n"
- " \"http1-errors-503\": %llu,\n"
- " \"http1-errors-4xx-others\": %llu,\n"
- " \"http1-errors-5xx-others\": %llu,\n"
- " \"http1-errors-others\": %llu,\n"
- " \"http2-errors-protocol\": %llu, \n"
- " \"http2-errors-internal\": %llu, \n"
- " \"http2-errors-flow_control\": %llu, \n"
- " \"http2-errors-settings_timeout\": %llu, \n"
- " \"http2-errors-stream_closed\": %llu, \n"
- " \"http2-errors-frame_size\": %llu, \n"
- " \"http2-errors-refused_stream\": %llu, \n"
- " \"http2-errors-cancel\": %llu, \n"
- " \"http2-errors-compression\": %llu, \n"
- " \"http2-errors-connect\": %llu, \n"
- " \"http2-errors-enhance_your_calm\": %llu, \n"
- " \"http2-errors-inadequate_security\": %llu, \n"
- " \"http2-errors-other\": %llu",
+ " \"http1-errors-400\": %" PRIu64 ",\n"
+ " \"http1-errors-403\": %" PRIu64 ",\n"
+ " \"http1-errors-404\": %" PRIu64 ",\n"
+ " \"http1-errors-405\": %" PRIu64 ",\n"
+ " \"http1-errors-416\": %" PRIu64 ",\n"
+ " \"http1-errors-417\": %" PRIu64 ",\n"
+ " \"http1-errors-500\": %" PRIu64 ",\n"
+ " \"http1-errors-502\": %" PRIu64 ",\n"
+ " \"http1-errors-503\": %" PRIu64 ",\n"
+ " \"http2-errors-protocol\": %" PRIu64 ", \n"
+ " \"http2-errors-internal\": %" PRIu64 ", \n"
+ " \"http2-errors-flow_control\": %" PRIu64 ", \n"
+ " \"http2-errors-settings_timeout\": %" PRIu64 ", \n"
+ " \"http2-errors-stream_closed\": %" PRIu64 ", \n"
+ " \"http2-errors-frame_size\": %" PRIu64 ", \n"
+ " \"http2-errors-refused_stream\": %" PRIu64 ", \n"
+ " \"http2-errors-cancel\": %" PRIu64 ", \n"
+ " \"http2-errors-compression\": %" PRIu64 ", \n"
+ " \"http2-errors-connect\": %" PRIu64 ", \n"
+ " \"http2-errors-enhance_your_calm\": %" PRIu64 ", \n"
+ " \"http2-errors-inadequate_security\": %" PRIu64 "\n",
esc->agg_errors_http1[E_HTTP_400], esc->agg_errors_http1[E_HTTP_403],
esc->agg_errors_http1[E_HTTP_404], esc->agg_errors_http1[E_HTTP_405],
esc->agg_errors_http1[E_HTTP_416], esc->agg_errors_http1[E_HTTP_417],
esc->agg_errors_http1[E_HTTP_500], esc->agg_errors_http1[E_HTTP_502],
- esc->agg_errors_http1[E_HTTP_503], esc->agg_errors_http1[E_HTTP_4XX],
- esc->agg_errors_http1[E_HTTP_5XX], esc->agg_errors_http1[E_HTTP_XXX],
- esc->agg_errors_http2[E_HTTP2_PROTOCOL], esc->agg_errors_http2[E_HTTP2_INTERNAL],
- esc->agg_errors_http2[E_HTTP2_FLOW_CONTROL], esc->agg_errors_http2[E_HTTP2_SETTINGS_TIMEOUT],
- esc->agg_errors_http2[E_HTTP2_STREAM_CLOSED], esc->agg_errors_http2[E_HTTP2_FRAME_SIZE],
- esc->agg_errors_http2[E_HTTP2_REFUSED_STREAM], esc->agg_errors_http2[E_HTTP2_CANCEL],
- esc->agg_errors_http2[E_HTTP2_COMPRESSION], esc->agg_errors_http2[E_HTTP2_CONNECT],
- esc->agg_errors_http2[E_HTTP2_ENHANCE_YOUR_CALM], esc->agg_errors_http2[E_HTTP2_INADEQUATE_SECURITY],
- esc->agg_errors_http2[E_HTTP2_OTHER]);
+ esc->agg_errors_http1[E_HTTP_503],
+ esc->agg_errors_http2[-H2O_HTTP2_ERROR_PROTOCOL], esc->agg_errors_http2[-H2O_HTTP2_ERROR_INTERNAL],
+ esc->agg_errors_http2[-H2O_HTTP2_ERROR_FLOW_CONTROL], esc->agg_errors_http2[-H2O_HTTP2_ERROR_SETTINGS_TIMEOUT],
+ esc->agg_errors_http2[-H2O_HTTP2_ERROR_STREAM_CLOSED], esc->agg_errors_http2[-H2O_HTTP2_ERROR_FRAME_SIZE],
+ esc->agg_errors_http2[-H2O_HTTP2_ERROR_REFUSED_STREAM], esc->agg_errors_http2[-H2O_HTTP2_ERROR_CANCEL],
+ esc->agg_errors_http2[-H2O_HTTP2_ERROR_COMPRESSION], esc->agg_errors_http2[-H2O_HTTP2_ERROR_CONNECT],
+ esc->agg_errors_http2[-H2O_HTTP2_ERROR_ENHANCE_YOUR_CALM], esc->agg_errors_http2[-H2O_HTTP2_ERROR_INADEQUATE_SECURITY]);
free(esc);
return ret;
#undef BUFSIZE
diff --git a/lib/http2/connection.c b/lib/http2/connection.c
index 35648e1616..5d49a73c6d 100644
--- a/lib/http2/connection.c
+++ b/lib/http2/connection.c
@@ -278,7 +278,7 @@ void close_connection(h2o_http2_conn_t *conn)
static inline void h2o_context_report_http2_error(h2o_context_t *ctx, int errnum)
{
- ctx->http2.emitted_errors[errnum]++;
+ ctx->http2.emitted_errors[-errnum]++;
}
void send_stream_error(h2o_http2_conn_t *conn, uint32_t stream_id, int errnum)
@@ -866,7 +866,7 @@ static void on_read(h2o_socket_t *sock, const char *err)
h2o_http2_conn_t *conn = sock->data;
if (err != NULL) {
- h2o_context_report_http2_error(conn->super.ctx, E_HTTP2_OTHER);
+ h2o_context_report_http2_error(conn->super.ctx, H2O_HTTP2_ERROR_OTHER);
h2o_socket_read_stop(conn->sock);
close_connection(conn);
return;
@@ -937,7 +937,7 @@ static void on_write_complete(h2o_socket_t *sock, const char *err)
/* close by error if necessary */
if (err != NULL) {
- h2o_context_report_http2_error(conn->super.ctx, E_HTTP2_OTHER);
+ h2o_context_report_http2_error(conn->super.ctx, H2O_HTTP2_ERROR_OTHER);
close_connection_now(conn);
return;
}
diff --git a/srcdoc/configure/status_directives.mt b/srcdoc/configure/status_directives.mt
index 6e56691190..60ef0bcf07 100644
--- a/srcdoc/configure/status_directives.mt
+++ b/srcdoc/configure/status_directives.mt
@@ -20,7 +20,7 @@ EOT
Access to the handler should be restricted, considering the fact that the status includes the details of in-flight HTTP requests.
The example below uses Basic authentication.
The information returned by the /json
handler can be filtered out using
-the optional show=module1|module2
parameter.
+the optional show=module1,module2
parameter.
There are currently three modules defined:
diff --git a/t/50status.t b/t/50status.t
index 65ff190919..61bd0d35cd 100644
--- a/t/50status.t
+++ b/t/50status.t
@@ -25,7 +25,7 @@ EOT
is scalar @requests, 1, "One request";
};
-subtest "json hander noreqs" => sub {
+subtest "json hander without requests" => sub {
my $server = spawn_h2o(<< "EOT");
hosts:
default:
@@ -36,12 +36,31 @@ hosts:
status: ON
EOT
- my $resp = `curl --silent -o /dev/stderr 'http://127.0.0.1:$server->{port}/s/json?show=main|errors' 2>&1 > /dev/null`;
+ my $resp = `curl --silent -o /dev/stderr 'http://127.0.0.1:$server->{port}/s/json?show=main,errors' 2>&1 > /dev/null`;
my $jresp = decode_json("$resp");
is $jresp->{'connections'}, 1, "One connection";
- is $jresp->{'requests'}, undef, "Zero request";
+ is $jresp->{'requests'}, undef, "Requests not present";
is $jresp->{'http1-errors-404'}, 0, "Internal errors monitoring";
};
+subtest "json hander check 404 error counter" => sub {
+ my $server = spawn_h2o(<< "EOT");
+hosts:
+ default:
+ paths:
+ /:
+ file.dir: @{[ DOC_ROOT ]}
+ /s:
+ status: ON
+EOT
+ my $resp = `curl --silent -o /dev/stderr 'http://127.0.0.1:$server->{port}/beeb98fcf148317be5fe5d763c658bc9ea9c087a' 2>&1 > /dev/null`;
+ my $resp = `curl --silent -o /dev/stderr 'http://127.0.0.1:$server->{port}/s/json?show=errors' 2>&1 > /dev/null`;
+ my $jresp = decode_json("$resp");
+ is $jresp->{'connections'}, undef, "Connections not present";
+ is $jresp->{'requests'}, undef, "Requests not present";
+ is $jresp->{'http1-errors-404'}, 1, "Found the 404 error";
+};
+
+
done_testing();
From 07f385039f113db4851ffa560b883bc5f8b38250 Mon Sep 17 00:00:00 2001
From: Frederik Deweerdt
Date: Mon, 23 May 2016 11:20:55 -0700
Subject: [PATCH 5/8] stats: various small fixes
- simplify `st_h2o_status_handler_t` error handler in `init` removing
error handling that level, and deferring to `final`.
- Remove H2O_HTTP2_ERROR_OTHER in favor of dedicated `read_closed` and
`write_closed` errors.
- rename http1 errors under H2O_STATUS_ERROR_XXX
- replace the remaining callers to `h2o_send_error_generic` to use
hardcoded errors
- append `DECL_` to the names of function declaring macros for clarity
---
include/h2o.h | 70 ++++++++++++++++++++---------------
lib/core/request.c | 8 ++--
lib/handler/status.c | 13 ++-----
lib/handler/status/errors.c | 45 +++++++++++++---------
lib/handler/status/requests.c | 30 ++++++++++-----
lib/http1.c | 31 +++++++++-------
lib/http2/connection.c | 10 ++---
7 files changed, 118 insertions(+), 89 deletions(-)
diff --git a/include/h2o.h b/include/h2o.h
index 7856f34bd8..221665014f 100644
--- a/include/h2o.h
+++ b/include/h2o.h
@@ -278,7 +278,7 @@ typedef struct st_h2o_protocol_callbacks_t {
typedef h2o_iovec_t (*final_status_handler_cb)(void *ctx, h2o_globalconf_t *gconf, h2o_req_t *req);
typedef struct st_h2o_status_handler_t {
h2o_iovec_t name;
- void *(*init)(h2o_iovec_t *error); /* optional call back, allocates a context that will be passed to per_thread() */
+ void *(*init)(void); /* optional callback, allocates a context that will be passed to per_thread() */
void (*per_thread)(void *priv, h2o_context_t *ctx); /* optional callback, will be called for each thread */
h2o_iovec_t (*final)(void *ctx, h2o_globalconf_t *gconf, h2o_req_t *req); /* mandatory, will be passed the optional context */
} h2o_status_handler_t;
@@ -431,24 +431,24 @@ typedef struct st_h2o_mimemap_type_t {
#define H2O_HTTP2_ERROR_CONNECT -10
#define H2O_HTTP2_ERROR_ENHANCE_YOUR_CALM -11
#define H2O_HTTP2_ERROR_INADEQUATE_SECURITY -12
+#define H2O_HTTP2_ERROR_MAX 13
/* end of the HTT2-spec defined errors */
-#define H2O_HTTP2_ERROR_OTHER -13
-#define H2O_HTTP2_ERROR_MAX 14
#define H2O_HTTP2_ERROR_INCOMPLETE -255 /* an internal value indicating that all data is not ready */
#define H2O_HTTP2_ERROR_PROTOCOL_CLOSE_IMMEDIATELY -256
enum {
- /* http1 */
- E_HTTP_400 = 0,
- E_HTTP_403,
- E_HTTP_404,
- E_HTTP_405,
- E_HTTP_416,
- E_HTTP_417,
- E_HTTP_500,
- E_HTTP_502,
- E_HTTP_503,
- E_HTTP_MAX,
+ /* http1 protocol errors */
+ H2O_STATUS_ERROR_400 = 0,
+ H2O_STATUS_ERROR_403,
+ H2O_STATUS_ERROR_404,
+ H2O_STATUS_ERROR_405,
+ H2O_STATUS_ERROR_413,
+ H2O_STATUS_ERROR_416,
+ H2O_STATUS_ERROR_417,
+ H2O_STATUS_ERROR_500,
+ H2O_STATUS_ERROR_502,
+ H2O_STATUS_ERROR_503,
+ H2O_STATUS_ERROR_MAX,
};
/**
@@ -519,10 +519,20 @@ struct st_h2o_context_t {
* timeout entry used for graceful shutdown
*/
h2o_timeout_entry_t _graceful_shutdown_timeout;
- /**
- * counter for http2 errors internally emitted by h2o
- */
- uint64_t emitted_errors[H2O_HTTP2_ERROR_MAX];
+ struct {
+ /**
+ * counter for http2 errors internally emitted by h2o
+ */
+ uint64_t protocol_level_errors[H2O_HTTP2_ERROR_MAX];
+ /**
+ * premature close on read
+ */
+ uint64_t read_closed;
+ /**
+ * premature close on write
+ */
+ uint64_t write_closed;
+ } events;
} http2;
struct {
@@ -550,7 +560,7 @@ struct st_h2o_context_t {
/**
* counter for http1 errors internally emitted by h2o
*/
- unsigned long emitted_errors[E_HTTP_MAX];
+ uint64_t http1_status_errors[H2O_STATUS_ERROR_MAX];
H2O_VECTOR(h2o_pathconf_t *) _pathconfs_inited;
};
@@ -1286,22 +1296,22 @@ void h2o_send_inline(h2o_req_t *req, const char *body, size_t len);
* sends the given information as an error response to the client
*/
void h2o_send_error_generic(h2o_req_t *req, int status, const char *reason, const char *body, int flags);
-#define H2O_SEND_ERROR_XXX(status) \
+#define DECL_H2O_SEND_ERROR_XXX(status) \
static inline void h2o_send_error_ ## status(h2o_req_t *req, const char *reason, const char *body, int flags) \
{ \
- req->conn->ctx->emitted_errors[E_HTTP_ ## status]++; \
+ req->conn->ctx->http1_status_errors[H2O_STATUS_ERROR_ ## status]++; \
h2o_send_error_generic(req, status, reason, body, flags); \
}
-H2O_SEND_ERROR_XXX(400)
-H2O_SEND_ERROR_XXX(403)
-H2O_SEND_ERROR_XXX(404)
-H2O_SEND_ERROR_XXX(405)
-H2O_SEND_ERROR_XXX(416)
-H2O_SEND_ERROR_XXX(417)
-H2O_SEND_ERROR_XXX(500)
-H2O_SEND_ERROR_XXX(502)
-H2O_SEND_ERROR_XXX(503)
+DECL_H2O_SEND_ERROR_XXX(400)
+DECL_H2O_SEND_ERROR_XXX(403)
+DECL_H2O_SEND_ERROR_XXX(404)
+DECL_H2O_SEND_ERROR_XXX(405)
+DECL_H2O_SEND_ERROR_XXX(416)
+DECL_H2O_SEND_ERROR_XXX(417)
+DECL_H2O_SEND_ERROR_XXX(500)
+DECL_H2O_SEND_ERROR_XXX(502)
+DECL_H2O_SEND_ERROR_XXX(503)
/**
* sends error response using zero timeout; can be called by output filters while processing the headers
diff --git a/lib/core/request.c b/lib/core/request.c
index 86a9478fdd..b86a38d136 100644
--- a/lib/core/request.c
+++ b/lib/core/request.c
@@ -478,12 +478,12 @@ void h2o_send_error_generic(h2o_req_t *req, int status, const char *reason, cons
h2o_send_inline(req, body, SIZE_MAX);
}
-#define SEND_ERROR_DEFERRED(status_) \
+#define DECL_SEND_ERROR_DEFERRED(status_) \
static void send_error_deferred_cb_ ## status_ (h2o_timeout_entry_t *entry) \
{ \
struct st_send_error_deferred_t *args = H2O_STRUCT_FROM_MEMBER(struct st_send_error_deferred_t, _timeout, entry); \
reset_response(args->req); \
- args->req->conn->ctx->emitted_errors[E_HTTP_ ## status_]++; \
+ args->req->conn->ctx->http1_status_errors[H2O_STATUS_ERROR_ ## status_]++; \
h2o_send_error_generic(args->req, args->status, args->reason, args->body, args->flags); \
} \
\
@@ -495,9 +495,9 @@ void h2o_send_error_generic(h2o_req_t *req, int status, const char *reason, cons
h2o_timeout_link(req->conn->ctx->loop, &req->conn->ctx->zero_timeout, &args->_timeout); \
}
-SEND_ERROR_DEFERRED(502)
+DECL_SEND_ERROR_DEFERRED(502)
-#undef SEND_ERROR_DEFERRED
+#undef DECL_SEND_ERROR_DEFERRED
void h2o_req_log_error(h2o_req_t *req, const char *module, const char *fmt, ...)
{
diff --git a/lib/handler/status.c b/lib/handler/status.c
index bacd6c59b1..b40d2bb5f9 100644
--- a/lib/handler/status.c
+++ b/lib/handler/status.c
@@ -38,7 +38,7 @@ struct st_h2o_status_context_t {
h2o_multithread_receiver_t receiver;
};
-struct status_ctx {
+struct st_status_ctx_t {
int active;
void *ctx;
};
@@ -49,7 +49,7 @@ struct st_h2o_status_collector_t {
} src;
pthread_mutex_t mutex;
size_t num_remaining_threads;
- H2O_VECTOR(struct status_ctx) status_ctx;
+ H2O_VECTOR(struct st_status_ctx_t) status_ctx;
};
struct st_h2o_status_message_t {
@@ -193,14 +193,7 @@ static int on_req_json(struct st_h2o_root_status_handler_t *self, h2o_req_t *req
}
}
if (sh->init) {
- h2o_iovec_t err = {NULL, 0};
- void *p;
- p = sh->init(&err);
- if (!p) {
- h2o_send_error_400(req, "Invalid Request", err.base, 0);
- return 0;
- }
- collector->status_ctx.entries[collector->status_ctx.size].ctx = p;
+ collector->status_ctx.entries[collector->status_ctx.size].ctx = sh->init();
}
collector->status_ctx.entries[collector->status_ctx.size].active = 1;
Skip:
diff --git a/lib/handler/status/errors.c b/lib/handler/status/errors.c
index daee7cf2a1..e840b253a2 100644
--- a/lib/handler/status/errors.c
+++ b/lib/handler/status/errors.c
@@ -24,23 +24,27 @@
#include
struct errors_status_ctx {
- uint64_t agg_errors_http1[E_HTTP_MAX];
+ uint64_t agg_errors_http1[H2O_STATUS_ERROR_MAX];
uint64_t agg_errors_http2[H2O_HTTP2_ERROR_MAX];
+ uint64_t h2_read_closed;
+ uint64_t h2_write_closed;
};
static void errors_status_per_thread(void *priv, h2o_context_t *ctx)
{
size_t i;
struct errors_status_ctx *esc = priv;
- for (i = 0; i < E_HTTP_MAX; i++) {
- esc->agg_errors_http1[i] += ctx->emitted_errors[i];
+ for (i = 0; i < H2O_STATUS_ERROR_MAX; i++) {
+ esc->agg_errors_http1[i] += ctx->http1_status_errors[i];
}
- for (i = 0; i < E_HTTP_MAX; i++) {
- esc->agg_errors_http2[i] += ctx->http2.emitted_errors[i];
+ for (i = 0; i < H2O_HTTP2_ERROR_MAX; i++) {
+ esc->agg_errors_http2[i] += ctx->http2.events.protocol_level_errors[i];
}
+ esc->h2_read_closed += ctx->http2.events.read_closed;
+ esc->h2_write_closed += ctx->http2.events.write_closed;
}
-static void *errors_status_init(h2o_iovec_t *err)
+static void *errors_status_init(void)
{
struct errors_status_ctx *ret;
@@ -55,6 +59,10 @@ static h2o_iovec_t errors_status_final(void *priv, h2o_globalconf_t *gconf, h2o_
struct errors_status_ctx *esc = priv;
h2o_iovec_t ret;
+#define H1_AGG_ERR(status_) \
+ esc->agg_errors_http1[H2O_STATUS_ERROR_ ## status_]
+#define H2_AGG_ERR(err_) \
+ esc->agg_errors_http2[-H2O_HTTP2_ERROR_ ## err_]
#define BUFSIZE (2*1024)
ret.base = h2o_mem_alloc_pool(&req->pool, BUFSIZE);
ret.len = snprintf(ret.base, BUFSIZE, ",\n"
@@ -78,21 +86,22 @@ static h2o_iovec_t errors_status_final(void *priv, h2o_globalconf_t *gconf, h2o_
" \"http2-errors-compression\": %" PRIu64 ", \n"
" \"http2-errors-connect\": %" PRIu64 ", \n"
" \"http2-errors-enhance_your_calm\": %" PRIu64 ", \n"
- " \"http2-errors-inadequate_security\": %" PRIu64 "\n",
- esc->agg_errors_http1[E_HTTP_400], esc->agg_errors_http1[E_HTTP_403],
- esc->agg_errors_http1[E_HTTP_404], esc->agg_errors_http1[E_HTTP_405],
- esc->agg_errors_http1[E_HTTP_416], esc->agg_errors_http1[E_HTTP_417],
- esc->agg_errors_http1[E_HTTP_500], esc->agg_errors_http1[E_HTTP_502],
- esc->agg_errors_http1[E_HTTP_503],
- esc->agg_errors_http2[-H2O_HTTP2_ERROR_PROTOCOL], esc->agg_errors_http2[-H2O_HTTP2_ERROR_INTERNAL],
- esc->agg_errors_http2[-H2O_HTTP2_ERROR_FLOW_CONTROL], esc->agg_errors_http2[-H2O_HTTP2_ERROR_SETTINGS_TIMEOUT],
- esc->agg_errors_http2[-H2O_HTTP2_ERROR_STREAM_CLOSED], esc->agg_errors_http2[-H2O_HTTP2_ERROR_FRAME_SIZE],
- esc->agg_errors_http2[-H2O_HTTP2_ERROR_REFUSED_STREAM], esc->agg_errors_http2[-H2O_HTTP2_ERROR_CANCEL],
- esc->agg_errors_http2[-H2O_HTTP2_ERROR_COMPRESSION], esc->agg_errors_http2[-H2O_HTTP2_ERROR_CONNECT],
- esc->agg_errors_http2[-H2O_HTTP2_ERROR_ENHANCE_YOUR_CALM], esc->agg_errors_http2[-H2O_HTTP2_ERROR_INADEQUATE_SECURITY]);
+ " \"http2-errors-inadequate_security\": %" PRIu64 ", \n"
+ " \"http2-read-closed\": %" PRIu64 ", \n"
+ " \"http2-write-closed\": %" PRIu64 "\n",
+ H1_AGG_ERR(400), H1_AGG_ERR(403), H1_AGG_ERR(404),
+ H1_AGG_ERR(405), H1_AGG_ERR(416), H1_AGG_ERR(417),
+ H1_AGG_ERR(500), H1_AGG_ERR(502), H1_AGG_ERR(503),
+ H2_AGG_ERR(PROTOCOL), H2_AGG_ERR(INTERNAL), H2_AGG_ERR(FLOW_CONTROL),
+ H2_AGG_ERR(SETTINGS_TIMEOUT), H2_AGG_ERR(STREAM_CLOSED), H2_AGG_ERR(FRAME_SIZE),
+ H2_AGG_ERR(REFUSED_STREAM), H2_AGG_ERR(CANCEL), H2_AGG_ERR(COMPRESSION),
+ H2_AGG_ERR(CONNECT), H2_AGG_ERR(ENHANCE_YOUR_CALM), H2_AGG_ERR(INADEQUATE_SECURITY),
+ esc->h2_read_closed, esc->h2_write_closed);
free(esc);
return ret;
#undef BUFSIZE
+#undef H1_AGG_ERR
+#undef H2_AGG_ERR
}
h2o_status_handler_t errors_status_handler = {
diff --git a/lib/handler/status/requests.c b/lib/handler/status/requests.c
index c67e89b1b5..439d2da093 100644
--- a/lib/handler/status/requests.c
+++ b/lib/handler/status/requests.c
@@ -22,9 +22,10 @@
#include "h2o.h"
-struct requests_status_ctx {
+struct st_requests_status_ctx_t {
h2o_logconf_t *logconf;
h2o_iovec_t req_data;
+ h2o_iovec_t init_error;
int first; /* used to know if a thread is the first one, we skip the leading
* coma in that case. */
};
@@ -32,7 +33,7 @@ struct requests_status_ctx {
struct st_collect_req_status_cbdata_t {
h2o_logconf_t *logconf;
h2o_buffer_t *buffer;
- int first; /* see requests_status_ctx */
+ int first; /* see st_requests_status_ctx_t */
};
static int collect_req_status(h2o_req_t *req, void *_cbdata)
@@ -67,9 +68,14 @@ static int collect_req_status(h2o_req_t *req, void *_cbdata)
static void requests_status_per_thread(void *priv, h2o_context_t *ctx)
{
- struct requests_status_ctx *rsc = priv;
+ struct st_requests_status_ctx_t *rsc = priv;
struct st_collect_req_status_cbdata_t cbdata = {rsc->logconf};
+ /* we encountered an error at init() time, return early */
+ if (rsc->init_error.base) {
+ return;
+ }
+
cbdata.first = rsc->first;
if (rsc->first) {
rsc->first = 0;
@@ -87,10 +93,11 @@ static void requests_status_per_thread(void *priv, h2o_context_t *ctx)
h2o_buffer_dispose(&cbdata.buffer);
}
-static void *requests_status_init(h2o_iovec_t *error)
+static void *requests_status_init(void)
{
- struct requests_status_ctx *rsc;
+ struct st_requests_status_ctx_t *rsc;
rsc = h2o_mem_alloc(sizeof(*rsc));
+ memset(rsc, 0, sizeof(*rsc));
rsc->first = 1;
#define ELEMENT(key, expr) "\"" key "\": \"" expr "\""
@@ -128,9 +135,8 @@ static void *requests_status_init(h2o_iovec_t *error)
if ((rsc->logconf = h2o_logconf_compile(fmt, H2O_LOGCONF_ESCAPE_JSON, errbuf)) == NULL) {
h2o_iovec_t resp = h2o_concat(NULL, h2o_iovec_init(H2O_STRLIT("failed to compile log format:")),
h2o_iovec_init(errbuf, strlen(errbuf)));
- *error = resp;
- free(rsc);
- return NULL;
+ rsc->init_error = resp;
+ return rsc;
}
}
@@ -141,8 +147,13 @@ static void *requests_status_init(h2o_iovec_t *error)
static h2o_iovec_t requests_status_final(void *priv, h2o_globalconf_t *gconf, h2o_req_t *req)
{
h2o_iovec_t ret;
- struct requests_status_ctx *rsc = priv;
+ struct st_requests_status_ctx_t *rsc = priv;
+ if (rsc->init_error.base) {
+ ret = h2o_strdup(&req->pool, rsc->init_error.base, rsc->init_error.len);
+ free(rsc->init_error.base);
+ goto out;
+ }
#define JSON_FOOTER "\n ]"
#define JSON_FOOTER_LEN 3
ret.base = h2o_mem_alloc_pool(&req->pool, rsc->req_data.len + JSON_FOOTER_LEN);
@@ -154,6 +165,7 @@ static h2o_iovec_t requests_status_final(void *priv, h2o_globalconf_t *gconf, h2
h2o_logconf_dispose(rsc->logconf);
free(rsc->req_data.base);
+out:
free(rsc);
return ret;
}
diff --git a/lib/http1.c b/lib/http1.c
index 53f5926f4e..d797e0978c 100644
--- a/lib/http1.c
+++ b/lib/http1.c
@@ -147,14 +147,19 @@ static void process_request(struct st_h2o_http1_conn_t *conn)
h2o_process_request(&conn->req);
}
-static void entity_read_send_error(struct st_h2o_http1_conn_t *conn, int status, const char *reason, const char *body)
-{
- conn->_req_entity_reader = NULL;
- set_timeout(conn, NULL, NULL);
- h2o_socket_read_stop(conn->sock);
- conn->req.http1_is_persistent = 0;
- h2o_send_error_generic(&conn->req, status, reason, body, H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION);
-}
+#define DECL_ENTITY_READ_SEND_ERROR_XXX(status_) \
+ static void entity_read_send_error_ ## status_(struct st_h2o_http1_conn_t *conn, const char *reason, const char *body) \
+ { \
+ conn->_req_entity_reader = NULL; \
+ set_timeout(conn, NULL, NULL); \
+ h2o_socket_read_stop(conn->sock); \
+ conn->req.http1_is_persistent = 0; \
+ conn->super.ctx->http1_status_errors[H2O_STATUS_ERROR_ ## status_]++; \
+ h2o_send_error_generic(&conn->req, status_, reason, body, H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION); \
+ }
+
+DECL_ENTITY_READ_SEND_ERROR_XXX(400)
+DECL_ENTITY_READ_SEND_ERROR_XXX(413)
static void on_entity_read_complete(struct st_h2o_http1_conn_t *conn)
{
@@ -178,7 +183,7 @@ static void handle_chunked_entity_read(struct st_h2o_http1_conn_t *conn)
inbuf->size = reader->prev_input_size + bufsz;
reader->prev_input_size = inbuf->size;
if (ret != -1 && inbuf->size - conn->_reqsize >= conn->super.ctx->globalconf->max_request_entity_size) {
- entity_read_send_error(conn, 413, "Request Entity Too Large", "request entity is too large");
+ entity_read_send_error_413(conn, "Request Entity Too Large", "request entity is too large");
return;
}
if (ret < 0) {
@@ -187,7 +192,7 @@ static void handle_chunked_entity_read(struct st_h2o_http1_conn_t *conn)
return;
}
/* error */
- entity_read_send_error(conn, 400, "Invalid Request", "broken chunked-encoding");
+ entity_read_send_error_400(conn, "Invalid Request", "broken chunked-encoding");
return;
}
/* complete */
@@ -242,7 +247,7 @@ static int create_entity_reader(struct st_h2o_http1_conn_t *conn, const struct p
if (entity_header->name_len == sizeof("transfer-encoding") - 1) {
/* transfer-encoding */
if (!h2o_lcstris(entity_header->value, entity_header->value_len, H2O_STRLIT("chunked"))) {
- entity_read_send_error(conn, 400, "Invalid Request", "unknown transfer-encoding");
+ entity_read_send_error_400(conn, "Invalid Request", "unknown transfer-encoding");
return -1;
}
return create_chunked_entity_reader(conn);
@@ -250,11 +255,11 @@ static int create_entity_reader(struct st_h2o_http1_conn_t *conn, const struct p
/* content-length */
size_t content_length = h2o_strtosize(entity_header->value, entity_header->value_len);
if (content_length == SIZE_MAX) {
- entity_read_send_error(conn, 400, "Invalid Request", "broken content-length header");
+ entity_read_send_error_400(conn, "Invalid Request", "broken content-length header");
return -1;
}
if (content_length > conn->super.ctx->globalconf->max_request_entity_size) {
- entity_read_send_error(conn, 413, "Request Entity Too Large", "request entity is too large");
+ entity_read_send_error_413(conn, "Request Entity Too Large", "request entity is too large");
return -1;
}
return create_content_length_entity_reader(conn, (size_t)content_length);
diff --git a/lib/http2/connection.c b/lib/http2/connection.c
index 5d49a73c6d..fe2bb6df41 100644
--- a/lib/http2/connection.c
+++ b/lib/http2/connection.c
@@ -276,9 +276,9 @@ void close_connection(h2o_http2_conn_t *conn)
}
}
-static inline void h2o_context_report_http2_error(h2o_context_t *ctx, int errnum)
+static inline void h2o_context_http2_protocol_error_count(h2o_context_t *ctx, int errnum)
{
- ctx->http2.emitted_errors[-errnum]++;
+ ctx->http2.events.protocol_level_errors[-errnum]++;
}
void send_stream_error(h2o_http2_conn_t *conn, uint32_t stream_id, int errnum)
@@ -286,7 +286,7 @@ void send_stream_error(h2o_http2_conn_t *conn, uint32_t stream_id, int errnum)
assert(stream_id != 0);
assert(conn->state < H2O_HTTP2_CONN_STATE_IS_CLOSING);
- h2o_context_report_http2_error(conn->super.ctx, -errnum);
+ h2o_context_http2_protocol_error_count(conn->super.ctx, -errnum);
h2o_http2_encode_rst_stream_frame(&conn->_write.buf, stream_id, -errnum);
h2o_http2_conn_request_write(conn);
@@ -866,7 +866,7 @@ static void on_read(h2o_socket_t *sock, const char *err)
h2o_http2_conn_t *conn = sock->data;
if (err != NULL) {
- h2o_context_report_http2_error(conn->super.ctx, H2O_HTTP2_ERROR_OTHER);
+ conn->super.ctx->http2.events.read_closed++;
h2o_socket_read_stop(conn->sock);
close_connection(conn);
return;
@@ -937,7 +937,7 @@ static void on_write_complete(h2o_socket_t *sock, const char *err)
/* close by error if necessary */
if (err != NULL) {
- h2o_context_report_http2_error(conn->super.ctx, H2O_HTTP2_ERROR_OTHER);
+ conn->super.ctx->http2.events.write_closed++;
close_connection_now(conn);
return;
}
From a24ec3d13a6006ccffa8802fec3c22f0459d965f Mon Sep 17 00:00:00 2001
From: Frederik Deweerdt
Date: Wed, 25 May 2016 10:12:02 -0700
Subject: [PATCH 6/8] Various renames
s/http1_status_errors/emitted_error_status/
s/DECL_H2O_SEND_ERROR_XXX/H2O_SEND_ERROR_XXX/
s/errors_status_ctx/st_errors_status_ctx_t/
rename aggregated errors to something that matches the data being
aggregated
---
include/h2o.h | 26 +++++++++++++-------------
lib/core/request.c | 2 +-
lib/handler/status/errors.c | 20 ++++++++++----------
lib/http1.c | 2 +-
4 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/include/h2o.h b/include/h2o.h
index 221665014f..77c186f38d 100644
--- a/include/h2o.h
+++ b/include/h2o.h
@@ -558,9 +558,9 @@ struct st_h2o_context_t {
} _timestamp_cache;
/**
- * counter for http1 errors internally emitted by h2o
+ * counter for http1 error status internally emitted by h2o
*/
- uint64_t http1_status_errors[H2O_STATUS_ERROR_MAX];
+ uint64_t emitted_error_status[H2O_STATUS_ERROR_MAX];
H2O_VECTOR(h2o_pathconf_t *) _pathconfs_inited;
};
@@ -1296,22 +1296,22 @@ void h2o_send_inline(h2o_req_t *req, const char *body, size_t len);
* sends the given information as an error response to the client
*/
void h2o_send_error_generic(h2o_req_t *req, int status, const char *reason, const char *body, int flags);
-#define DECL_H2O_SEND_ERROR_XXX(status) \
+#define H2O_SEND_ERROR_XXX(status) \
static inline void h2o_send_error_ ## status(h2o_req_t *req, const char *reason, const char *body, int flags) \
{ \
- req->conn->ctx->http1_status_errors[H2O_STATUS_ERROR_ ## status]++; \
+ req->conn->ctx->emitted_error_status[H2O_STATUS_ERROR_ ## status]++; \
h2o_send_error_generic(req, status, reason, body, flags); \
}
-DECL_H2O_SEND_ERROR_XXX(400)
-DECL_H2O_SEND_ERROR_XXX(403)
-DECL_H2O_SEND_ERROR_XXX(404)
-DECL_H2O_SEND_ERROR_XXX(405)
-DECL_H2O_SEND_ERROR_XXX(416)
-DECL_H2O_SEND_ERROR_XXX(417)
-DECL_H2O_SEND_ERROR_XXX(500)
-DECL_H2O_SEND_ERROR_XXX(502)
-DECL_H2O_SEND_ERROR_XXX(503)
+H2O_SEND_ERROR_XXX(400)
+H2O_SEND_ERROR_XXX(403)
+H2O_SEND_ERROR_XXX(404)
+H2O_SEND_ERROR_XXX(405)
+H2O_SEND_ERROR_XXX(416)
+H2O_SEND_ERROR_XXX(417)
+H2O_SEND_ERROR_XXX(500)
+H2O_SEND_ERROR_XXX(502)
+H2O_SEND_ERROR_XXX(503)
/**
* sends error response using zero timeout; can be called by output filters while processing the headers
diff --git a/lib/core/request.c b/lib/core/request.c
index b86a38d136..fcdf759b34 100644
--- a/lib/core/request.c
+++ b/lib/core/request.c
@@ -483,7 +483,7 @@ void h2o_send_error_generic(h2o_req_t *req, int status, const char *reason, cons
{ \
struct st_send_error_deferred_t *args = H2O_STRUCT_FROM_MEMBER(struct st_send_error_deferred_t, _timeout, entry); \
reset_response(args->req); \
- args->req->conn->ctx->http1_status_errors[H2O_STATUS_ERROR_ ## status_]++; \
+ args->req->conn->ctx->emitted_error_status[H2O_STATUS_ERROR_ ## status_]++; \
h2o_send_error_generic(args->req, args->status, args->reason, args->body, args->flags); \
} \
\
diff --git a/lib/handler/status/errors.c b/lib/handler/status/errors.c
index e840b253a2..ecbf02f7ed 100644
--- a/lib/handler/status/errors.c
+++ b/lib/handler/status/errors.c
@@ -23,9 +23,9 @@
#include "h2o.h"
#include
-struct errors_status_ctx {
- uint64_t agg_errors_http1[H2O_STATUS_ERROR_MAX];
- uint64_t agg_errors_http2[H2O_HTTP2_ERROR_MAX];
+struct st_errors_status_ctx_t {
+ uint64_t emitted_status_errors[H2O_STATUS_ERROR_MAX];
+ uint64_t h2_protocol_level_errors[H2O_HTTP2_ERROR_MAX];
uint64_t h2_read_closed;
uint64_t h2_write_closed;
};
@@ -33,12 +33,12 @@ struct errors_status_ctx {
static void errors_status_per_thread(void *priv, h2o_context_t *ctx)
{
size_t i;
- struct errors_status_ctx *esc = priv;
+ struct st_errors_status_ctx_t *esc = priv;
for (i = 0; i < H2O_STATUS_ERROR_MAX; i++) {
- esc->agg_errors_http1[i] += ctx->http1_status_errors[i];
+ esc->emitted_status_errors[i] += ctx->emitted_error_status[i];
}
for (i = 0; i < H2O_HTTP2_ERROR_MAX; i++) {
- esc->agg_errors_http2[i] += ctx->http2.events.protocol_level_errors[i];
+ esc->h2_protocol_level_errors[i] += ctx->http2.events.protocol_level_errors[i];
}
esc->h2_read_closed += ctx->http2.events.read_closed;
esc->h2_write_closed += ctx->http2.events.write_closed;
@@ -46,7 +46,7 @@ static void errors_status_per_thread(void *priv, h2o_context_t *ctx)
static void *errors_status_init(void)
{
- struct errors_status_ctx *ret;
+ struct st_errors_status_ctx_t *ret;
ret = h2o_mem_alloc(sizeof(*ret));
memset(ret, 0, sizeof(*ret));
@@ -56,13 +56,13 @@ static void *errors_status_init(void)
static h2o_iovec_t errors_status_final(void *priv, h2o_globalconf_t *gconf, h2o_req_t *req)
{
- struct errors_status_ctx *esc = priv;
+ struct st_errors_status_ctx_t *esc = priv;
h2o_iovec_t ret;
#define H1_AGG_ERR(status_) \
- esc->agg_errors_http1[H2O_STATUS_ERROR_ ## status_]
+ esc->emitted_status_errors[H2O_STATUS_ERROR_ ## status_]
#define H2_AGG_ERR(err_) \
- esc->agg_errors_http2[-H2O_HTTP2_ERROR_ ## err_]
+ esc->h2_protocol_level_errors[-H2O_HTTP2_ERROR_ ## err_]
#define BUFSIZE (2*1024)
ret.base = h2o_mem_alloc_pool(&req->pool, BUFSIZE);
ret.len = snprintf(ret.base, BUFSIZE, ",\n"
diff --git a/lib/http1.c b/lib/http1.c
index d797e0978c..8373699883 100644
--- a/lib/http1.c
+++ b/lib/http1.c
@@ -154,7 +154,7 @@ static void process_request(struct st_h2o_http1_conn_t *conn)
set_timeout(conn, NULL, NULL); \
h2o_socket_read_stop(conn->sock); \
conn->req.http1_is_persistent = 0; \
- conn->super.ctx->http1_status_errors[H2O_STATUS_ERROR_ ## status_]++; \
+ conn->super.ctx->emitted_error_status[H2O_STATUS_ERROR_ ## status_]++; \
h2o_send_error_generic(&conn->req, status_, reason, body, H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION); \
}
From bd158bc124672c6c85f25b5382b58ba8d52df4a9 Mon Sep 17 00:00:00 2001
From: Frederik Deweerdt
Date: Fri, 27 May 2016 09:04:30 -0700
Subject: [PATCH 7/8] - Rename 'errors' to 'events', since we're really
tracking events here - Modify the json key naming: s/_/-/ and add a dot to
denote categories
---
CMakeLists.txt | 2 +-
lib/handler/status.c | 4 +-
lib/handler/status/{errors.c => events.c} | 70 +++++++++++------------
t/50status.t | 8 +--
4 files changed, 42 insertions(+), 42 deletions(-)
rename lib/handler/status/{errors.c => events.c} (61%)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7e0cf97d49..adc7b3c9e5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -239,7 +239,7 @@ SET(LIB_SOURCE_FILES
lib/handler/redirect.c
lib/handler/reproxy.c
lib/handler/status.c
- lib/handler/status/errors.c
+ lib/handler/status/events.c
lib/handler/status/requests.c
lib/handler/configurator/access_log.c
lib/handler/configurator/compress.c
diff --git a/lib/handler/status.c b/lib/handler/status.c
index b40d2bb5f9..a873fbcbf2 100644
--- a/lib/handler/status.c
+++ b/lib/handler/status.c
@@ -21,7 +21,7 @@
*/
#include "h2o.h"
-extern h2o_status_handler_t errors_status_handler;
+extern h2o_status_handler_t events_status_handler;
extern h2o_status_handler_t requests_status_handler;
struct st_h2o_status_logger_t {
@@ -288,5 +288,5 @@ void h2o_status_register(h2o_pathconf_t *conf)
self->super.on_context_dispose = on_context_dispose;
self->super.on_req = on_req;
h2o_config_register_status_handler(conf->global, requests_status_handler);
- h2o_config_register_status_handler(conf->global, errors_status_handler);
+ h2o_config_register_status_handler(conf->global, events_status_handler);
}
diff --git a/lib/handler/status/errors.c b/lib/handler/status/events.c
similarity index 61%
rename from lib/handler/status/errors.c
rename to lib/handler/status/events.c
index ecbf02f7ed..3487e45ad9 100644
--- a/lib/handler/status/errors.c
+++ b/lib/handler/status/events.c
@@ -23,17 +23,17 @@
#include "h2o.h"
#include
-struct st_errors_status_ctx_t {
+struct st_events_status_ctx_t {
uint64_t emitted_status_errors[H2O_STATUS_ERROR_MAX];
uint64_t h2_protocol_level_errors[H2O_HTTP2_ERROR_MAX];
uint64_t h2_read_closed;
uint64_t h2_write_closed;
};
-static void errors_status_per_thread(void *priv, h2o_context_t *ctx)
+static void events_status_per_thread(void *priv, h2o_context_t *ctx)
{
size_t i;
- struct st_errors_status_ctx_t *esc = priv;
+ struct st_events_status_ctx_t *esc = priv;
for (i = 0; i < H2O_STATUS_ERROR_MAX; i++) {
esc->emitted_status_errors[i] += ctx->emitted_error_status[i];
}
@@ -44,9 +44,9 @@ static void errors_status_per_thread(void *priv, h2o_context_t *ctx)
esc->h2_write_closed += ctx->http2.events.write_closed;
}
-static void *errors_status_init(void)
+static void *events_status_init(void)
{
- struct st_errors_status_ctx_t *ret;
+ struct st_events_status_ctx_t *ret;
ret = h2o_mem_alloc(sizeof(*ret));
memset(ret, 0, sizeof(*ret));
@@ -54,9 +54,9 @@ static void *errors_status_init(void)
return ret;
}
-static h2o_iovec_t errors_status_final(void *priv, h2o_globalconf_t *gconf, h2o_req_t *req)
+static h2o_iovec_t events_status_final(void *priv, h2o_globalconf_t *gconf, h2o_req_t *req)
{
- struct st_errors_status_ctx_t *esc = priv;
+ struct st_events_status_ctx_t *esc = priv;
h2o_iovec_t ret;
#define H1_AGG_ERR(status_) \
@@ -66,29 +66,29 @@ static h2o_iovec_t errors_status_final(void *priv, h2o_globalconf_t *gconf, h2o_
#define BUFSIZE (2*1024)
ret.base = h2o_mem_alloc_pool(&req->pool, BUFSIZE);
ret.len = snprintf(ret.base, BUFSIZE, ",\n"
- " \"http1-errors-400\": %" PRIu64 ",\n"
- " \"http1-errors-403\": %" PRIu64 ",\n"
- " \"http1-errors-404\": %" PRIu64 ",\n"
- " \"http1-errors-405\": %" PRIu64 ",\n"
- " \"http1-errors-416\": %" PRIu64 ",\n"
- " \"http1-errors-417\": %" PRIu64 ",\n"
- " \"http1-errors-500\": %" PRIu64 ",\n"
- " \"http1-errors-502\": %" PRIu64 ",\n"
- " \"http1-errors-503\": %" PRIu64 ",\n"
- " \"http2-errors-protocol\": %" PRIu64 ", \n"
- " \"http2-errors-internal\": %" PRIu64 ", \n"
- " \"http2-errors-flow_control\": %" PRIu64 ", \n"
- " \"http2-errors-settings_timeout\": %" PRIu64 ", \n"
- " \"http2-errors-stream_closed\": %" PRIu64 ", \n"
- " \"http2-errors-frame_size\": %" PRIu64 ", \n"
- " \"http2-errors-refused_stream\": %" PRIu64 ", \n"
- " \"http2-errors-cancel\": %" PRIu64 ", \n"
- " \"http2-errors-compression\": %" PRIu64 ", \n"
- " \"http2-errors-connect\": %" PRIu64 ", \n"
- " \"http2-errors-enhance_your_calm\": %" PRIu64 ", \n"
- " \"http2-errors-inadequate_security\": %" PRIu64 ", \n"
- " \"http2-read-closed\": %" PRIu64 ", \n"
- " \"http2-write-closed\": %" PRIu64 "\n",
+ " \"http1-errors.400\": %" PRIu64 ",\n"
+ " \"http1-errors.403\": %" PRIu64 ",\n"
+ " \"http1-errors.404\": %" PRIu64 ",\n"
+ " \"http1-errors.405\": %" PRIu64 ",\n"
+ " \"http1-errors.416\": %" PRIu64 ",\n"
+ " \"http1-errors.417\": %" PRIu64 ",\n"
+ " \"http1-errors.500\": %" PRIu64 ",\n"
+ " \"http1-errors.502\": %" PRIu64 ",\n"
+ " \"http1-errors.503\": %" PRIu64 ",\n"
+ " \"http2-errors.protocol\": %" PRIu64 ", \n"
+ " \"http2-errors.internal\": %" PRIu64 ", \n"
+ " \"http2-errors.flow-control\": %" PRIu64 ", \n"
+ " \"http2-errors.settings-timeout\": %" PRIu64 ", \n"
+ " \"http2-errors.stream-closed\": %" PRIu64 ", \n"
+ " \"http2-errors.frame-size\": %" PRIu64 ", \n"
+ " \"http2-errors.refused-stream\": %" PRIu64 ", \n"
+ " \"http2-errors.cancel\": %" PRIu64 ", \n"
+ " \"http2-errors.compression\": %" PRIu64 ", \n"
+ " \"http2-errors.connect\": %" PRIu64 ", \n"
+ " \"http2-errors.enhance-your-calm\": %" PRIu64 ", \n"
+ " \"http2-errors.inadequate-security\": %" PRIu64 ", \n"
+ " \"http2.read-closed\": %" PRIu64 ", \n"
+ " \"http2.write-closed\": %" PRIu64 "\n",
H1_AGG_ERR(400), H1_AGG_ERR(403), H1_AGG_ERR(404),
H1_AGG_ERR(405), H1_AGG_ERR(416), H1_AGG_ERR(417),
H1_AGG_ERR(500), H1_AGG_ERR(502), H1_AGG_ERR(503),
@@ -104,9 +104,9 @@ static h2o_iovec_t errors_status_final(void *priv, h2o_globalconf_t *gconf, h2o_
#undef H2_AGG_ERR
}
-h2o_status_handler_t errors_status_handler = {
- { H2O_STRLIT("errors") },
- errors_status_init,
- errors_status_per_thread,
- errors_status_final,
+h2o_status_handler_t events_status_handler = {
+ { H2O_STRLIT("events") },
+ events_status_init,
+ events_status_per_thread,
+ events_status_final,
};
diff --git a/t/50status.t b/t/50status.t
index 61bd0d35cd..5abd18b108 100644
--- a/t/50status.t
+++ b/t/50status.t
@@ -36,11 +36,11 @@ hosts:
status: ON
EOT
- my $resp = `curl --silent -o /dev/stderr 'http://127.0.0.1:$server->{port}/s/json?show=main,errors' 2>&1 > /dev/null`;
+ my $resp = `curl --silent -o /dev/stderr 'http://127.0.0.1:$server->{port}/s/json?show=main,events' 2>&1 > /dev/null`;
my $jresp = decode_json("$resp");
is $jresp->{'connections'}, 1, "One connection";
is $jresp->{'requests'}, undef, "Requests not present";
- is $jresp->{'http1-errors-404'}, 0, "Internal errors monitoring";
+ is $jresp->{'http1-errors.404'}, 0, "Internal errors monitoring";
};
subtest "json hander check 404 error counter" => sub {
@@ -54,11 +54,11 @@ hosts:
status: ON
EOT
my $resp = `curl --silent -o /dev/stderr 'http://127.0.0.1:$server->{port}/beeb98fcf148317be5fe5d763c658bc9ea9c087a' 2>&1 > /dev/null`;
- my $resp = `curl --silent -o /dev/stderr 'http://127.0.0.1:$server->{port}/s/json?show=errors' 2>&1 > /dev/null`;
+ my $resp = `curl --silent -o /dev/stderr 'http://127.0.0.1:$server->{port}/s/json?show=events' 2>&1 > /dev/null`;
my $jresp = decode_json("$resp");
is $jresp->{'connections'}, undef, "Connections not present";
is $jresp->{'requests'}, undef, "Requests not present";
- is $jresp->{'http1-errors-404'}, 1, "Found the 404 error";
+ is $jresp->{'http1-errors.404'}, 1, "Found the 404 error";
};
From a3b35d17c2aa7f98f0db9328828c575f13c993f9 Mon Sep 17 00:00:00 2001
From: Frederik Deweerdt
Date: Tue, 31 May 2016 09:49:48 -0700
Subject: [PATCH 8/8] s/http1-errors/status-errors/ in the json output as well
---
lib/handler/status/events.c | 18 +++++++++---------
t/50status.t | 4 ++--
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/lib/handler/status/events.c b/lib/handler/status/events.c
index 3487e45ad9..147fe8e568 100644
--- a/lib/handler/status/events.c
+++ b/lib/handler/status/events.c
@@ -66,15 +66,15 @@ static h2o_iovec_t events_status_final(void *priv, h2o_globalconf_t *gconf, h2o_
#define BUFSIZE (2*1024)
ret.base = h2o_mem_alloc_pool(&req->pool, BUFSIZE);
ret.len = snprintf(ret.base, BUFSIZE, ",\n"
- " \"http1-errors.400\": %" PRIu64 ",\n"
- " \"http1-errors.403\": %" PRIu64 ",\n"
- " \"http1-errors.404\": %" PRIu64 ",\n"
- " \"http1-errors.405\": %" PRIu64 ",\n"
- " \"http1-errors.416\": %" PRIu64 ",\n"
- " \"http1-errors.417\": %" PRIu64 ",\n"
- " \"http1-errors.500\": %" PRIu64 ",\n"
- " \"http1-errors.502\": %" PRIu64 ",\n"
- " \"http1-errors.503\": %" PRIu64 ",\n"
+ " \"status-errors.400\": %" PRIu64 ",\n"
+ " \"status-errors.403\": %" PRIu64 ",\n"
+ " \"status-errors.404\": %" PRIu64 ",\n"
+ " \"status-errors.405\": %" PRIu64 ",\n"
+ " \"status-errors.416\": %" PRIu64 ",\n"
+ " \"status-errors.417\": %" PRIu64 ",\n"
+ " \"status-errors.500\": %" PRIu64 ",\n"
+ " \"status-errors.502\": %" PRIu64 ",\n"
+ " \"status-errors.503\": %" PRIu64 ",\n"
" \"http2-errors.protocol\": %" PRIu64 ", \n"
" \"http2-errors.internal\": %" PRIu64 ", \n"
" \"http2-errors.flow-control\": %" PRIu64 ", \n"
diff --git a/t/50status.t b/t/50status.t
index 5abd18b108..da7987eee4 100644
--- a/t/50status.t
+++ b/t/50status.t
@@ -40,7 +40,7 @@ EOT
my $jresp = decode_json("$resp");
is $jresp->{'connections'}, 1, "One connection";
is $jresp->{'requests'}, undef, "Requests not present";
- is $jresp->{'http1-errors.404'}, 0, "Internal errors monitoring";
+ is $jresp->{'status-errors.404'}, 0, "Internal errors monitoring";
};
subtest "json hander check 404 error counter" => sub {
@@ -58,7 +58,7 @@ EOT
my $jresp = decode_json("$resp");
is $jresp->{'connections'}, undef, "Connections not present";
is $jresp->{'requests'}, undef, "Requests not present";
- is $jresp->{'http1-errors.404'}, 1, "Found the 404 error";
+ is $jresp->{'status-errors.404'}, 1, "Found the 404 error";
};