From: Stefan Eissing Date: Fri, 4 Dec 2015 15:16:24 +0000 (+0000) Subject: v1.0.10 of mod_http2 backport, see CHANGES X-Git-Tag: 2.4.18~28 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5a236f3a83f7a412f0d53af95fd3dd4c5a527402;p=apache v1.0.10 of mod_http2 backport, see CHANGES git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1717980 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index b10b093f0a..e3e2b2ed4d 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,15 @@ Changes with Apache 2.4.18 + *) mod_http2: connection level window for flow control is set to protocol + maximum of 2GB-1, preventing window exhaustion when sending data on many + streams with higher cumulative window size. + Reducing write frequency unless push promises need to be flushed. + [Stefan Eissing] + + *) mod_http2: required minimum version of libnghttp2 is 1.2.1 + [Stefan Eissing] + *) mod_ssl: For the "SSLStaplingReturnResponderErrors off" case, make sure to only staple responses with certificate status "good". [Kaspar Brand] diff --git a/modules/http2/config.m4 b/modules/http2/config.m4 index e10bb8158f..05cf2ba36d 100644 --- a/modules/http2/config.m4 +++ b/modules/http2/config.m4 @@ -127,12 +127,12 @@ AC_DEFUN([APACHE_CHECK_NGHTTP2],[ fi fi - AC_MSG_CHECKING([for nghttp2 version >= 1.0.0]) + AC_MSG_CHECKING([for nghttp2 version >= 1.2.1]) AC_TRY_COMPILE([#include ],[ #if !defined(NGHTTP2_VERSION_NUM) #error "Missing nghttp2 version" #endif -#if NGHTTP2_VERSION_NUM < 0x010000 +#if NGHTTP2_VERSION_NUM < 0x010201 #error "Unsupported nghttp2 version " NGHTTP2_VERSION_TEXT #endif], [AC_MSG_RESULT(OK) @@ -154,6 +154,10 @@ AC_DEFUN([APACHE_CHECK_NGHTTP2],[ if test "x$liberrors" != "x"; then AC_MSG_WARN([nghttp2 library is unusable]) fi +dnl # nghttp2 >= 1.3.0: access to stream weights + AC_CHECK_FUNCS([nghttp2_stream_get_weight], + [APR_ADDTO(MOD_CPPFLAGS, ["-DH2_NG2_STREAM_API"])], []) +dnl # nghttp2 >= 1.5.0: changing stream priorities AC_CHECK_FUNCS([nghttp2_session_change_stream_priority], [APR_ADDTO(MOD_CPPFLAGS, ["-DH2_NG2_CHANGE_PRIO"])], []) else diff --git a/modules/http2/h2_conn.c b/modules/http2/h2_conn.c index cf44fd7c1f..b9b6e87bf0 100644 --- a/modules/http2/h2_conn.c +++ b/modules/http2/h2_conn.c @@ -195,55 +195,30 @@ apr_status_t h2_conn_process(conn_rec *c, request_rec *r, server_rec *s) static void fix_event_conn(conn_rec *c, conn_rec *master); -static int SLAVE_CONN_25DEV_STYLE = 1; - conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *pool) { - apr_socket_t *socket; conn_rec *c; AP_DEBUG_ASSERT(master); - if (SLAVE_CONN_25DEV_STYLE) { - /* 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)); - - 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; - } - else { - /* CAVEAT: it seems necessary to setup the conn_rec in the master - * connection thread. Other attempts crashed. - * HOWEVER: we setup the connection using the pools and other items - * from the master connection, since we do not want to allocate - * lots of resources here. - * Lets allocated pools and everything else when we actually start - * working on this new connection. - */ - /* Not sure about the scoreboard handle. Reusing the one from the main - * connection could make sense, is not really correct, but we cannot - * easily create new handles for our worker threads either. - * TODO - */ - socket = ap_get_module_config(master->conn_config, &core_module); - c = ap_run_create_connection(pool, master->base_server, - socket, - master->id^((long)pool), - master->sbh, - master->bucket_alloc); - } + /* 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)); if (c == NULL) { ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool, 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; } diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c index cce8ca7d5a..e5a158c86a 100644 --- a/modules/http2/h2_session.c +++ b/modules/http2/h2_session.c @@ -94,6 +94,8 @@ h2_stream *h2_session_open_stream(h2_session *session, int stream_id) return stream; } +#ifdef H2_NG2_STREAM_API + /** * Determine the importance of streams when scheduling tasks. * - if both stream depend on the same one, compare weights @@ -147,6 +149,20 @@ static int stream_pri_cmp(int sid1, int sid2, void *ctx) return spri_cmp(sid1, s1, sid2, s2, session); } +#else /* ifdef H2_NG2_STREAM_API */ + +/* In absence of nghttp2_stream API, which gives information about + * priorities since nghttp2 1.3.x, we just sort the streams by + * their identifier, aka. order of arrival. + */ +static int stream_pri_cmp(int sid1, int sid2, void *ctx) +{ + (void)ctx; + return sid1 - sid2; +} + +#endif /* (ifdef else) H2_NG2_STREAM_API */ + static apr_status_t stream_schedule(h2_session *session, h2_stream *stream, int eos) { @@ -884,7 +900,7 @@ apr_status_t h2_session_start(h2_session *session, int *rv) apr_status_t status = APR_SUCCESS; nghttp2_settings_entry settings[3]; size_t slen; - int i; + int win_size; AP_DEBUG_ASSERT(session); /* Start the conversation by submitting our SETTINGS frame */ @@ -947,10 +963,10 @@ apr_status_t h2_session_start(h2_session *session, int *rv) settings[slen].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; settings[slen].value = (uint32_t)session->max_stream_count; ++slen; - i = h2_config_geti(session->config, H2_CONF_WIN_SIZE); - if (i != H2_INITIAL_WINDOW_SIZE) { + win_size = h2_config_geti(session->config, H2_CONF_WIN_SIZE); + if (win_size != H2_INITIAL_WINDOW_SIZE) { settings[slen].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - settings[slen].value = i; + settings[slen].value = win_size; ++slen; } @@ -962,7 +978,25 @@ apr_status_t h2_session_start(h2_session *session, int *rv) APLOGNO(02935) "nghttp2_submit_settings: %s", nghttp2_strerror(*rv)); } - + else { + /* use maximum possible value for connection window size. We are only + * interested in per stream flow control. which have the initial window + * size configured above. + * Therefore, for our use, the connection window can only get in the + * way. Example: if we allow 100 streams with a 32KB window each, we + * buffer up to 3.2 MB of data. Unless we do separate connection window + * interim updates, any smaller connection window will lead to blocking + * in DATA flow. + */ + *rv = nghttp2_submit_window_update(session->ngh2, NGHTTP2_FLAG_NONE, + 0, NGHTTP2_MAX_WINDOW_SIZE - win_size); + if (*rv != 0) { + status = APR_EGENERAL; + ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, + APLOGNO(02970) "nghttp2_submit_window_update: %s", + nghttp2_strerror(*rv)); + } + } return status; } @@ -1272,7 +1306,7 @@ struct h2_stream *h2_session_push(h2_session *session, h2_stream *is, session->id, is->id, nghttp2_strerror(nid)); return NULL; } - + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_stream(%ld-%d): promised new stream %d for %s %s on %d", session->id, is->id, nid, @@ -1289,6 +1323,7 @@ struct h2_stream *h2_session_push(h2_session *session, h2_stream *is, h2_stream_cleanup(stream); stream = NULL; } + ++session->unsent_promises; } else { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, @@ -1539,14 +1574,23 @@ apr_status_t h2_session_process(h2_session *session) h2_session_resume_streams_with_data(session); if (h2_stream_set_has_unsubmitted(session->streams)) { + int unsent_submits = 0; + /* If we have responses ready, submit them now. */ while ((stream = h2_mplx_next_submit(session->mplx, session->streams))) { status = submit_response(session, stream); + ++unsent_submits; + + /* Unsent push promises are written immediately, as nghttp2 + * 1.5.0 realizes internal stream data structures only on + * send and we might need them for other submits. + * Also, to conserve memory, we send at least every 10 submits + * so that nghttp2 does not buffer all outbound items too + * long. + */ if (status == APR_SUCCESS - && nghttp2_session_want_write(session->ngh2)) { - int rv; - - rv = nghttp2_session_send(session->ngh2); + && (session->unsent_promises || unsent_submits > 10)) { + int rv = nghttp2_session_send(session->ngh2); if (rv != 0) { ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_session: send: %s", nghttp2_strerror(rv)); @@ -1558,6 +1602,8 @@ apr_status_t h2_session_process(h2_session *session) else { have_written = 1; wait_micros = 0; + session->unsent_promises = 0; + unsent_submits = 0; } } } @@ -1582,6 +1628,7 @@ apr_status_t h2_session_process(h2_session *session) else { have_written = 1; wait_micros = 0; + session->unsent_promises = 0; } } diff --git a/modules/http2/h2_session.h b/modules/http2/h2_session.h index 00347c93e4..cc2608089a 100644 --- a/modules/http2/h2_session.h +++ b/modules/http2/h2_session.h @@ -63,6 +63,8 @@ struct h2_session { int aborted; /* this session is being aborted */ int reprioritize; /* scheduled streams priority needs to * be re-evaluated */ + int unsent_promises; /* number of submitted, but not yet sent + * push promised */ apr_size_t frames_received; /* number of http/2 frames received */ apr_size_t max_stream_count; /* max number of open streams */ apr_size_t max_stream_mem; /* max buffer memory for a single stream */ diff --git a/modules/http2/h2_version.h b/modules/http2/h2_version.h index cf05c0dc35..b8ab5702bb 100644 --- a/modules/http2/h2_version.h +++ b/modules/http2/h2_version.h @@ -20,7 +20,7 @@ * @macro * Version number of the h2 module as c string */ -#define MOD_HTTP2_VERSION "1.0.8" +#define MOD_HTTP2_VERSION "1.0.10" /** * @macro @@ -28,7 +28,7 @@ * 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 0x010008 +#define MOD_HTTP2_VERSION_NUM 0x01000a #endif /* mod_h2_h2_version_h */