Skip to content

Commit 83e92a2

Browse files
committed
HTTP/2: fixed sendfile() aio handling.
With sendfile() in threads ("aio threads; sendfile on;"), client connection can block on writing, waiting for sendfile() to complete. In HTTP/2 this might result in the request hang, since an attempt to continue processing in thread event handler will call request's write event handler, which is usually stopped by ngx_http_v2_send_chain(): it does nothing if there are no additional data and stream->queued is set. Further, HTTP/2 resets stream's c->write->ready to 0 if writing blocks, so just fixing ngx_http_v2_send_chain() is not enough. Can be reproduced with test suite on Linux with: TEST_NGINX_GLOBALS_HTTP="aio threads; sendfile on;" prove h2*.t The following tests currently fail: h2_keepalive.t, h2_priority.t, h2_proxy_max_temp_file_size.t, h2.t, h2_trailers.t. Similarly, sendfile() with AIO preloading on FreeBSD can block as well, with similar results. This is, however, harder to reproduce, especially on modern FreeBSD systems, since sendfile() usually does not return EBUSY. Fix is to modify ngx_http_v2_send_chain() so it actually tries to send data to the main connection when called, and to make sure that c->write->ready is set by the relevant event handlers.
1 parent 2361e98 commit 83e92a2

File tree

3 files changed

+65
-10
lines changed

3 files changed

+65
-10
lines changed

src/http/ngx_http_copy_filter_module.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,16 +253,32 @@ static void
253253
ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev)
254254
{
255255
ngx_event_aio_t *aio;
256+
ngx_connection_t *c;
256257
ngx_http_request_t *r;
257258

258259
aio = ev->data;
259260
r = aio->data;
261+
c = r->connection;
260262

261263
r->main->blocked--;
262264
r->aio = 0;
263265
ev->complete = 0;
264266

265-
r->connection->write->handler(r->connection->write);
267+
#if (NGX_HTTP_V2)
268+
269+
if (r->stream) {
270+
/*
271+
* for HTTP/2, update write event to make sure processing will
272+
* reach the main connection to handle sendfile() preload
273+
*/
274+
275+
c->write->ready = 1;
276+
c->write->active = 0;
277+
}
278+
279+
#endif
280+
281+
c->write->handler(c->write);
266282
}
267283

268284
#endif
@@ -357,6 +373,20 @@ ngx_http_copy_thread_event_handler(ngx_event_t *ev)
357373
r->main->blocked--;
358374
r->aio = 0;
359375

376+
#if (NGX_HTTP_V2)
377+
378+
if (r->stream) {
379+
/*
380+
* for HTTP/2, update write event to make sure processing will
381+
* reach the main connection to handle sendfile() in threads
382+
*/
383+
384+
c->write->ready = 1;
385+
c->write->active = 0;
386+
}
387+
388+
#endif
389+
360390
if (r->done) {
361391
/*
362392
* trigger connection event handler if the subrequest was

src/http/ngx_http_upstream.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3927,6 +3927,20 @@ ngx_http_upstream_thread_event_handler(ngx_event_t *ev)
39273927
r->main->blocked--;
39283928
r->aio = 0;
39293929

3930+
#if (NGX_HTTP_V2)
3931+
3932+
if (r->stream) {
3933+
/*
3934+
* for HTTP/2, update write event to make sure processing will
3935+
* reach the main connection to handle sendfile() in threads
3936+
*/
3937+
3938+
c->write->ready = 1;
3939+
c->write->active = 0;
3940+
}
3941+
3942+
#endif
3943+
39303944
if (r->done) {
39313945
/*
39323946
* trigger connection event handler if the subrequest was

src/http/v2/ngx_http_v2_filter_module.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,9 @@ ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
14321432
size = 0;
14331433
#endif
14341434

1435+
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1436+
"http2 send chain: %p", in);
1437+
14351438
while (in) {
14361439
size = ngx_buf_size(in->buf);
14371440

@@ -1450,12 +1453,8 @@ ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
14501453
return NGX_CHAIN_ERROR;
14511454
}
14521455

1453-
if (stream->queued) {
1454-
fc->write->active = 1;
1455-
fc->write->ready = 0;
1456-
1457-
} else {
1458-
fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
1456+
if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
1457+
return NGX_CHAIN_ERROR;
14591458
}
14601459

14611460
return NULL;
@@ -1464,9 +1463,16 @@ ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
14641463
h2c = stream->connection;
14651464

14661465
if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
1467-
fc->write->active = 1;
1468-
fc->write->ready = 0;
1469-
return in;
1466+
1467+
if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
1468+
return NGX_CHAIN_ERROR;
1469+
}
1470+
1471+
if (ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
1472+
fc->write->active = 1;
1473+
fc->write->ready = 0;
1474+
return in;
1475+
}
14701476
}
14711477

14721478
if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
@@ -1809,6 +1815,11 @@ ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
18091815
static ngx_inline ngx_int_t
18101816
ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream)
18111817
{
1818+
if (stream->queued == 0) {
1819+
fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
1820+
return NGX_OK;
1821+
}
1822+
18121823
stream->blocked = 1;
18131824

18141825
if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) {

0 commit comments

Comments
 (0)