Send original headers to the upstream
Maxim Dounin
mdounin at mdounin.ru
Wed Jun 26 01:42:01 UTC 2024
Hello!
On Tue, Jun 25, 2024 at 11:22:14PM +0100, Kirill A. Korinsky wrote:
> Greetings,
>
> Here is a patch that adds a string to proxy_pass_request_headers as a
> possible value.
>
> Such a string is used as a prefix for headers which is overwritten by Nginx
> when it proxies the request to upstream.
>
> As a nice side effect, this feature preserves the original order of HTTP
> headers, which might be needed sometimes.
>
> It is only implemented on the proxy module because there is no other way to
> deliver the original request without touching it.
Could you please provide some more details about specific use
cases for such a feature?
As far as I see, using appropriate
proxy_set_header X-Original-Foo $http_foo;
directives should be mostly equivalent, except it won't preserve
the headers order and will merge duplicate headers. But these
aren't really guaranteed by HTTP anyway, and merging/reorder can
happen on any intermediate hosts.
>
> diff --git src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_proxy_module.c
> index 536482ec5..6a722042b 100644
> --- src/http/modules/ngx_http_proxy_module.c
> +++ src/http/modules/ngx_http_proxy_module.c
> @@ -117,6 +117,8 @@ typedef struct {
> ngx_uint_t headers_hash_max_size;
> ngx_uint_t headers_hash_bucket_size;
>
> + ngx_str_t request_headers_prefix;
> +
> #if (NGX_HTTP_SSL)
> ngx_uint_t ssl;
> ngx_uint_t ssl_protocols;
> @@ -215,6 +217,8 @@ static char *ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd,
> void *conf);
> static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd,
> void *conf);
> +static char *ngx_http_proxy_pass_request_headers(ngx_conf_t *cf, ngx_command_t *cmd,
> + void *conf);
> #if (NGX_HTTP_CACHE)
> static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd,
> void *conf);
> @@ -445,9 +449,9 @@ static ngx_command_t ngx_http_proxy_commands[] = {
>
> { ngx_string("proxy_pass_request_headers"),
> NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
> - ngx_conf_set_flag_slot,
> + ngx_http_proxy_pass_request_headers,
> NGX_HTTP_LOC_CONF_OFFSET,
> - offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_headers),
> + 0,
> NULL },
>
> { ngx_string("proxy_pass_request_body"),
> @@ -1389,7 +1393,10 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
> if (ngx_hash_find(&headers->hash, header[i].hash,
> header[i].lowcase_key, header[i].key.len))
> {
> - continue;
> + if (plcf->request_headers_prefix.len == 0) {
> + continue;
> + }
> + len += plcf->request_headers_prefix.len;
> }
>
> len += header[i].key.len + sizeof(": ") - 1
> @@ -1525,7 +1532,12 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
> if (ngx_hash_find(&headers->hash, header[i].hash,
> header[i].lowcase_key, header[i].key.len))
> {
> - continue;
> + if (plcf->request_headers_prefix.len == 0) {
> + continue;
> + }
> + b->last = ngx_copy(b->last,
> + plcf->request_headers_prefix.data,
> + plcf->request_headers_prefix.len);
> }
>
> b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
Note that as implemented, you won't be able to distinguish
original requests headers from the ones with prefix added. E.g.,
assuming prefix "X-Original-", and original request headers:
Host: foo
X-Original-Host: bar
you'll get
X-Original-Host: foo
X-Original-Host: bar
in the upstream request, and you won't be able to tell which one
is real.
Following the exiting proxy_set_header behaviour, I would rather
suggests that such a feature, if implemented, should drop all the
headers with the configured prefix.
> @@ -3349,6 +3361,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
> * conf->body_values = NULL;
> * conf->body_source = { 0, NULL };
> * conf->redirects = NULL;
> + * conf->request_headers_prefix = { NULL, 0 };
> * conf->ssl = 0;
> * conf->ssl_protocols = 0;
> * conf->ssl_ciphers = { 0, NULL };
> @@ -3727,6 +3740,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
> ngx_conf_merge_value(conf->upstream.intercept_errors,
> prev->upstream.intercept_errors, 0);
>
> + ngx_conf_merge_str_value(conf->request_headers_prefix,
> + prev->request_headers_prefix, "");
> +
> #if (NGX_HTTP_SSL)
>
> if (ngx_http_proxy_merge_ssl(cf, conf, prev) != NGX_OK) {
> @@ -4772,6 +4788,36 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
> }
>
>
> +static char *
> +ngx_http_proxy_pass_request_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
> +{
> + ngx_http_proxy_loc_conf_t *plcf = conf;
> +
> + ngx_str_t *value;
Style: wrong indentation of variable name, should be
ngx_str_t *value;
> +
> + if (plcf->upstream.pass_request_headers != NGX_CONF_UNSET) {
> + return "is duplicate";
> + }
> +
> + value = cf->args->elts;
> +
> + if (ngx_strcmp(value[1].data, "off") == 0) {
> + plcf->upstream.pass_request_headers = 0;
> + return NGX_CONF_OK;
> + }
> +
> + plcf->upstream.store = 1;
Seems to be a cut-n-paste leftover.
> +
> + if (ngx_strcmp(value[1].data, "on") == 0) {
> + return NGX_CONF_OK;
> + }
> +
> + plcf->request_headers_prefix = value[1];
Not sure if reusing "proxy_pass_request_headers" for such a
feature is a good idea. From the behaviour of
"proxy_pass_request_headers off;", I would rather assume that
"proxy_pass_request_headers X-Original-;" will pass all the
headers with the specified prefix, and not just the headers which
were modified.
> +
> + return NGX_CONF_OK;
> +}
> +
> +
> #if (NGX_HTTP_CACHE)
>
> static char *
>
> --
> wbr, Kirill
--
Maxim Dounin
http://mdounin.ru/
More information about the nginx
mailing list