Klaus Demo nginx / f72bcf8
HTTP/2: implemented per request timeouts (closes #626). Previously, there were only three timeouts used globally for the whole HTTP/2 connection: 1. Idle timeout for inactivity when there are no streams in processing (the "http2_idle_timeout" directive); 2. Receive timeout for incomplete frames when there are no streams in processing (the "http2_recv_timeout" directive); 3. Send timeout when there are frames waiting in the output queue (the "send_timeout" directive on a server level). Reaching one of these timeouts leads to HTTP/2 connection close. This left a number of scenarios when a connection can get stuck without any processing and timeouts: 1. A client has sent the headers block partially so nginx starts processing a new stream but cannot continue without the rest of HEADERS and/or CONTINUATION frames; 2. When nginx waits for the request body; 3. All streams are stuck on exhausted connection or stream windows. The first idea that was rejected was to detect when the whole connection gets stuck because of these situations and set the global receive timeout. The disadvantage of such approach would be inconsistent behaviour in some typical use cases. For example, if a user never replies to the browser's question about where to save the downloaded file, the stream will be eventually closed by a timeout. On the other hand, this will not happen if there's some activity in other concurrent streams. Now almost all the request timeouts work like in HTTP/1.x connections, so the "client_header_timeout", "client_body_timeout", and "send_timeout" are respected. These timeouts close the request. The global timeouts work as before. Previously, the c->write->delayed flag was abused to avoid setting timeouts on stream events. Now, the "active" and "ready" flags are manipulated instead to control the processing of individual streams. Valentin Bartenev 6 years ago
3 changed file(s) with 147 addition(s) and 63 deletion(s). Raw diff Collapse all Expand all
25722572 ngx_http_test_reading;
25732573 r->write_event_handler = ngx_http_writer;
25742574
2575 #if (NGX_HTTP_V2)
2576 if (r->stream) {
2577 return NGX_OK;
2578 }
2579 #endif
2580
25812575 wev = r->connection->write;
25822576
25832577 if (wev->ready && wev->delayed) {
26622656 }
26632657
26642658 if (r->buffered || r->postponed || (r == r->main && c->buffered)) {
2665
2666 #if (NGX_HTTP_V2)
2667 if (r->stream) {
2668 return;
2669 }
2670 #endif
26712659
26722660 if (!wev->delayed) {
26732661 ngx_add_timer(wev, clcf->send_timeout);
113113 u_char *pos, u_char *end);
114114 static u_char *ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c,
115115 u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
116 static u_char *ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c,
117 u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
116118 static u_char *ngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c,
117119 ngx_uint_t err);
118120
161163 static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r);
162164 static void ngx_http_v2_run_request(ngx_http_request_t *r);
163165 static ngx_int_t ngx_http_v2_init_request_body(ngx_http_request_t *r);
166 static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r);
164167
165168 static ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c,
166169 ngx_http_v2_stream_t *stream, ngx_uint_t status);
429432 "run http2 stream %ui", stream->node->id);
430433
431434 wev = stream->request->connection->write;
435
436 wev->active = 0;
437 wev->ready = 1;
438
432439 wev->handler(wev);
433440 }
434441
882889 ngx_buf_t *buf;
883890 ngx_int_t rc;
884891 ngx_temp_file_t *tf;
892 ngx_connection_t *fc;
885893 ngx_http_request_t *r;
886894 ngx_http_v2_stream_t *stream;
887895 ngx_http_request_body_t *rb;
918926 return ngx_http_v2_state_skip_padded(h2c, pos, end);
919927 }
920928
929 fc = r->connection;
921930 rb = r->request_body;
922931 tf = rb->temp_file;
923932 buf = rb->buf;
928937 if (r->headers_in.content_length_n != -1
929938 && r->headers_in.content_length_n < rb->rest)
930939 {
931 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
940 ngx_log_error(NGX_LOG_INFO, fc->log, 0,
932941 "client intended to send body data "
933942 "larger than declared");
934943
941950 if (clcf->client_max_body_size
942951 && clcf->client_max_body_size < rb->rest)
943952 {
944 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
953 ngx_log_error(NGX_LOG_ERR, fc->log, 0,
945954 "client intended to send "
946955 "too large chunked body: %O bytes", rb->rest);
947956
981990 }
982991
983992 if (h2c->state.length) {
993 if (rb->post_handler) {
994 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
995 ngx_add_timer(fc->read, clcf->client_body_timeout);
996 }
997
984998 return ngx_http_v2_state_save(h2c, pos, end,
985999 ngx_http_v2_state_read_data);
9861000 }
9921006 r->headers_in.content_length_n = rb->rest;
9931007
9941008 } else if (r->headers_in.content_length_n != rb->rest) {
995 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
1009 ngx_log_error(NGX_LOG_INFO, fc->log, 0,
9961010 "client prematurely closed stream: "
9971011 "only %O out of %O bytes of request body received",
9981012 rb->rest, r->headers_in.content_length_n);
10121026 }
10131027
10141028 if (rb->post_handler) {
1029 if (fc->read->timer_set) {
1030 ngx_del_timer(fc->read);
1031 }
1032
10151033 r->read_event_handler = ngx_http_block_reading;
10161034 rb->post_handler(r);
10171035 }
1036
1037 } else if (rb->post_handler) {
1038 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
1039 ngx_add_timer(fc->read, clcf->client_body_timeout);
10181040 }
10191041
10201042 if (h2c->state.padding) {
10261048 error:
10271049
10281050 if (rb->post_handler) {
1051 if (fc->read->timer_set) {
1052 ngx_del_timer(fc->read);
1053 }
10291054
10301055 if (stream->skip_data == NGX_HTTP_V2_DATA_ERROR) {
10311056 rc = (r->headers_in.content_length_n == -1)
12171242 ngx_uint_t indexed, size_update, prefix;
12181243
12191244 if (end - pos < 1) {
1220 return ngx_http_v2_state_save(h2c, pos, end,
1221 ngx_http_v2_state_header_block);
1245 return ngx_http_v2_state_headers_save(h2c, pos, end,
1246 ngx_http_v2_state_header_block);
12221247 }
12231248
12241249 if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)
12611286
12621287 if (value < 0) {
12631288 if (value == NGX_AGAIN) {
1264 return ngx_http_v2_state_save(h2c, pos, end,
1265 ngx_http_v2_state_header_block);
1289 return ngx_http_v2_state_headers_save(h2c, pos, end,
1290 ngx_http_v2_state_header_block);
12661291 }
12671292
12681293 if (value == NGX_DECLINED) {
13321357 }
13331358
13341359 if (end - pos < 1) {
1335 return ngx_http_v2_state_save(h2c, pos, end,
1336 ngx_http_v2_state_field_len);
1360 return ngx_http_v2_state_headers_save(h2c, pos, end,
1361 ngx_http_v2_state_field_len);
13371362 }
13381363
13391364 huff = *pos >> 7;
13411366
13421367 if (len < 0) {
13431368 if (len == NGX_AGAIN) {
1344 return ngx_http_v2_state_save(h2c, pos, end,
1345 ngx_http_v2_state_field_len);
1369 return ngx_http_v2_state_headers_save(h2c, pos, end,
1370 ngx_http_v2_state_field_len);
13461371 }
13471372
13481373 if (len == NGX_DECLINED) {
14341459 }
14351460
14361461 if (h2c->state.length) {
1437 return ngx_http_v2_state_save(h2c, pos, end,
1438 ngx_http_v2_state_field_huff);
1462 return ngx_http_v2_state_headers_save(h2c, pos, end,
1463 ngx_http_v2_state_field_huff);
14391464 }
14401465
14411466 if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) {
14791504 }
14801505
14811506 if (h2c->state.length) {
1482 return ngx_http_v2_state_save(h2c, pos, end,
1483 ngx_http_v2_state_field_raw);
1507 return ngx_http_v2_state_headers_save(h2c, pos, end,
1508 ngx_http_v2_state_field_raw);
14841509 }
14851510
14861511 if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) {
17531778 }
17541779
17551780 if ((size_t) (end - pos) < len + NGX_HTTP_V2_FRAME_HEADER_SIZE) {
1756 return ngx_http_v2_state_save(h2c, pos, end, handler);
1781 return ngx_http_v2_state_headers_save(h2c, pos, end, handler);
17571782 }
17581783
17591784 p = pos + len;
22282253
22292254 wev = stream->request->connection->write;
22302255
2231 if (!wev->timer_set) {
2232 wev->delayed = 0;
2256 wev->active = 0;
2257 wev->ready = 1;
2258
2259 if (!wev->delayed) {
22332260 wev->handler(wev);
22342261 }
22352262 }
22612288
22622289 wev = stream->request->connection->write;
22632290
2264 if (!wev->timer_set) {
2265 wev->delayed = 0;
2291 wev->active = 0;
2292 wev->ready = 1;
2293
2294 if (!wev->delayed) {
22662295 wev->handler(wev);
22672296
22682297 if (h2c->send_window == 0) {
23662395 h2c->state.incomplete = 1;
23672396
23682397 return end;
2398 }
2399
2400
2401 static u_char *
2402 ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c, u_char *pos,
2403 u_char *end, ngx_http_v2_handler_pt handler)
2404 {
2405 ngx_event_t *rev;
2406 ngx_http_request_t *r;
2407 ngx_http_core_srv_conf_t *cscf;
2408
2409 if (h2c->state.stream) {
2410 r = h2c->state.stream->request;
2411 rev = r->connection->read;
2412
2413 if (!rev->timer_set) {
2414 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
2415 ngx_add_timer(rev, cscf->client_header_timeout);
2416 }
2417 }
2418
2419 return ngx_http_v2_state_save(h2c, pos, end, handler);
23692420 }
23702421
23712422
27472798 ngx_memcpy(log, h2c->connection->log, sizeof(ngx_log_t));
27482799
27492800 log->data = ctx;
2801 log->action = "reading client request headers";
27502802
27512803 ngx_memzero(rev, sizeof(ngx_event_t));
27522804
35533605 ngx_http_v2_read_request_body(ngx_http_request_t *r,
35543606 ngx_http_client_body_handler_pt post_handler)
35553607 {
3556 ngx_http_v2_stream_t *stream;
3608 ngx_http_v2_stream_t *stream;
3609 ngx_http_core_loc_conf_t *clcf;
35573610
35583611 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
35593612 "http2 read request body");
35893642
35903643 r->request_body->post_handler = post_handler;
35913644
3592 r->read_event_handler = ngx_http_test_reading;
3645 r->read_event_handler = ngx_http_v2_read_client_request_body_handler;
35933646 r->write_event_handler = ngx_http_request_empty_handler;
35943647
3648 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
3649 ngx_add_timer(r->connection->read, clcf->client_body_timeout);
3650
35953651 return NGX_AGAIN;
3652 }
3653
3654
3655 static void
3656 ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r)
3657 {
3658 ngx_connection_t *fc;
3659
3660 fc = r->connection;
3661
3662 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
3663 "http2 read client request body handler");
3664
3665 if (fc->read->timedout) {
3666 ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out");
3667
3668 fc->timedout = 1;
3669 r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD;
3670
3671 ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
3672 return;
3673 }
3674
3675 if (fc->error) {
3676 ngx_log_error(NGX_LOG_INFO, fc->log, 0,
3677 "client prematurely closed stream");
3678
3679 r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD;
3680
3681 ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
3682 return;
3683 }
35963684 }
35973685
35983686
36463734
36473735 if (!stream->out_closed) {
36483736 if (ngx_http_v2_send_rst_stream(h2c, node->id,
3649 NGX_HTTP_V2_INTERNAL_ERROR)
3737 fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR
3738 : NGX_HTTP_V2_INTERNAL_ERROR)
36503739 != NGX_OK)
36513740 {
36523741 h2c->connection->error = 1;
36833772
36843773 ev = fc->read;
36853774
3686 if (ev->active || ev->disabled) {
3687 ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
3688 "fake read event was activated");
3689 }
3690
36913775 if (ev->timer_set) {
36923776 ngx_del_timer(ev);
36933777 }
36983782
36993783 ev = fc->write;
37003784
3701 if (ev->active || ev->disabled) {
3702 ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
3703 "fake write event was activated");
3704 }
3705
37063785 if (ev->timer_set) {
37073786 ngx_del_timer(ev);
37083787 }
37363815 fc = ev->data;
37373816 r = fc->data;
37383817
3739 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
3818 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
37403819 "http2 close stream handler");
3820
3821 if (ev->timedout) {
3822 ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out");
3823
3824 fc->timedout = 1;
3825
3826 ngx_http_v2_close_stream(r->stream, NGX_HTTP_REQUEST_TIME_OUT);
3827 return;
3828 }
37413829
37423830 ngx_http_v2_close_stream(r->stream, 0);
37433831 }
37463834 static void
37473835 ngx_http_v2_handle_connection_handler(ngx_event_t *rev)
37483836 {
3749 ngx_connection_t *c;
3837 ngx_connection_t *c;
3838 ngx_http_v2_connection_t *h2c;
3839
3840 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
3841 "http2 handle connection handler");
37503842
37513843 rev->handler = ngx_http_v2_read_handler;
37523844
37563848 }
37573849
37583850 c = rev->data;
3851 h2c = c->data;
3852
3853 if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
3854 ngx_http_v2_finalize_connection(h2c, 0);
3855 return;
3856 }
37593857
37603858 ngx_http_v2_handle_connection(c->data);
37613859 }
38773975
38783976 if (stream->queued) {
38793977 stream->queued = 0;
3880
38813978 ev = fc->write;
3882 ev->delayed = 0;
38833979
38843980 } else {
38853981 ev = fc->read;
39484044
39494045 wev = stream->request->connection->write;
39504046
3951 if (!wev->timer_set) {
3952 wev->delayed = 0;
4047 wev->active = 0;
4048 wev->ready = 1;
4049
4050 if (!wev->delayed) {
39534051 wev->handler(wev);
39544052 }
39554053 }
798798 if (in == NULL) {
799799
800800 if (stream->queued) {
801 fc->write->delayed = 1;
801 fc->write->active = 1;
802 fc->write->ready = 0;
803
802804 } else {
803805 fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
804806 }
809811 h2c = stream->connection;
810812
811813 if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
812 fc->write->delayed = 1;
814 fc->write->active = 1;
815 fc->write->ready = 0;
813816 return in;
814817 }
815818
946949 }
947950
948951 if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
949 fc->write->delayed = 1;
952 fc->write->active = 1;
953 fc->write->ready = 0;
950954 }
951955
952956 return in;
10771081
10781082 if (stream->queued) {
10791083 fc->buffered |= NGX_HTTP_V2_BUFFERED;
1080 fc->write->delayed = 1;
1084 fc->write->active = 1;
1085 fc->write->ready = 0;
10811086 return NGX_AGAIN;
10821087 }
10831088
13151320
13161321 wev = stream->request->connection->write;
13171322
1318 /*
1319 * This timer can only be set if the stream was delayed because of rate
1320 * limit. In that case the event should be triggered by the timer.
1321 */
1322
1323 if (!wev->timer_set) {
1324 wev->delayed = 0;
1325
1323 if (!wev->delayed) {
13261324 stream->handled = 1;
13271325 ngx_queue_insert_tail(&h2c->posted, &stream->queue);
13281326 }