[PATCH] HTTP/2: added support for setting custom push request headers

Alessandro Ghedini alessandro at ghedini.me
Mon Feb 12 12:35:13 UTC 2018


# HG changeset patch
# User Alessandro Ghedini <alessandro at ghedini.me>
# Date 1518438578 0
#      Mon Feb 12 12:29:38 2018 +0000
# Branch http2-push-header
# Node ID 4eb0c9e8da0bc52065578e4ee78df1833617ac35
# Parent  a49af443656f2b65ca5de9d8cad5594f44e18ff7
HTTP/2: added support for setting custom push request headers.

This implementa the http2_push_header configuration directive that makes
it possible to set custom request headers for pushed requests.

Complex values are evaluated in the context of the original request,
so it's possible to copy its headers into pushed requests.

Example usage:

    http2_push_header User-Agent $http_user_agent;

diff -r a49af443656f -r 4eb0c9e8da0b src/http/v2/ngx_http_v2.c
--- a/src/http/v2/ngx_http_v2.c	Thu Feb 08 12:11:30 2018 +0300
+++ b/src/http/v2/ngx_http_v2.c	Mon Feb 12 12:29:38 2018 +0000
@@ -2516,15 +2516,22 @@
 
 ngx_int_t
 ngx_http_v2_push_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t depend,
-    size_t request_length, ngx_str_t *path, ngx_str_t *authority)
+    size_t request_length, ngx_str_t *path, ngx_str_t *authority,
+    ngx_http_headers_in_t *headers_in)
 {
     ngx_int_t              rc;
     ngx_str_t              value;
+    ngx_uint_t             i;
+    ngx_list_part_t       *part;
+    ngx_table_elt_t       *hdr, *h;
     ngx_connection_t      *fc;
+    ngx_http_header_t     *hh;
     ngx_http_request_t    *r;
     ngx_http_v2_node_t    *node;
     ngx_http_v2_stream_t  *stream;
 
+    ngx_http_core_main_conf_t  *cmcf;
+
     node = ngx_http_v2_get_node_by_id(h2c, h2c->last_push, 1);
 
     if (node == NULL) {
@@ -2605,6 +2612,57 @@
         goto error;
     }
 
+    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+    part = &headers_in->headers.part;
+    hdr = part->elts;
+
+    for (i = 0; /* void */; i++) {
+
+        if (i >= part->nelts) {
+            if (part->next == NULL) {
+                break;
+            }
+
+            part = part->next;
+            hdr = part->elts;
+            i = 0;
+        }
+
+        h = ngx_list_push(&r->headers_in.headers);
+        if (h == NULL) {
+            goto error;
+        }
+
+        h->key = hdr[i].key;
+
+        h->hash = hdr[i].hash;
+
+        h->value.len = hdr[i].value.len;
+
+        h->value.data = ngx_pnalloc(r->stream->pool, h->value.len + 1);
+        if (h->key.data == NULL) {
+            h->hash = 0;
+            goto error;
+        }
+
+        (void) ngx_cpystrn(h->value.data, hdr[i].value.data, h->value.len + 1);
+
+        h->lowcase_key = h->key.data;
+
+        hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
+                           h->lowcase_key, h->key.len);
+
+        if (hh == NULL) {
+            continue;
+        }
+
+        rc = hh->handler(r, h, hh->offset);
+        if (rc != NGX_OK) {
+            goto error;
+        }
+    }
+
     fc->write->handler = ngx_http_v2_run_request_handler;
     ngx_post_event(fc->write, &ngx_posted_events);
 
diff -r a49af443656f -r 4eb0c9e8da0b src/http/v2/ngx_http_v2.h
--- a/src/http/v2/ngx_http_v2.h	Thu Feb 08 12:11:30 2018 +0300
+++ b/src/http/v2/ngx_http_v2.h	Mon Feb 12 12:29:38 2018 +0000
@@ -285,7 +285,7 @@
 
 ngx_int_t ngx_http_v2_push_stream(ngx_http_v2_connection_t *h2c,
     ngx_uint_t depend, size_t request_length, ngx_str_t *path,
-    ngx_str_t *authority);
+    ngx_str_t *authority, ngx_http_headers_in_t *headers_in);
 
 void ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc);
 
diff -r a49af443656f -r 4eb0c9e8da0b src/http/v2/ngx_http_v2_filter_module.c
--- a/src/http/v2/ngx_http_v2_filter_module.c	Thu Feb 08 12:11:30 2018 +0300
+++ b/src/http/v2/ngx_http_v2_filter_module.c	Mon Feb 12 12:29:38 2018 +0000
@@ -907,13 +907,19 @@
 ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,
     ngx_str_t *authority)
 {
-    u_char                    *start, *pos, *tmp;
-    size_t                     len;
-    ngx_table_elt_t           *host;
-    ngx_connection_t          *fc;
-    ngx_http_v2_stream_t      *stream;
-    ngx_http_v2_out_frame_t   *frame;
-    ngx_http_v2_connection_t  *h2c;
+    u_char                         *start, *pos, *tmp;
+    size_t                          len;
+    ngx_str_t                       value;
+    ngx_uint_t                      i;
+    ngx_list_part_t                *part;
+    ngx_table_elt_t                *host, *h;
+    ngx_connection_t               *fc;
+    ngx_http_headers_in_t           headers_in;
+    ngx_http_v2_stream_t           *stream;
+    ngx_http_v2_loc_conf_t         *h2lcf;
+    ngx_http_v2_out_frame_t        *frame;
+    ngx_http_v2_connection_t       *h2c;
+    ngx_http_v2_push_header_val_t  *hv;
 
     fc = r->connection;
 
@@ -972,6 +978,43 @@
           + authority->len
           + 1;
 
+    h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
+
+    if ((h2lcf->push_headers != NULL) && (h2lcf->push_headers->nelts > 0)) {
+
+        if (ngx_list_init(&headers_in.headers, r->pool,
+                          h2lcf->push_headers->nelts,
+                          sizeof(ngx_table_elt_t))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
+
+        hv = h2lcf->push_headers->elts;
+        for (i = 0; i < h2lcf->push_headers->nelts; i++) {
+
+            if (ngx_http_complex_value(r, &hv[i].value, &value) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
+            if (value.len) {
+                h = ngx_list_push(&headers_in.headers);
+                if (h == NULL) {
+                    return NGX_ERROR;
+                }
+
+                h->key = hv[i].key;
+
+                h->hash = hv[i].hash;
+
+                h->value = value;
+
+                len += 1 + NGX_HTTP_V2_INT_OCTETS + h->key.len
+                         + NGX_HTTP_V2_INT_OCTETS + h->value.len;
+            }
+        }
+    }
+
     tmp = ngx_palloc(r->pool, len);
     pos = ngx_pnalloc(r->pool, len);
 
@@ -1018,6 +1061,26 @@
         *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
     }
 
+    part = &headers_in.headers.part;
+    h = part->elts;
+
+    for (i = 0; /* void */; i++) {
+
+        if (i >= part->nelts) {
+            if (part->next == NULL) {
+                break;
+            }
+
+            part = part->next;
+            h = part->elts;
+            i = 0;
+        }
+
+        *pos++ = 0;
+        pos = ngx_http_v2_write_name(pos, h[i].key.data, h[i].key.len, tmp);
+        pos = ngx_http_v2_write_value(pos, h[i].value.data, h[i].value.len, tmp);
+    }
+
     frame = ngx_http_v2_create_push_frame(r, start, pos);
     if (frame == NULL) {
         return NGX_ERROR;
@@ -1028,7 +1091,7 @@
     stream->queued++;
 
     return ngx_http_v2_push_stream(h2c, stream->node->id, pos - start,
-                                   path, &host->value);
+                                   path, &host->value, &headers_in);
 }
 
 
diff -r a49af443656f -r 4eb0c9e8da0b src/http/v2/ngx_http_v2_module.c
--- a/src/http/v2/ngx_http_v2_module.c	Thu Feb 08 12:11:30 2018 +0300
+++ b/src/http/v2/ngx_http_v2_module.c	Mon Feb 12 12:29:38 2018 +0000
@@ -28,6 +28,8 @@
     void *child);
 
 static char *ngx_http_v2_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_http_v2_push_header(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 
 static char *ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post,
     void *data);
@@ -152,6 +154,13 @@
       0,
       NULL },
 
+    { ngx_string("http2_push_header"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+      ngx_http_v2_push_header,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("spdy_recv_buffer_size"),
       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
       ngx_http_v2_spdy_deprecated,
@@ -416,6 +425,7 @@
      * set by ngx_pcalloc():
      *
      *     h2lcf->pushes = NULL;
+     *     h2lcf->push_headers = NULL;
      */
 
     h2lcf->chunk_size = NGX_CONF_UNSET_SIZE;
@@ -441,6 +451,10 @@
         conf->pushes = prev->pushes;
     }
 
+    if (conf->push && conf->push_headers == NULL) {
+        conf->push_headers = prev->push_headers;
+    }
+
     ngx_conf_merge_value(conf->push_preload, prev->push_preload, 0);
 
     return NGX_CONF_OK;
@@ -506,6 +520,55 @@
 
 
 static char *
+ngx_http_v2_push_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_v2_loc_conf_t *h2lcf = conf;
+
+    ngx_str_t                         *value;
+    ngx_http_v2_push_header_val_t     *hv;
+    ngx_http_compile_complex_value_t   ccv;
+
+    value = cf->args->elts;
+
+    if (h2lcf->push_headers == NULL) {
+        h2lcf->push_headers = ngx_array_create(cf->pool, 1,
+                                         sizeof(ngx_http_v2_push_header_val_t));
+        if (h2lcf->push_headers == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    hv = ngx_array_push(h2lcf->push_headers);
+    if (hv == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    hv->key = value[1];
+
+    ngx_strlow(hv->key.data, hv->key.data, hv->key.len);
+
+    hv->hash = ngx_hash_key(hv->key.data, hv->key.len);
+
+    if (value[2].len == 0) {
+        ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));
+
+    } else {
+        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &value[2];
+        ccv.complex_value = &hv->value;
+
+        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
 ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)
 {
     size_t *sp = data;
diff -r a49af443656f -r 4eb0c9e8da0b src/http/v2/ngx_http_v2_module.h
--- a/src/http/v2/ngx_http_v2_module.h	Thu Feb 08 12:11:30 2018 +0300
+++ b/src/http/v2/ngx_http_v2_module.h	Mon Feb 12 12:29:38 2018 +0000
@@ -41,9 +41,17 @@
 
     ngx_flag_t                      push;
     ngx_array_t                    *pushes;
+    ngx_array_t                    *push_headers;
 } ngx_http_v2_loc_conf_t;
 
 
+typedef struct {
+    ngx_http_complex_value_t   value;
+    ngx_str_t                  key;
+    ngx_uint_t                 hash;
+} ngx_http_v2_push_header_val_t;
+
+
 extern ngx_module_t  ngx_http_v2_module;
 
 


More information about the nginx-devel mailing list