Correct way of finalizing a HTTP/2 connection

Jiří Setnička jiri.setnicka at cdn77.com
Wed Sep 4 15:51:22 UTC 2024


Hello,
I noticed that in some situations not all queued HTTP/2 frames may be 
sent before closing the HTTP/2 connection.

This could happen when the ngx_http_v2_finalize_connection is called 
with some frames in the h2c->last_out queue and with the c->write not 
ready. In such situation, the GOAWAY frame is pushed into the queue and 
ngx_http_v2_send_output_queue is called immediately, but no frames are 
sent due to the not ready c->write. During normal processing, these 
frames would be sent when the socket became ready again, but during the 
connection finalization the NGX_AGAIN is silently ignored, the queue in 
h2c->last_out is thrown away and the TCP connection is closed.

Connection finalization with a non-empty queue of frames could be 
triggered by some error in the connection itself (disconnected client, 
...) or by breaking some constraints like a number of concurrent 
streams. In the first case, the 
"best-effort" ngx_http_v2_send_output_queue before shutdown is the only 
thing we can do. But in the second case, the client is still able to 
receive the remaining frames, some of them useful for him (GOAWAY frame 
with an error reason, RST_STREAM frame providing information, if the 
request could be repeated), but these queued frames are never sent.

I think if it is worth modifying the ngx_http_v2_finalize_connection and 
ngx_http_v2_lingering_close in such way, that a lingering write handler 
would be set in a similar way as the lingering read handler to allow the 
queue to be sent until the lingering timeout. Any thoughts?

Also, I'm not fully sure about the reason behind that the h2c->last_out 
being set to NULL in ngx_http_v2_finalize_connection, when the event 
handlers of streams could generate more frames and again populating the 
h2c->last_out. Is there some reason why it is done in this way?

Sincerely
Jiří Setnička


More information about the nginx-devel mailing list