Ticket #959: preread_buffer.patch

File preread_buffer.patch, 13.6 KB (added by Valentin V. Bartenev, 10 years ago)
  • src/http/v2/ngx_http_v2.c

    # HG changeset patch
    # User Valentin Bartenev <vbart@nginx.com>
    # Date 1463759806 -10800
    #      Fri May 20 18:56:46 2016 +0300
    # Node ID 49502e14684e98e7373d957919c513860d98b88c
    # Parent  654d2dae97d308fb8f9ef61216d5c7a8f52ee686
    HTTP/2: implemented preread buffer for request body (closes #959).
    
    Previously, the stream's window was kept zero in order to prevent a client
    from sending the request body before it was requested (see 887cca40ba6a for
    details).  Until such initial window was acknowledged all requests with
    data were rejected (see 0aa07850922f for details).
    
    That approach revealed a number of problems:
    
     1. Some clients (notably MS IE/Edge, Safari, iOS applications) show an error
        or even crash if a stream is rejected;
    
     2. This requires at least one RTT for every request with body before the
        client receives window update and able to send data.
    
    To overcome these problems the new directive "http2_body_preread_size" is
    introduced.  It sets the initial window and configures a special per stream
    preread buffer that is used to save all incoming data before the body is
    requested and processed.
    
    If the directive's value is lower than the default initial window (65535),
    as previously, all streams with data will be rejected until the new window
    is acknowledged.  Otherwise, no special processing is used and all requests
    with data are welcome right from the connection start.
    
    The default value is chosen to 64k, which is bigger than the default initial
    window.  Setting it to zero is fully complaint to the previous behavior.
    
    diff -r 654d2dae97d3 -r 49502e14684e src/http/v2/ngx_http_v2.c
    a b  
    4848
    4949#define NGX_HTTP_V2_DEFAULT_FRAME_SIZE           (1 << 14)
    5050
    51 #define NGX_HTTP_V2_MAX_WINDOW                   ((1U << 31) - 1)
    52 #define NGX_HTTP_V2_DEFAULT_WINDOW               65535
    53 
    54 #define NGX_HTTP_V2_INITIAL_WINDOW               0
    55 
    5651#define NGX_HTTP_V2_ROOT                         (void *) -1
    5752
    5853
    static u_char *  
    891886ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
    892887    u_char *end)
    893888{
    894     size_t                 size;
    895     ngx_int_t              rc;
    896     ngx_uint_t             last;
    897     ngx_http_v2_stream_t  *stream;
     889    size_t                   size;
     890    ngx_buf_t               *buf;
     891    ngx_int_t                rc;
     892    ngx_uint_t               last;
     893    ngx_http_request_t      *r;
     894    ngx_http_v2_stream_t    *stream;
     895    ngx_http_v2_srv_conf_t  *h2scf;
    898896
    899897    stream = h2c->state.stream;
    900898
    ngx_http_v2_state_read_data(ngx_http_v2_  
    919917        last = 0;
    920918    }
    921919
    922     rc = ngx_http_v2_process_request_body(stream->request, pos, size, last);
    923 
    924     if (rc != NGX_OK) {
    925         stream->skip_data = 1;
    926         ngx_http_finalize_request(stream->request, rc);
     920    r = stream->request;
     921
     922    if (r->request_body) {
     923        rc = ngx_http_v2_process_request_body(r, pos, size, last);
     924
     925        if (rc != NGX_OK) {
     926            stream->skip_data = 1;
     927            ngx_http_finalize_request(r, rc);
     928        }
     929
     930    } else if (size) {
     931        buf = stream->preread;
     932
     933        if (buf == NULL) {
     934            h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
     935
     936            buf = ngx_create_temp_buf(r->pool, h2scf->preread_size);
     937            if (buf == NULL) {
     938                return ngx_http_v2_connection_error(h2c,
     939                                                    NGX_HTTP_V2_INTERNAL_ERROR);
     940            }
     941
     942            stream->preread = buf;
     943        }
     944
     945        buf->last = ngx_cpymem(buf->last, pos, size);
    927946    }
    928947
    929948    pos += size;
    ngx_http_v2_state_headers(ngx_http_v2_co  
    10581077        goto rst_stream;
    10591078    }
    10601079
    1061     if (!h2c->settings_ack && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG))
     1080    if (!h2c->settings_ack
     1081        && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG)
     1082        && h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW)
    10621083    {
    10631084        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
    10641085                      "client sent stream with data "
    ngx_http_v2_send_settings(ngx_http_v2_co  
    24342455
    24352456        buf->last = ngx_http_v2_write_uint16(buf->last,
    24362457                                         NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING);
    2437         buf->last = ngx_http_v2_write_uint32(buf->last,
    2438                                              NGX_HTTP_V2_INITIAL_WINDOW);
     2458        buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size);
    24392459
    24402460        buf->last = ngx_http_v2_write_uint16(buf->last,
    24412461                                           NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING);
    ngx_http_v2_create_stream(ngx_http_v2_co  
    26432663    ngx_http_log_ctx_t        *ctx;
    26442664    ngx_http_request_t        *r;
    26452665    ngx_http_v2_stream_t      *stream;
     2666    ngx_http_v2_srv_conf_t    *h2scf;
    26462667    ngx_http_core_srv_conf_t  *cscf;
    26472668
    26482669    fc = h2c->free_fake_connections;
    ngx_http_v2_create_stream(ngx_http_v2_co  
    27562777    stream->request = r;
    27572778    stream->connection = h2c;
    27582779
     2780    h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
     2781
    27592782    stream->send_window = h2c->init_window;
    2760     stream->recv_window = NGX_HTTP_V2_INITIAL_WINDOW;
     2783    stream->recv_window = h2scf->preread_size;
    27612784
    27622785    h2c->processing++;
    27632786
    ngx_http_v2_read_request_body(ngx_http_r  
    34113434    ngx_http_client_body_handler_pt post_handler)
    34123435{
    34133436    off_t                      len;
     3437    ssize_t                    size;
     3438    ngx_buf_t                 *buf;
     3439    ngx_int_t                  rc;
    34143440    ngx_http_v2_stream_t      *stream;
     3441    ngx_http_v2_srv_conf_t    *h2scf;
    34153442    ngx_http_request_body_t   *rb;
    34163443    ngx_http_core_loc_conf_t  *clcf;
    34173444    ngx_http_v2_connection_t  *h2c;
    ngx_http_v2_read_request_body(ngx_http_r  
    34443471
    34453472    r->request_body = rb;
    34463473
     3474    h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
    34473475    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    34483476
    34493477    len = r->headers_in.content_length_n;
    34503478
    34513479    if (r->request_body_no_buffering && !stream->in_closed) {
    3452         r->request_body_in_file_only = 0;
    3453 
    3454         if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {
    3455             len = clcf->client_body_buffer_size;
     3480        size = ngx_max(clcf->client_body_buffer_size, h2scf->preread_size);
     3481
     3482        if (len < 0 || len > (off_t) size) {
     3483            len = size;
    34563484        }
    34573485
    34583486        if (len > NGX_HTTP_V2_MAX_WINDOW) {
    34593487            len = NGX_HTTP_V2_MAX_WINDOW;
    34603488        }
    3461     }
    3462 
    3463     if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size
    3464         && !r->request_body_in_file_only)
     3489
     3490        rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
     3491
     3492    } else if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size
     3493               && !r->request_body_in_file_only)
    34653494    {
    34663495        rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
    34673496
    ngx_http_v2_read_request_body(ngx_http_r  
    34783507        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    34793508    }
    34803509
     3510    buf = stream->preread;
     3511
    34813512    if (stream->in_closed) {
    34823513        r->request_body_no_buffering = 0;
     3514
     3515        if (buf) {
     3516            rc = ngx_http_v2_process_request_body(r, buf->pos,
     3517                                                  buf->last - buf->pos, 1);
     3518
     3519            ngx_pfree(r->pool, buf->start);
     3520
     3521            return rc;
     3522        }
     3523
    34833524        return ngx_http_v2_process_request_body(r, NULL, 0, 1);
    34843525    }
    34853526
    3486     if (len) {
    3487         if (r->request_body_no_buffering) {
    3488             stream->recv_window = (size_t) len;
    3489 
    3490         } else {
    3491             stream->no_flow_control = 1;
    3492             stream->recv_window = NGX_HTTP_V2_MAX_WINDOW;
     3527    if (buf) {
     3528        rc = ngx_http_v2_process_request_body(r, buf->pos,
     3529                                              buf->last - buf->pos, 0);
     3530
     3531        ngx_pfree(r->pool, buf->start);
     3532
     3533        if (rc != NGX_OK) {
     3534            stream->skip_data = 1;
     3535            return rc;
    34933536        }
    3494 
    3495         if (ngx_http_v2_send_window_update(stream->connection, stream->node->id,
    3496                                            stream->recv_window)
     3537    }
     3538
     3539    if (r->request_body_no_buffering) {
     3540        size = (size_t) len - h2scf->preread_size;
     3541
     3542    } else {
     3543        stream->no_flow_control = 1;
     3544        size = NGX_HTTP_V2_MAX_WINDOW - stream->recv_window;
     3545    }
     3546
     3547    if (size > 0) {
     3548        if (ngx_http_v2_send_window_update(stream->connection,
     3549                                           stream->node->id, size)
    34973550            == NGX_ERROR)
    34983551        {
    34993552            stream->skip_data = 1;
    ngx_http_v2_read_request_body(ngx_http_r  
    35083561                return NGX_HTTP_INTERNAL_SERVER_ERROR;
    35093562            }
    35103563        }
    3511     }
    3512 
    3513     ngx_add_timer(r->connection->read, clcf->client_body_timeout);
     3564
     3565        stream->recv_window += size;
     3566    }
     3567
     3568    if (!buf) {
     3569        ngx_add_timer(r->connection->read, clcf->client_body_timeout);
     3570    }
    35143571
    35153572    r->read_event_handler = ngx_http_v2_read_client_request_body_handler;
    35163573    r->write_event_handler = ngx_http_request_empty_handler;
    ngx_http_v2_process_request_body(ngx_htt  
    35293586    ngx_http_request_body_t   *rb;
    35303587    ngx_http_core_loc_conf_t  *clcf;
    35313588
     3589    fc = r->connection;
    35323590    rb = r->request_body;
    3533 
    3534     if (rb == NULL) {
    3535         return NGX_OK;
    3536     }
    3537 
    3538     fc = r->connection;
    35393591    buf = rb->buf;
    35403592
    35413593    if (size) {
  • src/http/v2/ngx_http_v2.h

    diff -r 654d2dae97d3 -r 49502e14684e src/http/v2/ngx_http_v2.h
    a b  
    4646#define NGX_HTTP_V2_PADDED_FLAG          0x08
    4747#define NGX_HTTP_V2_PRIORITY_FLAG        0x20
    4848
     49#define NGX_HTTP_V2_MAX_WINDOW           ((1U << 31) - 1)
     50#define NGX_HTTP_V2_DEFAULT_WINDOW       65535
     51
    4952
    5053typedef struct ngx_http_v2_connection_s   ngx_http_v2_connection_t;
    5154typedef struct ngx_http_v2_node_s         ngx_http_v2_node_t;
    struct ngx_http_v2_stream_s {  
    174177    ssize_t                          send_window;
    175178    size_t                           recv_window;
    176179
     180    ngx_buf_t                       *preread;
     181
    177182    ngx_http_v2_out_frame_t         *free_frames;
    178183    ngx_chain_t                     *free_frame_headers;
    179184    ngx_chain_t                     *free_bufs;
  • src/http/v2/ngx_http_v2_module.c

    diff -r 654d2dae97d3 -r 49502e14684e src/http/v2/ngx_http_v2_module.c
    a b static char *ngx_http_v2_merge_loc_conf(  
    3030static char *ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post,
    3131    void *data);
    3232static char *ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data);
     33static char *ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data);
    3334static char *ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post,
    3435    void *data);
    3536static char *ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data);
    static ngx_conf_post_t ngx_http_v2_recv  
    4142    { ngx_http_v2_recv_buffer_size };
    4243static ngx_conf_post_t  ngx_http_v2_pool_size_post =
    4344    { ngx_http_v2_pool_size };
     45static ngx_conf_post_t  ngx_http_v2_preread_size_post =
     46    { ngx_http_v2_preread_size };
    4447static ngx_conf_post_t  ngx_http_v2_streams_index_mask_post =
    4548    { ngx_http_v2_streams_index_mask };
    4649static ngx_conf_post_t  ngx_http_v2_chunk_size_post =
    static ngx_command_t ngx_http_v2_comman  
    8487      offsetof(ngx_http_v2_srv_conf_t, max_header_size),
    8588      NULL },
    8689
     90    { ngx_string("http2_body_preread_size"),
     91      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
     92      ngx_conf_set_size_slot,
     93      NGX_HTTP_SRV_CONF_OFFSET,
     94      offsetof(ngx_http_v2_srv_conf_t, preread_size),
     95      &ngx_http_v2_preread_size_post },
     96
    8797    { ngx_string("http2_streams_index_size"),
    8898      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
    8999      ngx_conf_set_num_slot,
    ngx_http_v2_create_srv_conf(ngx_conf_t *  
    316326    h2scf->max_field_size = NGX_CONF_UNSET_SIZE;
    317327    h2scf->max_header_size = NGX_CONF_UNSET_SIZE;
    318328
     329    h2scf->preread_size = NGX_CONF_UNSET_SIZE;
     330
    319331    h2scf->streams_index_mask = NGX_CONF_UNSET_UINT;
    320332
    321333    h2scf->recv_timeout = NGX_CONF_UNSET_MSEC;
    ngx_http_v2_merge_srv_conf(ngx_conf_t *c  
    341353    ngx_conf_merge_size_value(conf->max_header_size, prev->max_header_size,
    342354                              16384);
    343355
     356    ngx_conf_merge_size_value(conf->preread_size, prev->preread_size, 65536);
     357
    344358    ngx_conf_merge_uint_value(conf->streams_index_mask,
    345359                              prev->streams_index_mask, 32 - 1);
    346360
    ngx_http_v2_pool_size(ngx_conf_t *cf, vo  
    420434
    421435
    422436static char *
     437ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data)
     438{
     439    size_t *sp = data;
     440
     441    if (*sp > NGX_HTTP_V2_MAX_WINDOW) {
     442        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
     443                           "the maximum body preread buffer size is %uz",
     444                           NGX_HTTP_V2_MAX_WINDOW);
     445
     446        return NGX_CONF_ERROR;
     447    }
     448
     449    return NGX_CONF_OK;
     450}
     451
     452
     453static char *
    423454ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post, void *data)
    424455{
    425456    ngx_uint_t *np = data;
  • src/http/v2/ngx_http_v2_module.h

    diff -r 654d2dae97d3 -r 49502e14684e src/http/v2/ngx_http_v2_module.h
    a b typedef struct {  
    2525    ngx_uint_t                      concurrent_streams;
    2626    size_t                          max_field_size;
    2727    size_t                          max_header_size;
     28    size_t                          preread_size;
    2829    ngx_uint_t                      streams_index_mask;
    2930    ngx_msec_t                      recv_timeout;
    3031    ngx_msec_t                      idle_timeout;