]> granicus.if.org Git - apache/commitdiff
v1.0.10 of mod_http2 backport, see CHANGES
authorStefan Eissing <icing@apache.org>
Fri, 4 Dec 2015 15:16:24 +0000 (15:16 +0000)
committerStefan Eissing <icing@apache.org>
Fri, 4 Dec 2015 15:16:24 +0000 (15:16 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1717980 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
modules/http2/config.m4
modules/http2/h2_conn.c
modules/http2/h2_session.c
modules/http2/h2_session.h
modules/http2/h2_version.h

diff --git a/CHANGES b/CHANGES
index b10b093f0a8eeba315d471e67765cbcd42d69723..e3e2b2ed4d7476a056cfd3d1b4d0c2e6f0ec6b39 100644 (file)
--- 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]
 
index e10bb8158f7b4523b9d0e40bc22c0b19ab35a331..05cf2ba36d0f397b906448d2174a45b8db73a25c 100644 (file)
@@ -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 <nghttp2/nghttp2ver.h>],[
 #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
index cf44fd7c1fcdf7e321eafb2cd09126416b9e329b..b9b6e87bf0e51eb6c035501ddf27327a5ddac7c6 100644 (file)
@@ -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;
 }
 
index cce8ca7d5ae95297d25e923950c9e06b555730b2..e5a158c86a0dce38375eb41ae5c4ad79d8cd6754 100644 (file)
@@ -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;
             }
         }
         
index 00347c93e432359ad9c2b2f4b842948636217ac3..cc2608089ac1a55c22f86bb981771cd4d4a05c15 100644 (file)
@@ -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 */
index cf05c0dc351e0284bacadb590b90d2ffdccf19d2..b8ab5702bb863751b6d3244551dcbead89e565bd 100644 (file)
@@ -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 */