Opened 4 months ago

Closed 4 months ago

#1957 closed defect (invalid)

http2 window_size

Reported by: lucacorti@… Owned by:
Priority: minor Milestone:
Component: nginx-module Version: 1.17.x
Keywords: Cc:
uname -a: FreeBSD host 12.1-RELEASE-p3 FreeBSD 12.1-RELEASE-p3 GENERIC amd64
nginx -V: nginx version: nginx/1.17.9
built with OpenSSL 1.1.1d-freebsd 10 Sep 2019
TLS SNI support enabled
configure arguments: --prefix=/usr/local/etc/nginx --with-cc-opt='-I /usr/local/include' --with-ld-opt='-L /usr/local/lib' --conf-path=/usr/local/etc/nginx/nginx.conf --sbin-path=/usr/local/sbin/nginx --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --user=www --group=www --modules-path=/usr/local/libexec/nginx --with-file-aio --http-client-body-temp-path=/var/tmp/nginx/client_body_temp --http-fastcgi-temp-path=/var/tmp/nginx/fastcgi_temp --http-proxy-temp-path=/var/tmp/nginx/proxy_temp --http-scgi-temp-path=/var/tmp/nginx/scgi_temp --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi_temp --http-log-path=/var/log/nginx/access.log --with-http_v2_module --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-pcre --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --without-mail_imap_module --without-mail_pop3_module --without-mail_smtp_module --with-mail_ssl_module --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-threads --with-mail=dynamic --with-stream=dynamic

Description

This is tested on both NGINX 1.17 and 1.16 with the same results.

Basically, I'm doing a simple GET request with my HTTP/2 implementation.

Upon connection I'm receiving a SETTINGS frame for an INITIAL_WINDOW_SIZE of 65536, and adjusting the window size accordingly from the default INITIAL_WINDOW_SIZE (65535) by adding the difference between the new and the old value 65535 + (65536 - 65535) = 1, so the new connection window_size is 65536.

Then I'm receiving a WINDOW_UPDATE frame with an INCREMENT of 2147418112. This would increment the connection WINDOW_SIZE to 65536 + 2147418112 = 2147483648 which exceeds by 1 the maximum HTTP/2 allowed window size. So I'm sending a GO_AWAY frame with error FLOW_CONTROL_ERROR.

Client behaviour looks correct to me, so it seems to be an off-by-one in the server HTTP/2 flow control window management.

15:17:57.945 [debug] SENT %Ankh.HTTP2.Frame.Settings{flags: %Ankh.HTTP2.Frame.Settings.Flags{ack: false}, length: 36, payload: %Ankh.HTTP2.Frame.Settings.Payload{settings: [header_table_size: 4096, enable_push: true, max_concurrent_streams: 128, initial_window_size: 65535, max_frame_size: 16384, max_header_list_size: 128]}, stream_id: 0, type: 4} [<<0, 0, 36, 4, 0, 0, 0, 0, 0>>, <<0, 1, 0, 0, 16, 0>>, <<0, 2, 0, 0, 0, 1>>, <<0, 3, 0, 0, 0, 128>>, <<0, 4, 0, 0, 255, 255>>, <<0, 5, 0, 0, 64, 0>>, <<0, 6, 0, 0, 0, 128>>]

15:18:00.055 [debug] STREAM 1 :idle -> :open hbf: nil
 
15:18:00.055 [debug] SENT %Ankh.HTTP2.Frame.Headers{flags: %Ankh.HTTP2.Frame.Headers.Flags{end_headers: true, end_stream: false, padded: false, priority: false}, length: 17, payload: %Ankh.HTTP2.Frame.Headers.Payload{exclusive: false, hbf: <<132, 135, 65, 140, 241, 227, 194, 242, 142, 164, 140, 131, 66, 87, 50, 127, 130>>, pad_length: 0, stream_dependency: 0, weight: 0}, stream_id: 1, type: 1} [<<0, 0, 17, 1, 4, 0, 0, 0, 1>>, <<132, 135, 65, 140, 241, 227, 194, 242, 142, 164, 140, 131, 66, 87, 50, 127, 130>>]
 
15:18:00.056 [debug] RECVD %Ankh.HTTP2.Frame.Settings{flags: %Ankh.HTTP2.Frame.Settings.Flags{ack: true}, length: 0, payload: nil, stream_id: 0, type: 4} <<0, 0, 0, 4, 1, 0, 0, 0, 0>>
 
15:18:00.056 [debug] RECVD %Ankh.HTTP2.Frame.Settings{flags: %Ankh.HTTP2.Frame.Settings.Flags{ack: false}, length: 18, payload: %Ankh.HTTP2.Frame.Settings.Payload{settings: [max_frame_size: 16777215, initial_window_size: 65536, max_concurrent_streams: 128]}, stream_id: 0, type: 4} <<0, 0, 18, 4, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 128, 0, 4, 0, 1, 0, 0, 0, 5, 0, 255, 255, 255>>
 
15:18:00.056 [debug] SENT %Ankh.HTTP2.Frame.Settings{flags: %Ankh.HTTP2.Frame.Settings.Flags{ack: true}, length: 0, payload: nil, stream_id: 0, type: 4} [<<0, 0, 0, 4, 1, 0, 0, 0, 0>>]
 
15:18:00.056 [debug] window_size: 65535 + (65536 - 65535) = 65536
 
15:18:00.056 [debug] STREAM 1 window_size: 65535 + (65536 - 65535) = 65536
 
15:18:00.056 [debug] RECVD %Ankh.HTTP2.Frame.WindowUpdate{flags: nil, length: 4, payload: %Ankh.HTTP2.Frame.WindowUpdate.Payload{increment: 2147418112}, stream_id: 0, type: 8} <<0, 0, 4, 8, 0, 0, 0, 0, 0, 127, 255, 0, 0>>
 
15:18:00.057 [debug] SENT %Ankh.HTTP2.Frame.GoAway{flags: nil, length: 8, payload: %Ankh.HTTP2.Frame.GoAway.Payload{data: "", error_code: :flow_control_error, last_stream_id: 0}, stream_id: 0, type: 7} [<<0, 0, 8, 7, 0, 0, 0, 0, 0>>, <<0, 0, 0, 0>>, <<0, 0, 0, 3>>, ""]

Change History (1)

comment:1 by Sergey Kandaurov, 4 months ago

Resolution: invalid
Status: newclosed

Hi Luca,

Thank you for your report!

Please see RFC 7540, Section 6.5.2. Defined SETTINGS Parameters:

   SETTINGS_INITIAL_WINDOW_SIZE (0x4):  Indicates the sender's initial
      window size (in octets) for stream-level flow control.  The
      initial value is 2^16-1 (65,535) octets.

Please see RFC 7540, Section 6.9. WINDOW_UPDATE:

   The WINDOW_UPDATE frame can be specific to a stream or to the entire
   connection.  In the former case, the frame's stream identifier
   indicates the affected stream; in the latter, the value "0" indicates
   that the entire connection is the subject of the frame.

So, you mixed stream-level and connection-level flow control.

Note: See TracTickets for help on using tickets.