Mercurial > hg > nginx
comparison src/http/v2/ngx_http_v2.c @ 6833:3834951e32ab
HTTP/2: fixed posted streams handling.
A bug was introduced by 82efcedb310b that could lead to timing out of
responses or segmentation fault, when accept_mutex was enabled.
The output queue in HTTP/2 can contain frames from different streams.
When the queue is sent, all related write handlers need to be called.
In order to do so, the streams were added to the h2c->posted queue
after handling sent frames. Then this queue was processed in
ngx_http_v2_write_handler().
If accept_mutex is enabled, the event's "ready" flag is set but its
handler is not called immediately. Instead, the event is added to
the ngx_posted_events queue. At the same time in this queue can be
events from upstream connections. Such events can result in sending
output queue before ngx_http_v2_write_handler() is triggered. And
at the time ngx_http_v2_write_handler() is called, the output queue
can be already empty with some streams added to h2c->posted.
But after 82efcedb310b, these streams weren't processed if all frames
have already been sent and the output queue was empty. This might lead
to a situation when a number of streams were get stuck in h2c->posted
queue for a long time. Eventually these streams might get closed by
the send timeout.
In the worst case this might also lead to a segmentation fault, if
already freed stream was left in the h2c->posted queue. This could
happen if one of the streams was terminated but wasn't closed, due to
the HEADERS frame or a partially sent DATA frame left in the output
queue. If this happened the ngx_http_v2_filter_cleanup() handler
removed the stream from the h2c->waiting or h2c->posted queue on
termination stage, before the frame has been sent, and the stream
was again added to the h2c->posted queue after the frame was sent.
In order to fix all these problems and simplify the code, write
events of fake stream connections are now added to ngx_posted_events
instead of using a custom h2c->posted queue.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Mon, 28 Nov 2016 20:58:14 +0300 |
parents | 52bd8cc17f34 |
children | e02f1977846b |
comparison
equal
deleted
inserted
replaced
6832:ec10ce307dc0 | 6833:3834951e32ab |
---|---|
284 | 284 |
285 h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol | 285 h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol |
286 : ngx_http_v2_state_preface; | 286 : ngx_http_v2_state_preface; |
287 | 287 |
288 ngx_queue_init(&h2c->waiting); | 288 ngx_queue_init(&h2c->waiting); |
289 ngx_queue_init(&h2c->posted); | |
290 ngx_queue_init(&h2c->dependencies); | 289 ngx_queue_init(&h2c->dependencies); |
291 ngx_queue_init(&h2c->closed); | 290 ngx_queue_init(&h2c->closed); |
292 | 291 |
293 c->data = h2c; | 292 c->data = h2c; |
294 | 293 |
418 | 417 |
419 static void | 418 static void |
420 ngx_http_v2_write_handler(ngx_event_t *wev) | 419 ngx_http_v2_write_handler(ngx_event_t *wev) |
421 { | 420 { |
422 ngx_int_t rc; | 421 ngx_int_t rc; |
423 ngx_queue_t *q; | |
424 ngx_connection_t *c; | 422 ngx_connection_t *c; |
425 ngx_http_v2_stream_t *stream; | |
426 ngx_http_v2_connection_t *h2c; | 423 ngx_http_v2_connection_t *h2c; |
427 | 424 |
428 c = wev->data; | 425 c = wev->data; |
429 h2c = c->data; | 426 h2c = c->data; |
430 | 427 |
453 rc = ngx_http_v2_send_output_queue(h2c); | 450 rc = ngx_http_v2_send_output_queue(h2c); |
454 | 451 |
455 if (rc == NGX_ERROR) { | 452 if (rc == NGX_ERROR) { |
456 ngx_http_v2_finalize_connection(h2c, 0); | 453 ngx_http_v2_finalize_connection(h2c, 0); |
457 return; | 454 return; |
458 } | |
459 | |
460 while (!ngx_queue_empty(&h2c->posted)) { | |
461 q = ngx_queue_head(&h2c->posted); | |
462 | |
463 ngx_queue_remove(q); | |
464 | |
465 stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); | |
466 | |
467 stream->handled = 0; | |
468 | |
469 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
470 "run http2 stream %ui", stream->node->id); | |
471 | |
472 wev = stream->request->connection->write; | |
473 | |
474 wev->active = 0; | |
475 wev->ready = 1; | |
476 | |
477 wev->handler(wev); | |
478 } | 455 } |
479 | 456 |
480 h2c->blocked = 0; | 457 h2c->blocked = 0; |
481 | 458 |
482 if (rc == NGX_AGAIN) { | 459 if (rc == NGX_AGAIN) { |
2252 | 2229 |
2253 ngx_queue_remove(q); | 2230 ngx_queue_remove(q); |
2254 | 2231 |
2255 stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); | 2232 stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); |
2256 | 2233 |
2257 stream->handled = 0; | 2234 stream->waiting = 0; |
2258 | 2235 |
2259 wev = stream->request->connection->write; | 2236 wev = stream->request->connection->write; |
2260 | 2237 |
2261 wev->active = 0; | 2238 wev->active = 0; |
2262 wev->ready = 1; | 2239 wev->ready = 1; |
4272 | 4249 |
4273 if (stream == NULL) { | 4250 if (stream == NULL) { |
4274 continue; | 4251 continue; |
4275 } | 4252 } |
4276 | 4253 |
4277 stream->handled = 0; | 4254 stream->waiting = 0; |
4278 | 4255 |
4279 r = stream->request; | 4256 r = stream->request; |
4280 fc = r->connection; | 4257 fc = r->connection; |
4281 | 4258 |
4282 fc->error = 1; | 4259 fc->error = 1; |