]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
On the 2.4.x branch:
authorStefan Eissing <icing@apache.org>
Tue, 18 Apr 2017 13:12:38 +0000 (13:12 +0000)
committerStefan Eissing <icing@apache.org>
Tue, 18 Apr 2017 13:12:38 +0000 (13:12 +0000)
Merged /httpd/httpd/trunk:r1790850,1790855,1791377,1791388,1791669,1791773

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

13 files changed:
CHANGES
modules/http2/h2.h
modules/http2/h2_headers.c
modules/http2/h2_headers.h
modules/http2/h2_mplx.c
modules/http2/h2_mplx.h
modules/http2/h2_request.c
modules/http2/h2_session.c
modules/http2/h2_stream.c
modules/http2/h2_util.c
modules/http2/h2_util.h
modules/http2/h2_version.h
modules/http2/h2_workers.c

diff --git a/CHANGES b/CHANGES
index 47e759792b85b4f2706a2be7ac8b1f101e266018..02b6e1753a253f15a594f21000f6bae6422da155 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,19 @@
 
 Changes with Apache 2.4.26
 
+  *) mod_http2: MaxKeepAliveRequests now limits the number of times a 
+     slave connection gets reused. [Stefan Eissing]
+
+  *) mod_http2: client streams that lack the EOF flag get now forcefully
+     closed with a RST_STREAM (NO_ERROR) when the request has been answered.
+     [Stefan Eissing]     
+
+  *) mod_http2: only when 'HttpProtocolOptions Unsafe' is configured, will
+     control characters in response headers or trailers be forwarded to the
+     client. Otherwise, in the default configuration, a request will eiher 
+     fail with status 500 or the stream will be reset by a RST_STREAM frame. 
+     [Stefan Eissing]
+     
   *) mod_brotli: Add a new module for dynamic Brotli (RFC 7932) compression.
      [Evgeny Kotkov]
 
index df809fd4113bacb291af6c4237f9780dba0a619d..ad6979c1827eec72e9df4dee81ae287664c5e8c2 100644 (file)
@@ -154,5 +154,7 @@ typedef int h2_stream_pri_cmp(int stream_id1, int stream_id2, void *ctx);
 
 #define H2_TASK_ID_NOTE         "http2-task-id"
 #define H2_FILTER_DEBUG_NOTE    "http2-debug"
+#define H2_HDR_CONFORMANCE      "http2-hdr-conformance"
+#define H2_HDR_CONFORMANCE_UNSAFE      "unsafe"
 
 #endif /* defined(__mod_h2__h2__) */
index 8add79f507d93f375973c728deeaff92e6fdf385..ce7eaec2b3e5bd7055f2f6eea41060a19c18a204 100644 (file)
 #include "h2_headers.h"
 
 
+static int is_unsafe(server_rec *s) 
+{
+    core_server_config *conf = ap_get_core_module_config(s->module_config);
+    return (conf->http_conformance == AP_HTTP_CONFORMANCE_UNSAFE);
+}
+
 typedef struct {
     apr_bucket_refcount refcount;
     h2_headers *headers;
@@ -132,9 +138,19 @@ h2_headers *h2_headers_rcreate(request_rec *r, int status,
             headers->status = H2_ERR_HTTP_1_1_REQUIRED;
         }
     }
+    if (is_unsafe(r->server)) {
+        apr_table_setn(headers->notes, H2_HDR_CONFORMANCE, 
+                       H2_HDR_CONFORMANCE_UNSAFE);
+    }
     return headers;
 }
 
+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), pool);
+}
+
 h2_headers *h2_headers_die(apr_status_t type,
                              const h2_request *req, apr_pool_t *pool)
 {
index 412e93fae2523f78257c3db6867735f8303032f3..95e99ee81add7853eab9d9d2e7d5df20b515e41c 100644 (file)
@@ -55,6 +55,12 @@ h2_headers *h2_headers_create(int status, apr_table_t *header,
 h2_headers *h2_headers_rcreate(request_rec *r, int status, 
                                  apr_table_t *header, apr_pool_t *pool);
 
+/**
+ * Clone the headers into another pool. This will not copy any
+ * header strings.
+ */
+h2_headers *h2_headers_copy(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
index 357bf5eaadfde12571472ea20c8d77e3776b0af9..e0704b7a8888dd47457c2b3656b0eab3512d7e39 100644 (file)
@@ -158,13 +158,18 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent,
     apr_allocator_t *allocator;
     apr_thread_mutex_t *mutex;
     h2_mplx *m;
+    h2_ctx *ctx = h2_ctx_get(c, 0);
     ap_assert(conf);
     
     m = apr_pcalloc(parent, sizeof(h2_mplx));
     if (m) {
         m->id = c->id;
         m->c = c;
-
+        m->s = (ctx? h2_ctx_server_get(ctx) : NULL);
+        if (!m->s) {
+            m->s = c->base_server;
+        }
+        
         /* We create a pool with its own allocator to be used for
          * processing slave connections. This is the only way to have the
          * processing independant of its parent pool in the sense that it
@@ -258,7 +263,17 @@ static int input_consumed_signal(h2_mplx *m, h2_stream *stream)
 
 static int report_consumption_iter(void *ctx, void *val)
 {
-    input_consumed_signal(ctx, val);
+    h2_stream *stream = val;
+    h2_mplx *m = ctx;
+    
+    input_consumed_signal(m, stream);
+    if (stream->state == H2_SS_CLOSED_L
+        && (!stream->task || stream->task->worker_done)) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, 
+                      H2_STRM_LOG(APLOGNO(10026), stream, "remote close missing")); 
+        nghttp2_submit_rst_stream(stream->session->ngh2, NGHTTP2_FLAG_NONE, 
+                                  stream->id, NGHTTP2_NO_ERROR);
+    }
     return 1;
 }
 
@@ -276,8 +291,11 @@ static void task_destroy(h2_mplx *m, h2_task *task)
     int reuse_slave = 0;
     
     slave = task->c;
-    reuse_slave = ((m->spare_slaves->nelts < (m->limit_active * 3 / 2))
-                   && !task->rst_error);
+
+    if (m->s->keep_alive_max == 0 || slave->keepalives < m->s->keep_alive_max) {
+        reuse_slave = ((m->spare_slaves->nelts < (m->limit_active * 3 / 2))
+                       && !task->rst_error);
+    }
     
     if (slave) {
         if (reuse_slave && slave->keepalive == AP_CONN_KEEPALIVE) {
@@ -560,7 +578,10 @@ static apr_status_t out_close(h2_mplx *m, h2_task *task)
     if (!task) {
         return APR_ECONNABORTED;
     }
-
+    if (task->c) {
+        ++task->c->keepalives;
+    }
+    
     stream = h2_ihash_get(m->streams, task->stream_id);
     if (!stream) {
         return APR_ECONNABORTED;
@@ -703,8 +724,7 @@ static h2_task *next_stream_task(h2_mplx *m)
             }
             
             if (!stream->task) {
-            
-                m->c->keepalives++;
+
                 if (sid > m->max_stream_started) {
                     m->max_stream_started = sid;
                 }
index ed332c8bc3d16efb55d99c9b71404df4ce9a353a..61b1b99aba705f8844eea69af72d5ceb1dca10bc 100644 (file)
@@ -57,6 +57,7 @@ struct h2_mplx {
     long id;
     conn_rec *c;
     apr_pool_t *pool;
+    server_rec *s;                  /* server for master conn */
 
     unsigned int event_pending;
     unsigned int aborted;
index 7faf39340c2c12cd14f7952240c9f63765767274..dea676223d5331709f2aaa55bed158cb1e1fe189 100644 (file)
 typedef struct {
     apr_table_t *headers;
     apr_pool_t *pool;
+    apr_status_t status;
 } h1_ctx;
 
 static int set_h1_header(void *ctx, const char *key, const char *value)
 {
     h1_ctx *x = ctx;
-    size_t klen = strlen(key);
-    if (!h2_req_ignore_header(key, klen)) {
-        h2_headers_add_h1(x->headers, x->pool, key, klen, value, strlen(value));
-    }
-    return 1;
+    x->status = h2_req_add_header(x->headers, x->pool, key, strlen(key), 
+                                  value, strlen(value));
+    return (x->status == APR_SUCCESS)? 1 : 0;
 }
 
 apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool, 
@@ -90,10 +89,11 @@ apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool,
 
     x.pool = pool;
     x.headers = req->headers;
+    x.status = APR_SUCCESS;
     apr_table_do(set_h1_header, &x, r->headers_in, NULL);
     
     *preq = req;
-    return APR_SUCCESS;
+    return x.status;
 }
 
 apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool, 
@@ -143,7 +143,7 @@ apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
     }
     else {
         /* non-pseudo header, append to work bucket of stream */
-        status = h2_headers_add_h1(req->headers, pool, name, nlen, value, vlen);
+        status = h2_req_add_header(req->headers, pool, name, nlen, value, vlen);
     }
     
     return status;
index e23cb8d54b4ee6f216bd4f393d5a35130767ee7c..b22f7135b1be8cf60d3843e4dcb8ed59d06efb55 100644 (file)
@@ -792,13 +792,13 @@ static apr_status_t h2_session_create_int(h2_session **psession,
         return status;
     }
     
-    session->in_pending = h2_iq_create(session->pool, session->max_stream_count);
+    session->in_pending = h2_iq_create(session->pool, (int)session->max_stream_count);
     if (session->in_pending == NULL) {
         apr_pool_destroy(pool);
         return APR_ENOMEM;
     }
 
-    session->in_process = h2_iq_create(session->pool, session->max_stream_count);
+    session->in_process = h2_iq_create(session->pool, (int)session->max_stream_count);
     if (session->in_process == NULL) {
         apr_pool_destroy(pool);
         return APR_ENOMEM;
@@ -1080,13 +1080,16 @@ struct h2_stream *h2_session_push(h2_session *session, h2_stream *is,
 {
     h2_stream *stream;
     h2_ngheader *ngh;
-    int nid;
+    apr_status_t status;
+    int nid = 0;
     
-    ngh = h2_util_ngheader_make_req(is->pool, push->req);
-    nid = nghttp2_submit_push_promise(session->ngh2, 0, is->id, 
-                                      ngh->nv, ngh->nvlen, NULL);
-    if (nid <= 0) {
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, 
+    status = h2_req_create_ngheader(&ngh, is->pool, push->req);
+    if (status == APR_SUCCESS) {
+        nid = nghttp2_submit_push_promise(session->ngh2, 0, is->id, 
+                                          ngh->nv, ngh->nvlen, NULL);
+    }
+    if (status != APR_SUCCESS || nid <= 0) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, 
                       H2_STRM_LOG(APLOGNO(03075), is, 
                       "submitting push promise fail: %s"), nghttp2_strerror(nid));
         return NULL;
@@ -1280,16 +1283,25 @@ static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream,
     else if (stream->has_response) {
         h2_ngheader *nh;
         
-        nh = h2_util_ngheader_make(stream->pool, headers->headers);
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, 
-                      H2_STRM_LOG(APLOGNO(03072), stream, "submit %d trailers"), (int)nh->nvlen);
-        rv = nghttp2_submit_trailer(session->ngh2, stream->id, nh->nv, nh->nvlen);
+        status = h2_res_create_ngtrailer(&nh, stream->pool, headers);
+        
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, 
+                      H2_STRM_LOG(APLOGNO(03072), stream, "submit %d trailers"), 
+                      (int)nh->nvlen);
+        if (status == APR_SUCCESS) {
+            rv = nghttp2_submit_trailer(session->ngh2, stream->id, 
+                                        nh->nv, nh->nvlen);
+        }
+        else {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
+                          H2_STRM_LOG(APLOGNO(10024), stream, "invalid trailers"));
+            h2_stream_rst(stream, NGHTTP2_PROTOCOL_ERROR);
+        }
         goto leave;
     }
     else {
         nghttp2_data_provider provider, *pprovider = NULL;
         h2_ngheader *ngh;
-        apr_table_t *hout;
         const char *note;
         
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, 
@@ -1335,17 +1347,16 @@ static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream,
         }
         h2_session_set_prio(session, stream, stream->pref_priority);
         
-        hout = headers->headers;
         note = apr_table_get(headers->notes, H2_FILTER_DEBUG_NOTE);
         if (note && !strcmp("on", note)) {
             int32_t connFlowIn, connFlowOut;
 
             connFlowIn = nghttp2_session_get_effective_local_window_size(session->ngh2); 
             connFlowOut = nghttp2_session_get_remote_window_size(session->ngh2);
-            hout = apr_table_clone(stream->pool, hout);
-            apr_table_setn(hout, "conn-flow-in", 
+            headers = h2_headers_copy(stream->pool, headers);
+            apr_table_setn(headers->headers, "conn-flow-in", 
                            apr_itoa(stream->pool, connFlowIn));
-            apr_table_setn(hout, "conn-flow-out", 
+            apr_table_setn(headers->headers, "conn-flow-out", 
                            apr_itoa(stream->pool, connFlowOut));
         }
         
@@ -1357,17 +1368,24 @@ static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream,
             goto leave;
         }
         
-        ngh = h2_util_ngheader_make_res(stream->pool, headers->status, hout);
-        rv = nghttp2_submit_response(session->ngh2, stream->id,
-                                     ngh->nv, ngh->nvlen, pprovider);
-        stream->has_response = h2_headers_are_response(headers);
-        session->have_written = 1;
-        
-        if (stream->initiated_on) {
-            ++session->pushes_submitted;
+        status = h2_res_create_ngheader(&ngh, stream->pool, headers);
+        if (status == APR_SUCCESS) {
+            rv = nghttp2_submit_response(session->ngh2, stream->id,
+                                         ngh->nv, ngh->nvlen, pprovider);
+            stream->has_response = h2_headers_are_response(headers);
+            session->have_written = 1;
+            
+            if (stream->initiated_on) {
+                ++session->pushes_submitted;
+            }
+            else {
+                ++session->responses_submitted;
+            }
         }
         else {
-            ++session->responses_submitted;
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
+                          H2_STRM_LOG(APLOGNO(10025), stream, "invalid response"));
+            h2_stream_rst(stream, NGHTTP2_PROTOCOL_ERROR);
         }
     }
     
index 9784b4ec28d9feee5fefdc88e8c6351baa92b95b..4cd2132207e10be8a4fb205c8eadf80e0028dbe8 100644 (file)
@@ -1008,7 +1008,7 @@ apr_status_t h2_stream_in_consumed(h2_stream *stream, apr_off_t amount)
         apr_off_t consumed = amount;
         
         while (consumed > 0) {
-            int len = (consumed > INT_MAX)? INT_MAX : consumed;
+            int len = (consumed > INT_MAX)? INT_MAX : (int)consumed;
             nghttp2_session_consume(session->ngh2, stream->id, len);
             consumed -= len;
         }
index 0ac65ccf656b1a6a7f446174fc5df32348ad0311..f4c3aab3a9e3889f96903f9e9c3bc318edfcbc90 100644 (file)
@@ -1382,89 +1382,150 @@ static int count_header(void *ctx, const char *key, const char *value)
     return 1;
 }
 
-#define NV_ADD_LIT_CS(nv, k, v)     add_header(nv, k, sizeof(k) - 1, v, strlen(v))
-#define NV_ADD_CS_CS(nv, k, v)      add_header(nv, k, strlen(k), v, strlen(v))
+static const char *inv_field_name_chr(const char *token)
+{
+    const char *p = ap_scan_http_token(token);
+    if (p == token && *p == ':') {
+        p = ap_scan_http_token(++p);
+    }
+    return (p && *p)? p : NULL;
+}
 
-static int add_header(h2_ngheader *ngh, 
-                      const char *key, size_t key_len,
-                      const char *value, size_t val_len)
+static const char *inv_field_value_chr(const char *token)
 {
-    nghttp2_nv *nv = &ngh->nv[ngh->nvlen++];
-    
+    const char *p = ap_scan_http_field_content(token);
+    return (p && *p)? p : NULL;
+}
+
+typedef struct ngh_ctx {
+    apr_pool_t *p;
+    int unsafe;
+    h2_ngheader *ngh;
+    apr_status_t status;
+} ngh_ctx;
+
+static int add_header(ngh_ctx *ctx, const char *key, const char *value)
+{
+    nghttp2_nv *nv = &(ctx->ngh)->nv[(ctx->ngh)->nvlen++];
+    const char *p;
+
+    if (!ctx->unsafe) {
+        if ((p = inv_field_name_chr(key))) {
+            ap_log_perror(APLOG_MARK, APLOG_TRACE1, APR_EINVAL, ctx->p,
+                          "h2_request: head field '%s: %s' has invalid char %s", 
+                          key, value, p);
+            ctx->status = APR_EINVAL;
+            return 0;
+        }
+        if ((p = inv_field_value_chr(value))) {
+            ap_log_perror(APLOG_MARK, APLOG_TRACE1, APR_EINVAL, ctx->p,
+                          "h2_request: head field '%s: %s' has invalid char %s", 
+                          key, value, p);
+            ctx->status = APR_EINVAL;
+            return 0;
+        }
+    }
     nv->name = (uint8_t*)key;
-    nv->namelen = key_len;
+    nv->namelen = strlen(key);
     nv->value = (uint8_t*)value;
-    nv->valuelen = val_len;
+    nv->valuelen = strlen(value);
+    
     return 1;
 }
 
 static int add_table_header(void *ctx, const char *key, const char *value)
 {
     if (!h2_util_ignore_header(key)) {
-        add_header(ctx, key, strlen(key), value, strlen(value));
+        add_header(ctx, key, value);
     }
     return 1;
 }
 
-
-h2_ngheader *h2_util_ngheader_make(apr_pool_t *p, apr_table_t *header)
+static apr_status_t ngheader_create(h2_ngheader **ph, apr_pool_t *p, 
+                                    int unsafe, size_t key_count, 
+                                    const char *keys[], const char *values[],
+                                    apr_table_t *headers)
 {
-    h2_ngheader *ngh;
-    size_t n;
+    ngh_ctx ctx;
+    size_t n, i;
     
-    n = 0;
-    apr_table_do(count_header, &n, header, NULL);
+    ctx.p = p;
+    ctx.unsafe = unsafe;
     
-    ngh = apr_pcalloc(p, sizeof(h2_ngheader));
-    ngh->nv =  apr_pcalloc(p, n * sizeof(nghttp2_nv));
-    apr_table_do(add_table_header, ngh, header, NULL);
+    n = key_count;
+    apr_table_do(count_header, &n, headers, NULL);
+    
+    *ph = ctx.ngh = apr_pcalloc(p, sizeof(h2_ngheader));
+    if (!ctx.ngh) {
+        return APR_ENOMEM;
+    }
+    
+    ctx.ngh->nv =  apr_pcalloc(p, n * sizeof(nghttp2_nv));
+    if (!ctx.ngh->nv) {
+        return APR_ENOMEM;
+    }
+    
+    ctx.status = APR_SUCCESS;
+    for (i = 0; i < key_count; ++i) {
+        if (!add_header(&ctx, keys[i], values[i])) {
+            return ctx.status;
+        }
+    }
+    
+    apr_table_do(add_table_header, &ctx, headers, NULL);
 
-    return ngh;
+    return ctx.status;
 }
 
-h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p, 
-                                       int http_status, 
-                                       apr_table_t *header)
+static int is_unsafe(h2_headers *h)
 {
-    h2_ngheader *ngh;
-    size_t n;
-    
-    n = 1;
-    apr_table_do(count_header, &n, header, NULL);
-    
-    ngh = apr_pcalloc(p, sizeof(h2_ngheader));
-    ngh->nv =  apr_pcalloc(p, n * sizeof(nghttp2_nv));
-    NV_ADD_LIT_CS(ngh, ":status", apr_psprintf(p, "%d", http_status));
-    apr_table_do(add_table_header, ngh, header, NULL);
+    const char *v = apr_table_get(h->notes, H2_HDR_CONFORMANCE);
+    return (v && !strcmp(v, H2_HDR_CONFORMANCE_UNSAFE));
+}
 
-    return ngh;
+apr_status_t h2_res_create_ngtrailer(h2_ngheader **ph, apr_pool_t *p, 
+                                    h2_headers *headers)
+{
+    return ngheader_create(ph, p, is_unsafe(headers), 
+                           0, NULL, NULL, headers->headers);
+}
+                                     
+apr_status_t h2_res_create_ngheader(h2_ngheader **ph, apr_pool_t *p,
+                                    h2_headers *headers) 
+{
+    const char *keys[] = {
+        ":status"
+    };
+    const char *values[] = {
+        apr_psprintf(p, "%d", headers->status)
+    };
+    return ngheader_create(ph, p, is_unsafe(headers),  
+                           H2_ALEN(keys), keys, values, headers->headers);
 }
 
-h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p, 
-                                       const struct h2_request *req)
+apr_status_t h2_req_create_ngheader(h2_ngheader **ph, apr_pool_t *p, 
+                                    const struct h2_request *req)
 {
     
-    h2_ngheader *ngh;
-    size_t n;
+    const char *keys[] = {
+        ":scheme", 
+        ":authority", 
+        ":path", 
+        ":method", 
+    };
+    const char *values[] = {
+        req->scheme,
+        req->authority, 
+        req->path, 
+        req->method, 
+    };
     
-    ap_assert(req);
     ap_assert(req->scheme);
     ap_assert(req->authority);
     ap_assert(req->path);
     ap_assert(req->method);
 
-    n = 4;
-    apr_table_do(count_header, &n, req->headers, NULL);
-    
-    ngh = apr_pcalloc(p, sizeof(h2_ngheader));
-    ngh->nv =  apr_pcalloc(p, n * sizeof(nghttp2_nv));
-    NV_ADD_LIT_CS(ngh, ":scheme", req->scheme);
-    NV_ADD_LIT_CS(ngh, ":authority", req->authority);
-    NV_ADD_LIT_CS(ngh, ":path", req->path);
-    NV_ADD_LIT_CS(ngh, ":method", req->method);
-    apr_table_do(add_table_header, ngh, req->headers, NULL);
-
-    return ngh;
+    return ngheader_create(ph, p, 0, H2_ALEN(keys), keys, values, req->headers);
 }
 
 /*******************************************************************************
@@ -1481,7 +1542,6 @@ typedef struct {
 #define H2_LIT_ARGS(a)      (a),H2_ALEN(a)
 
 static literal IgnoredRequestHeaders[] = {
-/*H2_DEF_LITERAL("expect"),*/
     H2_DEF_LITERAL("upgrade"),
     H2_DEF_LITERAL("connection"),
     H2_DEF_LITERAL("keep-alive"),
@@ -1547,7 +1607,7 @@ int h2_res_ignore_trailer(const char *name, size_t len)
     return ignore_header(H2_LIT_ARGS(IgnoredResponseTrailers), name, len);
 }
 
-apr_status_t h2_headers_add_h1(apr_table_t *headers, apr_pool_t *pool, 
+apr_status_t h2_req_add_header(apr_table_t *headers, apr_pool_t *pool, 
                                const char *name, size_t nlen,
                                const char *value, size_t vlen)
 {
index 9b408fad3df47fb8f406d4ce3dd57ed48b4f1e36..4b901bfe681d3e31e3e3e57b418b06486a136547 100644 (file)
@@ -341,19 +341,21 @@ const char *h2_util_base64url_encode(const char *data,
 
 int h2_util_ignore_header(const char *name);
 
+struct h2_headers;
+
 typedef struct h2_ngheader {
     nghttp2_nv *nv;
     apr_size_t nvlen;
 } h2_ngheader;
 
-h2_ngheader *h2_util_ngheader_make(apr_pool_t *p, apr_table_t *header);
-h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p, 
-                                       int http_status
-                                       apr_table_t *header);
-h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p, 
-                                       const struct h2_request *req);
+apr_status_t h2_res_create_ngtrailer(h2_ngheader **ph, apr_pool_t *p, 
+                                     struct h2_headers *headers); 
+apr_status_t h2_res_create_ngheader(h2_ngheader **ph, apr_pool_t *p
+                                    struct h2_headers *headers); 
+apr_status_t h2_req_create_ngheader(h2_ngheader **ph, apr_pool_t *p, 
+                                    const struct h2_request *req);
 
-apr_status_t h2_headers_add_h1(apr_table_t *headers, apr_pool_t *pool, 
+apr_status_t h2_req_add_header(apr_table_t *headers, apr_pool_t *pool, 
                                const char *name, size_t nlen,
                                const char *value, size_t vlen);
 
index 528d21aed7d30c84813ddb07598f19c11278b381..72ac248c8d9f1f2bce08d27d4a1498e1f42b3ccc 100644 (file)
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.10.1"
+#define MOD_HTTP2_VERSION "1.10.2"
 
 /**
  * @macro
@@ -34,7 +34,7 @@
  * release. This is a 24 bit number with 8 bits for major number, 8 bits
  * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
  */
-#define MOD_HTTP2_VERSION_NUM 0x010a01
+#define MOD_HTTP2_VERSION_NUM 0x010a02
 
 
 #endif /* mod_h2_h2_version_h */
index 9c7afc64e6c0750882810e1f0a81c7fcfae3d10d..0bbb65223f7b1f0f1ec968db9981a64f74d8bdab 100644 (file)
@@ -143,6 +143,7 @@ static void cleanup_zombies(h2_workers *workers)
             slot->thread = NULL;
         }
         apr_atomic_dec32(&workers->worker_count);
+        slot->next = NULL;
         push_slot(&workers->free, slot);
     }
 }