You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2015/12/18 15:45:18 UTC

svn commit: r1720801 - in /httpd/httpd/trunk: ./ docs/manual/mod/ modules/http2/

Author: icing
Date: Fri Dec 18 14:45:18 2015
New Revision: 1720801

URL: http://svn.apache.org/viewvc?rev=1720801&view=rev
Log:
3 new timeout configuration directives for mod_http2

Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/docs/manual/mod/mod_http2.xml
    httpd/httpd/trunk/modules/http2/h2_config.c
    httpd/httpd/trunk/modules/http2/h2_config.h
    httpd/httpd/trunk/modules/http2/h2_conn.c
    httpd/httpd/trunk/modules/http2/h2_filter.c
    httpd/httpd/trunk/modules/http2/h2_filter.h
    httpd/httpd/trunk/modules/http2/h2_io.c
    httpd/httpd/trunk/modules/http2/h2_io.h
    httpd/httpd/trunk/modules/http2/h2_mplx.c
    httpd/httpd/trunk/modules/http2/h2_mplx.h
    httpd/httpd/trunk/modules/http2/h2_session.c
    httpd/httpd/trunk/modules/http2/h2_session.h
    httpd/httpd/trunk/modules/http2/h2_version.h

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Fri Dec 18 14:45:18 2015
@@ -1,9 +1,14 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) mod_http2: adding new config directives and the implementation behind
+     them: H2Timeout, H2KeepAliveTimeout, H2StreamTimeout. Documentation in
+     the http2 manual.
+     [Stefan Eissing]
+
   *) mod_http2: when running in async mpm processing, mod_http2 will cease
      processing on idle connections (e.g. no open streams) back to the mpm.
-     [Stefan Eissing]     
+     [Stefan Eissing]
 
   *) mod_http2: fixed bug in input window size calculation by moving chunked
      request body encoding into later stage of processing.

Modified: httpd/httpd/trunk/docs/manual/mod/mod_http2.xml
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/mod/mod_http2.xml?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/docs/manual/mod/mod_http2.xml (original)
+++ httpd/httpd/trunk/docs/manual/mod/mod_http2.xml Fri Dec 18 14:45:18 2015
@@ -709,4 +709,98 @@ H2PushPriority text/css   interleaved
         </usage>
     </directivesynopsis>
     
+    <directivesynopsis>
+        <name>H2Timeout</name>
+        <description>Timeout (in seconds) for HTTP/2 connections</description>
+        <syntax>H2Timeout seconds</syntax>
+        <default>H2Timeout 5</default>
+        <contextlist>
+            <context>server config</context>
+            <context>virtual host</context>
+        </contextlist>
+        <compatibility>Available in version 2.4.19 and later.</compatibility>
+
+        <usage>
+            <p>
+                This directive sets the timeout for read/write operations on
+                connections where HTTP/2 is negotiated. This can be used server wide or for specific
+                <directive module="core" type="section">VirtualHost</directive>s. 
+            </p>
+            <p>
+                This directive is similar to the 
+                <directive module="core" type="section">Timeout</directive>, but
+                applies only to HTTP/2 connections.
+            </p>
+            <p>
+                A value of 0 enforces no timeout.
+            </p>
+        </usage>
+    </directivesynopsis>
+
+    <directivesynopsis>
+        <name>H2KeepAliveTimeout</name>
+        <description>Timeout (in seconds) for idle HTTP/2 connections</description>
+        <syntax>H2KeepAliveTimeout seconds</syntax>
+        <default>H2KeepAliveTimeout 300</default>
+        <contextlist>
+            <context>server config</context>
+            <context>virtual host</context>
+        </contextlist>
+        <compatibility>Available in version 2.4.19 and later.</compatibility>
+
+        <usage>
+            <p>
+                This directive sets the timeout for read/write operations on
+                idle connections where HTTP/2 is negotiated. This can be used server wide or for specific
+                <directive module="core" type="section">VirtualHost</directive>s. 
+            </p>
+            <p>
+                This directive is similar to the 
+                <directive module="core" type="section">KeepAliveTimeout</directive>, but
+                applies only to HTTP/2 connections. A HTTP/2 connection is considered
+                idle when no streams are open, e.g. no requests are ongoing.
+            </p>
+            <p>
+                A value of 0 enforces no timeout.
+            </p>
+        </usage>
+    </directivesynopsis>
+
+    <directivesynopsis>
+        <name>H2StreamTimeout</name>
+        <description>Timeout (in seconds) for idle HTTP/2 connections</description>
+        <syntax>H2StreamTimeout seconds</syntax>
+        <default>H2StreamTimeout 120</default>
+        <contextlist>
+            <context>server config</context>
+            <context>virtual host</context>
+        </contextlist>
+        <compatibility>Available in version 2.4.19 and later.</compatibility>
+
+        <usage>
+            <p>
+                This directive sets the timeout for read/write operations on
+                HTTP/2 streams, e.g. individual requests. This can be used server wide or for specific
+                <directive module="core" type="section">VirtualHost</directive>s. 
+            </p>
+            <p>
+                Due to the nature of HTTP/2, which sends multiple requests over a single
+                connection and has priority scheduling, individual streams might not
+                see input for much longer times than HTTP/1.1 requests would. 
+            </p>
+            <p>
+                A value of 0 enforces no timeout, so could wait on chances to receive
+                input or write data indefinitely. This expose a server to
+                risks of thread exhaustion. 
+            </p>
+            <p>
+                Depending on your handling of pushed streams,
+                priorities and general responsiveness, a site might need to increase
+                this value. For example, if you PUSH a large resource <em>before</em>
+                the requested one, the initial stream will not write until the
+                pushed resource is fully sent.
+            </p>
+        </usage>
+    </directivesynopsis>
+
 </modulesynopsis>

Modified: httpd/httpd/trunk/modules/http2/h2_config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_config.c?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_config.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_config.c Fri Dec 18 14:45:18 2015
@@ -59,6 +59,9 @@ static h2_config defconf = {
     1,                      /* TLS cooldown secs */
     1,                      /* HTTP/2 server push enabled */
     NULL,                   /* map of content-type to priorities */
+    5,                      /* normal connection timeout */
+    5*60,                   /* idle connection timeout */
+    2*60,                   /* stream timeout */
 };
 
 static int files_per_session;
@@ -122,6 +125,9 @@ static void *h2_config_create(apr_pool_t
     conf->tls_cooldown_secs    = DEF_VAL;
     conf->h2_push              = DEF_VAL;
     conf->priorities           = NULL;
+    conf->h2_timeout           = DEF_VAL;
+    conf->h2_keepalive         = DEF_VAL;
+    conf->h2_stream_timeout    = DEF_VAL;
     
     return conf;
 }
@@ -167,6 +173,9 @@ void *h2_config_merge(apr_pool_t *pool,
     else {
         n->priorities       = add->priorities? add->priorities : base->priorities;
     }
+    n->h2_timeout           = H2_CONFIG_GET(add, base, h2_timeout);
+    n->h2_keepalive = H2_CONFIG_GET(add, base, h2_keepalive);
+    n->h2_stream_timeout    = H2_CONFIG_GET(add, base, h2_stream_timeout);
     
     return n;
 }
@@ -214,6 +223,12 @@ apr_int64_t h2_config_geti64(const h2_co
             return H2_CONFIG_GET(conf, &defconf, tls_cooldown_secs);
         case H2_CONF_PUSH:
             return H2_CONFIG_GET(conf, &defconf, h2_push);
+        case H2_CONF_TIMEOUT_SECS:
+            return H2_CONFIG_GET(conf, &defconf, h2_timeout);
+        case H2_CONF_KEEPALIVE_SECS:
+            return H2_CONFIG_GET(conf, &defconf, h2_keepalive);
+        case H2_CONF_STREAM_TIMEOUT_SECS:
+            return H2_CONFIG_GET(conf, &defconf, h2_stream_timeout);
         default:
             return DEF_VAL;
     }
@@ -511,6 +526,42 @@ static const char *h2_conf_set_tls_coold
     return NULL;
 }
 
+static const char *h2_conf_set_timeout(cmd_parms *parms,
+                                       void *arg, const char *value)
+{
+    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
+    (void)arg;
+    cfg->h2_timeout = (int)apr_atoi64(value);
+    if (cfg->h2_timeout < 0) {
+        return "value must be >= 0";
+    }
+    return NULL;
+}
+
+static const char *h2_conf_set_keepalive(cmd_parms *parms,
+                                                 void *arg, const char *value)
+{
+    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
+    (void)arg;
+    cfg->h2_keepalive = (int)apr_atoi64(value);
+    if (cfg->h2_keepalive < 0) {
+        return "value must be >= 0";
+    }
+    return NULL;
+}
+
+static const char *h2_conf_set_stream_timeout(cmd_parms *parms,
+                                              void *arg, const char *value)
+{
+    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
+    (void)arg;
+    cfg->h2_stream_timeout = (int)apr_atoi64(value);
+    if (cfg->h2_stream_timeout < 0) {
+        return "value must be >= 0";
+    }
+    return NULL;
+}
+
 
 #define AP_END_CMD     AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
 
@@ -549,6 +600,12 @@ const command_rec h2_cmds[] = {
                   RSRC_CONF, "off to disable HTTP/2 server push"),
     AP_INIT_TAKE23("H2PushPriority", h2_conf_add_push_priority, NULL,
                   RSRC_CONF, "define priority of PUSHed resources per content type"),
+    AP_INIT_TAKE1("H2Timeout", h2_conf_set_timeout, NULL,
+                  RSRC_CONF, "read/write timeout (seconds) for HTTP/2 connections"),
+    AP_INIT_TAKE1("H2KeepAliveTimeout", h2_conf_set_keepalive, NULL,
+                  RSRC_CONF, "timeout (seconds) for idle HTTP/2 connections, no streams open"),
+    AP_INIT_TAKE1("H2StreamTimeout", h2_conf_set_stream_timeout, NULL,
+                  RSRC_CONF, "read/write timeout (seconds) for HTTP/2 streams"),
     AP_END_CMD
 };
 

Modified: httpd/httpd/trunk/modules/http2/h2_config.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_config.h?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_config.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_config.h Fri Dec 18 14:45:18 2015
@@ -39,6 +39,9 @@ typedef enum {
     H2_CONF_TLS_WARMUP_SIZE,
     H2_CONF_TLS_COOLDOWN_SECS,
     H2_CONF_PUSH,
+    H2_CONF_TIMEOUT_SECS,
+    H2_CONF_KEEPALIVE_SECS,
+    H2_CONF_STREAM_TIMEOUT_SECS,
 } h2_config_var_t;
 
 struct apr_hash_t;
@@ -65,6 +68,10 @@ typedef struct h2_config {
     int tls_cooldown_secs;        /* Seconds of idle time before going back to small TLS records */
     int h2_push;                  /* if HTTP/2 server push is enabled */
     struct apr_hash_t *priorities;/* map of content-type to h2_priority records */
+    
+    int h2_timeout;               /* timeout for http/2 connections */
+    int h2_keepalive;             /* timeout for idle connections, no streams */
+    int h2_stream_timeout;        /* timeout for http/2 streams, slave connections */
 } h2_config;
 
 

Modified: httpd/httpd/trunk/modules/http2/h2_conn.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_conn.c?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_conn.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_conn.c Fri Dec 18 14:45:18 2015
@@ -132,7 +132,6 @@ static module *h2_conn_mpm_module(void)
 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
 {
     h2_session *session;
-    h2_filter_core_in *in;
     
     if (!workers) {
         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911) 
@@ -149,10 +148,6 @@ apr_status_t h2_conn_setup(h2_ctx *ctx,
 
     h2_ctx_session_set(ctx, session);
     
-    in = apr_pcalloc(session->pool, sizeof(*in));
-    in->session = session;
-    ap_add_input_filter("H2_IN", in, r, c);
-    
     ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
 
     return APR_SUCCESS;

Modified: httpd/httpd/trunk/modules/http2/h2_filter.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_filter.c?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_filter.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_filter.c Fri Dec 18 14:45:18 2015
@@ -22,14 +22,15 @@
 #include <scoreboard.h>
 
 #include "h2_private.h"
-#include "h2_session.h"
 #include "h2_conn_io.h"
 #include "h2_util.h"
 
 #include "h2_filter.h"
 
+#define UNSET       -1
+#define H2MIN(x,y) ((x) < (y) ? (x) : (y))
 
-static apr_status_t consume_brigade(h2_filter_core_in *in, 
+static apr_status_t consume_brigade(h2_filter_cin *cin, 
                                     apr_bucket_brigade *bb, 
                                     apr_read_type_e block)
 {
@@ -51,13 +52,13 @@ static apr_status_t consume_brigade(h2_f
             if (status == APR_SUCCESS && bucket_length > 0) {
                 apr_size_t consumed = 0;
 
-                status = h2_session_receive(in->session, bucket_data, 
-                                            bucket_length, &consumed);
+                status = cin->cb(cin->cb_ctx, bucket_data, bucket_length, &consumed);
                 if (status == APR_SUCCESS && bucket_length > consumed) {
                     /* We have data left in the bucket. Split it. */
                     status = apr_bucket_split(bucket, consumed);
                 }
                 readlen += consumed;
+                cin->last_read = apr_time_now();
             }
         }
         apr_bucket_delete(bucket);
@@ -69,6 +70,38 @@ static apr_status_t consume_brigade(h2_f
     return status;
 }
 
+static apr_status_t check_time_left(h2_filter_cin *cin,
+                                    apr_time_t *ptime_left)
+{
+    if (cin->timeout_secs > 0) {
+        *ptime_left = (cin->last_read + apr_time_from_sec(cin->timeout_secs)
+                       - apr_time_now());
+        if (*ptime_left <= 0)
+            return APR_TIMEUP;
+        
+        if (*ptime_left < apr_time_from_sec(1)) {
+            *ptime_left = apr_time_from_sec(1);
+        }
+    }
+    return APR_SUCCESS;
+}
+
+h2_filter_cin *h2_filter_cin_create(apr_pool_t *p, h2_filter_cin_cb *cb, void *ctx)
+{
+    h2_filter_cin *cin;
+    
+    cin = apr_pcalloc(p, sizeof(*cin));
+    cin->pool      = p;
+    cin->cb        = cb;
+    cin->cb_ctx    = ctx;
+    cin->last_read = UNSET;
+    return cin;
+}
+
+void h2_filter_cin_timeout_set(h2_filter_cin *cin, int timeout_secs)
+{
+    cin->timeout_secs = timeout_secs;
+}
 
 apr_status_t h2_filter_core_input(ap_filter_t* f,
                                   apr_bucket_brigade* brigade,
@@ -76,10 +109,12 @@ apr_status_t h2_filter_core_input(ap_fil
                                   apr_read_type_e block,
                                   apr_off_t readbytes) 
 {
-    h2_filter_core_in *in = f->ctx;
+    h2_filter_cin *cin = f->ctx;
     apr_status_t status = APR_SUCCESS;
+    apr_time_t saved_timeout = UNSET;
+    apr_time_t time_left = UNSET;
     
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, f->c,
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
                   "core_input: read, block=%d, mode=%d, readbytes=%ld", 
                   block, mode, (long)readbytes);
     
@@ -91,32 +126,54 @@ apr_status_t h2_filter_core_input(ap_fil
         return (block == APR_BLOCK_READ)? APR_SUCCESS : APR_EAGAIN;
     }
     
-    if (!f->bb) {
-        f->bb = apr_brigade_create(in->session->pool, f->c->bucket_alloc);
+    if (!cin->bb) {
+        cin->bb = apr_brigade_create(cin->pool, f->c->bucket_alloc);
+    }
+
+    if (!cin->socket) {
+        cin->socket = ap_get_conn_socket(f->c);
+        cin->last_read = apr_time_now(); /* first call */
     }
     
-    if (APR_BRIGADE_EMPTY(f->bb)) {
+    if (APR_BRIGADE_EMPTY(cin->bb)) {
         /* We only do a blocking read when we have no streams to process. So,
          * in httpd scoreboard lingo, we are in a KEEPALIVE connection state.
          * When reading non-blocking, we do have streams to process and update
          * child with NULL request. That way, any current request information
          * in the scoreboard is preserved.
          */
+        status = check_time_left(cin, &time_left);
+        if (status != APR_SUCCESS)
+            goto out;
+        
         if (block == APR_BLOCK_READ) {
             ap_update_child_status_from_conn(f->c->sbh, 
                                              SERVER_BUSY_KEEPALIVE, f->c);
+            if (time_left > 0) {
+                status = apr_socket_timeout_get(cin->socket, &saved_timeout);
+                AP_DEBUG_ASSERT(status == APR_SUCCESS);
+                status = apr_socket_timeout_set(cin->socket, H2MIN(time_left, saved_timeout));
+                AP_DEBUG_ASSERT(status == APR_SUCCESS);
+            }
         }
         else {
             ap_update_child_status(f->c->sbh, SERVER_BUSY_READ, NULL);
         }
         
-        status = ap_get_brigade(f->next, f->bb, AP_MODE_READBYTES,
+        status = ap_get_brigade(f->next, cin->bb, AP_MODE_READBYTES,
                                 block, readbytes);
     }
     
+out:
     switch (status) {
         case APR_SUCCESS:
-            return consume_brigade(in, f->bb, block);
+            if (saved_timeout != UNSET) {
+                apr_socket_timeout_set(cin->socket, saved_timeout);
+            }
+            status = consume_brigade(cin, cin->bb, block);
+            if (status == APR_SUCCESS) {
+                status = check_time_left(cin, &time_left);
+            }
         case APR_EOF:
         case APR_EAGAIN:
             break;

Modified: httpd/httpd/trunk/modules/http2/h2_filter.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_filter.h?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_filter.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_filter.h Fri Dec 18 14:45:18 2015
@@ -18,9 +18,23 @@
 
 struct h2_session;
 
-typedef struct {
-    struct h2_session *session;
-} h2_filter_core_in;
+typedef apr_status_t h2_filter_cin_cb(void *ctx, 
+                                      const char *data, apr_size_t len,
+                                      apr_size_t *readlen);
+
+typedef struct h2_filter_cin {
+    apr_pool_t *pool;
+    apr_bucket_brigade *bb;
+    h2_filter_cin_cb *cb;
+    void *cb_ctx;
+    apr_socket_t *socket;
+    int timeout_secs;
+    apr_time_t last_read;
+} h2_filter_cin;
+
+h2_filter_cin *h2_filter_cin_create(apr_pool_t *p, h2_filter_cin_cb *cb, void *ctx);
+
+void h2_filter_cin_timeout_set(h2_filter_cin *cin, int timeout_secs);
 
 apr_status_t h2_filter_core_input(ap_filter_t* filter,
                                   apr_bucket_brigade* brigade,

Modified: httpd/httpd/trunk/modules/http2/h2_io.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_io.c?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_io.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_io.c Fri Dec 18 14:45:18 2015
@@ -15,13 +15,18 @@
 
 #include <assert.h>
 
+#include <apr_thread_mutex.h>
+#include <apr_thread_cond.h>
+
 #include <httpd.h>
 #include <http_core.h>
 #include <http_log.h>
 #include <http_connection.h>
 
 #include "h2_private.h"
+#include "h2_h2.h"
 #include "h2_io.h"
+#include "h2_mplx.h"
 #include "h2_response.h"
 #include "h2_request.h"
 #include "h2_task.h"
@@ -95,6 +100,66 @@ apr_status_t h2_io_in_shutdown(h2_io *io
     return h2_io_in_close(io);
 }
 
+
+void h2_io_signal_init(h2_io *io, h2_io_op op, int timeout_secs, apr_thread_cond_t *cond)
+{
+    io->timed_op = op;
+    io->timed_cond = cond;
+    if (timeout_secs > 0) {
+        io->timeout_at = apr_time_now() + apr_time_from_sec(timeout_secs);
+    }
+    else {
+        io->timeout_at = 0; 
+    }
+}
+
+void h2_io_signal_exit(h2_io *io)
+{
+    io->timed_cond = NULL;
+    io->timeout_at = 0; 
+}
+
+apr_status_t h2_io_signal_wait(h2_mplx *m, h2_io *io)
+{
+    apr_status_t status;
+    
+    if (io->timeout_at != 0) {
+        status = apr_thread_cond_timedwait(io->timed_cond, m->lock, io->timeout_at);
+        if (APR_STATUS_IS_TIMEUP(status)) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c,  
+                          "h2_mplx(%ld-%d): stream timeout expired: %s",
+                          m->id, io->id, 
+                          (io->timed_op == H2_IO_READ)? "read" : "write");
+            h2_io_rst(io, H2_ERR_CANCEL);
+        }
+    }
+    else {
+        apr_thread_cond_wait(io->timed_cond, m->lock);
+        status = APR_SUCCESS;
+    }
+    if (io->orphaned && status == APR_SUCCESS) {
+        return APR_ECONNABORTED;
+    }
+    return status;
+}
+
+void h2_io_signal(h2_io *io, h2_io_op op)
+{
+    if (io->timed_cond && (io->timed_op == op || H2_IO_ANY == op)) {
+        apr_thread_cond_signal(io->timed_cond);
+    }
+}
+
+void h2_io_make_orphaned(h2_io *io, int error)
+{
+    io->orphaned = 1;
+    if (error) {
+        h2_io_rst(io, error);
+    }
+    /* if someone is waiting, wake him up */
+    h2_io_signal(io, H2_IO_ANY);
+}
+
 static int add_trailer(void *ctx, const char *key, const char *value)
 {
     apr_bucket_brigade *bb = ctx;

Modified: httpd/httpd/trunk/modules/http2/h2_io.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_io.h?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_io.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_io.h Fri Dec 18 14:45:18 2015
@@ -18,6 +18,7 @@
 
 struct h2_response;
 struct apr_thread_cond_t;
+struct h2_mplx;
 struct h2_request;
 
 
@@ -25,6 +26,12 @@ typedef apr_status_t h2_io_data_cb(void
 
 typedef int h2_stream_pri_cmp(int stream_id1, int stream_id2, void *ctx);
 
+typedef enum {
+    H2_IO_READ,
+    H2_IO_WRITE,
+    H2_IO_ANY,
+}
+h2_io_op;
 
 typedef struct h2_io h2_io;
 
@@ -39,16 +46,18 @@ struct h2_io {
     struct h2_response *response;/* response for submit, once created */
     int rst_error;
 
+    h2_io_op timed_op;           /* which operation is waited on */
+    struct apr_thread_cond_t *timed_cond; /* condition to wait on */
+    apr_time_t timeout_at;       /* when IO wait will time out */
+    
     int eos_in;
     int eos_in_written;
     apr_bucket_brigade *bbin;    /* input data for stream */
-    struct apr_thread_cond_t *input_arrived; /* block on reading */
     apr_size_t input_consumed;   /* how many bytes have been read */
     
     int eos_out;
     apr_bucket_brigade *bbout;   /* output data from stream */
     apr_bucket_alloc_t *bucket_alloc;
-    struct apr_thread_cond_t *output_drained; /* block on writing */
     
     int files_handles_owned;
     apr_bucket_brigade *tmp;     /* temporary data for chunking */
@@ -88,6 +97,14 @@ int h2_io_in_has_eos_for(h2_io *io);
  */
 int h2_io_out_has_data(h2_io *io);
 
+void h2_io_signal(h2_io *io, h2_io_op op);
+void h2_io_signal_init(h2_io *io, h2_io_op op, int timeout_secs, 
+                       struct apr_thread_cond_t *cond);
+void h2_io_signal_exit(h2_io *io);
+apr_status_t h2_io_signal_wait(struct h2_mplx *m, h2_io *io);
+
+void h2_io_make_orphaned(h2_io *io, int error);
+
 /*******************************************************************************
  * Input handling of streams.
  ******************************************************************************/

Modified: httpd/httpd/trunk/modules/http2/h2_mplx.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_mplx.c?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_mplx.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_mplx.c Fri Dec 18 14:45:18 2015
@@ -145,6 +145,7 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr
         m->workers = workers;
         
         m->file_handles_allowed = h2_config_geti(conf, H2_CONF_SESSION_FILES);
+        m->stream_timeout_secs = h2_config_geti(conf, H2_CONF_STREAM_TIMEOUT_SECS);
     }
     return m;
 }
@@ -248,10 +249,7 @@ static int io_stream_done(h2_mplx *m, h2
     }
     else {
         /* cleanup once task is done */
-        io->orphaned = 1;
-        if (rst_error) {
-            h2_io_rst(io, rst_error);
-        }
+        h2_io_make_orphaned(io, rst_error);
         return 1;
     }
 }
@@ -283,9 +281,9 @@ apr_status_t h2_mplx_release_and_join(h2
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c,
                       "h2_mplx(%ld): release_join -> destroy, (#ios=%ld)", 
                       m->id, (long)h2_io_set_size(m->stream_ios));
+        apr_thread_mutex_unlock(m->lock);
         h2_mplx_destroy(m);
         /* all gone */
-        /*apr_thread_mutex_unlock(m->lock);*/
     }
     return status;
 }
@@ -356,8 +354,9 @@ apr_status_t h2_mplx_in_read(h2_mplx *m,
     if (APR_SUCCESS == status) {
         h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
         if (io && !io->orphaned) {
-            io->input_arrived = iowait;
             H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_read_pre");
+            
+            h2_io_signal_init(io, H2_IO_READ, m->stream_timeout_secs, iowait);
             status = h2_io_in_read(io, bb, -1, trailers);
             while (APR_STATUS_IS_EAGAIN(status) 
                    && !is_aborted(m, &status)
@@ -365,11 +364,13 @@ apr_status_t h2_mplx_in_read(h2_mplx *m,
                 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, m->c,
                               "h2_mplx(%ld-%d): wait on in data (BLOCK_READ)", 
                               m->id, stream_id);
-                apr_thread_cond_wait(io->input_arrived, m->lock);
-                status = h2_io_in_read(io, bb, -1, trailers);
+                status = h2_io_signal_wait(m, io);
+                if (status == APR_SUCCESS) {
+                    status = h2_io_in_read(io, bb, -1, trailers);
+                }
             }
             H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_read_post");
-            io->input_arrived = NULL;
+            h2_io_signal_exit(io);
         }
         else {
             status = APR_EOF;
@@ -394,9 +395,7 @@ apr_status_t h2_mplx_in_write(h2_mplx *m
             H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_write_pre");
             status = h2_io_in_write(io, bb);
             H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_write_post");
-            if (io->input_arrived) {
-                apr_thread_cond_signal(io->input_arrived);
-            }
+            h2_io_signal(io, H2_IO_READ);
             io_process_events(m, io);
         }
         else {
@@ -420,9 +419,7 @@ apr_status_t h2_mplx_in_close(h2_mplx *m
         if (io && !io->orphaned) {
             status = h2_io_in_close(io);
             H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_close");
-            if (io->input_arrived) {
-                apr_thread_cond_signal(io->input_arrived);
-            }
+            h2_io_signal(io, H2_IO_READ);
             io_process_events(m, io);
         }
         else {
@@ -496,8 +493,8 @@ apr_status_t h2_mplx_out_readx(h2_mplx *
             
             status = h2_io_out_readx(io, cb, ctx, plen, peos);
             H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_post");
-            if (status == APR_SUCCESS && cb && io->output_drained) {
-                apr_thread_cond_signal(io->output_drained);
+            if (status == APR_SUCCESS && cb) {
+                h2_io_signal(io, H2_IO_WRITE);
             }
         }
         else {
@@ -529,8 +526,8 @@ apr_status_t h2_mplx_out_read_to(h2_mplx
             status = h2_io_out_read_to(io, bb, plen, peos);
             
             H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_read_to_post");
-            if (status == APR_SUCCESS && io->output_drained) {
-                apr_thread_cond_signal(io->output_drained);
+            if (status == APR_SUCCESS) {
+                h2_io_signal(io, H2_IO_WRITE);
             }
         }
         else {
@@ -576,7 +573,7 @@ h2_stream *h2_mplx_next_submit(h2_mplx *
                               "h2_mplx(%ld): stream for response %d closed, "
                               "resetting io to close request processing",
                               m->id, io->id);
-                io->orphaned = 1;
+                h2_io_make_orphaned(io, H2_ERR_STREAM_CLOSED);
                 if (io->task_done) {
                     io_destroy(m, io, 1);
                 }
@@ -585,14 +582,11 @@ h2_stream *h2_mplx_next_submit(h2_mplx *
                      * shutdown input and send out any events (e.g. window
                      * updates) asap. */
                     h2_io_in_shutdown(io);
-                    h2_io_rst(io, H2_ERR_STREAM_CLOSED);
                     io_process_events(m, io);
                 }
             }
             
-            if (io->output_drained) {
-                apr_thread_cond_signal(io->output_drained);
-            }
+            h2_io_signal(io, H2_IO_WRITE);
         }
         apr_thread_mutex_unlock(m->lock);
     }
@@ -610,28 +604,29 @@ static apr_status_t out_write(h2_mplx *m
      * We will not split buckets to enforce the limit to the last
      * byte. After all, the bucket is already in memory.
      */
-    while (!APR_BRIGADE_EMPTY(bb) 
-           && (status == APR_SUCCESS)
+    while (status == APR_SUCCESS 
+           && !APR_BRIGADE_EMPTY(bb) 
            && !is_aborted(m, &status)) {
         
         status = h2_io_out_write(io, bb, m->stream_max_mem, trailers,
                                  &m->file_handles_allowed);
-        /* Wait for data to drain until there is room again */
-        while (!APR_BRIGADE_EMPTY(bb) 
+        /* Wait for data to drain until there is room again or
+         * stream timeout expires */
+        h2_io_signal_init(io, H2_IO_WRITE, m->stream_timeout_secs, iowait);
+        while (status == APR_SUCCESS
+               && !APR_BRIGADE_EMPTY(bb) 
                && iowait
-               && status == APR_SUCCESS
                && (m->stream_max_mem <= h2_io_out_length(io))
                && !is_aborted(m, &status)) {
             trailers = NULL;
-            io->output_drained = iowait;
             if (f) {
                 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
                               "h2_mplx(%ld-%d): waiting for out drain", 
                               m->id, io->id);
             }
-            apr_thread_cond_wait(io->output_drained, m->lock);
-            io->output_drained = NULL;
+            status = h2_io_signal_wait(m, io);
         }
+        h2_io_signal_exit(io);
     }
     apr_brigade_cleanup(bb);
     
@@ -793,9 +788,7 @@ apr_status_t h2_mplx_out_rst(h2_mplx *m,
                 H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_rst");
                 
                 have_out_data_for(m, stream_id);
-                if (io->output_drained) {
-                    apr_thread_cond_signal(io->output_drained);
-                }
+                h2_io_signal(io, H2_IO_WRITE);
             }
             else {
                 status = APR_ECONNABORTED;

Modified: httpd/httpd/trunk/modules/http2/h2_mplx.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_mplx.h?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_mplx.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_mplx.h Fri Dec 18 14:45:18 2015
@@ -77,6 +77,7 @@ struct h2_mplx {
     
     int aborted;
     apr_size_t stream_max_mem;
+    int stream_timeout_secs;
     
     apr_pool_t *spare_pool;           /* spare pool, ready for next io */
     struct h2_workers *workers;

Modified: httpd/httpd/trunk/modules/http2/h2_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_session.c?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_session.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_session.c Fri Dec 18 14:45:18 2015
@@ -27,6 +27,7 @@
 #include "h2_bucket_eos.h"
 #include "h2_config.h"
 #include "h2_ctx.h"
+#include "h2_filter.h"
 #include "h2_h2.h"
 #include "h2_mplx.h"
 #include "h2_push.h"
@@ -66,6 +67,9 @@ static void update_window(void *ctx, int
                   session->id, stream_id, (long)bytes_read);
 }
 
+static apr_status_t h2_session_receive(void *ctx, 
+                                       const char *data, apr_size_t len,
+                                       apr_size_t *readlen);
 
 h2_stream *h2_session_open_stream(h2_session *session, int stream_id)
 {
@@ -708,7 +712,9 @@ static h2_session *h2_session_create_int
         
         session->max_stream_count = h2_config_geti(session->config, H2_CONF_MAX_STREAMS);
         session->max_stream_mem = h2_config_geti(session->config, H2_CONF_STREAM_MAX_MEM);
-
+        session->timeout_secs = h2_config_geti(session->config, H2_CONF_TIMEOUT_SECS);
+        session->keepalive_secs = h2_config_geti(session->config, H2_CONF_KEEPALIVE_SECS);
+        
         status = apr_thread_cond_create(&session->iowait, session->pool);
         if (status != APR_SUCCESS) {
             return NULL;
@@ -721,6 +727,11 @@ static h2_session *h2_session_create_int
         
         h2_mplx_set_consumed_cb(session->mplx, update_window, session);
         
+        /* Install the connection input filter that feeds the session */
+        session->cin = h2_filter_cin_create(session->pool, h2_session_receive, session);
+        h2_filter_cin_timeout_set(session->cin, session->timeout_secs);
+        ap_add_input_filter("H2_IN", session->cin, r, c);
+
         h2_conn_io_init(&session->io, c, session->config, session->pool);
         session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc);
         
@@ -1557,10 +1568,10 @@ static apr_status_t h2_session_send(h2_s
     return APR_SUCCESS;
 }
 
-apr_status_t h2_session_receive(h2_session *session, 
-                                const char *data, apr_size_t len,
-                                apr_size_t *readlen)
+static apr_status_t h2_session_receive(void *ctx, const char *data, 
+                                       apr_size_t len, apr_size_t *readlen)
 {
+    h2_session *session = ctx;
     if (len > 0) {
         ssize_t n = nghttp2_session_mem_recv(session->ngh2,
                                              (const uint8_t *)data, len);
@@ -1713,7 +1724,8 @@ apr_status_t h2_session_process(h2_sessi
                              || idle
                              || (!h2_stream_set_has_unsubmitted(session->streams)
                                  && !h2_stream_set_has_suspended(session->streams)));
-            
+                                 
+            h2_filter_cin_timeout_set(session->cin, idle? session->keepalive_secs : session->timeout_secs);
             status = h2_session_read(session, may_block && !async);
             
             ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, session->c,

Modified: httpd/httpd/trunk/modules/http2/h2_session.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_session.h?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_session.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_session.h Fri Dec 18 14:45:18 2015
@@ -41,6 +41,7 @@ struct apr_thread_mutext_t;
 struct apr_thread_cond_t;
 struct h2_ctx;
 struct h2_config;
+struct h2_filter_cin;
 struct h2_mplx;
 struct h2_priority;
 struct h2_push;
@@ -83,6 +84,10 @@ struct h2_session {
     apr_bucket_brigade *bbtmp;      /* brigade for keeping temporary data */
     struct apr_thread_cond_t *iowait; /* our cond when trywaiting for data */
     
+    int timeout_secs;               /* connection timeout (seconds) */
+    int keepalive_secs;             /* connection idle timeout (seconds) */
+    
+    struct h2_filter_cin *cin;      /* connection input filter context */
     h2_conn_io io;                  /* io on httpd conn filters */
 
     struct h2_mplx *mplx;           /* multiplexer for stream data */
@@ -123,14 +128,6 @@ h2_session *h2_session_rcreate(request_r
                                struct h2_workers *workers);
 
 /**
- * Recieve len bytes of raw HTTP/2 input data. Return the amount
- * consumed and if the session is done.
- */
-apr_status_t h2_session_receive(h2_session *session, 
-                                const char *data, apr_size_t len,
-                                apr_size_t *readlen);
-
-/**
  * Process the given HTTP/2 session until it is ended or a fatal
  * error occured.
  *

Modified: httpd/httpd/trunk/modules/http2/h2_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_version.h?rev=1720801&r1=1720800&r2=1720801&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_version.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_version.h Fri Dec 18 14:45:18 2015
@@ -20,7 +20,7 @@
  * @macro
  * Version number of the h2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.0.12-DEV"
+#define MOD_HTTP2_VERSION "1.0.12-DEVd"
 
 /**
  * @macro