Changeset 6412:4ba91a4c66a3 in nginx


Ignore:
Timestamp:
02/24/16 12:58:07 (19 months ago)
Author:
Valentin Bartenev <vbart@…>
Branch:
default
Message:

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);
  1. Receive timeout for incomplete frames when there are no streams in processing (the "http2_recv_timeout" directive);
  1. 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;
  1. When nginx waits for the request body;
  1. 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.

Location:
src/http
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/http/ngx_http_request.c

    r6409 r6412  
    25742574    r->write_event_handler = ngx_http_writer;
    25752575
    2576 #if (NGX_HTTP_V2)
    2577     if (r->stream) {
    2578         return NGX_OK;
    2579     }
    2580 #endif
    2581 
    25822576    wev = r->connection->write;
    25832577
     
    26642658
    26652659    if (r->buffered || r->postponed || (r == r->main && c->buffered)) {
    2666 
    2667 #if (NGX_HTTP_V2)
    2668         if (r->stream) {
    2669             return;
    2670         }
    2671 #endif
    26722660
    26732661        if (!wev->delayed) {
  • src/http/v2/ngx_http_v2.c

    r6411 r6412  
    115115static u_char *ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c,
    116116    u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
     117static u_char *ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c,
     118    u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
    117119static u_char *ngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c,
    118120    ngx_uint_t err);
     
    163165static void ngx_http_v2_run_request(ngx_http_request_t *r);
    164166static ngx_int_t ngx_http_v2_init_request_body(ngx_http_request_t *r);
     167static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r);
    165168
    166169static ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c,
     
    431434
    432435        wev = stream->request->connection->write;
     436
     437        wev->active = 0;
     438        wev->ready = 1;
     439
    433440        wev->handler(wev);
    434441    }
     
    884891    ngx_int_t                  rc;
    885892    ngx_temp_file_t           *tf;
     893    ngx_connection_t          *fc;
    886894    ngx_http_request_t        *r;
    887895    ngx_http_v2_stream_t      *stream;
     
    920928    }
    921929
     930    fc = r->connection;
    922931    rb = r->request_body;
    923932    tf = rb->temp_file;
     
    930939            && r->headers_in.content_length_n < rb->rest)
    931940        {
    932             ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
     941            ngx_log_error(NGX_LOG_INFO, fc->log, 0,
    933942                          "client intended to send body data "
    934943                          "larger than declared");
     
    943952                && clcf->client_max_body_size < rb->rest)
    944953            {
    945                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
     954                ngx_log_error(NGX_LOG_ERR, fc->log, 0,
    946955                              "client intended to send "
    947956                              "too large chunked body: %O bytes", rb->rest);
     
    983992
    984993    if (h2c->state.length) {
     994        if (rb->post_handler) {
     995            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
     996            ngx_add_timer(fc->read, clcf->client_body_timeout);
     997        }
     998
    985999        return ngx_http_v2_state_save(h2c, pos, end,
    9861000                                      ngx_http_v2_state_read_data);
     
    9941008
    9951009        } else if (r->headers_in.content_length_n != rb->rest) {
    996             ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
     1010            ngx_log_error(NGX_LOG_INFO, fc->log, 0,
    9971011                          "client prematurely closed stream: "
    9981012                          "only %O out of %O bytes of request body received",
     
    10141028
    10151029        if (rb->post_handler) {
     1030            if (fc->read->timer_set) {
     1031                ngx_del_timer(fc->read);
     1032            }
     1033
    10161034            r->read_event_handler = ngx_http_block_reading;
    10171035            rb->post_handler(r);
    10181036        }
     1037
     1038    } else if (rb->post_handler) {
     1039        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
     1040        ngx_add_timer(fc->read, clcf->client_body_timeout);
    10191041    }
    10201042
     
    10281050
    10291051    if (rb->post_handler) {
     1052        if (fc->read->timer_set) {
     1053            ngx_del_timer(fc->read);
     1054        }
    10301055
    10311056        if (stream->skip_data == NGX_HTTP_V2_DATA_ERROR) {
     
    12191244
    12201245    if (end - pos < 1) {
    1221         return ngx_http_v2_state_save(h2c, pos, end,
    1222                                       ngx_http_v2_state_header_block);
     1246        return ngx_http_v2_state_headers_save(h2c, pos, end,
     1247                                              ngx_http_v2_state_header_block);
    12231248    }
    12241249
     
    12631288    if (value < 0) {
    12641289        if (value == NGX_AGAIN) {
    1265             return ngx_http_v2_state_save(h2c, pos, end,
    1266                                           ngx_http_v2_state_header_block);
     1290            return ngx_http_v2_state_headers_save(h2c, pos, end,
     1291                                               ngx_http_v2_state_header_block);
    12671292        }
    12681293
     
    13341359
    13351360    if (end - pos < 1) {
    1336         return ngx_http_v2_state_save(h2c, pos, end,
    1337                                       ngx_http_v2_state_field_len);
     1361        return ngx_http_v2_state_headers_save(h2c, pos, end,
     1362                                              ngx_http_v2_state_field_len);
    13381363    }
    13391364
     
    13431368    if (len < 0) {
    13441369        if (len == NGX_AGAIN) {
    1345             return ngx_http_v2_state_save(h2c, pos, end,
    1346                                           ngx_http_v2_state_field_len);
     1370            return ngx_http_v2_state_headers_save(h2c, pos, end,
     1371                                                  ngx_http_v2_state_field_len);
    13471372        }
    13481373
     
    14361461
    14371462    if (h2c->state.length) {
    1438         return ngx_http_v2_state_save(h2c, pos, end,
    1439                                       ngx_http_v2_state_field_huff);
     1463        return ngx_http_v2_state_headers_save(h2c, pos, end,
     1464                                              ngx_http_v2_state_field_huff);
    14401465    }
    14411466
     
    14811506
    14821507    if (h2c->state.length) {
    1483         return ngx_http_v2_state_save(h2c, pos, end,
    1484                                       ngx_http_v2_state_field_raw);
     1508        return ngx_http_v2_state_headers_save(h2c, pos, end,
     1509                                              ngx_http_v2_state_field_raw);
    14851510    }
    14861511
     
    17551780
    17561781    if ((size_t) (end - pos) < len + NGX_HTTP_V2_FRAME_HEADER_SIZE) {
    1757         return ngx_http_v2_state_save(h2c, pos, end, handler);
     1782        return ngx_http_v2_state_headers_save(h2c, pos, end, handler);
    17581783    }
    17591784
     
    22302255            wev = stream->request->connection->write;
    22312256
    2232             if (!wev->timer_set) {
    2233                 wev->delayed = 0;
     2257            wev->active = 0;
     2258            wev->ready = 1;
     2259
     2260            if (!wev->delayed) {
    22342261                wev->handler(wev);
    22352262            }
     
    22632290        wev = stream->request->connection->write;
    22642291
    2265         if (!wev->timer_set) {
    2266             wev->delayed = 0;
     2292        wev->active = 0;
     2293        wev->ready = 1;
     2294
     2295        if (!wev->delayed) {
    22672296            wev->handler(wev);
    22682297
     
    23682397
    23692398    return end;
     2399}
     2400
     2401
     2402static u_char *
     2403ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c, u_char *pos,
     2404    u_char *end, ngx_http_v2_handler_pt handler)
     2405{
     2406    ngx_event_t               *rev;
     2407    ngx_http_request_t        *r;
     2408    ngx_http_core_srv_conf_t  *cscf;
     2409
     2410    if (h2c->state.stream) {
     2411        r = h2c->state.stream->request;
     2412        rev = r->connection->read;
     2413
     2414        if (!rev->timer_set) {
     2415            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
     2416            ngx_add_timer(rev, cscf->client_header_timeout);
     2417        }
     2418    }
     2419
     2420    return ngx_http_v2_state_save(h2c, pos, end, handler);
    23702421}
    23712422
     
    27492800
    27502801    log->data = ctx;
     2802    log->action = "reading client request headers";
    27512803
    27522804    ngx_memzero(rev, sizeof(ngx_event_t));
     
    35553607    ngx_http_client_body_handler_pt post_handler)
    35563608{
    3557     ngx_http_v2_stream_t  *stream;
     3609    ngx_http_v2_stream_t      *stream;
     3610    ngx_http_core_loc_conf_t  *clcf;
    35583611
    35593612    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     
    35913644    r->request_body->post_handler = post_handler;
    35923645
    3593     r->read_event_handler = ngx_http_test_reading;
     3646    r->read_event_handler = ngx_http_v2_read_client_request_body_handler;
    35943647    r->write_event_handler = ngx_http_request_empty_handler;
    35953648
     3649    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
     3650    ngx_add_timer(r->connection->read, clcf->client_body_timeout);
     3651
    35963652    return NGX_AGAIN;
     3653}
     3654
     3655
     3656static void
     3657ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r)
     3658{
     3659    ngx_connection_t  *fc;
     3660
     3661    fc = r->connection;
     3662
     3663    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
     3664                   "http2 read client request body handler");
     3665
     3666    if (fc->read->timedout) {
     3667        ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out");
     3668
     3669        fc->timedout = 1;
     3670        r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD;
     3671
     3672        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
     3673        return;
     3674    }
     3675
     3676    if (fc->error) {
     3677        ngx_log_error(NGX_LOG_INFO, fc->log, 0,
     3678                      "client prematurely closed stream");
     3679
     3680        r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD;
     3681
     3682        ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
     3683        return;
     3684    }
    35973685}
    35983686
     
    36483736    if (!stream->out_closed) {
    36493737        if (ngx_http_v2_send_rst_stream(h2c, node->id,
    3650                                         NGX_HTTP_V2_INTERNAL_ERROR)
     3738                                     fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR
     3739                                                  : NGX_HTTP_V2_INTERNAL_ERROR)
    36513740            != NGX_OK)
    36523741        {
     
    36853774    ev = fc->read;
    36863775
    3687     if (ev->active || ev->disabled) {
    3688         ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
    3689                       "fake read event was activated");
    3690     }
    3691 
    36923776    if (ev->timer_set) {
    36933777        ngx_del_timer(ev);
     
    37003784    ev = fc->write;
    37013785
    3702     if (ev->active || ev->disabled) {
    3703         ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
    3704                       "fake write event was activated");
    3705     }
    3706 
    37073786    if (ev->timer_set) {
    37083787        ngx_del_timer(ev);
     
    37383817    r = fc->data;
    37393818
    3740     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     3819    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
    37413820                   "http2 close stream handler");
     3821
     3822    if (ev->timedout) {
     3823        ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out");
     3824
     3825        fc->timedout = 1;
     3826
     3827        ngx_http_v2_close_stream(r->stream, NGX_HTTP_REQUEST_TIME_OUT);
     3828        return;
     3829    }
    37423830
    37433831    ngx_http_v2_close_stream(r->stream, 0);
     
    37483836ngx_http_v2_handle_connection_handler(ngx_event_t *rev)
    37493837{
    3750     ngx_connection_t  *c;
     3838    ngx_connection_t          *c;
     3839    ngx_http_v2_connection_t  *h2c;
     3840
     3841    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
     3842                   "http2 handle connection handler");
    37513843
    37523844    rev->handler = ngx_http_v2_read_handler;
     
    37583850
    37593851    c = rev->data;
     3852    h2c = c->data;
     3853
     3854    if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
     3855        ngx_http_v2_finalize_connection(h2c, 0);
     3856        return;
     3857    }
    37603858
    37613859    ngx_http_v2_handle_connection(c->data);
     
    38793977            if (stream->queued) {
    38803978                stream->queued = 0;
    3881 
    38823979                ev = fc->write;
    3883                 ev->delayed = 0;
    38843980
    38853981            } else {
     
    39504046                wev = stream->request->connection->write;
    39514047
    3952                 if (!wev->timer_set) {
    3953                     wev->delayed = 0;
     4048                wev->active = 0;
     4049                wev->ready = 1;
     4050
     4051                if (!wev->delayed) {
    39544052                    wev->handler(wev);
    39554053                }
  • src/http/v2/ngx_http_v2_filter_module.c

    r6401 r6412  
    800800
    801801        if (stream->queued) {
    802             fc->write->delayed = 1;
     802            fc->write->active = 1;
     803            fc->write->ready = 0;
     804
    803805        } else {
    804806            fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
     
    811813
    812814    if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
    813         fc->write->delayed = 1;
     815        fc->write->active = 1;
     816        fc->write->ready = 0;
    814817        return in;
    815818    }
     
    948951
    949952    if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
    950         fc->write->delayed = 1;
     953        fc->write->active = 1;
     954        fc->write->ready = 0;
    951955    }
    952956
     
    10791083    if (stream->queued) {
    10801084        fc->buffered |= NGX_HTTP_V2_BUFFERED;
    1081         fc->write->delayed = 1;
     1085        fc->write->active = 1;
     1086        fc->write->ready = 0;
    10821087        return NGX_AGAIN;
    10831088    }
     
    13171322    wev = stream->request->connection->write;
    13181323
    1319     /*
    1320      * This timer can only be set if the stream was delayed because of rate
    1321      * limit.  In that case the event should be triggered by the timer.
    1322      */
    1323 
    1324     if (!wev->timer_set) {
    1325         wev->delayed = 0;
    1326 
     1324    if (!wev->delayed) {
    13271325        stream->handled = 1;
    13281326        ngx_queue_insert_tail(&h2c->posted, &stream->queue);
Note: See TracChangeset for help on using the changeset viewer.