]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Backport:
authorGraham Leggett <minfrin@apache.org>
Sun, 26 Sep 2021 14:30:51 +0000 (14:30 +0000)
committerGraham Leggett <minfrin@apache.org>
Sun, 26 Sep 2021 14:30:51 +0000 (14:30 +0000)
*) mod_http2: when a server is restarted gracefully, any idle h2 worker
   threads are shut down immediately.
   Also, change OpenSSL API use for deprecations in OpenSSL 3.0.
   Adds all other, never proposed code changes to make a clean
   sync of http2 sources.
   trunk patch: http://svn.apache.org/r1893214
                http://svn.apache.org/r1893215
                http://svn.apache.org/r1893220
                and other never proposed code changes
   PR: https://github.com/apache/httpd/pull/270
   +1: icing, rpluem, minfrin

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1893658 13f79535-47bb-0310-9956-ffa450edef68

26 files changed:
CHANGES
STATUS
modules/http2/h2_alt_svc.c
modules/http2/h2_bucket_beam.c
modules/http2/h2_bucket_eos.c
modules/http2/h2_config.c
modules/http2/h2_conn.c
modules/http2/h2_conn.h
modules/http2/h2_conn_io.c
modules/http2/h2_from_h1.c
modules/http2/h2_headers.c
modules/http2/h2_headers.h
modules/http2/h2_mplx.c
modules/http2/h2_push.c
modules/http2/h2_push.h
modules/http2/h2_request.c
modules/http2/h2_session.c
modules/http2/h2_session.h
modules/http2/h2_task.c
modules/http2/h2_task.h
modules/http2/h2_util.c
modules/http2/h2_version.h
modules/http2/h2_workers.c
modules/http2/h2_workers.h
modules/http2/mod_http2.c
modules/http2/mod_proxy_http2.c

diff --git a/CHANGES b/CHANGES
index 5c2954103260ce616880ba6af2d10893ceabc2c6..cf583ee17c518cef69bc0e9cdd99d02fe2cacf2c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,12 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.4.50
 
+  *) mod_http2: when a server is restarted gracefully, any idle h2 worker
+     threads are shut down immediately.
+     Also, change OpenSSL API use for deprecations in OpenSSL 3.0.
+     Adds all other, never proposed code changes to make a clean
+     sync of http2 sources. [Stefan Eissing]
+
   *) mod_dav: Correctly handle errors returned by dav providers on REPORT
      requests. [Ruediger Pluem]
 
diff --git a/STATUS b/STATUS
index 44427b3c79a6bf44dacb4081fc1c574295a3ffb5..bb3638b43ebb91e3969d61c489ba2c305ea554fd 100644 (file)
--- a/STATUS
+++ b/STATUS
@@ -142,18 +142,6 @@ RELEASE SHOWSTOPPERS:
 PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
   [ start all new proposals below, under PATCHES PROPOSED. ]
 
-  *) mod_http2: when a server is restarted gracefully, any idle h2 worker
-     threads are shut down immediately.
-     Also, change OpenSSL API use for deprecations in OpenSLL 3.0.
-     Adds all other, never proposed code changes to make a clean
-     sync of http2 sources.
-     trunk patch: http://svn.apache.org/r1893214
-                  http://svn.apache.org/r1893215
-                  http://svn.apache.org/r1893220
-                  and other never proposed code changes
-     PR: https://github.com/apache/httpd/pull/270
-     +1: icing, rpluem, minfrin
-
 
 PATCHES PROPOSED TO BACKPORT FROM TRUNK:
   [ New proposals should be added at the end of the list ]
index c22f7eba836d883a1aef1310ead678d00ec60022..383e266b9d4c6093a3c979f31df9a615316408d7 100644 (file)
@@ -47,14 +47,14 @@ h2_alt_svc *h2_alt_svc_parse(const char *s, apr_pool_t *pool)
 {
     const char *sep = ap_strchr_c(s, '=');
     if (sep) {
-        const char *alpn = apr_pstrmemdup(pool, s, sep - s);
+        const char *alpn = apr_pstrmemdup(pool, s, (apr_size_t)(sep - s));
         const char *host = NULL;
         int port = 0;
         s = sep + 1;
         sep = ap_strchr_c(s, ':');  /* mandatory : */
         if (sep) {
             if (sep != s) {    /* optional host */
-                host = apr_pstrmemdup(pool, s, sep - s);
+                host = apr_pstrmemdup(pool, s, (apr_size_t)(sep - s));
             }
             s = sep + 1;
             if (*s) {          /* must be a port number */
index be535e137fe1829b285220365206bd1f3546c017..3e09fcd017cdf6dc370f7bcda1e279d6898859da 100644 (file)
@@ -216,6 +216,7 @@ static apr_status_t enter_yellow(h2_bucket_beam *beam, h2_beam_lock *pbl)
 
 static void leave_yellow(h2_bucket_beam *beam, h2_beam_lock *pbl)
 {
+    (void)beam;
     if (pbl->leave) {
         pbl->leave(pbl->mutex);
     }
@@ -228,7 +229,7 @@ static apr_off_t bucket_mem_used(apr_bucket *b)
     }
     else {
         /* should all have determinate length */
-        return b->length;
+        return (apr_off_t)b->length;
     }
 }
 
@@ -302,7 +303,7 @@ static void r_purge_sent(h2_bucket_beam *beam)
 static apr_size_t calc_space_left(h2_bucket_beam *beam)
 {
     if (beam->max_buf_size > 0) {
-        apr_off_t len = calc_buffered(beam);
+        apr_size_t len = calc_buffered(beam);
         return (beam->max_buf_size > len? (beam->max_buf_size - len) : 0);
     }
     return APR_SIZE_MAX;
@@ -777,6 +778,8 @@ static apr_status_t append_bucket(h2_bucket_beam *beam,
     apr_status_t status;
     int can_beam = 0, check_len;
     
+    (void)block;
+    (void)pbl;
     if (beam->aborted) {
         return APR_ECONNABORTED;
     }
@@ -815,8 +818,8 @@ static apr_status_t append_bucket(h2_bucket_beam *beam,
     }
     else {
         if (b->length == ((apr_size_t)-1)) {
-            const char *data;
-            status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+            const char *data2;
+            status = apr_bucket_read(b, &data2, &len, APR_BLOCK_READ);
             if (status != APR_SUCCESS) {
                 return status;
             }
@@ -958,7 +961,7 @@ apr_status_t h2_beam_receive(h2_bucket_beam *beam,
     /* Called from the receiver thread to take buckets from the beam */
     if (enter_yellow(beam, &bl) == APR_SUCCESS) {
         if (readbytes <= 0) {
-            readbytes = APR_SIZE_MAX;
+            readbytes = (apr_off_t)APR_SIZE_MAX;
         }
         remain = readbytes;
         
@@ -1028,7 +1031,7 @@ transfer:
                     }
                     ++beam->files_beamed;
                 }
-                ng = apr_brigade_insert_file(bb, fd, bsender->start, bsender->length, 
+                ng = apr_brigade_insert_file(bb, fd, bsender->start, (apr_off_t)bsender->length, 
                                              bb->p);
 #if APR_HAS_MMAP
                 /* disable mmap handling as this leads to segfaults when
@@ -1089,9 +1092,9 @@ transfer:
                  brecv != APR_BRIGADE_SENTINEL(bb);
                  brecv = APR_BUCKET_NEXT(brecv)) {
                 remain -= (beam->tx_mem_limits? bucket_mem_used(brecv) 
-                           : brecv->length);
+                           : (apr_off_t)brecv->length);
                 if (remain < 0) {
-                    apr_bucket_split(brecv, brecv->length+remain);
+                    apr_bucket_split(brecv, (apr_size_t)((apr_off_t)brecv->length+remain));
                     beam->recv_buffer = apr_brigade_split_ex(bb, 
                                                              APR_BUCKET_NEXT(brecv), 
                                                              beam->recv_buffer);
@@ -1258,6 +1261,7 @@ apr_size_t h2_beam_get_files_beamed(h2_bucket_beam *beam)
 
 int h2_beam_no_files(void *ctx, h2_bucket_beam *beam, apr_file_t *file)
 {
+    (void)ctx; (void)beam; (void)file;
     return 0;
 }
 
index c89d499e102674a1cc701fe6c67aeb6b95d41257..4fe7ea725f92d22176cbbebc0480b23a8ae5c9ae 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #include <assert.h>
 #include <stddef.h>
index a57062a0156e7ffd04d66ed00b34680c7a18429e..06368fd53b065f5141eff1eb627045bfd9085a11 100644 (file)
@@ -542,7 +542,7 @@ const struct h2_priority *h2_cconfig_get_priority(conn_rec *c, const char *conte
 {
     const h2_config *conf = h2_config_get(c);
     if (content_type && conf->priorities) {
-        size_t len = strcspn(content_type, "; \t");
+        apr_ssize_t len = (apr_ssize_t)strcspn(content_type, "; \t");
         h2_priority *prio = apr_hash_get(conf->priorities, content_type, len);
         return prio? prio : apr_hash_get(conf->priorities, "*", 1);
     }
@@ -711,7 +711,8 @@ static const char *h2_conf_add_push_priority(cmd_parms *cmd, void *_cfg,
     h2_dependency dependency;
     h2_priority *priority;
     int weight;
-    
+    (void)_cfg;
     if (!*ctype) {
         return "1st argument must be a mime-type, like 'text/css' or '*'";
     }
@@ -730,7 +731,7 @@ static const char *h2_conf_add_push_priority(cmd_parms *cmd, void *_cfg,
     else if (!strcasecmp("BEFORE", sdependency)) {
         dependency = H2_DEPENDANT_BEFORE;
         if (sweight) {
-            return "dependecy 'Before' does not allow a weight";
+            return "dependency 'Before' does not allow a weight";
         }
     } 
     else if (!strcasecmp("INTERLEAVED", sdependency)) {
@@ -754,7 +755,7 @@ static const char *h2_conf_add_push_priority(cmd_parms *cmd, void *_cfg,
     if (!cfg->priorities) {
         cfg->priorities = apr_hash_make(cmd->pool);
     }
-    apr_hash_set(cfg->priorities, ctype, strlen(ctype), priority);
+    apr_hash_set(cfg->priorities, ctype, (apr_ssize_t)strlen(ctype), priority);
     return NULL;
 }
 
index cc25e2442045133fe32a8fce9fa9137fafb61c43..018d5819a78ce2ee9c2e8adb28251f5bc7ead95f 100644 (file)
@@ -148,6 +148,13 @@ apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
     return status;
 }
 
+void h2_conn_child_stopping(apr_pool_t *pool, int graceful)
+{
+    if (workers && graceful) {
+        h2_workers_graceful_shutdown(workers);
+    }
+}
+
 h2_mpm_type_t h2_conn_mpm_type(void)
 {
     check_modules(0);
@@ -259,6 +266,8 @@ apr_status_t h2_conn_run(conn_rec *c)
 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
 {
     h2_session *session = h2_ctx_get_session(c);
+    
+    (void)c;
     if (session) {
         apr_status_t status = h2_session_pre_close(session, async_mpm);
         return (status == APR_SUCCESS)? DONE : status;
@@ -266,6 +275,13 @@ apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
     return DONE;
 }
 
+/* APR callback invoked if allocation fails. */
+static int abort_on_oom(int retcode)
+{
+    ap_abort_on_oom();
+    return retcode; /* unreachable, hopefully. */
+}
+
 conn_rec *h2_secondary_create(conn_rec *master, int sec_id, apr_pool_t *parent)
 {
     apr_allocator_t *allocator;
@@ -295,8 +311,9 @@ conn_rec *h2_secondary_create(conn_rec *master, int sec_id, apr_pool_t *parent)
         return NULL;
     }
     apr_allocator_owner_set(allocator, pool);
+    apr_pool_abort_set(abort_on_oom, pool);
     apr_pool_tag(pool, "h2_secondary_conn");
+
     c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
     if (c == NULL) {
         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master, 
index 3b8b33e3994f4822260ada3ef9428540dcb3ff71..de868cfa579b3d31a2336fa21bcb6726156bb851 100644 (file)
@@ -45,12 +45,17 @@ apr_status_t h2_conn_run(conn_rec *c);
  */
 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c);
 
-/* Initialize this child process for h2 connection work,
+/**
+ * Initialize this child process for h2 connection work,
  * to be called once during child init before multi processing
  * starts.
  */
 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s);
 
+/**
+ * Child is about to be stopped, release unused resources
+ */
+void h2_conn_child_stopping(apr_pool_t *pool, int graceful);
 
 typedef enum {
     H2_MPM_UNKNOWN,
index d0e099b421f8150cbe20c8421b16052121aaca74..f506c14e618d82f766f15df9518985a7bcd241a7 100644 (file)
  */
 #define WRITE_SIZE_MAX        (TLS_DATA_MAX) 
 
+#define BUF_REMAIN            ((apr_size_t)(bmax-off))
 
 static void h2_conn_io_bb_log(conn_rec *c, int stream_id, int level, 
                               const char *tag, apr_bucket_brigade *bb)
 {
     char buffer[16 * 1024];
     const char *line = "(null)";
-    apr_size_t bmax = sizeof(buffer)/sizeof(buffer[0]);
+    int bmax = sizeof(buffer)/sizeof(buffer[0]);
     int off = 0;
     apr_bucket *b;
     
+    (void)stream_id;
     if (bb) {
         memset(buffer, 0, bmax--);
         for (b = APR_BRIGADE_FIRST(bb); 
@@ -72,19 +74,19 @@ static void h2_conn_io_bb_log(conn_rec *c, int stream_id, int level,
             
             if (APR_BUCKET_IS_METADATA(b)) {
                 if (APR_BUCKET_IS_EOS(b)) {
-                    off += apr_snprintf(buffer+off, bmax-off, "eos ");
+                    off += apr_snprintf(buffer+off, BUF_REMAIN, "eos ");
                 }
                 else if (APR_BUCKET_IS_FLUSH(b)) {
-                    off += apr_snprintf(buffer+off, bmax-off, "flush ");
+                    off += apr_snprintf(buffer+off, BUF_REMAIN, "flush ");
                 }
                 else if (AP_BUCKET_IS_EOR(b)) {
-                    off += apr_snprintf(buffer+off, bmax-off, "eor ");
+                    off += apr_snprintf(buffer+off, BUF_REMAIN, "eor ");
                 }
                 else if (H2_BUCKET_IS_H2EOS(b)) {
-                    off += apr_snprintf(buffer+off, bmax-off, "h2eos ");
+                    off += apr_snprintf(buffer+off, BUF_REMAIN, "h2eos ");
                 }
                 else {
-                    off += apr_snprintf(buffer+off, bmax-off, "meta(unknown) ");
+                    off += apr_snprintf(buffer+off, BUF_REMAIN, "meta(unknown) ");
                 }
             }
             else {
@@ -116,10 +118,9 @@ static void h2_conn_io_bb_log(conn_rec *c, int stream_id, int level,
                     btype = "pool";
                 }
                 
-                off += apr_snprintf(buffer+off, bmax-off, "%s[%ld] ", 
+                off += apr_snprintf(buffer+off, BUF_REMAIN, "%s[%ld] ", 
                                     btype, 
-                                    (long)(b->length == ((apr_size_t)-1)? 
-                                           -1 : b->length));
+                                    (long)(b->length == ((apr_size_t)-1)? -1UL : b->length));
             }
         }
         line = *buffer? buffer : "(empty)";
@@ -159,7 +160,7 @@ apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, server_rec *s)
                       "h2_conn_io(%ld): init, buffering=%d, warmup_size=%ld, "
                       "cd_secs=%f", io->c->id, io->buffer_output, 
                       (long)io->warmup_size,
-                      ((float)io->cooldown_usecs/APR_USEC_PER_SEC));
+                      ((double)io->cooldown_usecs/APR_USEC_PER_SEC));
     }
 
     return APR_SUCCESS;
@@ -208,8 +209,8 @@ static apr_status_t read_to_scratch(h2_conn_io *io, apr_bucket *b)
         apr_bucket_file *f = (apr_bucket_file *)b->data;
         apr_file_t *fd = f->fd;
         apr_off_t offset = b->start;
-        apr_size_t len = b->length;
         
+        len = b->length;
         /* file buckets will either mmap (which we do not want) or
          * read 8000 byte chunks and split themself. However, we do
          * know *exactly* how many bytes we need where.
@@ -294,13 +295,13 @@ int h2_conn_io_needs_flush(h2_conn_io *io)
 {
     if (!io->is_flushed) {
         apr_off_t len = h2_brigade_mem_size(io->output);
-        if (len > io->flush_threshold) {
+        if (len > (apr_off_t)io->flush_threshold) {
             return 1;
         }
         /* if we do not exceed flush length due to memory limits,
          * we want at least flush when we have that amount of data. */
         apr_brigade_length(io->output, 0, &len);
-        return len > (4 * io->flush_threshold);
+        return len > (apr_off_t)(4 * io->flush_threshold);
     }
     return 0;
 }
index 073076b73f699b5e3ecc97cfaa70ab71003b4599..86e63286176f4a1b9e1970e6c4522610a12fee4c 100644 (file)
@@ -114,65 +114,11 @@ static void fix_vary(request_rec *r)
     }
 }
 
-static void set_basic_http_header(apr_table_t *headers, request_rec *r,
-                                  apr_pool_t *pool)
-{
-    char *date = NULL;
-    const char *proxy_date = NULL;
-    const char *server = NULL;
-    const char *us = ap_get_server_banner();
-    
-    /*
-     * keep the set-by-proxy server and date headers, otherwise
-     * generate a new server header / date header
-     */
-    if (r && r->proxyreq != PROXYREQ_NONE) {
-        proxy_date = apr_table_get(r->headers_out, "Date");
-        if (!proxy_date) {
-            /*
-             * proxy_date needs to be const. So use date for the creation of
-             * our own Date header and pass it over to proxy_date later to
-             * avoid a compiler warning.
-             */
-            date = apr_palloc(pool, APR_RFC822_DATE_LEN);
-            ap_recent_rfc822_date(date, r->request_time);
-        }
-        server = apr_table_get(r->headers_out, "Server");
-    }
-    else {
-        date = apr_palloc(pool, APR_RFC822_DATE_LEN);
-        ap_recent_rfc822_date(date, r? r->request_time : apr_time_now());
-    }
-    
-    apr_table_setn(headers, "Date", proxy_date ? proxy_date : date );
-    if (r) {
-        apr_table_unset(r->headers_out, "Date");
-    }
-    
-    if (!server && *us) {
-        server = us;
-    }
-    if (server) {
-        apr_table_setn(headers, "Server", server);
-        if (r) {
-            apr_table_unset(r->headers_out, "Server");
-        }
-    }
-}
-
-static int copy_header(void *ctx, const char *name, const char *value)
-{
-    apr_table_t *headers = ctx;
-    
-    apr_table_add(headers, name, value);
-    return 1;
-}
-
 static h2_headers *create_response(h2_task *task, request_rec *r)
 {
     const char *clheader;
     const char *ctype;
-    apr_table_t *headers;
+
     /*
      * Now that we are ready to send a response, we need to combine the two
      * header field tables into a single table.  If we don't do this, our
@@ -279,28 +225,24 @@ static h2_headers *create_response(h2_task *task, request_rec *r)
         apr_table_unset(r->headers_out, "Content-Length");
     }
     
-    headers = apr_table_make(r->pool, 10);
-    
-    set_basic_http_header(headers, r, r->pool);
-    if (r->status == HTTP_NOT_MODIFIED) {
-        apr_table_do(copy_header, headers, r->headers_out,
-                     "ETag",
-                     "Content-Location",
-                     "Expires",
-                     "Cache-Control",
-                     "Vary",
-                     "Warning",
-                     "WWW-Authenticate",
-                     "Proxy-Authenticate",
-                     "Set-Cookie",
-                     "Set-Cookie2",
-                     NULL);
+    /*
+     * keep the set-by-proxy server and date headers, otherwise
+     * generate a new server header / date header
+     */
+    if (r->proxyreq != PROXYREQ_RESPONSE
+            || !apr_table_get(r->headers_out, "Date")) {
+        char *date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
+        ap_recent_rfc822_date(date, r->request_time);
+        apr_table_setn(r->headers_out, "Date", date );
     }
-    else {
-        apr_table_do(copy_header, headers, r->headers_out, NULL);
+    if (r->proxyreq != PROXYREQ_RESPONSE) {
+        const char *us = ap_get_server_banner();
+        if (us) {
+            apr_table_setn(r->headers_out, "Server", us);
+        }
     }
     
-    return h2_headers_rcreate(r, r->status, headers, r->pool);
+    return h2_headers_rcreate(r, r->status, r->headers_out, r->pool);
 }
 
 typedef enum {
@@ -669,10 +611,10 @@ static void make_chunk(h2_task *task, apr_bucket_brigade *bb,
      * to the end of the brigade. */
     char buffer[128];
     apr_bucket *c;
-    int len;
+    apr_size_t len;
     
-    len = apr_snprintf(buffer, H2_ALEN(buffer), 
-                       "%"APR_UINT64_T_HEX_FMT"\r\n", (apr_uint64_t)chunk_len);
+    len = (apr_size_t)apr_snprintf(buffer, H2_ALEN(buffer), 
+                                   "%"APR_UINT64_T_HEX_FMT"\r\n", (apr_uint64_t)chunk_len);
     c = apr_bucket_heap_create(buffer, len, NULL, bb->bucket_alloc);
     APR_BUCKET_INSERT_BEFORE(first, c);
     c = apr_bucket_heap_create("\r\n", 2, NULL, bb->bucket_alloc);
index 271b1f1c61b69fd630d9ecaaeab4705863ed1e7e..b4e18f2127cbb76a154f11c5e8a31085d4f60acf 100644 (file)
@@ -113,9 +113,9 @@ apr_bucket *h2_bucket_headers_beam(struct h2_bucket_beam *beam,
 }
 
 
-h2_headers *h2_headers_create(int status, apr_table_t *headers_in, 
-                                apr_table_t *notes, apr_off_t raw_bytes,
-                                apr_pool_t *pool)
+h2_headers *h2_headers_create(int status, const apr_table_t *headers_in, 
+                              const apr_table_t *notes, apr_off_t raw_bytes,
+                              apr_pool_t *pool)
 {
     h2_headers *headers = apr_pcalloc(pool, sizeof(h2_headers));
     headers->status    = status;
@@ -141,7 +141,7 @@ apr_size_t h2_headers_length(h2_headers *headers)
 }
 
 h2_headers *h2_headers_rcreate(request_rec *r, int status,
-                                 apr_table_t *header, apr_pool_t *pool)
+                               const apr_table_t *header, apr_pool_t *pool)
 {
     h2_headers *headers = h2_headers_create(status, header, r->notes, 0, pool);
     if (headers->status == HTTP_FORBIDDEN) {
@@ -172,14 +172,12 @@ h2_headers *h2_headers_rcreate(request_rec *r, int status,
 
 h2_headers *h2_headers_copy(apr_pool_t *pool, h2_headers *h)
 {
-    return h2_headers_create(h->status, apr_table_copy(pool, h->headers), 
-                             apr_table_copy(pool, h->notes), h->raw_bytes, pool);
+    return h2_headers_create(h->status, h->headers, h->notes, h->raw_bytes, pool);
 }
 
 h2_headers *h2_headers_clone(apr_pool_t *pool, h2_headers *h)
 {
-    return h2_headers_create(h->status, apr_table_clone(pool, h->headers), 
-                             apr_table_clone(pool, h->notes), h->raw_bytes, pool);
+    return h2_headers_create(h->status, h->headers, h->notes, h->raw_bytes, pool);
 }
 
 h2_headers *h2_headers_die(apr_status_t type,
index 6679d73aecd98516a52f2e9cad616418e3d2f28a..fbaf68785f4305199845c3a4f23ef1cb72362320 100644 (file)
@@ -44,8 +44,8 @@ apr_bucket *h2_bucket_headers_beam(struct h2_bucket_beam *beam,
  * @param raw_bytes the raw network bytes (if known) used to transmit these
  * @param pool the memory pool to use
  */
-h2_headers *h2_headers_create(int status, apr_table_t *header, 
-                              apr_table_t *notes, apr_off_t raw_bytes, 
+h2_headers *h2_headers_create(int status, const apr_table_t *header,
+                              const apr_table_t *notes, apr_off_t raw_bytes,
                               apr_pool_t *pool);
 
 /**
@@ -56,7 +56,7 @@ h2_headers *h2_headers_create(int status, apr_table_t *header,
  * @param pool the memory pool to use
  */
 h2_headers *h2_headers_rcreate(request_rec *r, int status, 
-                                 apr_table_t *header, apr_pool_t *pool);
+                               const apr_table_t *header, apr_pool_t *pool);
 
 /**
  * Copy the headers into another pool. This will not copy any
@@ -72,7 +72,6 @@ h2_headers *h2_headers_clone(apr_pool_t *pool, h2_headers *h);
 
 /**
  * Create the headers for the given error.
- * @param stream_id id of the stream to create the headers for
  * @param type the error code
  * @param req the original h2_request
  * @param pool the memory pool to use
index 4cbecd03f4ad8d9fad42b38653559cf8c1528f59..e02ad4e58b33cacf9ce629d8a0450dc96d7228c2 100644 (file)
@@ -737,6 +737,12 @@ static h2_task *s_next_stream_task(h2_mplx *m)
             return stream->task;
         }
     }
+    if (m->tasks_active >= m->limit_active && !h2_iq_empty(m->q)) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c,
+                      "h2_session(%ld): delaying request processing. "
+                      "Current limit is %d and %d workers are in use.",
+                      m->id, m->limit_active, m->tasks_active);
+    }
     return NULL;
 }
 
@@ -948,7 +954,6 @@ static apr_status_t m_unschedule_slow_tasks(h2_mplx *m)
         task->redo = 1;
         h2_task_rst(task, H2_ERR_CANCEL);
     }
-    
     return rv;
 }
 
@@ -1131,14 +1136,36 @@ int h2_mplx_m_awaits_data(h2_mplx *m)
     return waiting;
 }
 
+static int reset_is_acceptable(h2_stream *stream)
+{
+    /* client may terminate a stream via H2 RST_STREAM message at any time.
+     * This is annyoing when we have committed resources (e.g. worker threads)
+     * to it, so our mood (e.g. willingness to commit resources on this
+     * connection in the future) goes down.
+     *
+     * This is a DoS protection. We do not want to make it too easy for
+     * a client to eat up server resources.
+     *
+     * However: there are cases where a RST_STREAM is the only way to end
+     * a request. This includes websockets and server-side-event streams (SSEs).
+     * The responses to such requests continue forever otherwise.
+     *
+     */
+    if (!stream->task) return 1; /* have not started or already ended for us. acceptable. */
+    if (!(stream->id & 0x01)) return 1; /* stream initiated by us. acceptable. */
+    if (!stream->has_response) return 0; /* no response headers produced yet. bad. */
+    if (!stream->out_data_frames) return 0; /* no response body data sent yet. bad. */
+    return 1; /* otherwise, be forgiving */
+}
+
 apr_status_t h2_mplx_m_client_rst(h2_mplx *m, int stream_id)
 {
     h2_stream *stream;
     apr_status_t status = APR_SUCCESS;
-    
+
     H2_MPLX_ENTER_ALWAYS(m);
     stream = h2_ihash_get(m->streams, stream_id);
-    if (stream && stream->task) {
+    if (stream && !reset_is_acceptable(stream)) {
         status = m_be_annoyed(m);
     }
     H2_MPLX_LEAVE(m);
index dc21e1ef648184c2665b157f091da8e62e1139a6..0a90a5d86fc20ce9a6f93e46a24104a5daccd1ed 100644 (file)
@@ -23,7 +23,7 @@
 #include <apr_time.h>
 
 #ifdef H2_OPENSSL
-#include <openssl/sha.h>
+#include <openssl/evp.h>
 #endif
 
 #include <httpd.h>
@@ -59,7 +59,7 @@ static const char *policy_str(h2_push_policy policy)
 
 typedef struct {
     const h2_request *req;
-    int push_policy;
+    apr_uint32_t push_policy;
     apr_pool_t *pool;
     apr_array_header_t *pushes;
     const char *s;
@@ -434,7 +434,7 @@ static int head_iter(void *ctx, const char *key, const char *value)
 }
 
 apr_array_header_t *h2_push_collect(apr_pool_t *p, const h2_request *req,
-                                    int push_policy, const h2_headers *res)
+                                    apr_uint32_t push_policy, const h2_headers *res)
 {
     if (req && push_policy != H2_PUSH_NONE) {
         /* Collect push candidates from the request/response pair.
@@ -472,27 +472,32 @@ typedef struct h2_push_diary_entry {
 
 
 #ifdef H2_OPENSSL
-static void sha256_update(SHA256_CTX *ctx, const char *s)
+static void sha256_update(EVP_MD_CTX *ctx, const char *s)
 {
-    SHA256_Update(ctx, s, strlen(s));
+    EVP_DigestUpdate(ctx, s, strlen(s));
 }
 
 static void calc_sha256_hash(h2_push_diary *diary, apr_uint64_t *phash, h2_push *push) 
 {
-    SHA256_CTX sha256;
+    EVP_MD_CTX *md;
     apr_uint64_t val;
-    unsigned char hash[SHA256_DIGEST_LENGTH];
+    unsigned char hash[EVP_MAX_MD_SIZE];
+    unsigned len;
     int i;
-    
-    SHA256_Init(&sha256);
-    sha256_update(&sha256, push->req->scheme);
-    sha256_update(&sha256, "://");
-    sha256_update(&sha256, push->req->authority);
-    sha256_update(&sha256, push->req->path);
-    SHA256_Final(hash, &sha256);
+
+    md = EVP_MD_CTX_create();
+    ap_assert(md != NULL);
+
+    i = EVP_DigestInit_ex(md, EVP_sha256(), NULL);
+    ap_assert(i == 1);
+    sha256_update(md, push->req->scheme);
+    sha256_update(md, "://");
+    sha256_update(md, push->req->authority);
+    sha256_update(md, push->req->path);
+    EVP_DigestFinal(md, hash, &len);
 
     val = 0;
-    for (i = 0; i != sizeof(val); ++i)
+    for (i = 0; i != len; ++i)
         val = val * 256 + hash[i];
     *phash = val >> (64 - diary->mask_bits);
 }
@@ -501,13 +506,14 @@ static void calc_sha256_hash(h2_push_diary *diary, apr_uint64_t *phash, h2_push
 
 static unsigned int val_apr_hash(const char *str) 
 {
-    apr_ssize_t len = strlen(str);
+    apr_ssize_t len = (apr_ssize_t)strlen(str);
     return apr_hashfunc_default(str, &len);
 }
 
 static void calc_apr_hash(h2_push_diary *diary, apr_uint64_t *phash, h2_push *push) 
 {
     apr_uint64_t val;
+    (void)diary;
 #if APR_UINT64_MAX > UINT_MAX
     val = ((apr_uint64_t)(val_apr_hash(push->req->scheme)) << 32);
     val ^= ((apr_uint64_t)(val_apr_hash(push->req->authority)) << 16);
@@ -653,7 +659,7 @@ apr_array_header_t *h2_push_diary_update(h2_session *session, apr_array_header_t
                 /* Intentional no APLOGNO */
                 ap_log_cerror(APLOG_MARK, GCSLOG_LEVEL, 0, session->c,
                               "push_diary_update: already there PUSH %s", push->req->path);
-                move_to_last(session->push_diary, idx);
+                move_to_last(session->push_diary, (apr_size_t)idx);
             }
             else {
                 /* Intentional no APLOGNO */
index d061dd8d141a1509bf2f3bfb0f88adfa3c49d150..5dc189f007fdc7b34d2cbcdd41f2f0d71f90de6d 100644 (file)
@@ -99,7 +99,7 @@ struct h2_push_diary {
  */
 apr_array_header_t *h2_push_collect(apr_pool_t *p, 
                                     const struct h2_request *req, 
-                                    int push_policy, 
+                                    apr_uint32_t push_policy, 
                                     const struct h2_headers *res);
 
 /**
index 5adf84151e9315e33dbdf7907c65fe27ddeb9e2c..7c4fb95ea482ac6871ab8dc52da06a6d2e727be9 100644 (file)
@@ -210,12 +210,74 @@ h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src)
     return dst;
 }
 
+#if !AP_MODULE_MAGIC_AT_LEAST(20120211, 106)
+static request_rec *my_ap_create_request(conn_rec *c)
+{
+    apr_pool_t *p;
+    request_rec *r;
+
+    apr_pool_create(&p, c->pool);
+    apr_pool_tag(p, "request");
+    r = apr_pcalloc(p, sizeof(request_rec));
+    AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)c);
+    r->pool            = p;
+    r->connection      = c;
+    r->server          = c->base_server;
+
+    r->user            = NULL;
+    r->ap_auth_type    = NULL;
+
+    r->allowed_methods = ap_make_method_list(p, 2);
+
+    r->headers_in      = apr_table_make(r->pool, 5);
+    r->trailers_in     = apr_table_make(r->pool, 5);
+    r->subprocess_env  = apr_table_make(r->pool, 25);
+    r->headers_out     = apr_table_make(r->pool, 12);
+    r->err_headers_out = apr_table_make(r->pool, 5);
+    r->trailers_out    = apr_table_make(r->pool, 5);
+    r->notes           = apr_table_make(r->pool, 5);
+
+    r->request_config  = ap_create_request_config(r->pool);
+    /* Must be set before we run create request hook */
+
+    r->proto_output_filters = c->output_filters;
+    r->output_filters  = r->proto_output_filters;
+    r->proto_input_filters = c->input_filters;
+    r->input_filters   = r->proto_input_filters;
+    ap_run_create_request(r);
+    r->per_dir_config  = r->server->lookup_defaults;
+
+    r->sent_bodyct     = 0;                      /* bytect isn't for body */
+
+    r->read_length     = 0;
+    r->read_body       = REQUEST_NO_BODY;
+
+    r->status          = HTTP_OK;  /* Until further notice */
+    r->header_only     = 0;
+    r->the_request     = NULL;
+
+    /* Begin by presuming any module can make its own path_info assumptions,
+     * until some module interjects and changes the value.
+     */
+    r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
+
+    r->useragent_addr = c->client_addr;
+    r->useragent_ip = c->client_ip;
+    return r;
+}
+#endif
+
 request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
 {
     int access_status = HTTP_OK;    
 
+#if AP_MODULE_MAGIC_AT_LEAST(20120211, 106)
     request_rec *r = ap_create_request(c);
+#else
+    request_rec *r = my_ap_create_request(c);
+#endif
 
+#if AP_MODULE_MAGIC_AT_LEAST(20120211, 107)
     ap_run_pre_read_request(r, c);
 
     /* Time to populate r with the data we have. */
@@ -244,6 +306,49 @@ request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
         r->status = HTTP_OK;
         goto die;
     }
+#else
+    {
+        const char *s;
+
+        r->headers_in = apr_table_clone(r->pool, req->headers);
+        ap_run_pre_read_request(r, c);
+
+        /* Time to populate r with the data we have. */
+        r->request_time = req->request_time;
+        r->method = apr_pstrdup(r->pool, req->method);
+        /* Provide quick information about the request method as soon as known */
+        r->method_number = ap_method_number_of(r->method);
+        if (r->method_number == M_GET && r->method[0] == 'H') {
+            r->header_only = 1;
+        }
+        ap_parse_uri(r, req->path ? req->path : "");
+        r->protocol = (char*)"HTTP/2.0";
+        r->proto_num = HTTP_VERSION(2, 0);
+        r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0",
+                                      r->method, req->path ? req->path : "");
+
+        /* Start with r->hostname = NULL, ap_check_request_header() will get it
+         * form Host: header, otherwise we get complains about port numbers.
+         */
+        r->hostname = NULL;
+        ap_update_vhost_from_headers(r);
+
+         /* we may have switched to another server */
+         r->per_dir_config = r->server->lookup_defaults;
+
+         s = apr_table_get(r->headers_in, "Expect");
+         if (s && s[0]) {
+            if (ap_cstr_casecmp(s, "100-continue") == 0) {
+                r->expecting_100 = 1;
+            }
+            else {
+                r->status = HTTP_EXPECTATION_FAILED;
+                access_status = r->status;
+                goto die;
+            }
+         }
+    }
+#endif
 
     /* we may have switched to another server */
     r->per_dir_config = r->server->lookup_defaults;
index 56254f2a8989cb8191d4b5ed532e1b7ddfb1c8b9..dc883b5b96f9132c19bcc6e6312640936f22ea1b 100644 (file)
@@ -73,7 +73,7 @@ static int h2_session_status_from_apr_status(apr_status_t rv)
     return NGHTTP2_ERR_PROTO;
 }
 
-h2_stream *h2_session_stream_get(h2_session *session, int stream_id)
+static h2_stream *get_stream(h2_session *session, int stream_id)
 {
     return nghttp2_session_get_stream_user_data(session->ngh2, stream_id);
 }
@@ -232,7 +232,7 @@ static int on_data_chunk_recv_cb(nghttp2_session *ngh2, uint8_t flags,
     h2_stream * stream;
     int rv = 0;
     
-    stream = h2_session_stream_get(session, stream_id);
+    stream = get_stream(session, stream_id);
     if (stream) {
         status = h2_stream_recv_DATA(stream, flags, data, len);
         dispatch_event(session, H2_SESSION_EV_STREAM_CHANGE, 0, "stream data rcvd");
@@ -258,7 +258,7 @@ static int on_stream_close_cb(nghttp2_session *ngh2, int32_t stream_id,
     h2_stream *stream;
     
     (void)ngh2;
-    stream = h2_session_stream_get(session, stream_id);
+    stream = get_stream(session, stream_id);
     if (stream) {
         if (error_code) {
             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
@@ -280,7 +280,7 @@ static int on_begin_headers_cb(nghttp2_session *ngh2,
     /* We may see HEADERs at the start of a stream or after all DATA
      * streams to carry trailers. */
     (void)ngh2;
-    s = h2_session_stream_get(session, frame->hd.stream_id);
+    s = get_stream(session, frame->hd.stream_id);
     if (s) {
         /* nop */
     }
@@ -301,7 +301,7 @@ static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame,
     apr_status_t status;
     
     (void)flags;
-    stream = h2_session_stream_get(session, frame->hd.stream_id);
+    stream = get_stream(session, frame->hd.stream_id);
     if (!stream) {
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(02920) 
                       "h2_stream(%ld-%d): on_header unknown stream",
@@ -349,14 +349,14 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
             /* This can be HEADERS for a new stream, defining the request,
              * or HEADER may come after DATA at the end of a stream as in
              * trailers */
-            stream = h2_session_stream_get(session, frame->hd.stream_id);
+            stream = get_stream(session, frame->hd.stream_id);
             if (stream) {
                 rv = h2_stream_recv_frame(stream, NGHTTP2_HEADERS, frame->hd.flags, 
                     frame->hd.length + H2_FRAME_HDR_LEN);
             }
             break;
         case NGHTTP2_DATA:
-            stream = h2_session_stream_get(session, frame->hd.stream_id);
+            stream = get_stream(session, frame->hd.stream_id);
             if (stream) {
                 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,  
                               H2_STRM_LOG(APLOGNO(02923), stream, 
@@ -390,7 +390,7 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
                           "h2_stream(%ld-%d): RST_STREAM by client, error=%d",
                           session->id, (int)frame->hd.stream_id,
                           (int)frame->rst_stream.error_code);
-            stream = h2_session_stream_get(session, frame->hd.stream_id);
+            stream = get_stream(session, frame->hd.stream_id);
             if (stream && stream->initiated_on) {
                 /* A stream reset on a request we sent it. Normal, when the
                  * client does not want it. */
@@ -505,7 +505,7 @@ static int on_send_data_cb(nghttp2_session *ngh2,
     ap_assert(frame->data.padlen <= (H2_MAX_PADLEN+1));
     padlen = (unsigned char)frame->data.padlen;
     
-    stream = h2_session_stream_get(session, stream_id);
+    stream = get_stream(session, stream_id);
     if (!stream) {
         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
                       APLOGNO(02924) 
@@ -595,7 +595,7 @@ static int on_frame_send_cb(nghttp2_session *ngh2,
                      (long)session->frames_sent);
     }
     
-    stream = h2_session_stream_get(session, stream_id);
+    stream = get_stream(session, stream_id);
     if (stream) {
         h2_stream_send_frame(stream, frame->hd.type, frame->hd.flags, 
             frame->hd.length + H2_FRAME_HDR_LEN);
@@ -620,7 +620,7 @@ static int on_invalid_header_cb(nghttp2_session *ngh2,
                       apr_pstrndup(session->pool, (const char *)name, namelen),
                       apr_pstrndup(session->pool, (const char *)value, valuelen));
     }
-    stream = h2_session_stream_get(session, frame->hd.stream_id);
+    stream = get_stream(session, frame->hd.stream_id);
     if (stream) {
         h2_stream_rst(stream, NGHTTP2_PROTOCOL_ERROR);
     }
@@ -774,7 +774,7 @@ static apr_status_t session_cleanup(h2_session *session, const char *trigger)
          * connection when sending the next request, this has the effect
          * that at least this one request will fail.
          */
-        ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c,
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
                       H2_SSSN_LOG(APLOGNO(03199), session, 
                       "connection disappeared without proper "
                       "goodbye, clients will be confused, should not happen"));
@@ -799,13 +799,17 @@ static apr_status_t session_pool_cleanup(void *data)
     h2_session *session;
     
     if ((session = h2_ctx_get_session(c))) {
+        int mpm_state = 0;
+        int level;
+
+        ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state);
+        level = (AP_MPMQ_STOPPING == mpm_state)? APLOG_DEBUG : APLOG_WARNING;
         /* if the session is still there, now is the last chance
          * to perform cleanup. Normally, cleanup should have happened
-         * earlier in the connection pre_close. Main reason is that
-         * any ongoing requests on secondary connections might still access
-         * data which has, at this time, already been freed. An example
-         * is mod_ssl that uses request hooks. */
-        ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c,
+         * earlier in the connection pre_close.
+         * However, when the server is stopping, it may shutdown connections
+         * without running the pre_close hooks. Do not want about that. */
+        ap_log_cerror(APLOG_MARK, level, 0, c,
                       H2_SSSN_LOG(APLOGNO(10020), session, 
                       "session cleanup triggered by pool cleanup. "
                       "this should have happened earlier already."));
@@ -1101,7 +1105,7 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s,
     (void)ng2s;
     (void)buf;
     (void)source;
-    stream = h2_session_stream_get(session, stream_id);
+    stream = get_stream(session, stream_id);
     if (!stream) {
         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
                       APLOGNO(02937) 
@@ -1550,7 +1554,7 @@ static void h2_session_in_flush(h2_session *session)
     int id;
     
     while ((id = h2_iq_shift(session->in_process)) > 0) {
-        h2_stream *stream = h2_session_stream_get(session, id);
+        h2_stream *stream = get_stream(session, id);
         if (stream) {
             ap_assert(!stream->scheduled);
             if (h2_stream_prep_processing(stream) == APR_SUCCESS) {
@@ -1563,7 +1567,7 @@ static void h2_session_in_flush(h2_session *session)
     }
 
     while ((id = h2_iq_shift(session->in_pending)) > 0) {
-        h2_stream *stream = h2_session_stream_get(session, id);
+        h2_stream *stream = get_stream(session, id);
         if (stream) {
             h2_stream_flush_input(stream);
         }
@@ -1713,8 +1717,8 @@ static void transit(h2_session *session, const char *action, h2_session_state ns
                     timeout = session->s->timeout;
                     update_child_status(session, SERVER_BUSY_READ, "idle");
                     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, 
-                                  H2_SSSN_LOG("", session, "enter idle, timeout = %d sec"), 
-                                  (int)apr_time_sec(H2MAX(session->s->timeout, session->s->keep_alive_timeout)));
+                                  H2_SSSN_LOG("", session, "enter idle, timeout = %d sec"),
+                                  (int)apr_time_sec(timeout));
                 }
                 else if (session->open_streams) {
                     s = "timeout";
@@ -1908,6 +1912,9 @@ static void h2_session_ev_mpm_stopping(h2_session *session, int arg, const char
             break;
         default:
             h2_session_shutdown_notice(session);
+#if !AP_MODULE_MAGIC_AT_LEAST(20120211, 110)
+            h2_workers_graceful_shutdown(session->workers);
+#endif
             break;
     }
 }
index 4f74b56e2c511794406cd4e9fb0af31774047564..3fd3088a0fd134353e6d28cf93fdf320b0b08b64 100644 (file)
@@ -187,11 +187,6 @@ void h2_session_close(h2_session *session);
  */
 int h2_session_push_enabled(h2_session *session);
 
-/**
- * Look up the stream in this session with the given id.
- */
-struct h2_stream *h2_session_stream_get(h2_session *session, int stream_id);
-
 /**
  * Submit a push promise on the stream and schedule the new steam for
  * processing..
index 74b814747197a2e8e3a988152ad77d958aadee79..5b32656a912d725657a34b35ce660e48f2170c9a 100644 (file)
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #include <assert.h>
 #include <stddef.h>
 
@@ -541,6 +541,7 @@ h2_task *h2_task_create(conn_rec *secondary, int stream_id,
 void h2_task_destroy(h2_task *task)
 {
     if (task->output.beam) {
+        h2_beam_log(task->output.beam, task->c, APLOG_TRACE2, "task_destroy");
         h2_beam_destroy(task->output.beam);
         task->output.beam = NULL;
     }
@@ -592,7 +593,7 @@ apr_status_t h2_task_do(h2_task *task, apr_thread_t *thread, int worker_id)
          * configurations by mod_h2 alone. 
          */
         task->c->id = (c->master->id << 8)^worker_id;
-        task->id = apr_psprintf(task->pool, "%ld-%d", c->master->id, 
+        task->id = apr_psprintf(task->pool, "%ld-%d", task->mplx->id,
                                 task->stream_id);
     }
         
index 43133f438396ee73322200670d305b5ad0f9e679..50f41b82557c607d36cf104294ca0032ff12f5f6 100644 (file)
@@ -21,7 +21,7 @@
 
 /**
  * A h2_task fakes a HTTP/1.1 request from the data in a HTTP/2 stream 
- * (HEADER+CONT.+DATA) the module recieves.
+ * (HEADER+CONT.+DATA) the module receives.
  *
  * In order to answer a HTTP/2 stream, we want all Apache httpd infrastructure
  * to be involved as usual, as if this stream can as a separate HTTP/1.1
@@ -112,7 +112,7 @@ void h2_task_rst(h2_task *task, int error);
 
 void h2_task_register_hooks(void);
 /*
- * One time, post config intialization.
+ * One time, post config initialization.
  */
 apr_status_t h2_task_init(apr_pool_t *pool, server_rec *s);
 
index 37afb14357e799fad9020d41399d5a9fe59eaa23..9b2b3de968250f974a35df50a5d5201cbdd341cf 100644 (file)
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #include <assert.h>
 #include <apr_strings.h>
 #include <apr_thread_mutex.h>
@@ -425,7 +425,7 @@ void h2_iq_clear(h2_iqueue *q)
 void h2_iq_sort(h2_iqueue *q, h2_iq_cmp *cmp, void *ctx)
 {
     /* Assume that changes in ordering are minimal. This needs,
-     * best case, q->nelts - 1 comparisions to check that nothing
+     * best case, q->nelts - 1 comparisons to check that nothing
      * changed.
      */
     if (q->nelts > 0) {
index c8b110643963e8674c0424b7fca9b9b973f4bf8a..40f40a2aa5c77bad9c6a43c4386950e1f30aef90 100644 (file)
@@ -27,7 +27,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.15.18"
+#define MOD_HTTP2_VERSION "1.15.24"
 
 /**
  * @macro
@@ -35,7 +35,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 0x010f12
+#define MOD_HTTP2_VERSION_NUM 0x010f18
 
 
 #endif /* mod_h2_h2_version_h */
index 2b7203456a91b2218009d24d627658143c435684..28bb428200d6954af9bfd07297931aa38cad8c9a 100644 (file)
@@ -41,6 +41,7 @@ struct h2_slot {
     apr_thread_t *thread;
     apr_thread_mutex_t *lock;
     apr_thread_cond_t *not_idle;
+    volatile apr_uint32_t timed_out;
 };
 
 static h2_slot *pop_slot(h2_slot *volatile *phead) 
@@ -71,47 +72,47 @@ static void push_slot(h2_slot *volatile *phead, h2_slot *slot)
 }
 
 static void* APR_THREAD_FUNC slot_run(apr_thread_t *thread, void *wctx);
+static void slot_done(h2_slot *slot);
 
 static apr_status_t activate_slot(h2_workers *workers, h2_slot *slot) 
 {
-    apr_status_t status;
+    apr_status_t rv;
     
     slot->workers = workers;
     slot->task = NULL;
 
+    apr_thread_mutex_lock(workers->lock);
     if (!slot->lock) {
-        status = apr_thread_mutex_create(&slot->lock,
+        rv = apr_thread_mutex_create(&slot->lock,
                                          APR_THREAD_MUTEX_DEFAULT,
                                          workers->pool);
-        if (status != APR_SUCCESS) {
-            push_slot(&workers->free, slot);
-            return status;
-        }
+        if (rv != APR_SUCCESS) goto cleanup;
     }
 
     if (!slot->not_idle) {
-        status = apr_thread_cond_create(&slot->not_idle, workers->pool);
-        if (status != APR_SUCCESS) {
-            push_slot(&workers->free, slot);
-            return status;
-        }
+        rv = apr_thread_cond_create(&slot->not_idle, workers->pool);
+        if (rv != APR_SUCCESS) goto cleanup;
     }
     
-    ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, workers->s,
+    ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s,
                  "h2_workers: new thread for slot %d", slot->id); 
 
     /* thread will either immediately start work or add itself
      * to the idle queue */
     apr_atomic_inc32(&workers->worker_count);
-    status = apr_thread_create(&slot->thread, workers->thread_attr,
+    slot->timed_out = 0;
+    rv = apr_thread_create(&slot->thread, workers->thread_attr,
                                slot_run, slot, workers->pool);
-    if (status != APR_SUCCESS) {
+    if (rv != APR_SUCCESS) {
         apr_atomic_dec32(&workers->worker_count);
+    }
+
+cleanup:
+    apr_thread_mutex_unlock(workers->lock);
+    if (rv != APR_SUCCESS) {
         push_slot(&workers->free, slot);
-        return status;
     }
-    
-    return APR_SUCCESS;
+    return rv;
 }
 
 static apr_status_t add_worker(h2_workers *workers)
@@ -127,11 +128,19 @@ static void wake_idle_worker(h2_workers *workers)
 {
     h2_slot *slot = pop_slot(&workers->idle);
     if (slot) {
+        int timed_out = 0;
         apr_thread_mutex_lock(slot->lock);
-        apr_thread_cond_signal(slot->not_idle);
+        timed_out = slot->timed_out;
+        if (!timed_out) {
+            apr_thread_cond_signal(slot->not_idle);
+        }
         apr_thread_mutex_unlock(slot->lock);
+        if (timed_out) {
+            slot_done(slot);
+            wake_idle_worker(workers);
+        }
     }
-    else if (workers->dynamic) {
+    else if (workers->dynamic && !workers->shutdown) {
         add_worker(workers);
     }
 }
@@ -185,13 +194,18 @@ static h2_fifo_op_t mplx_peek(void *head, void *ctx)
 static int get_next(h2_slot *slot)
 {
     h2_workers *workers = slot->workers;
+    int non_essential = slot->id >= workers->min_workers;
+    apr_status_t rv;
 
-    while (!workers->aborted) {
+    while (!workers->aborted && !slot->timed_out) {
         ap_assert(slot->task == NULL);
+        if (non_essential && workers->shutdown) {
+            /* Terminate non-essential worker on shutdown */
+            break;
+        }
         if (h2_fifo_try_peek(workers->mplxs, mplx_peek, slot) == APR_EOF) {
             /* The queue is terminated with the MPM child being cleaned up,
-             * just leave.
-             */
+             * just leave. */
             break;
         }
         if (slot->task) {
@@ -202,8 +216,18 @@ static int get_next(h2_slot *slot)
 
         apr_thread_mutex_lock(slot->lock);
         if (!workers->aborted) {
+
             push_slot(&workers->idle, slot);
-            apr_thread_cond_wait(slot->not_idle, slot->lock);
+            if (non_essential && workers->max_idle_duration) {
+                rv = apr_thread_cond_timedwait(slot->not_idle, slot->lock,
+                                               workers->max_idle_duration);
+                if (APR_TIMEUP == rv) {
+                    slot->timed_out = 1;
+                }
+            }
+            else {
+                apr_thread_cond_wait(slot->not_idle, slot->lock);
+            }
         }
         apr_thread_mutex_unlock(slot->lock);
     }
@@ -251,17 +275,36 @@ static void* APR_THREAD_FUNC slot_run(apr_thread_t *thread, void *wctx)
         } while (slot->task);
     }
 
-    slot_done(slot);
+    if (!slot->timed_out) {
+        slot_done(slot);
+    }
 
     apr_thread_exit(thread, APR_SUCCESS);
     return NULL;
 }
 
-static apr_status_t workers_pool_cleanup(void *data)
+static void wake_non_essential_workers(h2_workers *workers)
 {
-    h2_workers *workers = data;
     h2_slot *slot;
-    
+    /* pop all idle, signal the non essentials and add the others again */
+    if ((slot = pop_slot(&workers->idle))) {
+        wake_non_essential_workers(workers);
+        if (slot->id > workers->min_workers) {
+            apr_thread_mutex_lock(slot->lock);
+            apr_thread_cond_signal(slot->not_idle);
+            apr_thread_mutex_unlock(slot->lock);
+        }
+        else {
+            push_slot(&workers->idle, slot);
+        }
+    }
+}
+
+static void workers_abort_idle(h2_workers *workers)
+{
+    h2_slot *slot;
+
+    workers->shutdown = 1;
     workers->aborted = 1;
     h2_fifo_term(workers->mplxs);
 
@@ -271,14 +314,49 @@ static apr_status_t workers_pool_cleanup(void *data)
         apr_thread_cond_signal(slot->not_idle);
         apr_thread_mutex_unlock(slot->lock);
     }
+}
+
+static apr_status_t workers_pool_cleanup(void *data)
+{
+    h2_workers *workers = data;
+    apr_time_t end, timeout = apr_time_from_sec(1);
+    apr_status_t rv;
+    int n, wait_sec = 5;
 
-    /* wait for all the workers to become zombies and join them */
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
+                 "h2_workers: cleanup %d workers idling",
+                 (int)apr_atomic_read32(&workers->worker_count));
+    workers_abort_idle(workers);
+
+    /* wait for all the workers to become zombies and join them.
+     * this gets called after the mpm shuts down and all connections
+     * have either been handled (graceful) or we are forced exiting
+     * (ungrateful). Either way, we show limited patience. */
     apr_thread_mutex_lock(workers->lock);
-    if (apr_atomic_read32(&workers->worker_count)) {
-        apr_thread_cond_wait(workers->all_done, workers->lock);
+    end = apr_time_now() + apr_time_from_sec(wait_sec);
+    while ((n = apr_atomic_read32(&workers->worker_count)) > 0
+           && apr_time_now() < end) {
+        rv = apr_thread_cond_timedwait(workers->all_done, workers->lock, timeout);
+        if (APR_TIMEUP == rv) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
+                         APLOGNO(10290) "h2_workers: waiting for idle workers to close, "
+                         "still seeing %d workers living",
+                         apr_atomic_read32(&workers->worker_count));
+            continue;
+        }
+    }
+    if (n) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, workers->s,
+                     APLOGNO(10291) "h2_workers: cleanup, %d idle workers "
+                     "did not exit after %d seconds.",
+                     n, wait_sec);
     }
     apr_thread_mutex_unlock(workers->lock);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
+                 "h2_workers: cleanup all workers terminated");
     join_zombies(workers);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
+                 "h2_workers: cleanup zombie workers joined");
 
     return APR_SUCCESS;
 }
@@ -287,7 +365,7 @@ h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pchild,
                               int min_workers, int max_workers,
                               int idle_secs)
 {
-    apr_status_t status;
+    apr_status_t rv;
     h2_workers *workers;
     apr_pool_t *pool;
     int i, n;
@@ -311,8 +389,12 @@ h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pchild,
     workers->pool = pool;
     workers->min_workers = min_workers;
     workers->max_workers = max_workers;
-    workers->max_idle_secs = (idle_secs > 0)? idle_secs : 10;
+    workers->max_idle_duration = apr_time_from_sec((idle_secs > 0)? idle_secs : 10);
 
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
+                 "h2_workers: created with min=%d max=%d idle_timeout=%d sec",
+                 workers->min_workers, workers->max_workers,
+                 (int)apr_time_sec(workers->max_idle_duration));
     /* FIXME: the fifo set we use here has limited capacity. Once the
      * set is full, connections with new requests do a wait. Unfortunately,
      * we have optimizations in place there that makes such waiting "unfair"
@@ -324,16 +406,12 @@ h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pchild,
      * For now, we just make enough room to have many connections inside one
      * process.
      */
-    status = h2_fifo_set_create(&workers->mplxs, pool, 8 * 1024);
-    if (status != APR_SUCCESS) {
-        return NULL;
-    }
-    
-    status = apr_threadattr_create(&workers->thread_attr, workers->pool);
-    if (status != APR_SUCCESS) {
-        return NULL;
-    }
-    
+    rv = h2_fifo_set_create(&workers->mplxs, pool, 8 * 1024);
+    if (rv != APR_SUCCESS) goto cleanup;
+
+    rv = apr_threadattr_create(&workers->thread_attr, workers->pool);
+    if (rv != APR_SUCCESS) goto cleanup;
+
     if (ap_thread_stacksize != 0) {
         apr_threadattr_stacksize_set(workers->thread_attr,
                                      ap_thread_stacksize);
@@ -342,38 +420,39 @@ h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pchild,
                      (long)ap_thread_stacksize);
     }
     
-    status = apr_thread_mutex_create(&workers->lock,
-                                     APR_THREAD_MUTEX_DEFAULT,
-                                     workers->pool);
-    if (status == APR_SUCCESS) {        
-        status = apr_thread_cond_create(&workers->all_done, workers->pool);
+    rv = apr_thread_mutex_create(&workers->lock,
+                                 APR_THREAD_MUTEX_DEFAULT,
+                                 workers->pool);
+    if (rv != APR_SUCCESS) goto cleanup;
+    rv = apr_thread_cond_create(&workers->all_done, workers->pool);
+    if (rv != APR_SUCCESS) goto cleanup;
+
+    n = workers->nslots = workers->max_workers;
+    workers->slots = apr_pcalloc(workers->pool, n * sizeof(h2_slot));
+    if (workers->slots == NULL) {
+        n = workers->nslots = 0;
+        rv = APR_ENOMEM;
+        goto cleanup;
     }
-    if (status == APR_SUCCESS) {        
-        n = workers->nslots = workers->max_workers;
-        workers->slots = apr_pcalloc(workers->pool, n * sizeof(h2_slot));
-        if (workers->slots == NULL) {
-            workers->nslots = 0;
-            status = APR_ENOMEM;
-        }
-        for (i = 0; i < n; ++i) {
-            workers->slots[i].id = i;
-        }
+    for (i = 0; i < n; ++i) {
+        workers->slots[i].id = i;
     }
-    if (status == APR_SUCCESS) {
-        /* we activate all for now, TODO: support min_workers again.
-         * do this in reverse for vanity reasons so slot 0 will most
-         * likely be at head of idle queue. */
-        n = workers->max_workers;
-        for (i = n-1; i >= 0; --i) {
-            status = activate_slot(workers, &workers->slots[i]);
-        }
-        /* the rest of the slots go on the free list */
-        for(i = n; i < workers->nslots; ++i) {
-            push_slot(&workers->free, &workers->slots[i]);
-        }
-        workers->dynamic = (workers->worker_count < workers->max_workers);
+    /* we activate all for now, TODO: support min_workers again.
+     * do this in reverse for vanity reasons so slot 0 will most
+     * likely be at head of idle queue. */
+    n = workers->min_workers;
+    for (i = n-1; i >= 0; --i) {
+        rv = activate_slot(workers, &workers->slots[i]);
+        if (rv != APR_SUCCESS) goto cleanup;
+    }
+    /* the rest of the slots go on the free list */
+    for(i = n; i < workers->nslots; ++i) {
+        push_slot(&workers->free, &workers->slots[i]);
     }
-    if (status == APR_SUCCESS) {
+    workers->dynamic = (workers->worker_count < workers->max_workers);
+
+cleanup:
+    if (rv == APR_SUCCESS) {
         /* Stop/join the workers threads when the MPM child exits (pchild is
          * destroyed), and as a pre_cleanup of pchild thus before the threads
          * pools (children of workers->pool) so that they are not destroyed
@@ -396,3 +475,12 @@ apr_status_t h2_workers_unregister(h2_workers *workers, struct h2_mplx *m)
 {
     return h2_fifo_remove(workers->mplxs, m);
 }
+
+void h2_workers_graceful_shutdown(h2_workers *workers)
+{
+    workers->shutdown = 1;
+    workers->min_workers = 1;
+    workers->max_idle_duration = apr_time_from_sec(1);
+    h2_fifo_term(workers->mplxs);
+    wake_non_essential_workers(workers);
+}
index 772bb796d1676cc41e56462d3faf9b5a09306d6e..2aa3b3a3b312fbb1a637050427592c8b3dc8b184 100644 (file)
@@ -38,11 +38,12 @@ struct h2_workers {
     apr_pool_t *pool;
     
     int next_worker_id;
-    int min_workers;
-    int max_workers;
-    int max_idle_secs;
+    apr_uint32_t max_workers;
+    volatile apr_uint32_t min_workers; /* is changed during graceful shutdown */
+    volatile apr_interval_time_t max_idle_duration; /* is changed during graceful shutdown */
     
     volatile int aborted;
+    volatile int shutdown;
     int dynamic;
 
     apr_threadattr_t *thread_attr;
@@ -80,4 +81,9 @@ apr_status_t h2_workers_register(h2_workers *workers, struct h2_mplx *m);
  */
 apr_status_t h2_workers_unregister(h2_workers *workers, struct h2_mplx *m);
 
+/**
+ *  Shut down processing gracefully by terminating all idle workers.
+ */
+void h2_workers_graceful_shutdown(h2_workers *workers);
+
 #endif /* defined(__mod_h2__h2_workers__) */
index 24f1585725174ff0c61783cfd14b1372d96ace6c..3f2a61fc3eda4566322c5ed2dcb5da9465e3765e 100644 (file)
@@ -24,6 +24,7 @@
 #include <http_protocol.h>
 #include <http_request.h>
 #include <http_log.h>
+#include <mpm_common.h>
 
 #include "mod_http2.h"
 
@@ -228,7 +229,9 @@ static void h2_hooks(apr_pool_t *pool)
     /* Run once after a child process has been created.
      */
     ap_hook_child_init(h2_child_init, NULL, NULL, APR_HOOK_MIDDLE);
-
+#if AP_MODULE_MAGIC_AT_LEAST(20120211, 110)
+    ap_hook_child_stopping(h2_conn_child_stopping, NULL, NULL, APR_HOOK_MIDDLE);
+#endif
     h2_h2_register_hooks();
     h2_switch_register_hooks();
     h2_task_register_hooks();
index 893aa8fd316773e51099a27f9a38fc7b24e9aad2..4ea4fb97413d0074cb1901ea879fa726dbcfeeff 100644 (file)
@@ -397,18 +397,9 @@ run_connect:
     
     if (!ctx->p_conn->data && ctx->is_ssl) {
         /* New SSL connection: set a note on the connection about what
-         * protocol we want.
-         */
+         * protocol we need. */
         apr_table_setn(ctx->p_conn->connection->notes,
                        "proxy-request-alpn-protos", "h2");
-        if (ctx->p_conn->ssl_hostname) {
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->owner,
-                          "set SNI to %s for (%s)",
-                          ctx->p_conn->ssl_hostname,
-                          ctx->p_conn->hostname);
-            apr_table_setn(ctx->p_conn->connection->notes,
-                           "proxy-request-hostname", ctx->p_conn->ssl_hostname);
-        }
     }
 
     if (ctx->master->aborted) goto cleanup;