On Mon, Feb 12, 2018 at 03:30:21PM +0000, Alessandro Ghedini wrote: > On Mon, Feb 12, 2018 at 05:11:55PM +0300, Ruslan Ermilov wrote: > > On Mon, Feb 12, 2018 at 12:35:13PM +0000, Alessandro Ghedini wrote: > > > # HG changeset patch > > > # User Alessandro Ghedini <alessan...@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; > > > > If I'm not mistaken, both the original patch and this one > > do not send the copied headers in the PUSH_PROMISE frame. > > > > Given the https://trac.nginx.org/nginx/ticket/1478, and some > > research of how different implementations behave, I prepared > > the following patch: > > Also, your patch doesn't seem to work completely. You don't to copy the > request > headers in the request's headers_in, which means they don't get processed by > NGINX, if the pushed resource is handled by NGINX itself.
If it's of any help, I merged your patch and mine into one, which copies the headers (excluding Accept) into PUSH_PROMISE and r->headers_in like my original patch did, as well as HPACK encode them into PUSH_PROMISE instead of writing them as literal strings, as your patch did. This fixes the problems I mentioned in my previous email. # HG changeset patch # User Alessandro Ghedini <alessan...@ghedini.me> # Date 1518522249 0 # Tue Feb 13 11:44:09 2018 +0000 # Branch push-headers # Node ID bfd3019a0f63cf00364d44bab74b7864808abbd7 # Parent 8b0553239592f5d0fd419e5116b9d343838685cf HTTP/2: push additional headers (ticket #1478). When pushing requests, copy and push Accept-Encoding, Accept-Language, and User-Agent header fields from the original request. Includes changes made by Ruslan Ermilov. diff -r 8b0553239592 -r bfd3019a0f63 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Fri Feb 09 23:20:08 2018 +0300 +++ b/src/http/v2/ngx_http_v2.c Tue Feb 13 11:44:09 2018 +0000 @@ -156,6 +156,8 @@ ngx_str_t *value); static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value); +static ngx_int_t ngx_http_v2_copy_header(ngx_http_request_t *r, + ngx_table_elt_t *hdr); static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r); static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r, ngx_http_v2_header_t *header); @@ -2516,7 +2518,8 @@ 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; @@ -2605,6 +2608,28 @@ goto error; } + rc = ngx_http_v2_copy_header(r, headers_in->user_agent); + + if (rc != NGX_OK) { + goto error; + } + +#if (NGX_HTTP_GZIP) + rc = ngx_http_v2_copy_header(r, headers_in->accept_encoding); + + if (rc != NGX_OK) { + goto error; + } +#endif + +#if (NGX_HTTP_HEADERS) + rc = ngx_http_v2_copy_header(r, headers_in->accept_language); + + if (rc != NGX_OK) { + goto error; + } +#endif + fc->write->handler = ngx_http_v2_run_request_handler; ngx_post_event(fc->write, &ngx_posted_events); @@ -3479,6 +3504,63 @@ static ngx_int_t +ngx_http_v2_copy_header(ngx_http_request_t *r, ngx_table_elt_t *hdr) +{ + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + if (hdr == NULL) { + return NGX_OK; + } + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = hdr->hash; + + h->key.len = hdr->key.len; + + h->key.data = ngx_pnalloc(r->stream->pool, h->key.len + 1); + if (h->key.data == NULL) { + h->hash = 0; + return NGX_ERROR; + } + + (void) ngx_cpystrn(h->key.data, hdr->key.data, h->key.len + 1); + + h->value.len = hdr->value.len; + + h->value.data = ngx_pnalloc(r->stream->pool, h->value.len + 1); + if (h->key.data == NULL) { + h->hash = 0; + return NGX_ERROR; + } + + (void) ngx_cpystrn(h->value.data, hdr->value.data, h->value.len + 1); + + h->lowcase_key = h->key.data; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh == NULL) { + return NGX_ERROR; + } + + if (hh->handler(r, h, hh->offset) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r) { u_char *p; diff -r 8b0553239592 -r bfd3019a0f63 src/http/v2/ngx_http_v2.h --- a/src/http/v2/ngx_http_v2.h Fri Feb 09 23:20:08 2018 +0300 +++ b/src/http/v2/ngx_http_v2.h Tue Feb 13 11:44:09 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 8b0553239592 -r bfd3019a0f63 src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c Fri Feb 09 23:20:08 2018 +0300 +++ b/src/http/v2/ngx_http_v2_filter_module.c Tue Feb 13 11:44:09 2018 +0000 @@ -50,20 +50,37 @@ #define NGX_HTTP_V2_STATUS_404_INDEX 13 #define NGX_HTTP_V2_STATUS_500_INDEX 14 +#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16 +#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17 #define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28 #define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31 #define NGX_HTTP_V2_DATE_INDEX 33 #define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44 #define NGX_HTTP_V2_LOCATION_INDEX 46 #define NGX_HTTP_V2_SERVER_INDEX 54 +#define NGX_HTTP_V2_USER_AGENT_INDEX 58 #define NGX_HTTP_V2_VARY_INDEX 59 #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 +typedef struct { + ngx_str_t authority; +#if (NGX_HTTP_GZIP) + ngx_str_t accept_encoding; +#endif +#if (NGX_HTTP_HEADERS) + ngx_str_t accept_language; +#endif + ngx_str_t user_agent; +} ngx_http_v2_push_ctx_t; + + static ngx_int_t ngx_http_v2_push_resources(ngx_http_request_t *r); static ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r, - ngx_str_t *path, ngx_str_t *authority); + ngx_str_t *path, ngx_http_v2_push_ctx_t *ctx); +static ngx_int_t ngx_http_v2_indexed_header_encode(ngx_http_request_t *r, + u_char index, ngx_str_t *value, ngx_str_t *header); static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, ngx_uint_t lower); @@ -685,16 +702,17 @@ { u_char *start, *end, *last; ngx_int_t rc; - ngx_str_t path, authority; + ngx_str_t path; ngx_uint_t i, push; ngx_table_elt_t **h; ngx_http_v2_loc_conf_t *h2lcf; + ngx_http_v2_push_ctx_t ctx; ngx_http_complex_value_t *pushes; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http2 push resources"); - ngx_str_null(&authority); + ngx_memzero(&ctx, sizeof(ngx_http_v2_push_ctx_t)); h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module); @@ -715,7 +733,7 @@ continue; } - rc = ngx_http_v2_push_resource(r, &path, &authority); + rc = ngx_http_v2_push_resource(r, &path, &ctx); if (rc == NGX_ERROR) { return NGX_ERROR; @@ -880,7 +898,7 @@ if (push && path.len && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/')) { - rc = ngx_http_v2_push_resource(r, &path, &authority); + rc = ngx_http_v2_push_resource(r, &path, &ctx); if (rc == NGX_ERROR) { return NGX_ERROR; @@ -905,11 +923,17 @@ static ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path, - ngx_str_t *authority) + ngx_http_v2_push_ctx_t *ctx) { u_char *start, *pos, *tmp; size_t len; - ngx_table_elt_t *host; + ngx_table_elt_t *host, *user_agent; +#if (NGX_HTTP_GZIP) + ngx_table_elt_t *accept_encoding; +#endif +#if (NGX_HTTP_HEADERS) + ngx_table_elt_t *accept_language; +#endif ngx_connection_t *fc; ngx_http_v2_stream_t *stream; ngx_http_v2_out_frame_t *frame; @@ -950,31 +974,69 @@ return NGX_ABORT; } - if (authority->len == 0) { - - len = 1 + NGX_HTTP_V2_INT_OCTETS + host->value.len; - - tmp = ngx_palloc(r->pool, len); - pos = ngx_pnalloc(r->pool, len); - - if (pos == NULL || tmp == NULL) { +#if (NGX_HTTP_GZIP) + accept_encoding = r->headers_in.accept_encoding; +#endif + +#if (NGX_HTTP_HEADERS) + accept_language = r->headers_in.accept_language; +#endif + + user_agent = r->headers_in.user_agent; + + if (ctx->authority.len == 0) { + + if (ngx_http_v2_indexed_header_encode(r, NGX_HTTP_V2_AUTHORITY_INDEX, + &host->value, &ctx->authority) + != NGX_OK) + { return NGX_ERROR; } - authority->data = pos; - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX); - pos = ngx_http_v2_write_value(pos, host->value.data, host->value.len, - tmp); - - authority->len = pos - authority->data; +#if (NGX_HTTP_GZIP) + if (accept_encoding + && ngx_http_v2_indexed_header_encode(r, + NGX_HTTP_V2_ACCEPT_ENCODING_INDEX, + &accept_encoding->value, &ctx->accept_encoding) + != NGX_OK) + { + return NGX_ERROR; + } +#endif + +#if (NGX_HTTP_HEADERS) + if (accept_language + && ngx_http_v2_indexed_header_encode(r, + NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX, + &accept_language->value, &ctx->accept_language) + != NGX_OK) + { + return NGX_ERROR; + } +#endif + + if (user_agent + && ngx_http_v2_indexed_header_encode(r, + NGX_HTTP_V2_USER_AGENT_INDEX, + &user_agent->value, &ctx->user_agent) + != NGX_OK) + { + return NGX_ERROR; + } } len = (h2c->table_update ? 1 : 0) + 1 + 1 + NGX_HTTP_V2_INT_OCTETS + path->len - + authority->len - + 1; + + ctx->authority.len + + 1 +#if (NGX_HTTP_GZIP) + + ctx->accept_encoding.len +#endif +#if (NGX_HTTP_HEADERS) + + ctx->accept_language.len +#endif + + ctx->user_agent.len; tmp = ngx_palloc(r->pool, len); pos = ngx_pnalloc(r->pool, len); @@ -1006,7 +1068,7 @@ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 push header: \":authority: %V\"", &host->value); - pos = ngx_cpymem(pos, authority->data, authority->len); + pos = ngx_cpymem(pos, ctx->authority.data, ctx->authority.len); #if (NGX_HTTP_SSL) if (fc->ssl) { @@ -1022,6 +1084,36 @@ *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX); } +#if (NGX_HTTP_GZIP) + if (accept_encoding) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 push header: \"accept-encoding: %V\"", + &accept_encoding->value); + + pos = ngx_cpymem(pos, ctx->accept_encoding.data, + ctx->accept_encoding.len); + } +#endif + +#if (NGX_HTTP_HEADERS) + if (accept_language) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 push header: \"accept-language: %V\"", + &accept_language->value); + + pos = ngx_cpymem(pos, ctx->accept_language.data, + ctx->accept_language.len); + } +#endif + + if (user_agent) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 push header: \"user-agent: %V\"", + &user_agent->value); + + pos = ngx_cpymem(pos, ctx->user_agent.data, ctx->user_agent.len); + } + frame = ngx_http_v2_create_push_frame(r, start, pos); if (frame == NULL) { return NGX_ERROR; @@ -1032,7 +1124,34 @@ stream->queued++; return ngx_http_v2_push_stream(h2c, stream->node->id, pos - start, - path, &host->value); + path, &host->value, &r->headers_in); +} + + +static ngx_int_t +ngx_http_v2_indexed_header_encode(ngx_http_request_t *r, u_char index, + ngx_str_t *value, ngx_str_t *header) +{ + size_t len; + u_char *pos, *tmp; + + len = 1 + NGX_HTTP_V2_INT_OCTETS + value->len; + + tmp = ngx_palloc(r->pool, len); + pos = ngx_pnalloc(r->pool, len); + + if (pos == NULL || tmp == NULL) { + return NGX_ERROR; + } + + header->data = pos; + + *pos++ = ngx_http_v2_inc_indexed(index); + pos = ngx_http_v2_write_value(pos, value->data, value->len, tmp); + + header->len = pos - header->data; + + return NGX_OK; } _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel