Ticket #39773: patch-nginx_upload_module.tmp-ngx_http_upload_module.c.diff

File patch-nginx_upload_module.tmp-ngx_http_upload_module.c.diff, 50.9 KB (added by anthropologoi@…, 11 years ago)
  • nginx_upload_module.tmp/ngx_http_upload_module.c

    old new  
    9595} ngx_http_upload_field_template_t;
    9696
    9797/*
     98 * Template for a header
     99 */
     100typedef struct {
     101    ngx_http_complex_value_t      *name;
     102    ngx_http_complex_value_t      *value;
     103} ngx_http_upload_header_template_t;
     104
     105/*
    98106 * Filter for fields in output form
    99107 */
    100108typedef struct {
     
    106114#endif
    107115} ngx_http_upload_field_filter_t;
    108116
     117typedef struct {
     118    ngx_path_t                  *path;
     119    ngx_http_complex_value_t    dynamic;
     120    unsigned                    is_dynamic:1;
     121} ngx_http_upload_path_t;
     122
    109123/*
    110124 * Upload cleanup record
    111125 */
     
    124138typedef struct {
    125139    ngx_str_t                     url;
    126140    ngx_http_complex_value_t      *url_cv;
    127     ngx_path_t                    *state_store_path;
    128     ngx_path_t                    *store_path;
     141    ngx_http_upload_path_t        *state_store_path;
     142    ngx_http_upload_path_t        *store_path;
    129143    ngx_uint_t                    store_access;
    130144    size_t                        buffer_size;
    131145    size_t                        merge_buffer_size;
     
    137151    ngx_array_t                   *aggregate_field_templates;
    138152    ngx_array_t                   *field_filters;
    139153    ngx_array_t                   *cleanup_statuses;
     154    ngx_array_t                   *header_templates;
    140155    ngx_flag_t                    forward_args;
    141156    ngx_flag_t                    tame_arrays;
    142157    ngx_flag_t                    resumable_uploads;
     158    ngx_flag_t                    empty_field_names;
    143159    size_t                        limit_rate;
    144160
    145161    unsigned int                  md5:1;
    146162    unsigned int                  sha1:1;
     163    unsigned int                  sha256:1;
     164    unsigned int                  sha512:1;
    147165    unsigned int                  crc32:1;
    148166} ngx_http_upload_loc_conf_t;
    149167
     
    157175    u_char      sha1_digest[SHA_DIGEST_LENGTH * 2];
    158176} ngx_http_upload_sha1_ctx_t;
    159177
     178typedef struct ngx_http_upload_sha256_ctx_s {
     179    SHA256_CTX  sha256;
     180    u_char      sha256_digest[SHA256_DIGEST_LENGTH * 2];
     181} ngx_http_upload_sha256_ctx_t;
     182
     183typedef struct ngx_http_upload_sha512_ctx_s {
     184    SHA512_CTX  sha512;
     185    u_char      sha512_digest[SHA512_DIGEST_LENGTH * 2];
     186} ngx_http_upload_sha512_ctx_t;
     187
    160188struct ngx_http_upload_ctx_s;
    161189
    162190/*
     
    219247
    220248    ngx_http_upload_md5_ctx_t   *md5_ctx;   
    221249    ngx_http_upload_sha1_ctx_t  *sha1_ctx;   
     250    ngx_http_upload_sha256_ctx_t *sha256_ctx;
     251    ngx_http_upload_sha512_ctx_t *sha512_ctx;
    222252    uint32_t                    crc32;   
     253    ngx_path_t          *store_path;
     254    ngx_path_t          *state_store_path;
    223255
    224256    unsigned int        first_part:1;
    225257    unsigned int        discard_data:1;
     
    233265    unsigned int        raw_input:1;
    234266} ngx_http_upload_ctx_t;
    235267
     268static ngx_int_t ngx_http_upload_test_expect(ngx_http_request_t *r);
     269
     270static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
     271static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
     272
     273static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
     274static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in);
     275
     276static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in);
     277static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in);
     278
     279static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in);
     280
    236281static ngx_int_t ngx_http_upload_handler(ngx_http_request_t *r);
     282static ngx_int_t ngx_http_upload_options_handler(ngx_http_request_t *r);
    237283static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r);
    238284
    239285static void *ngx_http_upload_create_loc_conf(ngx_conf_t *cf);
     
    248294    ngx_http_variable_value_t *v, uintptr_t data);
    249295static ngx_int_t ngx_http_upload_sha1_variable(ngx_http_request_t *r,
    250296    ngx_http_variable_value_t *v, uintptr_t data);
     297static ngx_int_t ngx_http_upload_sha256_variable(ngx_http_request_t *r,
     298    ngx_http_variable_value_t *v, uintptr_t data);
     299static ngx_int_t ngx_http_upload_sha512_variable(ngx_http_request_t *r,
     300    ngx_http_variable_value_t *v, uintptr_t data);
    251301static ngx_int_t ngx_http_upload_file_size_variable(ngx_http_request_t *r,
    252302    ngx_http_variable_value_t *v, uintptr_t data);
    253303static void ngx_http_upload_content_range_variable_set(ngx_http_request_t *r,
     
    271321static ngx_int_t ngx_http_upload_merge_ranges(ngx_http_upload_ctx_t *u, ngx_http_upload_range_t *range_n);
    272322static ngx_int_t ngx_http_upload_parse_range(ngx_str_t *range, ngx_http_upload_range_t *range_n);
    273323
     324
    274325static void ngx_http_read_upload_client_request_body_handler(ngx_http_request_t *r);
    275326static ngx_int_t ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r);
    276327static ngx_int_t ngx_http_process_request_body(ngx_http_request_t *r, ngx_chain_t *body);
     
    279330
    280331static char *ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd,
    281332    void *conf);
     333static char *ngx_http_upload_add_header(ngx_conf_t *cf, ngx_command_t *cmd,
     334    void *conf);
     335static ngx_int_t ngx_http_upload_eval_path(ngx_http_request_t *r);
     336static ngx_int_t ngx_http_upload_eval_state_path(ngx_http_request_t *r);
    282337static char *ngx_http_upload_pass_form_field(ngx_conf_t *cf, ngx_command_t *cmd,
    283338    void *conf);
     339static char *ngx_http_upload_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd,
     340    void *conf);
     341static char *ngx_http_upload_merge_path_value(ngx_conf_t *cf, ngx_http_upload_path_t **path, ngx_http_upload_path_t *prev,
     342    ngx_path_init_t *init);
    284343static char *ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd,
    285344    void *conf);
    286345static void ngx_upload_cleanup_handler(void *data);
     
    391450    { ngx_string("upload_store"),
    392451      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
    393452                        |NGX_CONF_TAKE1234,
    394       ngx_conf_set_path_slot,
     453      ngx_http_upload_set_path_slot,
    395454      NGX_HTTP_LOC_CONF_OFFSET,
    396455      offsetof(ngx_http_upload_loc_conf_t, store_path),
    397456      NULL },
     
    401460     */
    402461    { ngx_string("upload_state_store"),
    403462      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
    404       ngx_conf_set_path_slot,
     463      ngx_http_upload_set_path_slot,
    405464      NGX_HTTP_LOC_CONF_OFFSET,
    406465      offsetof(ngx_http_upload_loc_conf_t, state_store_path),
    407466      NULL },
     
    575634       offsetof(ngx_http_upload_loc_conf_t, resumable_uploads),
    576635       NULL },
    577636
     637     /*
     638      * Specifies whether empty field names are allowed
     639      */
     640     { ngx_string("upload_empty_fiels_names"),
     641       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
     642                         |NGX_CONF_FLAG,
     643       ngx_conf_set_flag_slot,
     644       NGX_HTTP_LOC_CONF_OFFSET,
     645       offsetof(ngx_http_upload_loc_conf_t, empty_field_names),
     646       NULL },
     647
     648    /*
     649     * Specifies the name and content of the header that will be added to the response
     650     */
     651    { ngx_string("upload_add_header"),
     652      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
     653                        |NGX_CONF_TAKE2,
     654      ngx_http_upload_add_header,
     655      NGX_HTTP_LOC_CONF_OFFSET,
     656      offsetof(ngx_http_upload_loc_conf_t, header_templates),
     657      NULL},
     658
    578659      ngx_null_command
    579660}; /* }}} */
    580661
     
    658739      (uintptr_t) "0123456789ABCDEF",
    659740      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
    660741
     742    { ngx_string("upload_file_sha256"), NULL, ngx_http_upload_sha256_variable,
     743      (uintptr_t) "0123456789abcdef",
     744      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
     745
     746    { ngx_string("upload_file_sha256_uc"), NULL, ngx_http_upload_sha256_variable,
     747      (uintptr_t) "0123456789ABCDEF",
     748      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
     749
     750    { ngx_string("upload_file_sha512"), NULL, ngx_http_upload_sha512_variable,
     751      (uintptr_t) "0123456789abcdef",
     752      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
     753
     754    { ngx_string("upload_file_sha512_uc"), NULL, ngx_http_upload_sha512_variable,
     755      (uintptr_t) "0123456789ABCDEF",
     756      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
     757
    661758    { ngx_string("upload_file_crc32"), NULL, ngx_http_upload_crc32_variable,
    662759      (uintptr_t) offsetof(ngx_http_upload_ctx_t, crc32),
    663760      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
     
    688785    ngx_http_upload_ctx_t     *u;
    689786    ngx_int_t                 rc;
    690787
     788    if(r->method & NGX_HTTP_OPTIONS)
     789        return ngx_http_upload_options_handler(r);
     790
    691791    if (!(r->method & NGX_HTTP_POST))
    692792        return NGX_HTTP_NOT_ALLOWED;
    693793
     
    724824    }else
    725825        u->sha1_ctx = NULL;
    726826
     827    if(ulcf->sha256) {
     828        if(u->sha256_ctx == NULL) {
     829            u->sha256_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_sha256_ctx_t));
     830            if (u->sha256_ctx == NULL) {
     831                return NGX_HTTP_INTERNAL_SERVER_ERROR;
     832            }
     833        }
     834    }else
     835        u->sha256_ctx = NULL;
     836
     837    if(ulcf->sha512) {
     838        if(u->sha512_ctx == NULL) {
     839            u->sha512_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_sha512_ctx_t));
     840            if (u->sha512_ctx == NULL) {
     841                return NGX_HTTP_INTERNAL_SERVER_ERROR;
     842            }
     843        }
     844    }else
     845        u->sha512_ctx = NULL;
     846
    727847    u->calculate_crc32 = ulcf->crc32;
    728848
    729849    u->request = r;
     
    746866        return rc;
    747867    }
    748868
     869    rc = ngx_http_upload_eval_path(r);
     870
     871    if(rc != NGX_OK) {
     872        upload_shutdown_ctx(u);
     873        return rc;
     874    }
     875
     876    rc = ngx_http_upload_eval_state_path(r);
     877
     878    if(rc != NGX_OK) {
     879        upload_shutdown_ctx(u);
     880        return rc;
     881    }
     882
     883    if (ngx_http_upload_test_expect(r) != NGX_OK) {
     884        upload_shutdown_ctx(u);
     885        return NGX_HTTP_INTERNAL_SERVER_ERROR;
     886    }
     887
    749888    if(upload_start(u, ulcf) != NGX_OK)
    750889        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    751890
     
    758897    return NGX_DONE;
    759898} /* }}} */
    760899
     900static ngx_int_t ngx_http_upload_add_headers(ngx_http_request_t *r, ngx_http_upload_loc_conf_t *ulcf) { /* {{{ */
     901    ngx_str_t                            name;
     902    ngx_str_t                            value;
     903    ngx_http_upload_header_template_t    *t;
     904    ngx_table_elt_t                      *h;
     905    ngx_uint_t                           i;
     906
     907    if(ulcf->header_templates != NULL) {
     908        t = ulcf->header_templates->elts;
     909        for(i = 0; i < ulcf->header_templates->nelts; i++) {
     910            if(ngx_http_complex_value(r, t->name, &name) != NGX_OK) {
     911                return NGX_ERROR;
     912            }
     913
     914            if(ngx_http_complex_value(r, t->value, &value) != NGX_OK) {
     915                return NGX_ERROR;
     916            }
     917
     918            if(name.len != 0 && value.len != 0) {
     919                h = ngx_list_push(&r->headers_out.headers);
     920                if(h == NULL) {
     921                    return NGX_ERROR;
     922                }
     923
     924                h->hash = 1;
     925                h->key.len = name.len;
     926                h->key.data = name.data;
     927                h->value.len = value.len;
     928                h->value.data = value.data;
     929            }
     930
     931            t++;
     932        }
     933    }
     934
     935    return NGX_OK;
     936} /* }}} */
     937
     938static ngx_int_t /* {{{  */
     939ngx_http_upload_eval_path(ngx_http_request_t *r) {
     940    ngx_http_upload_ctx_t       *u;
     941    ngx_http_upload_loc_conf_t  *ulcf;
     942    ngx_str_t                   value;
     943
     944    ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
     945    u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
     946
     947    if(ulcf->store_path->is_dynamic) {
     948        u->store_path = ngx_pcalloc(r->pool, sizeof(ngx_path_t));
     949        if(u->store_path == NULL) {
     950            return NGX_ERROR;
     951        }
     952
     953        ngx_memcpy(u->store_path, ulcf->store_path->path, sizeof(ngx_path_t));
     954
     955        if(ngx_http_complex_value(r, &ulcf->store_path->dynamic, &value) != NGX_OK) {
     956            return NGX_ERROR;
     957        }
     958
     959        u->store_path->name.data = value.data;
     960        u->store_path->name.len = value.len;
     961    }
     962    else{
     963        u->store_path = ulcf->store_path->path;
     964    }
     965
     966    return NGX_OK;
     967} /* }}} */
     968
     969static ngx_int_t /* {{{  */
     970ngx_http_upload_eval_state_path(ngx_http_request_t *r) {
     971    ngx_http_upload_ctx_t       *u;
     972    ngx_http_upload_loc_conf_t  *ulcf;
     973    ngx_str_t                   value;
     974
     975    ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
     976    u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
     977
     978    if(ulcf->state_store_path->is_dynamic) {
     979        u->state_store_path = ngx_pcalloc(r->pool, sizeof(ngx_path_t));
     980        if(u->store_path == NULL) {
     981            return NGX_ERROR;
     982        }
     983
     984        ngx_memcpy(u->state_store_path, ulcf->state_store_path->path, sizeof(ngx_path_t));
     985
     986        if(ngx_http_complex_value(r, &ulcf->state_store_path->dynamic, &value) != NGX_OK) {
     987            return NGX_ERROR;
     988        }
     989
     990        u->state_store_path->name.data = value.data;
     991        u->state_store_path->name.len = value.len;
     992    }
     993    else{
     994        u->state_store_path = ulcf->state_store_path->path;
     995    }
     996
     997    return NGX_OK;
     998} /* }}} */
     999
     1000static ngx_int_t ngx_http_upload_options_handler(ngx_http_request_t *r) { /* {{{ */
     1001    ngx_http_upload_loc_conf_t *ulcf;
     1002
     1003    ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
     1004
     1005    r->headers_out.status = NGX_HTTP_OK;
     1006
     1007    if(ngx_http_upload_add_headers(r, ulcf) != NGX_OK) {
     1008        return NGX_HTTP_INTERNAL_SERVER_ERROR;
     1009    }
     1010
     1011    r->header_only = 1;
     1012    r->headers_out.content_length_n = 0;
     1013    r->allow_ranges = 0;
     1014
     1015    return ngx_http_send_header(r);
     1016} /* }}} */
     1017
    7611018static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r) { /* {{{ */
    7621019    ngx_http_upload_loc_conf_t  *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
    7631020    ngx_http_upload_ctx_t       *ctx = ngx_http_get_module_ctx(r, ngx_http_upload_module);
     
    7711028    ngx_str_t                   dummy = ngx_string("<ngx_upload_module_dummy>");
    7721029    ngx_table_elt_t             *h;
    7731030
     1031    if(ngx_http_upload_add_headers(r, ulcf) != NGX_OK) {
     1032        return NGX_HTTP_INTERNAL_SERVER_ERROR;
     1033    }
     1034
    7741035    if(ctx->prevent_output) {
    7751036        r->headers_out.status = NGX_HTTP_CREATED;
    7761037
     
    9521213    ngx_http_upload_loc_conf_t  *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
    9531214
    9541215    ngx_file_t  *file = &u->output_file;
    955     ngx_path_t  *path = ulcf->store_path;
     1216    ngx_path_t  *path = u->store_path;
     1217    ngx_path_t  *state_path = u->state_store_path;
    9561218    uint32_t    n;
    9571219    ngx_uint_t  i;
    9581220    ngx_int_t   rc;
     
    9921254                           "hashed path: %s", file->name.data);
    9931255
    9941256            if(u->partial_content) {
     1257                ngx_file_t *state_file = &u->state_file;
    9951258                if(u->merge_buffer == NULL) {
    9961259                    u->merge_buffer = ngx_palloc(r->pool, ulcf->merge_buffer_size);
    9971260
     
    9991262                        return NGX_UPLOAD_NOMEM;
    10001263                }
    10011264
    1002                 u->state_file.name.len = file->name.len + sizeof(".state") - 1;
    1003                 u->state_file.name.data = ngx_palloc(u->request->pool, u->state_file.name.len + 1);
     1265                state_file->name.len = state_path->name.len + 1 + state_path->len + u->session_id.len + sizeof(".state");
     1266                state_file->name.data = ngx_palloc(u->request->pool, state_file->name.len + 1);
    10041267
    1005                 if(u->state_file.name.data == NULL)
     1268                if(state_file->name.data == NULL)
    10061269                    return NGX_UPLOAD_NOMEM;
    10071270
    1008                 ngx_memcpy(u->state_file.name.data, file->name.data, file->name.len);
     1271                ngx_memcpy(state_file->name.data, state_path->name.data, state_path->name.len);
     1272                (void) ngx_sprintf(state_file->name.data + state_path->name.len + 1 + state_path->len,
     1273                        "%V.state%Z", &u->session_id);
    10091274
    1010                 /*
    1011                  * NOTE: we add terminating zero for system calls
    1012                  */
    1013                 ngx_memcpy(u->state_file.name.data + file->name.len, ".state", sizeof(".state") - 1 + 1);
     1275                ngx_create_hashed_filename(state_path, state_file->name.data, state_file->name.len);
    10141276
    10151277                ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
    1016                                "hashed path of state file: %s", u->state_file.name.data);
     1278                               "hashed path of state file: %s", state_file->name.data);
    10171279            }
    10181280
    10191281            file->fd = ngx_open_file(file->name.data, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN, ulcf->store_access);
     
    11171379        if(u->sha1_ctx != NULL)
    11181380            SHA1_Init(&u->sha1_ctx->sha1);
    11191381
     1382        if(u->sha256_ctx != NULL)
     1383            SHA256_Init(&u->sha256_ctx->sha256);
     1384
     1385        if(u->sha512_ctx != NULL)
     1386            SHA512_Init(&u->sha512_ctx->sha512);
     1387
    11201388        if(u->calculate_crc32)
    11211389            ngx_crc32_init(u->crc32);
    11221390
     
    11501418#if (NGX_PCRE)
    11511419                rc = ngx_regex_exec(f[i].regex, &u->field_name, NULL, 0);
    11521420
    1153                 if (rc != NGX_REGEX_NO_MATCHED && rc < 0) {
     1421                /* Modified by Naren to work around iMovie and Quicktime which send empty values Added:  &&  u->field_name.len > 0 */
     1422                if ((ulcf->empty_field_names && rc != NGX_REGEX_NO_MATCHED && rc < 0 && u->field_name.len != 0)
     1423                    || (!ulcf->empty_field_names && rc != NGX_REGEX_NO_MATCHED && rc < 0))
     1424                {
    11541425                    return NGX_UPLOAD_SCRIPTERROR;
    11551426                }
    11561427
     
    11661437            }
    11671438        }
    11681439
    1169         if(pass_field && u->field_name.len > 0) {
     1440        if(pass_field && u->field_name.len != 0) {
    11701441            /*
    11711442             * Here we do a small hack: the content of a non-file field
    11721443             * is not known until ngx_http_upload_flush_output_buffer
     
    12071478        if(u->sha1_ctx)
    12081479            SHA1_Final(u->sha1_ctx->sha1_digest, &u->sha1_ctx->sha1);
    12091480
     1481        if(u->sha256_ctx)
     1482            SHA256_Final(u->sha256_ctx->sha256_digest, &u->sha256_ctx->sha256);
     1483
     1484        if(u->sha512_ctx)
     1485            SHA512_Final(u->sha512_ctx->sha512_digest, &u->sha512_ctx->sha512);
     1486
    12101487        if(u->calculate_crc32)
    12111488            ngx_crc32_final(u->crc32);
    12121489
     
    13691646        if(u->sha1_ctx)
    13701647            SHA1_Update(&u->sha1_ctx->sha1, buf, len);
    13711648
     1649        if(u->sha256_ctx)
     1650            SHA256_Update(&u->sha256_ctx->sha256, buf, len);
     1651
     1652        if(u->sha512_ctx)
     1653            SHA512_Update(&u->sha512_ctx->sha512, buf, len);
     1654
    13721655        if(u->calculate_crc32)
    13731656            ngx_crc32_update(&u->crc32, buf, len);
    13741657
     
    16781961    ngx_http_upload_merger_state_t ms;
    16791962    off_t        remaining;
    16801963    ssize_t      rc;
    1681     int          result;
     1964    __attribute__((__unused__)) int result;
    16821965    ngx_buf_t    in_buf;
    16831966    ngx_buf_t    out_buf;
    16841967    ngx_http_upload_loc_conf_t  *ulcf = ngx_http_get_module_loc_conf(u->request, ngx_http_upload_module);
     
    17992082    conf->forward_args = NGX_CONF_UNSET;
    18002083    conf->tame_arrays = NGX_CONF_UNSET;
    18012084    conf->resumable_uploads = NGX_CONF_UNSET;
     2085    conf->empty_field_names = NGX_CONF_UNSET;
    18022086
    18032087    conf->buffer_size = NGX_CONF_UNSET_SIZE;
    18042088    conf->merge_buffer_size = NGX_CONF_UNSET_SIZE;
     
    18092093    conf->limit_rate = NGX_CONF_UNSET_SIZE;
    18102094
    18112095    /*
     2096     * conf->header_templates,
    18122097     * conf->field_templates,
    18132098     * conf->aggregate_field_templates,
    18142099     * and conf->field_filters are
     
    18302115    }
    18312116
    18322117    if(conf->url.len != 0) {
    1833 #if defined nginx_version && nginx_version >= 7052
    1834         ngx_conf_merge_path_value(cf,
     2118        ngx_http_upload_merge_path_value(cf,
    18352119                                  &conf->store_path,
    18362120                                  prev->store_path,
    18372121                                  &ngx_http_upload_temp_path);
    18382122
    1839         ngx_conf_merge_path_value(cf,
     2123        ngx_http_upload_merge_path_value(cf,
    18402124                                  &conf->state_store_path,
    18412125                                  prev->state_store_path,
    18422126                                  &ngx_http_upload_temp_path);
    1843 #else
    1844         ngx_conf_merge_path_value(conf->store_path,
    1845                                   prev->store_path,
    1846                                   NGX_HTTP_PROXY_TEMP_PATH, 1, 2, 0,
    1847                                   ngx_garbage_collector_temp_handler, cf);
    1848 
    1849         ngx_conf_merge_path_value(conf->state_store_path,
    1850                                   prev->state_store_path,
    1851                                   NGX_HTTP_PROXY_TEMP_PATH, 1, 2, 0,
    1852                                   ngx_garbage_collector_temp_handler, cf);
    1853 #endif
    18542127    }
    18552128
    18562129    ngx_conf_merge_uint_value(conf->store_access,
     
    18972170            prev->resumable_uploads : 0;
    18982171    }
    18992172
     2173    if(conf->empty_field_names == NGX_CONF_UNSET) {
     2174        conf->empty_field_names = (prev->empty_field_names != NGX_CONF_UNSET) ?
     2175            prev->empty_field_names : 0;
     2176    }
     2177
    19002178    if(conf->field_templates == NULL) {
    19012179        conf->field_templates = prev->field_templates;
    19022180    }
     
    19122190            conf->sha1 = prev->sha1;
    19132191        }
    19142192
     2193        if(prev->sha256) {
     2194            conf->sha256 = prev->sha256;
     2195        }
     2196
     2197        if(prev->sha512) {
     2198            conf->sha512 = prev->sha512;
     2199        }
     2200
    19152201        if(prev->crc32) {
    19162202            conf->crc32 = prev->crc32;
    19172203        }
     
    19252211        conf->cleanup_statuses = prev->cleanup_statuses;
    19262212    }
    19272213
     2214    if(conf->header_templates == NULL) {
     2215        conf->header_templates = prev->header_templates;
     2216    }
     2217
    19282218    return NGX_CONF_OK;
    19292219} /* }}} */
    19302220
     
    20662356    return NGX_OK;
    20672357} /* }}} */
    20682358
     2359static ngx_int_t /* {{{ ngx_http_upload_sha256_variable */
     2360ngx_http_upload_sha256_variable(ngx_http_request_t *r,
     2361    ngx_http_variable_value_t *v,  uintptr_t data)
     2362{
     2363    ngx_uint_t             i;
     2364    ngx_http_upload_ctx_t  *u;
     2365    u_char                 *c;
     2366    u_char                 *hex_table;
     2367
     2368    u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
     2369
     2370    if(u->sha256_ctx == NULL || u->partial_content) {
     2371        v->not_found = 1;
     2372        return NGX_OK;
     2373    }
     2374
     2375    v->valid = 1;
     2376    v->no_cacheable = 0;
     2377    v->not_found = 0;
     2378
     2379    hex_table = (u_char*)data;
     2380    c = u->sha256_ctx->sha256_digest + SHA256_DIGEST_LENGTH * 2;
     2381
     2382    i = SHA256_DIGEST_LENGTH;
     2383
     2384    do{
     2385        i--;
     2386        *--c = hex_table[u->sha256_ctx->sha256_digest[i] & 0xf];
     2387        *--c = hex_table[u->sha256_ctx->sha256_digest[i] >> 4];
     2388    }while(i != 0);
     2389
     2390    v->data = u->sha256_ctx->sha256_digest;
     2391    v->len = SHA256_DIGEST_LENGTH * 2;
     2392
     2393    return NGX_OK;
     2394} /* }}} */
     2395
     2396static ngx_int_t /* {{{ ngx_http_upload_sha512_variable */
     2397ngx_http_upload_sha512_variable(ngx_http_request_t *r,
     2398    ngx_http_variable_value_t *v,  uintptr_t data)
     2399{
     2400    ngx_uint_t             i;
     2401    ngx_http_upload_ctx_t  *u;
     2402    u_char                 *c;
     2403    u_char                 *hex_table;
     2404
     2405    u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
     2406
     2407    if(u->sha512_ctx == NULL || u->partial_content) {
     2408        v->not_found = 1;
     2409        return NGX_OK;
     2410    }
     2411
     2412    v->valid = 1;
     2413    v->no_cacheable = 0;
     2414    v->not_found = 0;
     2415
     2416    hex_table = (u_char*)data;
     2417    c = u->sha512_ctx->sha512_digest + SHA512_DIGEST_LENGTH * 2;
     2418
     2419    i = SHA512_DIGEST_LENGTH;
     2420
     2421    do{
     2422        i--;
     2423        *--c = hex_table[u->sha512_ctx->sha512_digest[i] & 0xf];
     2424        *--c = hex_table[u->sha512_ctx->sha512_digest[i] >> 4];
     2425    }while(i != 0);
     2426
     2427    v->data = u->sha512_ctx->sha512_digest;
     2428    v->len = SHA512_DIGEST_LENGTH * 2;
     2429
     2430    return NGX_OK;
     2431} /* }}} */
     2432
    20692433static ngx_int_t /* {{{ ngx_http_upload_crc32_variable */
    20702434ngx_http_upload_crc32_variable(ngx_http_request_t *r,
    20712435    ngx_http_variable_value_t *v,  uintptr_t data)
     
    22992663                                       ", upload_file_md5_uc"
    23002664                                       ", upload_file_sha1"
    23012665                                       ", upload_file_sha1_uc"
     2666                                       ", upload_file_sha256"
     2667                                       ", upload_file_sha256_uc"
     2668                                       ", upload_file_sha512"
     2669                                       ", upload_file_sha512_uc"
    23022670                                       ", upload_file_crc32"
    23032671                                       ", upload_content_range"
    23042672                                       " and upload_file_size"
     
    23122680                if(v->get_handler == ngx_http_upload_sha1_variable)
    23132681                    ulcf->sha1 = 1;
    23142682
     2683                if(v->get_handler == ngx_http_upload_sha256_variable)
     2684                    ulcf->sha256 = 1;
     2685
     2686                if(v->get_handler == ngx_http_upload_sha512_variable)
     2687                    ulcf->sha512 = 1;
     2688
    23152689                if(v->get_handler == ngx_http_upload_crc32_variable)
    23162690                    ulcf->crc32 = 1;
    23172691            }
     
    23962770    return NGX_CONF_OK;
    23972771} /* }}} */
    23982772
    2399 static char * /* {{{ ngx_http_upload_cleanup */
    2400 ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     2773static char * /* {{{ ngx_http_upload_add_header */
     2774ngx_http_upload_add_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    24012775{
    2402     ngx_http_upload_loc_conf_t *ulcf = conf;
    2403 
    24042776    ngx_str_t                  *value;
    2405     ngx_uint_t                 i;
    2406     ngx_int_t                  status, lo, hi;
    2407     uint16_t                   *s;
     2777    ngx_http_upload_header_template_t *h;
     2778    ngx_array_t                 **field;
     2779    ngx_http_compile_complex_value_t   ccv;
     2780
     2781    field = (ngx_array_t**) (((u_char*)conf) + cmd->offset);
    24082782
    24092783    value = cf->args->elts;
    24102784
    2411     if (ulcf->cleanup_statuses == NULL) {
    2412         ulcf->cleanup_statuses = ngx_array_create(cf->pool, 1,
    2413                                         sizeof(uint16_t));
    2414         if (ulcf->cleanup_statuses == NULL) {
     2785    /*
     2786     * Add new entry to header template list
     2787     */
     2788    if (*field == NULL) {
     2789        *field = ngx_array_create(cf->pool, 1,
     2790                                  sizeof(ngx_http_upload_header_template_t));
     2791        if (*field == NULL) {
    24152792            return NGX_CONF_ERROR;
    24162793        }
    24172794    }
    24182795
    2419     for (i = 1; i < cf->args->nelts; i++) {
    2420         if(value[i].len > 4 && value[i].data[3] == '-') {
    2421             lo = ngx_atoi(value[i].data, 3);
    2422 
    2423             if (lo == NGX_ERROR) {
    2424                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
    2425                                    "invalid lower bound \"%V\"", &value[i]);
    2426                 return NGX_CONF_ERROR;
    2427             }
    2428 
    2429             hi = ngx_atoi(value[i].data + 4, value[i].len - 4);
     2796    h = ngx_array_push(*field);
     2797    if (h == NULL) {
     2798        return NGX_CONF_ERROR;
     2799    }
     2800
     2801    /*
     2802     * Compile header name
     2803     */
     2804    h->name = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
     2805    if(h->name == NULL) {
     2806        return NGX_CONF_ERROR;
     2807    }
     2808
     2809    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
     2810
     2811    ccv.cf = cf;
     2812    ccv.value = &value[1];
     2813    ccv.complex_value = h->name;
     2814
     2815    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
     2816        return NGX_CONF_ERROR;
     2817    }
     2818
     2819    /*
     2820     * Compile header value
     2821     */
     2822    h->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
     2823    if(h->value == NULL) {
     2824        return NGX_CONF_ERROR;
     2825    }
     2826
     2827    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
     2828
     2829    ccv.cf = cf;
     2830    ccv.value = &value[2];
     2831    ccv.complex_value = h->value;
     2832
     2833    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
     2834        return NGX_CONF_ERROR;
     2835    }
     2836
     2837    return NGX_CONF_OK;
     2838} /* }}} */
     2839
     2840static char * /* {{{ ngx_http_upload_cleanup */
     2841ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     2842{
     2843    ngx_http_upload_loc_conf_t *ulcf = conf;
     2844
     2845    ngx_str_t                  *value;
     2846    ngx_uint_t                 i;
     2847    ngx_int_t                  status, lo, hi;
     2848    uint16_t                   *s;
     2849
     2850    value = cf->args->elts;
     2851
     2852    if (ulcf->cleanup_statuses == NULL) {
     2853        ulcf->cleanup_statuses = ngx_array_create(cf->pool, 1,
     2854                                        sizeof(uint16_t));
     2855        if (ulcf->cleanup_statuses == NULL) {
     2856            return NGX_CONF_ERROR;
     2857        }
     2858    }
     2859
     2860    for (i = 1; i < cf->args->nelts; i++) {
     2861        if(value[i].len > 4 && value[i].data[3] == '-') {
     2862            lo = ngx_atoi(value[i].data, 3);
     2863
     2864            if (lo == NGX_ERROR) {
     2865                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
     2866                                   "invalid lower bound \"%V\"", &value[i]);
     2867                return NGX_CONF_ERROR;
     2868            }
     2869
     2870            hi = ngx_atoi(value[i].data + 4, value[i].len - 4);
    24302871
    24312872            if (hi == NGX_ERROR) {
    24322873                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
     
    24532894            hi = lo = status;
    24542895        }
    24552896
    2456         if (lo < 400 || hi > 599) {
     2897        if (lo < 200 || hi > 599) {
    24572898            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
    2458                                "value(s) \"%V\" must be between 400 and 599",
     2899                               "value(s) \"%V\" must be between 200 and 599",
    24592900                               &value[i]);
    24602901            return NGX_CONF_ERROR;
    24612902        }
     
    25232964    return NGX_CONF_OK;
    25242965} /* }}} */
    25252966
     2967static char * /* {{{ ngx_http_upload_set_path_slot */
     2968ngx_http_upload_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     2969{
     2970    char  *p = conf;
     2971
     2972    ssize_t      level;
     2973    ngx_str_t   *value;
     2974    ngx_uint_t   i, n;
     2975    ngx_http_upload_path_t *path, **slot;
     2976    ngx_http_compile_complex_value_t   ccv;
     2977
     2978    slot = (ngx_http_upload_path_t **) (p + cmd->offset);
     2979
     2980    if (*slot) {
     2981        return "is duplicate";
     2982    }
     2983
     2984    path = ngx_pcalloc(cf->pool, sizeof(ngx_http_upload_path_t));
     2985    if (path == NULL) {
     2986        return NGX_CONF_ERROR;
     2987    }
     2988
     2989    path->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
     2990    if (path->path == NULL) {
     2991        return NGX_CONF_ERROR;
     2992    }
     2993
     2994    value = cf->args->elts;
     2995
     2996    path->path->name = value[1];
     2997
     2998    if (path->path->name.data[path->path->name.len - 1] == '/') {
     2999        path->path->name.len--;
     3000    }
     3001
     3002    if (ngx_conf_full_name(cf->cycle, &path->path->name, 0) != NGX_OK) {
     3003        return NULL;
     3004    }
     3005
     3006    path->path->len = 0;
     3007    path->path->manager = NULL;
     3008    path->path->loader = NULL;
     3009    path->path->conf_file = cf->conf_file->file.name.data;
     3010    path->path->line = cf->conf_file->line;
     3011
     3012    for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {
     3013        level = ngx_atoi(value[n].data, value[n].len);
     3014        if (level == NGX_ERROR || level == 0) {
     3015            return "invalid value";
     3016        }
     3017
     3018        path->path->level[i] = level;
     3019        path->path->len += level + 1;
     3020    }
     3021
     3022    while (i < 3) {
     3023        path->path->level[i++] = 0;
     3024    }
     3025
     3026    *slot = path;
     3027
     3028    if(ngx_http_script_variables_count(&value[1])) {
     3029        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
     3030
     3031        ccv.cf = cf;
     3032        ccv.value = &value[1];
     3033        ccv.complex_value = &path->dynamic;
     3034
     3035        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
     3036            return NGX_CONF_ERROR;
     3037        }
     3038
     3039        path->is_dynamic = 1;
     3040    }
     3041    else {
     3042        if (ngx_add_path(cf, &path->path) == NGX_ERROR) {
     3043            return NGX_CONF_ERROR;
     3044        }
     3045    }
     3046
     3047    return NGX_CONF_OK;
     3048} /* }}} */
     3049
     3050
     3051static char * /* {{{ ngx_http_upload_merge_path_value */
     3052ngx_http_upload_merge_path_value(ngx_conf_t *cf, ngx_http_upload_path_t **path, ngx_http_upload_path_t *prev,
     3053    ngx_path_init_t *init)
     3054{
     3055    if (*path) {
     3056        return NGX_CONF_OK;
     3057    }
     3058
     3059    if (prev) {
     3060        *path = prev;
     3061        return NGX_CONF_OK;
     3062    }
     3063
     3064    *path = ngx_palloc(cf->pool, sizeof(ngx_http_upload_path_t));
     3065    if(*path == NULL) {
     3066        return NGX_CONF_ERROR;
     3067    }
     3068
     3069    (*path)->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
     3070    if((*path)->path == NULL) {
     3071        return NGX_CONF_ERROR;
     3072    }
     3073
     3074    (*path)->path->name = init->name;
     3075
     3076    if(ngx_conf_full_name(cf->cycle, &(*path)->path->name, 0) != NGX_OK) {
     3077        return NGX_CONF_ERROR;
     3078    }
     3079
     3080    (*path)->path->level[0] = init->level[0];
     3081    (*path)->path->level[1] = init->level[1];
     3082    (*path)->path->level[2] = init->level[2];
     3083
     3084    (*path)->path->len = init->level[0] + (init->level[0] ? 1 : 0)
     3085                   + init->level[1] + (init->level[1] ? 1 : 0)
     3086                   + init->level[2] + (init->level[2] ? 1 : 0);
     3087
     3088    (*path)->path->manager = NULL;
     3089    (*path)->path->loader = NULL;
     3090    (*path)->path->conf_file = NULL;
     3091
     3092    if(ngx_add_path(cf, &(*path)->path) != NGX_OK) {
     3093        return NGX_CONF_ERROR;
     3094    }
     3095
     3096    return NGX_CONF_OK;
     3097} /* }}} */
     3098
     3099static ngx_int_t
     3100ngx_http_write_request_body(ngx_http_request_t *r)
     3101{
     3102    ssize_t                    n;
     3103    ngx_chain_t               *cl;
     3104    ngx_temp_file_t           *tf;
     3105    ngx_http_request_body_t   *rb;
     3106    ngx_http_core_loc_conf_t  *clcf;
     3107
     3108    rb = r->request_body;
     3109
     3110    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     3111                   "http write client request body, bufs %p", rb->bufs);
     3112
     3113    if (rb->temp_file == NULL) {
     3114        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
     3115        if (tf == NULL) {
     3116            return NGX_ERROR;
     3117        }
     3118
     3119        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
     3120
     3121        tf->file.fd = NGX_INVALID_FILE;
     3122        tf->file.log = r->connection->log;
     3123        tf->path = clcf->client_body_temp_path;
     3124        tf->pool = r->pool;
     3125        tf->warn = "a client request body is buffered to a temporary file";
     3126        tf->log_level = r->request_body_file_log_level;
     3127        tf->persistent = r->request_body_in_persistent_file;
     3128        tf->clean = r->request_body_in_clean_file;
     3129
     3130        if (r->request_body_file_group_access) {
     3131            tf->access = 0660;
     3132        }
     3133
     3134        rb->temp_file = tf;
     3135
     3136        if (rb->bufs == NULL) {
     3137            /* empty body with r->request_body_in_file_only */
     3138
     3139            if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
     3140                                     tf->persistent, tf->clean, tf->access)
     3141                != NGX_OK)
     3142            {
     3143                return NGX_ERROR;
     3144            }
     3145
     3146            return NGX_OK;
     3147        }
     3148    }
     3149
     3150    if (rb->bufs == NULL) {
     3151        return NGX_OK;
     3152    }
     3153
     3154    n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);
     3155
     3156    /* TODO: n == 0 or not complete and level event */
     3157
     3158    if (n == NGX_ERROR) {
     3159        return NGX_ERROR;
     3160    }
     3161
     3162    rb->temp_file->offset += n;
     3163
     3164    /* mark all buffers as written */
     3165
     3166    for (cl = rb->bufs; cl; cl = cl->next) {
     3167        cl->buf->pos = cl->buf->last;
     3168    }
     3169
     3170    rb->bufs = NULL;
     3171
     3172    return NGX_OK;
     3173}
     3174
     3175static ngx_int_t
     3176ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
     3177{
     3178    if (r->headers_in.chunked) {
     3179        return ngx_http_request_body_chunked_filter(r, in);
     3180
     3181    } else {
     3182        return ngx_http_request_body_length_filter(r, in);
     3183    }
     3184}
     3185
     3186static ngx_int_t
     3187ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
     3188{
     3189#if (NGX_DEBUG)
     3190    ngx_chain_t               *cl;
     3191#endif
     3192    ngx_http_request_body_t   *rb;
     3193
     3194    rb = r->request_body;
     3195
     3196#if (NGX_DEBUG)
     3197
     3198    for (cl = rb->bufs; cl; cl = cl->next) {
     3199        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
     3200                       "http body old buf t:%d f:%d %p, pos %p, size: %z "
     3201                       "file: %O, size: %z",
     3202                       cl->buf->temporary, cl->buf->in_file,
     3203                       cl->buf->start, cl->buf->pos,
     3204                       cl->buf->last - cl->buf->pos,
     3205                       cl->buf->file_pos,
     3206                       cl->buf->file_last - cl->buf->file_pos);
     3207    }
     3208
     3209    for (cl = in; cl; cl = cl->next) {
     3210        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
     3211                       "http body new buf t:%d f:%d %p, pos %p, size: %z "
     3212                       "file: %O, size: %z",
     3213                       cl->buf->temporary, cl->buf->in_file,
     3214                       cl->buf->start, cl->buf->pos,
     3215                       cl->buf->last - cl->buf->pos,
     3216                       cl->buf->file_pos,
     3217                       cl->buf->file_last - cl->buf->file_pos);
     3218    }
     3219
     3220#endif
     3221
     3222    /* TODO: coalesce neighbouring buffers */
     3223
     3224    if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
     3225        return NGX_HTTP_INTERNAL_SERVER_ERROR;
     3226    }
     3227
     3228    return NGX_OK;
     3229}
     3230
     3231
     3232static ngx_int_t
     3233ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
     3234{
     3235    size_t                     size;
     3236    ngx_int_t                  rc;
     3237    ngx_buf_t                 *b;
     3238    ngx_chain_t               *cl, *tl, *out, **ll;
     3239    ngx_http_request_body_t   *rb;
     3240
     3241    rb = r->request_body;
     3242
     3243    if (rb->rest == -1) {
     3244        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     3245                       "http request body content length filter");
     3246
     3247        rb->rest = r->headers_in.content_length_n;
     3248    }
     3249
     3250    out = NULL;
     3251    ll = &out;
     3252
     3253    for (cl = in; cl; cl = cl->next) {
     3254
     3255        tl = ngx_chain_get_free_buf(r->pool, &rb->free);
     3256        if (tl == NULL) {
     3257            return NGX_HTTP_INTERNAL_SERVER_ERROR;
     3258        }
     3259
     3260        b = tl->buf;
     3261
     3262        ngx_memzero(b, sizeof(ngx_buf_t));
     3263
     3264        b->temporary = 1;
     3265        b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
     3266        b->start = cl->buf->start;
     3267        b->pos = cl->buf->pos;
     3268        b->last = cl->buf->last;
     3269        b->end = cl->buf->end;
     3270
     3271        size = cl->buf->last - cl->buf->pos;
     3272
     3273        if ((off_t) size < rb->rest) {
     3274            cl->buf->pos = cl->buf->last;
     3275            rb->rest -= size;
     3276
     3277        } else {
     3278            cl->buf->pos += rb->rest;
     3279            rb->rest = 0;
     3280            b->last = cl->buf->pos;
     3281            b->last_buf = 1;
     3282        }
     3283
     3284        *ll = tl;
     3285        ll = &tl->next;
     3286    }
     3287
     3288    rc = ngx_http_request_body_save_filter(r, out);
     3289
     3290    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
     3291                            (ngx_buf_tag_t) &ngx_http_read_client_request_body);
     3292
     3293    return rc;
     3294}
     3295
     3296static ngx_int_t
     3297ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
     3298{
     3299    size_t                     size;
     3300    ngx_int_t                  rc;
     3301    ngx_buf_t                 *b;
     3302    ngx_chain_t               *cl, *out, *tl, **ll;
     3303    ngx_http_request_body_t   *rb;
     3304    ngx_http_core_loc_conf_t  *clcf;
     3305
     3306    rb = r->request_body;
     3307
     3308    if (rb->rest == -1) {
     3309
     3310        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     3311                       "http request body chunked filter");
     3312
     3313        rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
     3314        if (rb->chunked == NULL) {
     3315            return NGX_HTTP_INTERNAL_SERVER_ERROR;
     3316        }
     3317
     3318        r->headers_in.content_length_n = 0;
     3319        rb->rest = 3;
     3320    }
     3321
     3322    out = NULL;
     3323    ll = &out;
     3324
     3325    for (cl = in; cl; cl = cl->next) {
     3326
     3327        for ( ;; ) {
     3328
     3329            ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
     3330                           "http body chunked buf "
     3331                           "t:%d f:%d %p, pos %p, size: %z file: %O, size: %z",
     3332                           cl->buf->temporary, cl->buf->in_file,
     3333                           cl->buf->start, cl->buf->pos,
     3334                           cl->buf->last - cl->buf->pos,
     3335                           cl->buf->file_pos,
     3336                           cl->buf->file_last - cl->buf->file_pos);
     3337
     3338            rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
     3339
     3340            if (rc == NGX_OK) {
     3341
     3342                /* a chunk has been parsed successfully */
     3343
     3344                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
     3345
     3346                if (clcf->client_max_body_size
     3347                    && clcf->client_max_body_size
     3348                       < r->headers_in.content_length_n + rb->chunked->size)
     3349                {
     3350                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
     3351                                  "client intended to send too large chunked "
     3352                                  "body: %O bytes",
     3353                                  r->headers_in.content_length_n
     3354                                  + rb->chunked->size);
     3355
     3356                    r->lingering_close = 1;
     3357
     3358                    return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
     3359                }
     3360
     3361                tl = ngx_chain_get_free_buf(r->pool, &rb->free);
     3362                if (tl == NULL) {
     3363                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
     3364                }
     3365
     3366                b = tl->buf;
     3367
     3368                ngx_memzero(b, sizeof(ngx_buf_t));
     3369
     3370                b->temporary = 1;
     3371                b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
     3372                b->start = cl->buf->start;
     3373                b->pos = cl->buf->pos;
     3374                b->last = cl->buf->last;
     3375                b->end = cl->buf->end;
     3376
     3377                *ll = tl;
     3378                ll = &tl->next;
     3379
     3380                size = cl->buf->last - cl->buf->pos;
     3381
     3382                if ((off_t) size > rb->chunked->size) {
     3383                   cl->buf->pos += rb->chunked->size;
     3384                    r->headers_in.content_length_n += rb->chunked->size;
     3385                    rb->chunked->size = 0;
     3386
     3387                } else {
     3388                    rb->chunked->size -= size;
     3389                    r->headers_in.content_length_n += size;
     3390                    cl->buf->pos = cl->buf->last;
     3391                }
     3392
     3393                b->last = cl->buf->pos;
     3394
     3395                continue;
     3396            }
     3397
     3398            if (rc == NGX_DONE) {
     3399
     3400                /* a whole response has been parsed successfully */
     3401
     3402                rb->rest = 0;
     3403
     3404                tl = ngx_chain_get_free_buf(r->pool, &rb->free);
     3405                if (tl == NULL) {
     3406                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
     3407                }
     3408
     3409                b = tl->buf;
     3410
     3411                ngx_memzero(b, sizeof(ngx_buf_t));
     3412
     3413                b->last_buf = 1;
     3414
     3415                *ll = tl;
     3416                ll = &tl->next;
     3417
     3418                break;
     3419            }
     3420
     3421            if (rc == NGX_AGAIN) {
     3422
     3423                /* set rb->rest, amount of data we want to see next time */
     3424
     3425                rb->rest = rb->chunked->length;
     3426
     3427                break;
     3428            }
     3429
     3430            /* invalid */
     3431
     3432            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
     3433                          "client sent invalid chunked body");
     3434
     3435            return NGX_HTTP_BAD_REQUEST;
     3436        }
     3437    }
     3438
     3439    rc = ngx_http_request_body_save_filter(r, out);
     3440
     3441    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
     3442                            (ngx_buf_tag_t) &ngx_http_read_client_request_body);
     3443
     3444    return rc;
     3445}
     3446
     3447static ngx_int_t
     3448ngx_http_do_read_client_request_body(ngx_http_request_t *r)
     3449{
     3450    off_t                      rest;
     3451    size_t                     size;
     3452    ssize_t                    n;
     3453    ngx_int_t                  rc;
     3454    ngx_buf_t                 *b;
     3455    ngx_chain_t               *cl, out;
     3456    ngx_connection_t          *c;
     3457    ngx_http_request_body_t   *rb;
     3458    ngx_http_core_loc_conf_t  *clcf;
     3459
     3460    c = r->connection;
     3461    rb = r->request_body;
     3462
     3463    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
     3464                   "http read client request body");
     3465
     3466    for ( ;; ) {
     3467        for ( ;; ) {
     3468            if (rb->buf->last == rb->buf->end) {
     3469
     3470                /* pass buffer to request body filter chain */
     3471
     3472                out.buf = rb->buf;
     3473                out.next = NULL;
     3474
     3475                rc = ngx_http_request_body_filter(r, &out);
     3476
     3477                if (rc != NGX_OK) {
     3478                    return rc;
     3479                }
     3480
     3481                /* write to file */
     3482
     3483                if (ngx_http_write_request_body(r) != NGX_OK) {
     3484                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
     3485                }
     3486
     3487                /* update chains */
     3488
     3489                rc = ngx_http_request_body_filter(r, NULL);
     3490
     3491                if (rc != NGX_OK) {
     3492                    return rc;
     3493                }
     3494
     3495                if (rb->busy != NULL) {
     3496                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
     3497                }
     3498
     3499                rb->buf->pos = rb->buf->start;
     3500                rb->buf->last = rb->buf->start;
     3501            }
     3502            size = rb->buf->end - rb->buf->last;
     3503            rest = rb->rest - (rb->buf->last - rb->buf->pos);
     3504
     3505            if ((off_t) size > rest) {
     3506                size = (size_t) rest;
     3507            }
     3508
     3509            n = c->recv(c, rb->buf->last, size);
     3510
     3511            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
     3512                           "http client request body recv %z", n);
     3513
     3514            if (n == NGX_AGAIN) {
     3515                break;
     3516            }
     3517
     3518            if (n == 0) {
     3519                ngx_log_error(NGX_LOG_INFO, c->log, 0,
     3520                              "client prematurely closed connection");
     3521            }
     3522
     3523            if (n == 0 || n == NGX_ERROR) {
     3524                c->error = 1;
     3525                return NGX_HTTP_BAD_REQUEST;
     3526            }
     3527
     3528            rb->buf->last += n;
     3529            r->request_length += n;
     3530
     3531            if (n == rest) {
     3532                /* pass buffer to request body filter chain */
     3533
     3534                out.buf = rb->buf;
     3535                out.next = NULL;
     3536
     3537                rc = ngx_http_request_body_filter(r, &out);
     3538
     3539                if (rc != NGX_OK) {
     3540                    return rc;
     3541                }
     3542            }
     3543
     3544            if (rb->rest == 0) {
     3545                break;
     3546            }
     3547
     3548            if (rb->buf->last < rb->buf->end) {
     3549                break;
     3550            }
     3551        }
     3552
     3553        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
     3554                       "http client request body rest %O", rb->rest);
     3555                      if (rb->rest == 0) {
     3556            break;
     3557        }
     3558
     3559        if (!c->read->ready) {
     3560            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
     3561            ngx_add_timer(c->read, clcf->client_body_timeout);
     3562
     3563            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
     3564                return NGX_HTTP_INTERNAL_SERVER_ERROR;
     3565            }
     3566
     3567            return NGX_AGAIN;
     3568        }
     3569    }
     3570
     3571    if (c->read->timer_set) {
     3572        ngx_del_timer(c->read);
     3573    }
     3574
     3575    if (rb->temp_file || r->request_body_in_file_only) {
     3576
     3577        /* save the last part */
     3578
     3579        if (ngx_http_write_request_body(r) != NGX_OK) {
     3580            return NGX_HTTP_INTERNAL_SERVER_ERROR;
     3581        }
     3582
     3583        cl = ngx_chain_get_free_buf(r->pool, &rb->free);
     3584        if (cl == NULL) {
     3585            return NGX_HTTP_INTERNAL_SERVER_ERROR;
     3586        }
     3587
     3588        b = cl->buf;
     3589
     3590        ngx_memzero(b, sizeof(ngx_buf_t));
     3591
     3592        b->in_file = 1;
     3593        b->file_last = rb->temp_file->file.offset;
     3594        b->file = &rb->temp_file->file;
     3595
     3596        rb->bufs = cl;
     3597    }
     3598
     3599    r->read_event_handler = ngx_http_block_reading;
     3600
     3601    rb->post_handler(r);
     3602
     3603    return NGX_OK;
     3604}
     3605
     3606
     3607static void
     3608ngx_http_read_client_request_body_handler(ngx_http_request_t *r)
     3609{
     3610    ngx_int_t  rc;
     3611
     3612    if (r->connection->read->timedout) {
     3613        r->connection->timedout = 1;
     3614        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
     3615        return;
     3616    }
     3617
     3618    rc = ngx_http_do_read_client_request_body(r);
     3619
     3620    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
     3621        ngx_http_finalize_request(r, rc);
     3622    }
     3623}
     3624
     3625
    25263626ngx_int_t /* {{{ ngx_http_read_upload_client_request_body */
    25273627ngx_http_read_upload_client_request_body(ngx_http_request_t *r) {
    25283628    ssize_t                    size, preread;
     
    26253725
    26263726            /* the whole request body may be placed in r->header_in */
    26273727
    2628             rb->to_write = rb->bufs;
    2629 
    2630             r->read_event_handler = ngx_http_read_upload_client_request_body_handler;
     3728            rb->buf = r->header_in;
     3729            r->read_event_handler = ngx_http_read_client_request_body_handler;
     3730            r->write_event_handler = ngx_http_request_empty_handler;
    26313731
    26323732            return ngx_http_do_read_upload_client_request_body(r);
    26333733        }
     
    26843784
    26853785    *next = cl;
    26863786
    2687     rb->to_write = rb->bufs;
     3787    /*
     3788     * rb->to_write = rb->bufs;
     3789     */
    26883790
    26893791    r->read_event_handler = ngx_http_read_upload_client_request_body_handler;
    26903792
     
    27663868        for ( ;; ) {
    27673869            if (rb->buf->last == rb->buf->end) {
    27683870
    2769                 rc = ngx_http_process_request_body(r, rb->to_write);
     3871                 rc = ngx_http_process_request_body(r, rb->bufs);
    27703872
    27713873                switch(rc) {
    27723874                    case NGX_OK:
     
    27813883                    default:
    27823884                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    27833885                }
    2784 
    2785                 rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
     3886              /*
     3887               * rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
     3888               */
    27863889                rb->buf->last = rb->buf->start;
    27873890            }
    27883891
     
    28743977        ngx_del_timer(c->read);
    28753978    }
    28763979
    2877     rc = ngx_http_process_request_body(r, rb->to_write);
     3980    rc = ngx_http_process_request_body(r, rb->bufs);
    28783981
    28793982    switch(rc) {
    28803983        case NGX_OK:
     
    32994402                    return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
    33004403                }
    33014404
     4405                if( (upload_ctx->content_range_n.end - upload_ctx->content_range_n.start + 1)
     4406                    != headers_in->content_length_n)
     4407                {
     4408                    ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0,
     4409                                  "range length is not equal to content length");
     4410                    return NGX_HTTP_RANGE_NOT_SATISFIABLE;
     4411                }
     4412
    33024413                upload_ctx->partial_content = 1;
    33034414            }
    33044415        }
     
    34364547        return NGX_ERROR;
    34374548    }
    34384549
    3439     if(range_n->start >= range_n->end || range_n->start >= range_n->total
    3440         || range_n->end > range_n->total)
     4550    if(range_n->start > range_n->end || range_n->start >= range_n->total
     4551        || range_n->end >= range_n->total)
    34414552    {
    34424553        return NGX_ERROR;
    34434554    }
     
    36734784    }
    36744785} /* }}} */
    36754786
     4787static ngx_int_t /* {{{ */
     4788ngx_http_upload_test_expect(ngx_http_request_t *r)
     4789{
     4790    ngx_int_t   n;
     4791    ngx_str_t  *expect;
     4792
     4793    if (r->expect_tested
     4794        || r->headers_in.expect == NULL
     4795        || r->http_version < NGX_HTTP_VERSION_11)
     4796    {
     4797        return NGX_OK;
     4798    }
     4799
     4800    r->expect_tested = 1;
     4801
     4802    expect = &r->headers_in.expect->value;
     4803
     4804    if (expect->len != sizeof("100-continue") - 1
     4805        || ngx_strncasecmp(expect->data, (u_char *) "100-continue",
     4806                           sizeof("100-continue") - 1)
     4807           != 0)
     4808    {
     4809        return NGX_OK;
     4810    }
     4811
     4812    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     4813                   "send 100 Continue");
     4814
     4815    n = r->connection->send(r->connection,
     4816                            (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
     4817                            sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
     4818
     4819    if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
     4820        return NGX_OK;
     4821    }
     4822
     4823    /* we assume that such small packet should be send successfully */
     4824
     4825    return NGX_ERROR;
     4826} /* }}} */