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