HTTP/2: sending RST_STREAM with NO_ERROR to discard request body.

RFC 7540 states that "A server can send a complete response prior to the client
sending an entire request if the response does not depend on any portion of the
request that has not been sent and received.  When this is true, a server MAY
request that the client abort transmission of a request without error by sending
a RST_STREAM with an error code of NO_ERROR after sending a complete response
(i.e., a frame with the END_STREAM flag)."

This should prevent a client from blocking on the stream window, since it isn't
maintained for closed streams.  Currently, quite big initial stream windows are
used, so such blocking is very unlikly, but that will be changed in the further
patches.
This commit is contained in:
Valentin Bartenev 2016-04-01 15:56:03 +03:00
parent 7cee215f15
commit cedba685a1
2 changed files with 25 additions and 14 deletions

View File

@ -3709,7 +3709,7 @@ ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c,
return NGX_ERROR; return NGX_ERROR;
} }
stream->out_closed = 1; stream->rst_sent = 1;
fc = stream->request->connection; fc = stream->request->connection;
fc->error = 1; fc->error = 1;
@ -3744,13 +3744,23 @@ ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc)
return; return;
} }
if (!stream->out_closed) { if (!stream->rst_sent && !h2c->connection->error) {
if (ngx_http_v2_send_rst_stream(h2c, node->id,
fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR if (!stream->out_closed) {
: NGX_HTTP_V2_INTERNAL_ERROR) if (ngx_http_v2_send_rst_stream(h2c, node->id,
!= NGX_OK) fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR
{ : NGX_HTTP_V2_INTERNAL_ERROR)
h2c->connection->error = 1; != NGX_OK)
{
h2c->connection->error = 1;
}
} else if (!stream->in_closed) {
if (ngx_http_v2_send_rst_stream(h2c, node->id, NGX_HTTP_V2_NO_ERROR)
!= NGX_OK)
{
h2c->connection->error = 1;
}
} }
} }
@ -3942,23 +3952,23 @@ ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,
c = h2c->connection; c = h2c->connection;
if (h2c->state.stream) {
h2c->state.stream->out_closed = 1;
ngx_http_v2_close_stream(h2c->state.stream, NGX_HTTP_BAD_REQUEST);
}
h2c->blocked = 1; h2c->blocked = 1;
if (!c->error && ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) { if (!c->error && ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) {
(void) ngx_http_v2_send_output_queue(h2c); (void) ngx_http_v2_send_output_queue(h2c);
} }
c->error = 1;
if (h2c->state.stream) {
ngx_http_v2_close_stream(h2c->state.stream, NGX_HTTP_BAD_REQUEST);
}
if (!h2c->processing) { if (!h2c->processing) {
ngx_http_close_connection(c); ngx_http_close_connection(c);
return; return;
} }
c->error = 1;
c->read->handler = ngx_http_empty_handler; c->read->handler = ngx_http_empty_handler;
c->write->handler = ngx_http_empty_handler; c->write->handler = ngx_http_empty_handler;

View File

@ -194,6 +194,7 @@ struct ngx_http_v2_stream_s {
unsigned exhausted:1; unsigned exhausted:1;
unsigned in_closed:1; unsigned in_closed:1;
unsigned out_closed:1; unsigned out_closed:1;
unsigned rst_sent:1;
unsigned skip_data:2; unsigned skip_data:2;
}; };