Mercurial > hg > nginx
comparison src/http/ngx_http_copy_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 | 555533169506 |
children | ec2e6893caaa |
comparison
equal
deleted
inserted
replaced
7974:555533169506 | 7975:a7a77549265e |
---|---|
251 | 251 |
252 static void | 252 static void |
253 ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev) | 253 ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev) |
254 { | 254 { |
255 ngx_event_aio_t *aio; | 255 ngx_event_aio_t *aio; |
256 ngx_connection_t *c; | |
256 ngx_http_request_t *r; | 257 ngx_http_request_t *r; |
257 | 258 |
258 aio = ev->data; | 259 aio = ev->data; |
259 r = aio->data; | 260 r = aio->data; |
261 c = r->connection; | |
260 | 262 |
261 r->main->blocked--; | 263 r->main->blocked--; |
262 r->aio = 0; | 264 r->aio = 0; |
263 ev->complete = 0; | 265 ev->complete = 0; |
264 | 266 |
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); | |
266 } | 282 } |
267 | 283 |
268 #endif | 284 #endif |
269 #endif | 285 #endif |
270 | 286 |
355 "http thread: \"%V?%V\"", &r->uri, &r->args); | 371 "http thread: \"%V?%V\"", &r->uri, &r->args); |
356 | 372 |
357 r->main->blocked--; | 373 r->main->blocked--; |
358 r->aio = 0; | 374 r->aio = 0; |
359 | 375 |
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 | |
360 if (r->done) { | 390 if (r->done) { |
361 /* | 391 /* |
362 * trigger connection event handler if the subrequest was | 392 * trigger connection event handler if the subrequest was |
363 * already finalized; this can happen if the handler is used | 393 * already finalized; this can happen if the handler is used |
364 * for sendfile() in threads | 394 * for sendfile() in threads |