diff --git a/ChangeLog b/ChangeLog index 6a6b2829..a86a3507 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +v1.0.14 +-------------------------------------------------------------------------------- + * fixed segfault on connection shutdown + v1.0.13 -------------------------------------------------------------------------------- * reworked connection state handling. Improved scoreboard update (server-status). diff --git a/configure.ac b/configure.ac index 50ed8ea6..9e606512 100644 --- a/configure.ac +++ b/configure.ac @@ -14,7 +14,7 @@ # AC_PREREQ([2.69]) -AC_INIT([mod_http2], [1.0.13], [stefan.eissing@greenbytes.de]) +AC_INIT([mod_http2], [1.0.14], [stefan.eissing@greenbytes.de]) LT_PREREQ([2.2.6]) LT_INIT() diff --git a/mod-h2.xcodeproj/project.pbxproj b/mod-h2.xcodeproj/project.pbxproj index dbfae548..d38e64d4 100644 --- a/mod-h2.xcodeproj/project.pbxproj +++ b/mod-h2.xcodeproj/project.pbxproj @@ -72,12 +72,17 @@ B25574A01BEB6EFC0058F97B /* h2_workers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = h2_workers.h; sourceTree = ""; }; B25574A21BEB6EFC0058F97B /* h2.m4 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = h2.m4; sourceTree = ""; }; B25574A41BEB6EFC0058F97B /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.am; sourceTree = ""; }; - B25574A61BEB6EFC0058F97B /* mod_h2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mod_h2.h; sourceTree = ""; }; B25574A71BEB6EFC0058F97B /* mod_http2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mod_http2.c; sourceTree = ""; }; B256C4BA1ADD5FF10042C760 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; B27BBD2D1A6575C200C58A41 /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = ""; }; B2A6EF2A1A9E3A93005DFC5B /* INSTALL */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = INSTALL; sourceTree = ""; }; B2A6EF2C1A9F2452005DFC5B /* DISCUSS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DISCUSS; sourceTree = ""; }; + B2AB9AB71C2ADBE100908DD6 /* h2_filter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h2_filter.c; sourceTree = ""; }; + B2AB9AB81C2ADBE100908DD6 /* h2_filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = h2_filter.h; sourceTree = ""; }; + B2AB9AB91C2ADBE100908DD6 /* h2_push.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h2_push.c; sourceTree = ""; }; + B2AB9ABA1C2ADBE100908DD6 /* h2_push.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = h2_push.h; sourceTree = ""; }; + B2AB9ABB1C2ADBE100908DD6 /* h2_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = h2_version.h; sourceTree = ""; }; + B2AB9ABC1C2ADBE100908DD6 /* mod_http2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mod_http2.h; sourceTree = ""; }; B2E5D7841AAEEF8C001FD280 /* configure.ac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = configure.ac; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.sh; }; B2E5D7851AAF1D87001FD280 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.am; sourceTree = ""; }; B2E5D7861AAF1F4D001FD280 /* AUTHORS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS; sourceTree = ""; }; @@ -113,47 +118,52 @@ B255746F1BEB6EFC0058F97B /* h2_bucket_eos.h */, B25574701BEB6EFC0058F97B /* h2_config.c */, B25574711BEB6EFC0058F97B /* h2_config.h */, - B25574721BEB6EFC0058F97B /* h2_conn.c */, B25574741BEB6EFC0058F97B /* h2_conn_io.c */, - B25574731BEB6EFC0058F97B /* h2_conn.h */, B25574751BEB6EFC0058F97B /* h2_conn_io.h */, + B25574721BEB6EFC0058F97B /* h2_conn.c */, + B25574731BEB6EFC0058F97B /* h2_conn.h */, B25574761BEB6EFC0058F97B /* h2_ctx.c */, B25574771BEB6EFC0058F97B /* h2_ctx.h */, + B2AB9AB71C2ADBE100908DD6 /* h2_filter.c */, + B2AB9AB81C2ADBE100908DD6 /* h2_filter.h */, B25574781BEB6EFC0058F97B /* h2_from_h1.c */, B25574791BEB6EFC0058F97B /* h2_from_h1.h */, B255747A1BEB6EFC0058F97B /* h2_h2.c */, B255747B1BEB6EFC0058F97B /* h2_h2.h */, - B255747C1BEB6EFC0058F97B /* h2_io.c */, - B255747D1BEB6EFC0058F97B /* h2_io.h */, B255747E1BEB6EFC0058F97B /* h2_io_set.c */, B255747F1BEB6EFC0058F97B /* h2_io_set.h */, + B255747C1BEB6EFC0058F97B /* h2_io.c */, + B255747D1BEB6EFC0058F97B /* h2_io.h */, B25574801BEB6EFC0058F97B /* h2_mplx.c */, B25574811BEB6EFC0058F97B /* h2_mplx.h */, B25574821BEB6EFC0058F97B /* h2_private.h */, + B2AB9AB91C2ADBE100908DD6 /* h2_push.c */, + B2AB9ABA1C2ADBE100908DD6 /* h2_push.h */, B25574831BEB6EFC0058F97B /* h2_request.c */, B25574841BEB6EFC0058F97B /* h2_request.h */, B25574851BEB6EFC0058F97B /* h2_response.c */, B25574861BEB6EFC0058F97B /* h2_response.h */, B25574871BEB6EFC0058F97B /* h2_session.c */, B25574881BEB6EFC0058F97B /* h2_session.h */, - B25574891BEB6EFC0058F97B /* h2_stream.c */, - B255748A1BEB6EFC0058F97B /* h2_stream.h */, B255748B1BEB6EFC0058F97B /* h2_stream_set.c */, B255748C1BEB6EFC0058F97B /* h2_stream_set.h */, + B25574891BEB6EFC0058F97B /* h2_stream.c */, + B255748A1BEB6EFC0058F97B /* h2_stream.h */, B255748D1BEB6EFC0058F97B /* h2_switch.c */, B255748E1BEB6EFC0058F97B /* h2_switch.h */, - B255748F1BEB6EFC0058F97B /* h2_task.c */, - B25574901BEB6EFC0058F97B /* h2_task.h */, B25574911BEB6EFC0058F97B /* h2_task_input.c */, B25574921BEB6EFC0058F97B /* h2_task_input.h */, B25574931BEB6EFC0058F97B /* h2_task_output.c */, B25574941BEB6EFC0058F97B /* h2_task_output.h */, B25574951BEB6EFC0058F97B /* h2_task_queue.c */, B25574961BEB6EFC0058F97B /* h2_task_queue.h */, + B255748F1BEB6EFC0058F97B /* h2_task.c */, + B25574901BEB6EFC0058F97B /* h2_task.h */, B25574971BEB6EFC0058F97B /* h2_to_h1.c */, B25574981BEB6EFC0058F97B /* h2_to_h1.h */, B25574991BEB6EFC0058F97B /* h2_util.c */, B255749A1BEB6EFC0058F97B /* h2_util.h */, + B2AB9ABB1C2ADBE100908DD6 /* h2_version.h */, B255749C1BEB6EFC0058F97B /* h2_version.h.in */, B255749D1BEB6EFC0058F97B /* h2_worker.c */, B255749E1BEB6EFC0058F97B /* h2_worker.h */, @@ -161,8 +171,8 @@ B25574A01BEB6EFC0058F97B /* h2_workers.h */, B25574A11BEB6EFC0058F97B /* m4 */, B25574A41BEB6EFC0058F97B /* Makefile.am */, - B25574A61BEB6EFC0058F97B /* mod_h2.h */, B25574A71BEB6EFC0058F97B /* mod_http2.c */, + B2AB9ABC1C2ADBE100908DD6 /* mod_http2.h */, ); path = mod_http2; sourceTree = ""; diff --git a/mod_http2/.gitignore b/mod_http2/.gitignore index e51662e8..2cc059ac 100644 --- a/mod_http2/.gitignore +++ b/mod_http2/.gitignore @@ -3,6 +3,5 @@ *.lo *.la .libs -h2_version.h Makefile.in Makefile diff --git a/mod_http2/h2_conn.c b/mod_http2/h2_conn.c index f7048602..262748fc 100644 --- a/mod_http2/h2_conn.c +++ b/mod_http2/h2_conn.c @@ -204,58 +204,53 @@ apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c) static void fix_event_conn(conn_rec *c, conn_rec *master); -conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *pool) +conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *p, + apr_thread_t *thread, apr_socket_t *socket) { conn_rec *c; AP_DEBUG_ASSERT(master); - + ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master, + "h2_conn(%ld): created from master", master->id); + /* This is like the slave connection creation from 2.5-DEV. A * very efficient way - not sure how compatible this is, since * the core hooks are no longer run. * But maybe it's is better this way, not sure yet. */ - c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec)); + c = (conn_rec *) apr_palloc(p, sizeof(conn_rec)); if (c == NULL) { - ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool, + ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master, APLOGNO(02913) "h2_task: creating conn"); return NULL; } memcpy(c, master, sizeof(conn_rec)); - c->id = (master->id & (long)pool); - c->master = master; - c->input_filters = NULL; - c->output_filters = NULL; - c->pool = pool; - return c; -} - -apr_status_t h2_slave_setup(h2_task *task, apr_bucket_alloc_t *bucket_alloc, - apr_thread_t *thread, apr_socket_t *socket) -{ - conn_rec *master = task->mplx->c; - - ap_log_perror(APLOG_MARK, APLOG_TRACE3, 0, task->pool, - "h2_conn(%ld): created from master", master->id); - - /* Ok, we are just about to start processing the connection and - * the worker is calling us to setup all necessary resources. - * We can borrow some from the worker itself and some we do as - * sub-resources from it, so that we get a nice reuse of - * pools. - */ - task->c->pool = task->pool; - task->c->current_thread = thread; - task->c->bucket_alloc = bucket_alloc; + + /* Replace these */ + c->id = (master->id & (long)p); + c->master = master; + c->pool = p; + c->current_thread = thread; + c->conn_config = ap_create_conn_config(p); + c->notes = apr_table_make(p, 5); + c->input_filters = NULL; + c->output_filters = NULL; + c->bucket_alloc = apr_bucket_alloc_create(p); + c->cs = NULL; + c->data_in_input_filters = 0; + c->data_in_output_filters = 0; + c->clogging_input_filters = 1; + c->log = NULL; + c->log_id = NULL; - task->c->conn_config = ap_create_conn_config(task->pool); - task->c->notes = apr_table_make(task->pool, 5); + /* TODO: these should be unique to this thread */ + c->sbh = master->sbh; - /* In order to do this in 2.4.x, we need to add a member to conn_rec */ - task->c->master = master; + /* Simulate that we had already a request on this connection. */ + c->keepalives = 1; - ap_set_module_config(task->c->conn_config, &core_module, socket); + ap_set_module_config(c->conn_config, &core_module, socket); /* This works for mpm_worker so far. Other mpm modules have * different needs, unfortunately. The most interesting one @@ -266,17 +261,14 @@ apr_status_t h2_slave_setup(h2_task *task, apr_bucket_alloc_t *bucket_alloc, /* all fine */ break; case H2_MPM_EVENT: - fix_event_conn(task->c, master); + fix_event_conn(c, master); break; default: /* fingers crossed */ break; } - /* Simulate that we had already a request on this connection. */ - task->c->keepalives = 1; - - return APR_SUCCESS; + return c; } /* This is an internal mpm event.c struct which is disguised diff --git a/mod_http2/h2_conn.h b/mod_http2/h2_conn.h index d59eb46d..66fa2e58 100644 --- a/mod_http2/h2_conn.h +++ b/mod_http2/h2_conn.h @@ -56,9 +56,7 @@ typedef enum { h2_mpm_type_t h2_conn_mpm_type(void); -conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *stream_pool); - -apr_status_t h2_slave_setup(struct h2_task *task, apr_bucket_alloc_t *bucket_alloc, - apr_thread_t *thread, apr_socket_t *socket); +conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *p, + apr_thread_t *thread, apr_socket_t *socket); #endif /* defined(__mod_h2__h2_conn__) */ diff --git a/mod_http2/h2_h2.c b/mod_http2/h2_h2.c index b6a8f9f4..6217c83e 100644 --- a/mod_http2/h2_h2.c +++ b/mod_http2/h2_h2.c @@ -673,7 +673,7 @@ static int h2_h2_post_read_req(request_rec *r) /* setup the correct output filters to process the response * on the proper mod_http2 way. */ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "adding task output filter"); - if (task->serialize_headers) { + if (task->ser_headers) { ap_add_output_filter("H1_TO_H2_RESP", task, r, r->connection); } else { diff --git a/mod_http2/h2_io.c b/mod_http2/h2_io.c index c1d65fb9..2ff45b2c 100644 --- a/mod_http2/h2_io.c +++ b/mod_http2/h2_io.c @@ -32,13 +32,13 @@ #include "h2_task.h" #include "h2_util.h" -h2_io *h2_io_create(int id, apr_pool_t *pool, apr_bucket_alloc_t *bucket_alloc) +h2_io *h2_io_create(int id, apr_pool_t *pool) { h2_io *io = apr_pcalloc(pool, sizeof(*io)); if (io) { io->id = id; io->pool = pool; - io->bucket_alloc = bucket_alloc; + io->bucket_alloc = apr_bucket_alloc_create(pool); } return io; } diff --git a/mod_http2/h2_io.h b/mod_http2/h2_io.h index b89f8b87..22c71c21 100644 --- a/mod_http2/h2_io.h +++ b/mod_http2/h2_io.h @@ -71,7 +71,7 @@ struct h2_io { /** * Creates a new h2_io for the given stream id. */ -h2_io *h2_io_create(int id, apr_pool_t *pool, apr_bucket_alloc_t *bucket_alloc); +h2_io *h2_io_create(int id, apr_pool_t *pool); /** * Frees any resources hold by the h2_io instance. diff --git a/mod_http2/h2_mplx.c b/mod_http2/h2_mplx.c index bdcc2b5e..54c83fdf 100644 --- a/mod_http2/h2_mplx.c +++ b/mod_http2/h2_mplx.c @@ -136,8 +136,6 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, return NULL; } - m->bucket_alloc = apr_bucket_alloc_create(m->pool); - m->q = h2_tq_create(m->pool, h2_config_geti(conf, H2_CONF_MAX_STREAMS)); m->stream_ios = h2_io_set_create(m->pool); m->ready_ios = h2_io_set_create(m->pool); @@ -266,6 +264,8 @@ apr_status_t h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait) workers_unregister(m); status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { + /* disable WINDOW_UPDATE callbacks */ + h2_mplx_set_consumed_cb(m, NULL, NULL); while (!h2_io_set_iter(m->stream_ios, stream_done_iter, m)) { /* iterator until all h2_io have been orphaned or destroyed */ } @@ -901,15 +901,14 @@ static h2_io *open_io(h2_mplx *m, int stream_id) m->spare_pool = NULL; } - io = h2_io_create(stream_id, io_pool, m->bucket_alloc); + io = h2_io_create(stream_id, io_pool); h2_io_set_add(m->stream_ios, io); return io; } -apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, - const h2_request *req, int eos, +apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, const h2_request *req, h2_stream_pri_cmp *cmp, void *ctx) { apr_status_t status; @@ -922,9 +921,8 @@ apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, if (APR_SUCCESS == status) { h2_io *io = open_io(m, stream_id); io->request = req; - io->request_body = !eos; - if (eos) { + if (!io->request->body) { status = h2_io_in_close(io); } @@ -942,9 +940,9 @@ apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, return status; } -h2_task *h2_mplx_pop_task(h2_mplx *m, h2_worker *w, int *has_more) +const h2_request *h2_mplx_pop_request(h2_mplx *m, int *has_more) { - h2_task *task = NULL; + const h2_request *req = NULL; apr_status_t status; AP_DEBUG_ASSERT(m); @@ -955,18 +953,15 @@ h2_task *h2_mplx_pop_task(h2_mplx *m, h2_worker *w, int *has_more) status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { int sid; - while (!task && (sid = h2_tq_shift(m->q)) > 0) { - /* Anything not already setup correctly in the task - * needs to be so now, as task will be executed right about - * when this method returns. */ + while (!req && (sid = h2_tq_shift(m->q)) > 0) { h2_io *io = h2_io_set_get(m->stream_ios, sid); if (io) { - task = h2_worker_create_task(w, m, io->request, !io->request_body); + req = io->request; } } *has_more = !h2_tq_empty(m->q); apr_thread_mutex_unlock(m->lock); } - return task; + return req; } diff --git a/mod_http2/h2_mplx.h b/mod_http2/h2_mplx.h index 5e4831c3..cc791764 100644 --- a/mod_http2/h2_mplx.h +++ b/mod_http2/h2_mplx.h @@ -44,7 +44,6 @@ struct h2_stream; struct h2_request; struct h2_io_set; struct apr_thread_cond_t; -struct h2_worker; struct h2_workers; struct h2_stream_set; struct h2_task_queue; @@ -65,7 +64,6 @@ struct h2_mplx { volatile int refs; conn_rec *c; apr_pool_t *pool; - apr_bucket_alloc_t *bucket_alloc; unsigned int aborted : 1; @@ -165,12 +163,10 @@ apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout, * @param m the multiplexer * @param stream_id the identifier of the stream * @param r the request to be processed - * @param eos if input is complete * @param cmp the stream priority compare function * @param ctx context data for the compare function */ -apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, - const struct h2_request *r, int eos, +apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, const struct h2_request *r, h2_stream_pri_cmp *cmp, void *ctx); /** @@ -182,7 +178,7 @@ apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, */ apr_status_t h2_mplx_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx); -struct h2_task *h2_mplx_pop_task(h2_mplx *mplx, struct h2_worker *w, int *has_more); +const struct h2_request *h2_mplx_pop_request(h2_mplx *mplx, int *has_more); /** * Register a callback for the amount of input data consumed per stream. The diff --git a/mod_http2/h2_push.c b/mod_http2/h2_push.c index 9997c576..1ba4ea6f 100644 --- a/mod_http2/h2_push.c +++ b/mod_http2/h2_push.c @@ -30,6 +30,19 @@ #include "h2_request.h" #include "h2_response.h" +static const char *policy_str(h2_push_policy policy) +{ + switch (policy) { + case H2_PUSH_NONE: + return "none"; + case H2_PUSH_FAST_LOAD: + return "fast-load"; + case H2_PUSH_HEAD: + return "head"; + default: + return "default"; + } +} typedef struct { const h2_request *req; @@ -269,6 +282,7 @@ static int add_push(link_ctx *ctx) if (apr_uri_parse(ctx->pool, ctx->link, &uri) == APR_SUCCESS) { if (uri.path && same_authority(ctx->req, &uri)) { char *path; + const char *method; apr_table_t *headers; h2_request *req; h2_push *push; @@ -283,6 +297,14 @@ static int add_push(link_ctx *ctx) push = apr_pcalloc(ctx->pool, sizeof(*push)); + switch (ctx->req->push_policy) { + case H2_PUSH_HEAD: + method = "HEAD"; + break; + default: + method = "GET"; + break; + } headers = apr_table_make(ctx->pool, 5); apr_table_do(set_header, headers, ctx->req->headers, "User-Agent", @@ -290,7 +312,7 @@ static int add_push(link_ctx *ctx) "Accept-Language", NULL); req = h2_request_createn(0, ctx->pool, ctx->req->config, - "GET", ctx->req->scheme, + method, ctx->req->scheme, ctx->req->authority, path, headers); /* atm, we do not push on pushes */ @@ -374,23 +396,58 @@ static int head_iter(void *ctx, const char *key, const char *value) apr_array_header_t *h2_push_collect(apr_pool_t *p, const h2_request *req, const h2_response *res) { - /* Collect push candidates from the request/response pair. - * - * One source for pushes are "rel=preload" link headers - * in the response. - * - * TODO: This may be extended in the future by hooks or callbacks - * where other modules can provide push information directly. - */ - if (res->headers) { - link_ctx ctx; - - memset(&ctx, 0, sizeof(ctx)); - ctx.req = req; - ctx.pool = p; - - apr_table_do(head_iter, &ctx, res->headers, NULL); - return ctx.pushes; + if (req && req->push_policy != H2_PUSH_NONE) { + /* Collect push candidates from the request/response pair. + * + * One source for pushes are "rel=preload" link headers + * in the response. + * + * TODO: This may be extended in the future by hooks or callbacks + * where other modules can provide push information directly. + */ + if (res->headers) { + link_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); + ctx.req = req; + ctx.pool = p; + + apr_table_do(head_iter, &ctx, res->headers, NULL); + if (ctx.pushes) { + apr_table_setn(res->headers, "push-policy", policy_str(req->push_policy)); + } + return ctx.pushes; + } } return NULL; } + +void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_enabled) +{ + h2_push_policy policy = H2_PUSH_NONE; + if (push_enabled) { + const char *val = apr_table_get(req->headers, "accept-push-policy"); + if (val) { + if (ap_find_token(p, val, "fast-load")) { + policy = H2_PUSH_FAST_LOAD; + } + else if (ap_find_token(p, val, "head")) { + policy = H2_PUSH_HEAD; + } + else if (ap_find_token(p, val, "default")) { + policy = H2_PUSH_DEFAULT; + } + else if (ap_find_token(p, val, "none")) { + policy = H2_PUSH_NONE; + } + else { + /* nothing known found in this header, go by default */ + policy = H2_PUSH_DEFAULT; + } + } + else { + policy = H2_PUSH_DEFAULT; + } + } + req->push_policy = policy; +} diff --git a/mod_http2/h2_push.h b/mod_http2/h2_push.h index 871548ce..f3a8601c 100644 --- a/mod_http2/h2_push.h +++ b/mod_http2/h2_push.h @@ -19,6 +19,13 @@ struct h2_request; struct h2_response; struct h2_ngheader; +typedef enum { + H2_PUSH_NONE, + H2_PUSH_DEFAULT, + H2_PUSH_HEAD, + H2_PUSH_FAST_LOAD, +} h2_push_policy; + typedef struct h2_push { const struct h2_request *req; } h2_push; @@ -28,4 +35,6 @@ apr_array_header_t *h2_push_collect(apr_pool_t *p, const struct h2_request *req, const struct h2_response *res); +void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_enabled); + #endif /* defined(__mod_h2__h2_push__) */ diff --git a/mod_http2/h2_request.c b/mod_http2/h2_request.c index 04227efe..6fafa45f 100644 --- a/mod_http2/h2_request.c +++ b/mod_http2/h2_request.c @@ -32,6 +32,7 @@ #include "h2_private.h" #include "h2_config.h" #include "h2_mplx.h" +#include "h2_push.h" #include "h2_request.h" #include "h2_task.h" #include "h2_util.h" @@ -272,7 +273,7 @@ apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, } req->eoh = 1; - req->push = push; + h2_push_policy_determine(req, pool, push); /* In the presence of trailers, force behaviour of chunked encoding */ s = apr_table_get(req->headers, "Trailer"); diff --git a/mod_http2/h2_request.h b/mod_http2/h2_request.h index 17e4b23b..cc01ed12 100644 --- a/mod_http2/h2_request.h +++ b/mod_http2/h2_request.h @@ -43,8 +43,8 @@ struct h2_request { unsigned int chunked : 1; /* iff requst body needs to be forwarded as chunked */ unsigned int eoh : 1; /* iff end-of-headers has been seen and request is complete */ - unsigned int push : 1; /* iff server push is possible for this request */ - + unsigned int body : 1; /* iff this request has a body */ + unsigned int push_policy; /* which push policy to use for this request */ const struct h2_config *config; }; diff --git a/mod_http2/h2_session.c b/mod_http2/h2_session.c index 6a3d2615..d867e38c 100644 --- a/mod_http2/h2_session.c +++ b/mod_http2/h2_session.c @@ -684,6 +684,7 @@ static void h2_session_destroy(h2_session *session) session->id, (int)h2_stream_set_size(session->streams)); } if (session->mplx) { + h2_mplx_set_consumed_cb(session->mplx, NULL, NULL); h2_mplx_release_and_join(session->mplx, session->iowait); session->mplx = NULL; } diff --git a/mod_http2/h2_stream.c b/mod_http2/h2_stream.c index 45d4cd14..95c5b4a1 100644 --- a/mod_http2/h2_stream.c +++ b/mod_http2/h2_stream.c @@ -298,13 +298,14 @@ apr_status_t h2_stream_schedule(h2_stream *stream, int eos, int push_enabled, eos, push_enabled); if (status == APR_SUCCESS) { if (!eos) { + stream->request->body = 1; stream->bbin = apr_brigade_create(stream->pool, stream->session->c->bucket_alloc); } stream->input_remaining = stream->request->content_length; status = h2_mplx_process(stream->session->mplx, stream->id, - stream->request, eos, cmp, ctx); + stream->request, cmp, ctx); stream->scheduled = 1; ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c, diff --git a/mod_http2/h2_task.c b/mod_http2/h2_task.c index 93d20ade..fb7ccc3a 100644 --- a/mod_http2/h2_task.c +++ b/mod_http2/h2_task.c @@ -143,9 +143,9 @@ static int h2_task_pre_conn(conn_rec* c, void *arg) } h2_task *h2_task_create(long session_id, const h2_request *req, - apr_pool_t *pool, h2_mplx *mplx, int eos) + apr_pool_t *pool, h2_mplx *mplx) { - h2_task *task = apr_pcalloc(pool, sizeof(h2_task)); + h2_task *task = apr_pcalloc(pool, sizeof(h2_task)); if (task == NULL) { ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool, APLOGNO(02941) "h2_task(%ld-%d): create stream task", @@ -154,72 +154,35 @@ h2_task *h2_task_create(long session_id, const h2_request *req, return NULL; } - task->id = apr_psprintf(pool, "%ld-%d", session_id, req->id); - task->stream_id = req->id; - task->pool = pool; - task->mplx = mplx; - task->c = h2_conn_create(mplx->c, task->pool); + task->id = apr_psprintf(pool, "%ld-%d", session_id, req->id); + task->stream_id = req->id; + task->mplx = mplx; + task->request = req; + task->input_eos = !req->body; + task->ser_headers = h2_config_geti(req->config, H2_CONF_SER_HEADERS); - task->request = req; - task->input_eos = eos; - return task; } -apr_status_t h2_task_destroy(h2_task *task) -{ - (void)task; - return APR_SUCCESS; -} - -apr_status_t h2_task_do(h2_task *task, h2_worker *worker) +apr_status_t h2_task_do(h2_task *task, conn_rec *c, apr_thread_cond_t *cond, + apr_socket_t *socket) { - apr_status_t status = APR_SUCCESS; - AP_DEBUG_ASSERT(task); + task->io = cond; + task->input = h2_task_input_create(task, c->pool, c->bucket_alloc); + task->output = h2_task_output_create(task, c->pool); - task->serialize_headers = h2_config_geti(task->request->config, H2_CONF_SER_HEADERS); - - status = h2_worker_setup_task(worker, task); + ap_process_connection(c, socket); - /* save in connection that this one is a pseudo connection */ - h2_ctx_create_for(task->c, task); - - if (status == APR_SUCCESS) { - task->input = h2_task_input_create(task, task->pool, - task->c->bucket_alloc); - task->output = h2_task_output_create(task, task->pool); - - ap_process_connection(task->c, h2_worker_get_socket(worker)); - - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, - "h2_task(%s): processing done", task->id); - } - else { - ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, task->c, - APLOGNO(02957) "h2_task(%s): error setting up h2_task", - task->id); - } + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, + "h2_task(%s): processing done", task->id); - if (task->input) { - h2_task_input_destroy(task->input); - task->input = NULL; - } + h2_task_input_destroy(task->input); + h2_task_output_close(task->output); + h2_task_output_destroy(task->output); + task->io = NULL; - if (task->output) { - h2_task_output_close(task->output); - h2_task_output_destroy(task->output); - task->output = NULL; - } - - if (task->io) { - apr_thread_cond_signal(task->io); - } - - h2_worker_release_task(worker, task); - h2_mplx_task_done(task->mplx, task->stream_id); - - return status; + return APR_SUCCESS; } static apr_status_t h2_task_process_request(const h2_request *req, conn_rec *c) @@ -261,7 +224,7 @@ static int h2_task_process_conn(conn_rec* c) ctx = h2_ctx_get(c, 0); if (h2_ctx_is_task(ctx)) { - if (!ctx->task->serialize_headers) { + if (!ctx->task->ser_headers) { ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "h2_h2, processing request directly"); h2_task_process_request(ctx->task->request, c); diff --git a/mod_http2/h2_task.h b/mod_http2/h2_task.h index c0aff2c8..08ce6c09 100644 --- a/mod_http2/h2_task.h +++ b/mod_http2/h2_task.h @@ -48,30 +48,23 @@ typedef struct h2_task h2_task; struct h2_task { const char *id; int stream_id; - apr_pool_t *pool; - apr_bucket_alloc_t *bucket_alloc; - struct h2_mplx *mplx; - struct conn_rec *c; const struct h2_request *request; - unsigned int filters_set : 1; - unsigned int input_eos : 1; - unsigned int serialize_headers : 1; + unsigned int filters_set : 1; + unsigned int input_eos : 1; + unsigned int ser_headers : 1; struct h2_task_input *input; struct h2_task_output *output; - struct apr_thread_cond_t *io; /* used to wait for events on */ }; h2_task *h2_task_create(long session_id, const struct h2_request *req, - apr_pool_t *pool, struct h2_mplx *mplx, - int eos); - -apr_status_t h2_task_destroy(h2_task *task); + apr_pool_t *pool, struct h2_mplx *mplx); -apr_status_t h2_task_do(h2_task *task, struct h2_worker *worker); +apr_status_t h2_task_do(h2_task *task, conn_rec *c, + struct apr_thread_cond_t *cond, apr_socket_t *socket); void h2_task_register_hooks(void); diff --git a/mod_http2/h2_task_input.c b/mod_http2/h2_task_input.c index 921d0339..992e9569 100644 --- a/mod_http2/h2_task_input.c +++ b/mod_http2/h2_task_input.c @@ -51,8 +51,8 @@ h2_task_input *h2_task_input_create(h2_task *task, apr_pool_t *pool, input->task = task; input->bb = NULL; - if (task->serialize_headers) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, + if (task->ser_headers) { + ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "h2_task_input(%s): serialize request %s %s", task->id, task->request->method, task->request->path); input->bb = apr_brigade_create(pool, bucket_alloc); diff --git a/mod_http2/h2_version.h b/mod_http2/h2_version.h new file mode 100644 index 00000000..2637ff2f --- /dev/null +++ b/mod_http2/h2_version.h @@ -0,0 +1,34 @@ +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 + +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef mod_h2_h2_version_h +#define mod_h2_h2_version_h + +/** + * @macro + * Version number of the h2 module as c string + */ +#define MOD_HTTP2_VERSION "1.0.14-DEVa" + +/** + * @macro + * Numerical representation of the version number of the h2 module + * release. This is a 24 bit number with 8 bits for major number, 8 bits + * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. + */ +#define MOD_HTTP2_VERSION_NUM 0x01000e + + +#endif /* mod_h2_h2_version_h */ diff --git a/mod_http2/h2_worker.c b/mod_http2/h2_worker.c index 54f0450c..8f988192 100644 --- a/mod_http2/h2_worker.c +++ b/mod_http2/h2_worker.c @@ -24,6 +24,8 @@ #include "h2_private.h" #include "h2_conn.h" +#include "h2_ctx.h" +#include "h2_h2.h" #include "h2_mplx.h" #include "h2_request.h" #include "h2_task.h" @@ -34,6 +36,11 @@ static void* APR_THREAD_FUNC execute(apr_thread_t *thread, void *wctx) h2_worker *worker = (h2_worker *)wctx; apr_status_t status = APR_SUCCESS; h2_mplx *m; + const h2_request *req; + h2_task *task; + conn_rec *c, *master; + int stream_id; + (void)thread; /* Furthermore, other code might want to see the socket for @@ -50,15 +57,32 @@ static void* APR_THREAD_FUNC execute(apr_thread_t *thread, void *wctx) return NULL; } - worker->task = NULL; m = NULL; while (!worker->aborted) { - status = worker->get_next(worker, &m, &worker->task, worker->ctx); + status = worker->get_next(worker, &m, &req, worker->ctx); - if (worker->task) { - h2_task_do(worker->task, worker); - worker->task = NULL; - apr_thread_cond_signal(worker->io); + if (req) { + stream_id = req->id; + master = m->c; + c = h2_slave_create(master, worker->task_pool, + worker->thread, worker->socket); + if (!c) { + ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, c, + APLOGNO(02957) "h2_task(%s): error setting up slave connection", + task->id); + h2_mplx_out_rst(m, task->stream_id, H2_ERR_INTERNAL_ERROR); + } + else { + task = h2_task_create(m->id, req, worker->task_pool, m); + h2_ctx_create_for(c, task); + h2_task_do(task, c, worker->io, worker->socket); + + apr_thread_cond_signal(worker->io); + } + apr_pool_clear(worker->task_pool); + /* task is gone */ + task = NULL; + h2_mplx_task_done(m, stream_id); } } @@ -124,6 +148,7 @@ h2_worker *h2_worker_create(int id, } apr_pool_pre_cleanup_register(w->pool, w, cleanup_join_thread); + apr_pool_create(&w->task_pool, w->pool); apr_thread_create(&w->thread, attr, execute, w, w->pool); } return w; @@ -158,52 +183,12 @@ int h2_worker_is_aborted(h2_worker *worker) } h2_task *h2_worker_create_task(h2_worker *worker, h2_mplx *m, - const h2_request *req, int eos) + const h2_request *req) { h2_task *task; - /* Create a subpool from the worker one to be used for all things - * with life-time of this task execution. - */ - if (!worker->task_pool) { - apr_pool_create(&worker->task_pool, worker->pool); - worker->pool_reuses = 100; - } - task = h2_task_create(m->id, req, worker->task_pool, m, eos); - - /* Link the task to the worker which provides useful things such - * as mutex, a socket etc. */ - task->io = worker->io; - + task = h2_task_create(m->id, req, worker->task_pool, m); return task; } -apr_status_t h2_worker_setup_task(h2_worker *worker, h2_task *task) { - apr_status_t status; - - - status = h2_slave_setup(task, apr_bucket_alloc_create(task->pool), - worker->thread, worker->socket); - - return status; -} - -void h2_worker_release_task(h2_worker *worker, struct h2_task *task) -{ - task->io = NULL; - task->pool = NULL; - if (worker->pool_reuses-- <= 0) { - apr_pool_destroy(worker->task_pool); - worker->task_pool = NULL; - } - else { - apr_pool_clear(worker->task_pool); - } -} - -apr_socket_t *h2_worker_get_socket(h2_worker *worker) -{ - return worker->socket; -} - diff --git a/mod_http2/h2_worker.h b/mod_http2/h2_worker.h index 6bf9bf31..fc0f359e 100644 --- a/mod_http2/h2_worker.h +++ b/mod_http2/h2_worker.h @@ -31,7 +31,7 @@ typedef struct h2_worker h2_worker; * gets aborted (idle timeout, for example). */ typedef apr_status_t h2_worker_mplx_next_fn(h2_worker *worker, struct h2_mplx **pm, - struct h2_task **ptask, + const struct h2_request **preq, void *ctx); /* Invoked just before the worker thread exits. */ @@ -54,8 +54,6 @@ struct h2_worker { void *ctx; unsigned int aborted : 1; - int pool_reuses; - struct h2_task *task; }; /** @@ -145,10 +143,6 @@ int h2_worker_get_id(h2_worker *worker); int h2_worker_is_aborted(h2_worker *worker); struct h2_task *h2_worker_create_task(h2_worker *worker, struct h2_mplx *m, - const struct h2_request *req, int eos); -apr_status_t h2_worker_setup_task(h2_worker *worker, struct h2_task *task); -void h2_worker_release_task(h2_worker *worker, struct h2_task *task); - -apr_socket_t *h2_worker_get_socket(h2_worker *worker); - + const struct h2_request *req); + #endif /* defined(__mod_h2__h2_worker__) */ diff --git a/mod_http2/h2_workers.c b/mod_http2/h2_workers.c index 7aec7947..89aa4efd 100644 --- a/mod_http2/h2_workers.c +++ b/mod_http2/h2_workers.c @@ -25,7 +25,7 @@ #include "h2_private.h" #include "h2_mplx.h" -#include "h2_task.h" +#include "h2_request.h" #include "h2_task_queue.h" #include "h2_worker.h" #include "h2_workers.h" @@ -68,20 +68,20 @@ static void cleanup_zombies(h2_workers *workers, int lock) * the h2_workers lock. */ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, - h2_task **ptask, void *ctx) + const h2_request **preq, void *ctx) { apr_status_t status; h2_mplx *m = NULL; - h2_task *task = NULL; + const h2_request *req = NULL; apr_time_t max_wait, start_wait; int has_more = 0; h2_workers *workers = (h2_workers *)ctx; - if (*pm && ptask != NULL) { + if (*pm && preq != NULL) { /* We have a h2_mplx instance and the worker wants the next task. * Try to get one from the given mplx. */ - *ptask = h2_mplx_pop_task(*pm, worker, &has_more); - if (*ptask) { + *preq = h2_mplx_pop_request(*pm, &has_more); + if (*preq) { return APR_SUCCESS; } } @@ -94,7 +94,7 @@ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, *pm = NULL; } - if (!ptask) { + if (!preq) { /* the worker does not want a next task, we're done. */ return APR_SUCCESS; @@ -109,7 +109,7 @@ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s, "h2_worker(%d): looking for work", h2_worker_get_id(worker)); - while (!task && !h2_worker_is_aborted(worker) && !workers->aborted) { + while (!req && !h2_worker_is_aborted(worker) && !workers->aborted) { /* Get the next h2_mplx to process that has a task to hand out. * If it does, place it at the end of the queu and return the @@ -121,12 +121,12 @@ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, * we do a timed wait or block indefinitely. */ m = NULL; - while (!task && !H2_MPLX_LIST_EMPTY(&workers->mplxs)) { + while (!req && !H2_MPLX_LIST_EMPTY(&workers->mplxs)) { m = H2_MPLX_LIST_FIRST(&workers->mplxs); H2_MPLX_REMOVE(m); - task = h2_mplx_pop_task(m, worker, &has_more); - if (task) { + req = h2_mplx_pop_request(m, &has_more); + if (req) { if (has_more) { H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m); } @@ -137,7 +137,7 @@ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, } } - if (!task) { + if (!req) { /* Need to wait for either a new mplx to arrive. */ cleanup_zombies(workers, 0); @@ -174,16 +174,16 @@ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, /* Here, we either have gotten task and mplx for the worker or * needed to give up with more than enough workers. */ - if (task) { + if (req) { ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s, - "h2_worker(%d): start task(%s)", - h2_worker_get_id(worker), task->id); + "h2_worker(%d): start request(%ld-%d)", + h2_worker_get_id(worker), m->id, req->id); /* Since we hand out a reference to the worker, we increase * its ref count. */ h2_mplx_reference(m); *pm = m; - *ptask = task; + *preq = req; if (has_more && workers->idle_worker_count > 1) { apr_thread_cond_signal(workers->mplx_added); diff --git a/mod_http2/h2_workers.h b/mod_http2/h2_workers.h index f79d5cac..16ec4443 100644 --- a/mod_http2/h2_workers.h +++ b/mod_http2/h2_workers.h @@ -25,6 +25,7 @@ struct apr_thread_mutex_t; struct apr_thread_cond_t; struct h2_mplx; +struct h2_request; struct h2_task; struct h2_task_queue; diff --git a/mod_http2/mod_http2.c b/mod_http2/mod_http2.c index 8e95e519..53a460fe 100644 --- a/mod_http2/mod_http2.c +++ b/mod_http2/mod_http2.c @@ -32,6 +32,7 @@ #include "h2_config.h" #include "h2_ctx.h" #include "h2_h2.h" +#include "h2_push.h" #include "h2_request.h" #include "h2_switch.h" #include "h2_version.h" @@ -171,7 +172,7 @@ static char *value_of_H2PUSH(apr_pool_t *p, server_rec *s, ctx = h2_ctx_rget(r); if (ctx) { h2_task *task = h2_ctx_get_task(ctx); - return task && task->request->push? "on" : "off"; + return (task && task->request->push_policy != H2_PUSH_NONE)? "on" : "off"; } } else if (c) {