]> granicus.if.org Git - apache/commitdiff
using socket timeouts to stutter keepalives, so that MPM stopping can be properly...
authorStefan Eissing <icing@apache.org>
Thu, 21 Jan 2016 15:00:30 +0000 (15:00 +0000)
committerStefan Eissing <icing@apache.org>
Thu, 21 Jan 2016 15:00:30 +0000 (15:00 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1725973 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
modules/http2/h2_session.c
modules/http2/h2_session.h
modules/http2/h2_version.h

diff --git a/CHANGES b/CHANGES
index 44ad544bd86a323513a4cffdd2ca7b646bb6f56b..d6e7f5d7d0edb1e73e255c6ee5b35f6ce9f414b5 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,11 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) mod_http2: keep-alive blocking reads are done with 1 second timeouts to
+     check for MPM stopping. Will announce early GOAWAY and finish processing
+     open streams, then close.
+     [Stefan Eissing]
+  
   *) mod_proxy_hcheck: Provide for dynamic background health
      checks on reverse proxies associated with BalancerMember
      workers. [Jim Jagielski]
index 3a404eff75d6225aacdc820ffd3c39590dd4a901..386c69fbe0e4dd4cc667d65f4c864824b0f1f14c 100644 (file)
@@ -1825,15 +1825,26 @@ static void h2_session_ev_no_io(h2_session *session, int arg, const char *msg)
 {
     switch (session->state) {
         case H2_SESSION_ST_BUSY:
+        case H2_SESSION_ST_LOCAL_SHUTDOWN:
+        case H2_SESSION_ST_REMOTE_SHUTDOWN:
             /* nothing for input and output to do. If we remain
              * in this state, we go into a tight loop and suck up
              * CPU cycles. Ideally, we'd like to do a blocking read, but that
              * is not possible if we have scheduled tasks and wait
              * for them to produce something. */
             if (h2_stream_set_is_empty(session->streams)) {
-                /* When we have no streams, no task event are possible,
-                 * switch to blocking reads */
-                transit(session, "no io", H2_SESSION_ST_IDLE);
+                if (!is_accepting_streams(session)) {
+                    /* We are no longer accepting new streams and have
+                     * finished processing existing ones. Time to leave. */
+                    h2_session_shutdown(session, arg, msg);
+                    transit(session, "no io", H2_SESSION_ST_DONE);
+                }
+                else {
+                    /* When we have no streams, no task event are possible,
+                     * switch to blocking reads */
+                    transit(session, "no io", H2_SESSION_ST_IDLE);
+                    session->keepalive_remain = session->keepalive_secs;
+                }
             }
             else if (!h2_stream_set_has_unsubmitted(session->streams)
                      && !h2_stream_set_has_suspended(session->streams)) {
@@ -1841,6 +1852,7 @@ static void h2_session_ev_no_io(h2_session *session, int arg, const char *msg)
                  * new output data from task processing, 
                  * switch to blocking reads. */
                 transit(session, "no io", H2_SESSION_ST_IDLE);
+                session->keepalive_remain = session->keepalive_secs;
             }
             else {
                 /* Unable to do blocking reads, as we wait on events from
@@ -1904,6 +1916,19 @@ static void h2_session_ev_ngh2_done(h2_session *session, int arg, const char *ms
     }
 }
 
+static void h2_session_ev_mpm_stopping(h2_session *session, int arg, const char *msg)
+{
+    switch (session->state) {
+        case H2_SESSION_ST_DONE:
+        case H2_SESSION_ST_LOCAL_SHUTDOWN:
+            /* nop */
+            break;
+        default:
+            h2_session_shutdown(session, arg, msg);
+            break;
+    }
+}
+
 static void dispatch_event(h2_session *session, h2_session_event_t ev, 
                       int arg, const char *msg)
 {
@@ -1941,6 +1966,9 @@ static void dispatch_event(h2_session *session, h2_session_event_t ev,
         case H2_SESSION_EV_NGH2_DONE:
             h2_session_ev_ngh2_done(session, arg, msg);
             break;
+        case H2_SESSION_EV_MPM_STOPPING:
+            h2_session_ev_mpm_stopping(session, arg, msg);
+            break;
         default:
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
                           "h2_session(%ld): unknown event %d", 
@@ -1967,6 +1995,13 @@ apr_status_t h2_session_process(h2_session *session, int async)
     while (1) {
         have_read = have_written = 0;
 
+        if (!ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
+            if (mpm_state == AP_MPMQ_STOPPING) {
+                dispatch_event(session, H2_SESSION_EV_MPM_STOPPING, 0, NULL);
+                break;
+            }
+        }
+        
         switch (session->state) {
             case H2_SESSION_ST_INIT:
                 if (!h2_is_acceptable_connection(c, 1)) {
@@ -1988,21 +2023,9 @@ apr_status_t h2_session_process(h2_session *session, int async)
                 break;
                 
             case H2_SESSION_ST_IDLE:
-                if ((status = ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state))) {
-                    goto out;
-                }
-                if (mpm_state == AP_MPMQ_STOPPING) {
-                    dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
-                    break;
-                }
-                /* We'd like to wait in smaller increments, using a 1 second
-                 * timeout maybe, trying n times. That would allow us to exit
-                 * on MPMQ_STOPPING earlier.
-                 * Unfortunately, once a socket timeout happened, SSL reports
-                 * EOF on reads and the connection is gone. Not sure if we can
-                 * avoid that...
-                 */
-                h2_filter_cin_timeout_set(session->cin, session->keepalive_secs);
+                /* We wait in smaller increments, using a 1 second timeout.
+                 * That gives us the chance to check for MPMQ_STOPPING often. */
+                h2_filter_cin_timeout_set(session->cin, 1);
                 ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_KEEPALIVE, c);
                 status = h2_session_read(session, 1, 10);
                 if (status == APR_SUCCESS) {
@@ -2013,7 +2036,10 @@ apr_status_t h2_session_process(h2_session *session, int async)
                     /* nothing to read */
                 }
                 else if (APR_STATUS_IS_TIMEUP(status)) {
-                    dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
+                    if (--session->keepalive_remain <= 0) {
+                        dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
+                    }
+                    /* continue keepalive handling */
                 }
                 else {
                     dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
index 17a4ec5a6b2e68560a9c596f5b7cdf6f002240df..011e0561c905f1e700eee1e6a22456fc8f1fffea 100644 (file)
@@ -76,6 +76,7 @@ typedef enum {
     H2_SESSION_EV_STREAM_READY,     /* stream signalled availability of headers/data */
     H2_SESSION_EV_DATA_READ,        /* connection data has been read */
     H2_SESSION_EV_NGH2_DONE,        /* nghttp2 wants neither read nor write anything */
+    H2_SESSION_EV_MPM_STOPPING,     /* the process is stopping */
 } h2_session_event_t;
 
 typedef struct h2_session {
@@ -113,6 +114,7 @@ typedef struct h2_session {
     
     int timeout_secs;               /* connection timeout (seconds) */
     int keepalive_secs;             /* connection idle timeout (seconds) */
+    int keepalive_remain;           /* remaining seconds of keepalive */
     
     apr_pool_t *pool;               /* pool to use in session handling */
     apr_bucket_brigade *bbtmp;      /* brigade for keeping temporary data */
index 4de6aa4cb646d4b908bc754b4c55af7fff5ccbae..40b06cc7b4775e0608d687366bcdb59e5f18b70f 100644 (file)
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.2.3-DEV"
+#define MOD_HTTP2_VERSION "1.3.0-DEV"
 
 /**
  * @macro
@@ -34,7 +34,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 0x010203
+#define MOD_HTTP2_VERSION_NUM 0x010300
 
 
 #endif /* mod_h2_h2_version_h */