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;