From: Stefan Eissing Date: Mon, 14 Dec 2015 13:02:50 +0000 (+0000) Subject: internal rewiring to prepare for return from connection processing frequently before... X-Git-Tag: 2.5.0-alpha~2533 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e21d2c87d65f51101fb80427f56418c3af5f363;p=thirdparty%2Fapache%2Fhttpd.git internal rewiring to prepare for return from connection processing frequently before session end git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1719901 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/http2/h2_conn.c b/modules/http2/h2_conn.c index 9216c230e76..2b8a058d2e9 100644 --- a/modules/http2/h2_conn.c +++ b/modules/http2/h2_conn.c @@ -126,12 +126,10 @@ static module *h2_conn_mpm_module(void) { return mpm_module; } -apr_status_t h2_conn_process(conn_rec *c, request_rec *r, server_rec *s) +apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r) { - apr_status_t status; h2_session *session; const h2_config *config; - int rv; if (!workers) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911) @@ -139,28 +137,34 @@ apr_status_t h2_conn_process(conn_rec *c, request_rec *r, server_rec *s) return APR_EGENERAL; } - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "h2_conn_process start"); - - if (!s && r) { - s = r->server; - } - - config = s? h2_config_sget(s) : h2_config_get(c); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "h2_conn_setup"); + config = h2_config_sget(h2_ctx_server_get(ctx)); if (r) { session = h2_session_rcreate(r, config, workers); } else { session = h2_session_create(c, config, workers); } + + h2_ctx_session_set(ctx, session); + return APR_SUCCESS; +} + +apr_status_t h2_conn_process(h2_ctx *ctx) +{ + apr_status_t status; + h2_session *session; + conn_rec *c; + int rv; + + session = h2_ctx_session_get(ctx); + c = session->c; if (!h2_is_acceptable_connection(c, 1)) { nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE, 0, NGHTTP2_INADEQUATE_SECURITY, NULL, 0); } - /* What do install instead? */ - ap_remove_input_filter_byhandle(c->input_filters, "reqtimeout"); - ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c); status = h2_session_start(session, &rv); @@ -176,20 +180,34 @@ apr_status_t h2_conn_process(conn_rec *c, request_rec *r, server_rec *s) status = h2_session_process(session); - ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c, - "h2_session(%ld): done", session->id); - /* Make sure this connection gets closed properly. */ - ap_update_child_status_from_conn(c->sbh, SERVER_CLOSING, c); - c->keepalive = AP_CONN_CLOSE; - if (c->cs) { - c->cs->state = CONN_STATE_WRITE_COMPLETION; + if (status == APR_EOF) { + ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c, + "h2_session(%ld): done", session->id); + /* Make sure this connection gets closed properly. */ + ap_update_child_status_from_conn(c->sbh, SERVER_CLOSING, c); + c->keepalive = AP_CONN_CLOSE; + if (c->cs) { + c->cs->state = CONN_STATE_WRITE_COMPLETION; + } + + h2_session_close(session); + /* hereafter session will be gone */ } - - h2_session_close(session); - /* hereafter session will be gone */ + return status; } +apr_status_t h2_conn_run(struct h2_ctx *ctx) +{ + apr_status_t status; + + do { + status = h2_conn_process(ctx); + } while (status == APR_SUCCESS); + + return (status == APR_EOF)? APR_SUCCESS : status; +} + static void fix_event_conn(conn_rec *c, conn_rec *master); @@ -220,8 +238,8 @@ conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *pool) return c; } -apr_status_t h2_conn_setup(h2_task *task, apr_bucket_alloc_t *bucket_alloc, - apr_thread_t *thread, apr_socket_t *socket) +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; diff --git a/modules/http2/h2_conn.h b/modules/http2/h2_conn.h index 917a57e0392..c8ecbfd7f04 100644 --- a/modules/http2/h2_conn.h +++ b/modules/http2/h2_conn.h @@ -16,18 +16,36 @@ #ifndef __mod_h2__h2_conn__ #define __mod_h2__h2_conn__ +struct h2_ctx; struct h2_task; /** - * Process the connection that is now starting the HTTP/2 - * conversation. Return when the HTTP/2 session is done - * and the connection will close. + * Setup the connection and our context for HTTP/2 processing * + * @param ctx the http2 context to setup * @param c the connection HTTP/2 is starting on * @param r the upgrade request that still awaits an answer, optional - * @param s the server selected by request or, if NULL, connection */ -apr_status_t h2_conn_process(conn_rec *c, request_rec *r, server_rec *s); +apr_status_t h2_conn_setup(struct h2_ctx *ctx, conn_rec *c, request_rec *r); + +/** + * Process the HTTP/2 connection. Return whenever blocking reads or + * long writes have to be performed. + * + * @param ctx the http2 context to process + * @return APR_SUCCESS as long as processing needs to continue, APR_EOF + * when HTTP/2 session is done. + */ +apr_status_t h2_conn_process(struct h2_ctx *ctx); + +/** + * Run the HTTP/2 connection. Return when the HTTP/2 session is done + * and the connection will close or a fatal error occured. + * + * @param ctx the http2 context to run + * @return APR_SUCCESS when session is done. + */ +apr_status_t h2_conn_run(struct h2_ctx *ctx); /* Initialize this child process for h2 connection work, * to be called once during child init before multi processing @@ -49,7 +67,7 @@ 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_conn_setup(struct h2_task *task, apr_bucket_alloc_t *bucket_alloc, - apr_thread_t *thread, apr_socket_t *socket); +apr_status_t h2_slave_setup(struct h2_task *task, apr_bucket_alloc_t *bucket_alloc, + apr_thread_t *thread, apr_socket_t *socket); #endif /* defined(__mod_h2__h2_conn__) */ diff --git a/modules/http2/h2_ctx.c b/modules/http2/h2_ctx.c index cedab8a09c6..bf8dab20c9b 100644 --- a/modules/http2/h2_ctx.c +++ b/modules/http2/h2_ctx.c @@ -30,6 +30,7 @@ static h2_ctx *h2_ctx_create(const conn_rec *c) h2_ctx *ctx = apr_pcalloc(c->pool, sizeof(h2_ctx)); AP_DEBUG_ASSERT(ctx); ap_set_module_config(c->conn_config, &http2_module, ctx); + h2_ctx_server_set(ctx, c->base_server); return ctx; } @@ -78,6 +79,11 @@ void h2_ctx_session_set(h2_ctx *ctx, struct h2_session *session) ctx->session = session; } +server_rec *h2_ctx_server_get(h2_ctx *ctx) +{ + return ctx? ctx->server : NULL; +} + h2_ctx *h2_ctx_server_set(h2_ctx *ctx, server_rec *s) { ctx->server = s; diff --git a/modules/http2/h2_ctx.h b/modules/http2/h2_ctx.h index 09b9766d744..4f85ddcdf49 100644 --- a/modules/http2/h2_ctx.h +++ b/modules/http2/h2_ctx.h @@ -57,6 +57,7 @@ h2_ctx *h2_ctx_protocol_set(h2_ctx *ctx, const char *proto); /* Set the server_rec relevant for this context. */ h2_ctx *h2_ctx_server_set(h2_ctx *ctx, server_rec *s); +server_rec *h2_ctx_server_get(h2_ctx *ctx); struct h2_session *h2_ctx_session_get(h2_ctx *ctx); void h2_ctx_session_set(h2_ctx *ctx, struct h2_session *session); diff --git a/modules/http2/h2_h2.c b/modules/http2/h2_h2.c index 912b7a88f39..353a9e6c01d 100644 --- a/modules/http2/h2_h2.c +++ b/modules/http2/h2_h2.c @@ -554,15 +554,19 @@ int h2_allows_h2_upgrade(conn_rec *c) /******************************************************************************* * Register various hooks */ -static const char* const mod_ssl[] = {"mod_ssl.c", NULL}; +static const char* const mod_ssl[] = { "mod_ssl.c", NULL}; +static const char* const mod_reqtimeout[] = { "mod_reqtimeout.c", NULL}; void h2_h2_register_hooks(void) { - /* When the connection processing actually starts, we might to - * take over, if h2* was selected as protocol. + /* Our main processing needs to run quite late. Definitely after mod_ssl, + * as we need its connection filters, but also before reqtimeout as its + * method of timeouts is specific to HTTP/1.1 (as of now). + * The core HTTP/1 processing run as REALLY_LAST, so we will have + * a chance to take over before it. */ ap_hook_process_connection(h2_h2_process_conn, - mod_ssl, NULL, APR_HOOK_MIDDLE); + mod_ssl, mod_reqtimeout, APR_HOOK_LAST); /* With "H2SerializeHeaders On", we install the filter in this hook * that parses the response. This needs to happen before any other post @@ -574,6 +578,7 @@ void h2_h2_register_hooks(void) int h2_h2_process_conn(conn_rec* c) { + apr_status_t status; h2_ctx *ctx; if (c->master) { @@ -586,19 +591,16 @@ int h2_h2_process_conn(conn_rec* c) /* our stream pseudo connection */ return DECLINED; } - - if (h2_ctx_protocol_get(c)) { - /* Something has been negotiated */ - } - else if (!strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c)) - && h2_allows_h2_direct(c) - && h2_is_acceptable_connection(c, 1)) { - /* connection still is on http/1.1 and H2Direct is enabled. + + if (!ctx && c->keepalives == 0 + && !strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c)) + && h2_allows_h2_direct(c) + && h2_is_acceptable_connection(c, 1)) { + /* Fresh connection still is on http/1.1 and H2Direct is enabled. * Otherwise connection is in a fully acceptable state. * -> peek at the first 24 incoming bytes */ apr_bucket_brigade *temp; - apr_status_t status; char *s = NULL; apr_size_t slen; @@ -630,19 +632,17 @@ int h2_h2_process_conn(conn_rec* c) apr_brigade_destroy(temp); } - else { - /* the connection is not HTTP/1.1 or not for us, don't touch it */ - return DECLINED; - } - /* If "h2" was selected as protocol (by whatever mechanism), take over - * the connection. - */ if (ctx) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, - "h2_h2, connection, h2 active"); - - return h2_conn_process(c, NULL, ctx->server); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "process_conn"); + if (!h2_ctx_session_get(ctx)) { + status = h2_conn_setup(ctx, c, NULL); + if (status != APR_SUCCESS) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, "conn_setup"); + return status; + } + } + return h2_conn_process(ctx); } ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, declined"); diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c index 1d79364e878..5ed4e4267a4 100644 --- a/modules/http2/h2_session.c +++ b/modules/http2/h2_session.c @@ -1596,6 +1596,7 @@ apr_status_t h2_session_process(h2_session *session) "h2_session: send: %s", nghttp2_strerror(rv)); if (nghttp2_is_fatal(rv)) { h2_session_abort(session, status, rv); + status = APR_EGENERAL; goto end_process; } } @@ -1622,6 +1623,7 @@ apr_status_t h2_session_process(h2_session *session) "h2_session: send: %s", nghttp2_strerror(rv)); if (nghttp2_is_fatal(rv)) { h2_session_abort(session, status, rv); + status = APR_EGENERAL; goto end_process; } } @@ -1760,6 +1762,8 @@ apr_status_t h2_session_process(h2_session *session) h2_conn_io_flush(&session->io); } } + /* normal end of session */ + status = APR_EOF; end_process: return status; diff --git a/modules/http2/h2_switch.c b/modules/http2/h2_switch.c index daddb8d3103..bc41b6149fe 100644 --- a/modules/http2/h2_switch.c +++ b/modules/http2/h2_switch.c @@ -155,12 +155,15 @@ static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s, ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER"); /* Ok, start an h2_conn on this one. */ - status = h2_conn_process(r->connection, r, r->server); - if (status != DONE) { - /* Nothing really to do about this. */ + h2_ctx_server_set(ctx, r->server); + status = h2_conn_setup(ctx, r->connection, r); + if (status != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, - "session proessed, unexpected status"); + "session setup"); + return status; } + + return h2_conn_run(ctx); } return DONE; } diff --git a/modules/http2/h2_worker.c b/modules/http2/h2_worker.c index a42f246ef91..54f0450c441 100644 --- a/modules/http2/h2_worker.c +++ b/modules/http2/h2_worker.c @@ -182,8 +182,8 @@ apr_status_t h2_worker_setup_task(h2_worker *worker, h2_task *task) { apr_status_t status; - status = h2_conn_setup(task, apr_bucket_alloc_create(task->pool), - worker->thread, worker->socket); + status = h2_slave_setup(task, apr_bucket_alloc_create(task->pool), + worker->thread, worker->socket); return status; }