]> git.kaiwu.me - nginx.git/commitdiff
Headers filter: inheritance control for add_header and add_trailer.
authorRoman Arutyunyan <arut@nginx.com>
Mon, 14 Jul 2025 17:44:05 +0000 (21:44 +0400)
committerRoman Arutyunyan <arutyunyan.roman@gmail.com>
Sat, 25 Oct 2025 15:46:20 +0000 (19:46 +0400)
The new directives add_header_inherit and add_trailer_inherit allow
to alter inheritance rules for the values specified in the add_header
and add_trailer directives in a convenient way.

The "merge" parameter enables appending the values from the previous level
to the current level values.

The "off" parameter cancels inheritance of the values from the previous
configuration level, similar to add_header "" (2194e75bb).

The "on" parameter (default) enables the standard inheritance behaviour,
which is to inherit values from the previous level only if there are no
directives on the current level.

The inheritance rules themselves are inherited in a standard way.  Thus,
for example, "add_header_inherit merge;" specified at the top level will
be inherited in all nested levels recursively unless redefined below.

src/http/modules/ngx_http_headers_filter_module.c

index 90e6da8b38edcf99c6051e898751a14c54f81328..93177c748b92076e9534379d28bc41126b779d75 100644 (file)
 #include <ngx_http.h>
 
 
+#define NGX_HTTP_HEADERS_INHERIT_OFF    0
+#define NGX_HTTP_HEADERS_INHERIT_ON     1
+#define NGX_HTTP_HEADERS_INHERIT_MERGE  2
+
+
 typedef struct ngx_http_header_val_s  ngx_http_header_val_t;
 
 typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
@@ -49,6 +54,8 @@ typedef struct {
     ngx_http_complex_value_t  *expires_value;
     ngx_array_t               *headers;
     ngx_array_t               *trailers;
+    ngx_uint_t                 headers_inherit;
+    ngx_uint_t                 trailers_inherit;
 } ngx_http_headers_conf_t;
 
 
@@ -97,6 +104,14 @@ static ngx_http_set_header_t  ngx_http_set_headers[] = {
 };
 
 
+static ngx_conf_enum_t  ngx_http_headers_inherit[] = {
+    { ngx_string("off"),   NGX_HTTP_HEADERS_INHERIT_OFF },
+    { ngx_string("on"),    NGX_HTTP_HEADERS_INHERIT_ON },
+    { ngx_string("merge"), NGX_HTTP_HEADERS_INHERIT_MERGE },
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_command_t  ngx_http_headers_filter_commands[] = {
 
     { ngx_string("expires"),
@@ -123,6 +138,22 @@ static ngx_command_t  ngx_http_headers_filter_commands[] = {
       offsetof(ngx_http_headers_conf_t, trailers),
       NULL },
 
+    { ngx_string("add_header_inherit"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+                        |NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_headers_conf_t, headers_inherit),
+      &ngx_http_headers_inherit },
+
+    { ngx_string("add_trailer_inherit"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+                        |NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_headers_conf_t, trailers_inherit),
+      &ngx_http_headers_inherit },
+
       ngx_null_command
 };
 
@@ -657,6 +688,8 @@ ngx_http_headers_create_conf(ngx_conf_t *cf)
      */
 
     conf->expires = NGX_HTTP_EXPIRES_UNSET;
+    conf->headers_inherit = NGX_CONF_UNSET;
+    conf->trailers_inherit = NGX_CONF_UNSET;
 
     return conf;
 }
@@ -668,6 +701,8 @@ ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
     ngx_http_headers_conf_t *prev = parent;
     ngx_http_headers_conf_t *conf = child;
 
+    ngx_http_header_val_t  *hv;
+
     if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
         conf->expires = prev->expires;
         conf->expires_time = prev->expires_time;
@@ -678,12 +713,43 @@ ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
         }
     }
 
-    if (conf->headers == NULL) {
-        conf->headers = prev->headers;
+    ngx_conf_merge_uint_value(conf->headers_inherit, prev->headers_inherit,
+                              NGX_HTTP_HEADERS_INHERIT_ON);
+    ngx_conf_merge_uint_value(conf->trailers_inherit, prev->trailers_inherit,
+                              NGX_HTTP_HEADERS_INHERIT_ON);
+
+    if (conf->headers_inherit != NGX_HTTP_HEADERS_INHERIT_OFF
+        && prev->headers)
+    {
+        if (conf->headers == NULL) {
+            conf->headers = prev->headers;
+
+        } else if (conf->headers_inherit == NGX_HTTP_HEADERS_INHERIT_MERGE) {
+            hv = ngx_array_push_n(conf->headers, prev->headers->nelts);
+            if (hv == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            ngx_memcpy(hv, prev->headers->elts,
+                       sizeof(ngx_http_header_val_t) * prev->headers->nelts);
+        }
     }
 
-    if (conf->trailers == NULL) {
-        conf->trailers = prev->trailers;
+    if (conf->trailers_inherit != NGX_HTTP_HEADERS_INHERIT_OFF
+        && prev->trailers)
+    {
+        if (conf->trailers == NULL) {
+            conf->trailers = prev->trailers;
+
+        } else if (conf->trailers_inherit == NGX_HTTP_HEADERS_INHERIT_MERGE) {
+            hv = ngx_array_push_n(conf->trailers, prev->trailers->nelts);
+            if (hv == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            ngx_memcpy(hv, prev->trailers->elts,
+                       sizeof(ngx_http_header_val_t) * prev->trailers->nelts);
+        }
     }
 
     return NGX_CONF_OK;