Mercurial > hg > nginx
comparison src/http/v2/ngx_http_v2_filter_module.c @ 7975:a7a77549265e
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.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Thu, 25 Nov 2021 22:02:10 +0300 |
parents | 80359395b345 |
children | 32b0ba4855a6 |
comparison
equal
deleted
inserted
replaced
7974:555533169506 | 7975:a7a77549265e |
---|---|
1430 | 1430 |
1431 #if (NGX_SUPPRESS_WARN) | 1431 #if (NGX_SUPPRESS_WARN) |
1432 size = 0; | 1432 size = 0; |
1433 #endif | 1433 #endif |
1434 | 1434 |
1435 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, | |
1436 "http2 send chain: %p", in); | |
1437 | |
1435 while (in) { | 1438 while (in) { |
1436 size = ngx_buf_size(in->buf); | 1439 size = ngx_buf_size(in->buf); |
1437 | 1440 |
1438 if (size || in->buf->last_buf) { | 1441 if (size || in->buf->last_buf) { |
1439 break; | 1442 break; |
1448 ngx_log_error(NGX_LOG_ERR, fc->log, 0, | 1451 ngx_log_error(NGX_LOG_ERR, fc->log, 0, |
1449 "output on closed stream"); | 1452 "output on closed stream"); |
1450 return NGX_CHAIN_ERROR; | 1453 return NGX_CHAIN_ERROR; |
1451 } | 1454 } |
1452 | 1455 |
1453 if (stream->queued) { | 1456 if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) { |
1457 return NGX_CHAIN_ERROR; | |
1458 } | |
1459 | |
1460 return NULL; | |
1461 } | |
1462 | |
1463 h2c = stream->connection; | |
1464 | |
1465 if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) { | |
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) { | |
1454 fc->write->active = 1; | 1472 fc->write->active = 1; |
1455 fc->write->ready = 0; | 1473 fc->write->ready = 0; |
1456 | 1474 return in; |
1457 } else { | 1475 } |
1458 fc->buffered &= ~NGX_HTTP_V2_BUFFERED; | |
1459 } | |
1460 | |
1461 return NULL; | |
1462 } | |
1463 | |
1464 h2c = stream->connection; | |
1465 | |
1466 if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) { | |
1467 fc->write->active = 1; | |
1468 fc->write->ready = 0; | |
1469 return in; | |
1470 } | 1476 } |
1471 | 1477 |
1472 if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) { | 1478 if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) { |
1473 cl = ngx_alloc_chain_link(r->pool); | 1479 cl = ngx_alloc_chain_link(r->pool); |
1474 if (cl == NULL) { | 1480 if (cl == NULL) { |
1807 | 1813 |
1808 | 1814 |
1809 static ngx_inline ngx_int_t | 1815 static ngx_inline ngx_int_t |
1810 ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream) | 1816 ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream) |
1811 { | 1817 { |
1818 if (stream->queued == 0) { | |
1819 fc->buffered &= ~NGX_HTTP_V2_BUFFERED; | |
1820 return NGX_OK; | |
1821 } | |
1822 | |
1812 stream->blocked = 1; | 1823 stream->blocked = 1; |
1813 | 1824 |
1814 if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) { | 1825 if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) { |
1815 fc->error = 1; | 1826 fc->error = 1; |
1816 return NGX_ERROR; | 1827 return NGX_ERROR; |