[PATCH] HTTP/2: added support for setting custom push request headers
Alessandro Ghedini
alessandro at ghedini.me
Mon Feb 12 15:30:21 UTC 2018
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 <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;
>
> 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.
E.g.
daemon off;
worker_processes 1;
master_process off;
events {
worker_connections 1024;
}
http {
access_log /dev/stdout;
error_log logs/error.log debug;
server {
listen 4433 ssl http2;
server_name localhost;
ssl_certificate cert.crt;
ssl_certificate_key cert.key;
http2_max_concurrent_pushes 200;
http2_push_preload on;
gzip on;
gzip_types text/plain;
gzip_min_length 1;
location /hello {
http2_push /a;
root html;
}
location /a {
return 200 "a";
}
}
}
% nghttp https://127.0.0.1:4433/hello -vn
[ 0.001] Connected
[WARNING] Certificate verification failed: Hostname mismatch
The negotiated protocol: h2
[ 0.003] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[ 0.003] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
(dep_stream_id=0, weight=201, exclusive=0)
[ 0.003] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
(dep_stream_id=0, weight=101, exclusive=0)
[ 0.003] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
(dep_stream_id=0, weight=1, exclusive=0)
[ 0.003] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
(dep_stream_id=7, weight=1, exclusive=0)
[ 0.003] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
(dep_stream_id=3, weight=1, exclusive=0)
[ 0.003] send HEADERS frame <length=44, flags=0x25, stream_id=13>
; END_STREAM | END_HEADERS | PRIORITY
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
; Open new stream
:method: GET
:path: /hello
:scheme: https
:authority: 127.0.0.1:4433
accept: */*
accept-encoding: gzip, deflate
user-agent: nghttp2/1.29.0
[ 0.004] recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
(niv=3)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):128]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65536]
[SETTINGS_MAX_FRAME_SIZE(0x05):16777215]
[ 0.004] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
(window_size_increment=2147418112)
[ 0.004] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
[ 0.004] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
[ 0.004] recv (stream_id=13) :method: GET
[ 0.004] recv (stream_id=13) :path: /a
[ 0.004] recv (stream_id=13) :authority: 127.0.0.1:4433
[ 0.004] recv (stream_id=13) :scheme: https
[ 0.004] recv (stream_id=13) accept-encoding: gzip, deflate
[ 0.004] recv (stream_id=13) user-agent: nghttp2/1.29.0
[ 0.004] recv PUSH_PROMISE frame <length=47, flags=0x04, stream_id=13>
; END_HEADERS
(padlen=0, promised_stream_id=2)
[ 0.004] recv (stream_id=13) :status: 200
[ 0.004] recv (stream_id=13) server: nginx/1.13.9
[ 0.004] recv (stream_id=13) date: Mon, 12 Feb 2018 15:18:39 GMT
[ 0.004] recv (stream_id=13) content-type: text/plain
[ 0.004] recv (stream_id=13) last-modified: Thu, 08 Feb 2018 15:43:05 GMT
[ 0.004] recv (stream_id=13) etag: W/"5a7c7009-1f5"
[ 0.004] recv (stream_id=13) content-encoding: gzip
[ 0.004] recv HEADERS frame <length=105, flags=0x04, stream_id=13>
; END_HEADERS
(padlen=0)
; First response header
[ 0.004] recv DATA frame <length=32, flags=0x01, stream_id=13>
; END_STREAM
[ 0.004] recv (stream_id=2) :status: 200
[ 0.004] recv (stream_id=2) server: nginx/1.13.9
[ 0.004] recv (stream_id=2) date: Mon, 12 Feb 2018 15:18:39 GMT
[ 0.004] recv (stream_id=2) content-type: text/plain
[ 0.004] recv (stream_id=2) content-length: 1
[ 0.004] recv HEADERS frame <length=48, flags=0x04, stream_id=2>
; END_HEADERS
(padlen=0)
; First push response header
[ 0.004] recv DATA frame <length=1, flags=0x01, stream_id=2>
; END_STREAM
[ 0.004] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
(last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[])
Notice how the response of the pushed request doesn't actually get compressed.
Additionally from the access logs I see:
127.0.0.1 - - [12/Feb/2018:15:18:39 +0000] "GET /hello HTTP/2.0" 200 32 "-" "nghttp2/1.29.0"
127.0.0.1 - - [12/Feb/2018:15:18:39 +0000] "GET /a HTTP/2.0" 200 1 "-" "-"
The second one is the pushed request, notice how it doesn't have a user agent
unlike the first request (which is the one coming from the client directly).
With my patch (the one with the custom config directive, but this mostly applies
to the other one as well) however I get this:
% nghttp https://127.0.0.1:4433/hello -vn
[ 0.002] Connected
[WARNING] Certificate verification failed: Hostname mismatch
The negotiated protocol: h2
[ 0.004] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[ 0.004] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
(dep_stream_id=0, weight=201, exclusive=0)
[ 0.004] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
(dep_stream_id=0, weight=101, exclusive=0)
[ 0.004] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
(dep_stream_id=0, weight=1, exclusive=0)
[ 0.004] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
(dep_stream_id=7, weight=1, exclusive=0)
[ 0.004] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
(dep_stream_id=3, weight=1, exclusive=0)
[ 0.004] send HEADERS frame <length=44, flags=0x25, stream_id=13>
; END_STREAM | END_HEADERS | PRIORITY
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
; Open new stream
:method: GET
:path: /hello
:scheme: https
:authority: 127.0.0.1:4433
accept: */*
accept-encoding: gzip, deflate
user-agent: nghttp2/1.29.0
[ 0.004] recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
(niv=3)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):128]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65536]
[SETTINGS_MAX_FRAME_SIZE(0x05):16777215]
[ 0.004] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
(window_size_increment=2147418112)
[ 0.004] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
[ 0.005] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
[ 0.005] recv (stream_id=13) :method: GET
[ 0.005] recv (stream_id=13) :path: /a
[ 0.005] recv (stream_id=13) :authority: 127.0.0.1:4433
[ 0.005] recv (stream_id=13) :scheme: https
[ 0.005] recv (stream_id=13) accept-encoding: gzip, deflate
[ 0.005] recv (stream_id=13) user-agent: nghttp2/1.29.0
[ 0.005] recv PUSH_PROMISE frame <length=67, flags=0x04, stream_id=13>
; END_HEADERS
(padlen=0, promised_stream_id=2)
[ 0.005] recv (stream_id=13) :status: 200
[ 0.005] recv (stream_id=13) server: nginx/1.13.9
[ 0.005] recv (stream_id=13) date: Mon, 12 Feb 2018 15:27:14 GMT
[ 0.005] recv (stream_id=13) content-type: text/plain
[ 0.005] recv (stream_id=13) last-modified: Thu, 08 Feb 2018 15:43:05 GMT
[ 0.005] recv (stream_id=13) etag: W/"5a7c7009-1f5"
[ 0.005] recv (stream_id=13) content-encoding: gzip
[ 0.005] recv HEADERS frame <length=105, flags=0x04, stream_id=13>
; END_HEADERS
(padlen=0)
; First response header
[ 0.005] recv DATA frame <length=32, flags=0x01, stream_id=13>
; END_STREAM
[ 0.006] recv (stream_id=2) :status: 200
[ 0.006] recv (stream_id=2) server: nginx/1.13.9
[ 0.006] recv (stream_id=2) date: Mon, 12 Feb 2018 15:27:14 GMT
[ 0.006] recv (stream_id=2) content-type: text/plain
[ 0.006] recv (stream_id=2) content-encoding: gzip
[ 0.006] recv HEADERS frame <length=62, flags=0x04, stream_id=2>
; END_HEADERS
(padlen=0)
; First push response header
[ 0.006] recv DATA frame <length=21, flags=0x01, stream_id=2>
; END_STREAM
[ 0.006] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
(last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[])
The second response is compressed and the logs look like this:
127.0.0.1 - - [12/Feb/2018:15:27:14 +0000] "GET /hello HTTP/2.0" 200 32 "-" "nghttp2/1.29.0"
127.0.0.1 - - [12/Feb/2018:15:27:14 +0000] "GET /a HTTP/2.0" 200 21 "-" "nghttp2/1.29.0"
Cheers
More information about the nginx-devel
mailing list