]> granicus.if.org Git - apache/commitdiff
incoming trailers passed into chunked request bodies, outgoing trailers not supported yet
authorStefan Eissing <icing@apache.org>
Fri, 20 Nov 2015 13:58:32 +0000 (13:58 +0000)
committerStefan Eissing <icing@apache.org>
Fri, 20 Nov 2015 13:58:32 +0000 (13:58 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1715363 13f79535-47bb-0310-9956-ffa450edef68

modules/http2/h2_request.c
modules/http2/h2_response.h
modules/http2/h2_session.c
modules/http2/h2_stream.c
modules/http2/h2_stream.h
modules/http2/h2_util.c
modules/http2/h2_util.h

index 2a697a0eda8461e72b8b654091b82613d0270e10..a5f7d9d4fb4567bf189fcb4e77e3bb60017bc7f0 100644 (file)
@@ -69,14 +69,7 @@ static apr_status_t add_h1_header(h2_request *req, apr_pool_t *pool,
 {
     char *hname, *hvalue;
     
-    if (H2_HD_MATCH_LIT("expect", name, nlen)
-        || H2_HD_MATCH_LIT("upgrade", name, nlen)
-        || H2_HD_MATCH_LIT("connection", name, nlen)
-        || H2_HD_MATCH_LIT("proxy-connection", name, nlen)
-        || H2_HD_MATCH_LIT("transfer-encoding", name, nlen)
-        || H2_HD_MATCH_LIT("keep-alive", name, nlen)
-        || H2_HD_MATCH_LIT("http2-settings", name, nlen)) {
-        /* ignore these. */
+    if (h2_req_ignore_header(name, nlen)) {
         return APR_SUCCESS;
     }
     else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
@@ -115,7 +108,10 @@ typedef struct {
 static int set_h1_header(void *ctx, const char *key, const char *value)
 {
     h1_ctx *x = ctx;
-    add_h1_header(x->req, x->pool, key, strlen(key), value, strlen(value));
+    size_t klen = strlen(key);
+    if (!h2_req_ignore_header(key, klen)) {
+        add_h1_header(x->req, x->pool, key, klen, value, strlen(value));
+    }
     return 1;
 }
 
@@ -222,23 +218,11 @@ apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos)
         return APR_EINVAL;
     }
 
-    /* be safe, some header we do not accept on h2(c) */
-    apr_table_unset(req->headers, "expect");
-    apr_table_unset(req->headers, "upgrade");
-    apr_table_unset(req->headers, "connection");
-    apr_table_unset(req->headers, "proxy-connection");
-    apr_table_unset(req->headers, "transfer-encoding");
-    apr_table_unset(req->headers, "keep-alive");
-    apr_table_unset(req->headers, "http2-settings");
-
-    if (!apr_table_get(req->headers, "Host")) {
-        /* Need to add a "Host" header if not already there to
-         * make virtual hosts work correctly. */
-        if (!req->authority) {
-            return APR_BADARG;
-        }
-        apr_table_set(req->headers, "Host", req->authority);
+    /* Always set the "Host" header from :authority, see rfc7540, ch. 8.1.2.3 */
+    if (!req->authority) {
+        return APR_BADARG;
     }
+    apr_table_setn(req->headers, "Host", req->authority);
 
     s = apr_table_get(req->headers, "Content-Length");
     if (s) {
@@ -290,15 +274,7 @@ static apr_status_t add_h1_trailer(h2_request *req, apr_pool_t *pool,
 {
     char *hname, *hvalue;
     
-    if (H2_HD_MATCH_LIT("expect", name, nlen)
-        || H2_HD_MATCH_LIT("upgrade", name, nlen)
-        || H2_HD_MATCH_LIT("connection", name, nlen)
-        || H2_HD_MATCH_LIT("host", name, nlen)
-        || H2_HD_MATCH_LIT("proxy-connection", name, nlen)
-        || H2_HD_MATCH_LIT("transfer-encoding", name, nlen)
-        || H2_HD_MATCH_LIT("keep-alive", name, nlen)
-        || H2_HD_MATCH_LIT("http2-settings", name, nlen)) {
-        /* ignore these. */
+    if (h2_req_ignore_trailer(name, nlen)) {
         return APR_SUCCESS;
     }
     
index 59f7b035aa5f84d782ccfa05b3b9122170b97c76..4085a41bdfa59acb63a560ac98db2ade0afea3a6 100644 (file)
@@ -24,6 +24,7 @@ typedef struct h2_response {
     int http_status;
     apr_off_t content_length;
     apr_table_t *header;
+    apr_table_t *trailer;
 } h2_response;
 
 h2_response *h2_response_create(int stream_id,
index 46898c43160211cbe4b99bde2cbb00af494a71fe..d70eefd2965c3e339ac2d87ec869013692dc1074 100644 (file)
@@ -1076,6 +1076,18 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s,
     }
     
     if (eos) {
+        apr_table_t *trailers = h2_stream_get_trailers(stream);
+        if (trailers && !apr_is_empty_table(trailers)) {
+            h2_ngheader *nh;
+            int rv;
+            
+            nh = h2_util_ngheader_make(stream->pool, trailers);
+            rv = nghttp2_submit_trailer(ng2s, stream->id, nh->nv, nh->nvlen);
+            if (rv < 0) {
+                nread = rv;
+            }
+        }
+        
         *data_flags |= NGHTTP2_DATA_FLAG_EOF;
     }
     
index 594175a78541e459bc8639f56eb1a344b33433cb..ad7f5df102dad3cce3c85059d884242cd120a1bf 100644 (file)
@@ -304,17 +304,22 @@ apr_status_t h2_stream_schedule(h2_stream *stream, int eos,
         status = h2_mplx_process(stream->session->mplx, stream->id, 
                                  stream->request, eos, cmp, ctx);
         stream->scheduled = 1;
+        
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c,
+                      "h2_stream(%ld-%d): scheduled %s %s://%s%s",
+                      stream->session->id, stream->id,
+                      stream->request->method, stream->request->scheme,
+                      stream->request->authority, stream->request->path);
     }
     else {
         h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR);
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c,
+                      "h2_stream(%ld-%d): RST=2 (internal err) %s %s://%s%s",
+                      stream->session->id, stream->id,
+                      stream->request->method, stream->request->scheme,
+                      stream->request->authority, stream->request->path);
     }
     
-    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, stream->session->c,
-                  "h2_stream(%ld-%d): scheduled %s %s://%s%s",
-                  stream->session->id, stream->id,
-                  stream->request->method, stream->request->scheme,
-                  stream->request->authority, stream->request->path);
-    
     return status;
 }
 
@@ -365,6 +370,22 @@ static apr_status_t input_add_data(h2_stream *stream,
     return status;
 }
 
+static int input_add_header(void *str, const char *key, const char *value)
+{
+    h2_stream *stream = str;
+    apr_status_t status = input_add_data(stream, key, strlen(key), 0);
+    if (status == APR_SUCCESS) {
+        status = input_add_data(stream, ": ", 2, 0);
+        if (status == APR_SUCCESS) {
+            status = input_add_data(stream, value, strlen(value), 0);
+            if (status == APR_SUCCESS) {
+                status = input_add_data(stream, "\r\n", 2, 0);
+            }
+        }
+    }
+    return (status == APR_SUCCESS);
+}
+
 apr_status_t h2_stream_close_input(h2_stream *stream)
 {
     apr_status_t status = APR_SUCCESS;
@@ -381,7 +402,15 @@ apr_status_t h2_stream_close_input(h2_stream *stream)
     H2_STREAM_IN(APLOG_TRACE2, stream, "close_pre");
     if (close_input(stream) && stream->bbin) {
         if (stream->request->chunked) {
-            status = input_add_data(stream, "0\r\n\r\n", 5, 0);
+            apr_table_t *trailers = stream->request->trailers;
+            if (trailers && !apr_is_empty_table(trailers)) {
+                status = input_add_data(stream, "0\r\n", 3, 0);
+                apr_table_do(input_add_header, stream, trailers, NULL);
+                status = input_add_data(stream, "\r\n", 2, 0);
+            }
+            else {
+                status = input_add_data(stream, "0\r\n\r\n", 5, 0);
+            }
         }
         
         if (status == APR_SUCCESS) {
@@ -610,3 +639,9 @@ apr_status_t h2_stream_submit_pushes(h2_stream *stream)
     }
     return status;
 }
+
+apr_table_t *h2_stream_get_trailers(h2_stream *stream)
+{
+    /* TODO */
+    return NULL;
+}
index e5990a2bf38c2ddd1a392819c5811f1a92eb2e94..79801722f4a315a689bf8be189d4c5a062d174cc 100644 (file)
@@ -290,4 +290,14 @@ int h2_stream_needs_submit(h2_stream *stream);
  */
 apr_status_t h2_stream_submit_pushes(h2_stream *stream);
 
+/**
+ * Get optional trailers for this stream, may be NULL. Meaningful
+ * results can only be expected when the end of the response body has
+ * been reached.
+ *
+ * @param stream to ask for trailers
+ * @return trailers for NULL
+ */
+apr_table_t *h2_stream_get_trailers(h2_stream *stream);
+
 #endif /* defined(__mod_h2__h2_stream__) */
index e80a0268804501f0211effb309517d192cf567a5..76ecc27642093aa75b34e3ecc0b2ec94c71ac344 100644 (file)
@@ -834,6 +834,21 @@ static int add_table_header(void *ctx, const char *key, const char *value)
 }
 
 
+h2_ngheader *h2_util_ngheader_make(apr_pool_t *p, apr_table_t *header)
+{
+    h2_ngheader *ngh;
+    size_t n;
+    
+    n = 0;
+    apr_table_do(count_header, &n, header, NULL);
+    
+    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);
+
+    return ngh;
+}
+
 h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p, 
                                        int http_status, 
                                        apr_table_t *header)
@@ -879,3 +894,94 @@ h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p,
     return ngh;
 }
 
+/*******************************************************************************
+ * header HTTP/1 <-> HTTP/2 conversions
+ ******************************************************************************/
+
+typedef struct {
+    const char *name;
+    size_t len;
+} literal;
+
+#define H2_DEF_LITERAL(n)   { (n), (sizeof(n)-1) }
+#define H2_ALEN(a)          (sizeof(a)/sizeof((a)[0]))
+#define H2_LIT_ARGS(a)      (a),H2_ALEN(a)
+
+static literal IgnoredRequestHeaders[] = {
+    H2_DEF_LITERAL("host"),
+    H2_DEF_LITERAL("expect"),
+    H2_DEF_LITERAL("upgrade"),
+    H2_DEF_LITERAL("connection"),
+    H2_DEF_LITERAL("keep-alive"),
+    H2_DEF_LITERAL("http2-settings"),
+    H2_DEF_LITERAL("proxy-connection"),
+    H2_DEF_LITERAL("transfer-encoding"),
+};
+static literal IgnoredRequestTrailers[] = { /* Ignore, see rfc7230, ch. 4.1.2 */
+    H2_DEF_LITERAL("te"),
+    H2_DEF_LITERAL("host"),
+    H2_DEF_LITERAL("range"),
+    H2_DEF_LITERAL("cookie"),
+    H2_DEF_LITERAL("expect"),
+    H2_DEF_LITERAL("pragma"),
+    H2_DEF_LITERAL("max-forwards"),
+    H2_DEF_LITERAL("cache-control"),
+    H2_DEF_LITERAL("authorization"),
+    H2_DEF_LITERAL("content-length"),       
+    H2_DEF_LITERAL("proxy-authorization"),
+};    
+static literal IgnoredResponseTrailers[] = {
+    H2_DEF_LITERAL("age"),
+    H2_DEF_LITERAL("date"),
+    H2_DEF_LITERAL("vary"),
+    H2_DEF_LITERAL("cookie"),
+    H2_DEF_LITERAL("expires"),
+    H2_DEF_LITERAL("warning"),
+    H2_DEF_LITERAL("location"),
+    H2_DEF_LITERAL("retry-after"),
+    H2_DEF_LITERAL("cache-control"),
+    H2_DEF_LITERAL("www-authenticate"),
+    H2_DEF_LITERAL("proxy-authenticate"),
+};
+
+static int ignore_header(const literal *lits, size_t llen,
+                         const char *name, size_t nlen)
+{
+    const literal *lit;
+    int i;
+    
+    for (i = 0; i < llen; ++i) {
+        lit = &lits[i];
+        if (lit->len == nlen && !apr_strnatcasecmp(lit->name, name)) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int h2_req_ignore_header(const char *name, size_t len)
+{
+    return ignore_header(H2_LIT_ARGS(IgnoredRequestHeaders), name, len);
+}
+
+int h2_req_ignore_trailer(const char *name, size_t len)
+{
+    return (h2_req_ignore_header(name, len) 
+            || ignore_header(H2_LIT_ARGS(IgnoredRequestTrailers), name, len));
+}
+
+int h2_res_ignore_trailer(const char *name, size_t len)
+{
+    return ignore_header(H2_LIT_ARGS(IgnoredResponseTrailers), name, len);
+}
+
+void h2_req_strip_ignored_header(apr_table_t *headers)
+{
+    int i;
+    for (i = 0; i < H2_ALEN(IgnoredRequestHeaders); ++i) {
+        apr_table_unset(headers, IgnoredRequestHeaders[i].name);
+    }
+}
+
+
index 51efb8cf2d8bef14cfd9b747a5910d5b6fa4dc61..8f8be2993e0ed5b383665474c47cfbeb66388595 100644 (file)
@@ -30,6 +30,11 @@ char *h2_strlwr(char *s);
 
 void h2_util_camel_case_header(char *s, size_t len);
 
+int h2_req_ignore_header(const char *name, size_t len);
+int h2_req_ignore_trailer(const char *name, size_t len);
+void h2_req_strip_ignored_header(apr_table_t *headers);
+int h2_res_ignore_trailer(const char *name, size_t len);
+
 /**
  * Return != 0 iff the string s contains the token, as specified in
  * HTTP header syntax, rfc7230.
@@ -75,6 +80,7 @@ typedef struct h2_ngheader {
     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);