diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 387f77f9b..3fa8f86c1 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -81,13 +81,10 @@ typedef struct { typedef struct { ngx_http_status_t status; + ngx_http_chunked_t chunked; ngx_http_proxy_vars_t vars; size_t internal_body_length; - ngx_uint_t state; - off_t size; - off_t length; - ngx_uint_t head; /* unsigned head:1 */ } ngx_http_proxy_ctx_t; @@ -1252,7 +1249,7 @@ ngx_http_proxy_reinit_request(ngx_http_request_t *r) ctx->status.count = 0; ctx->status.start = NULL; ctx->status.end = NULL; - ctx->state = 0; + ctx->chunked.state = 0; r->upstream->process_header = ngx_http_proxy_process_status_line; r->upstream->pipe->input_filter = ngx_http_proxy_copy_filter; @@ -1617,265 +1614,6 @@ ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) } -static ngx_inline ngx_int_t -ngx_http_proxy_parse_chunked(ngx_http_request_t *r, ngx_buf_t *buf) -{ - u_char *pos, ch, c; - ngx_int_t rc; - ngx_http_proxy_ctx_t *ctx; - enum { - sw_chunk_start = 0, - sw_chunk_size, - sw_chunk_extension, - sw_chunk_extension_almost_done, - sw_chunk_data, - sw_after_data, - sw_after_data_almost_done, - sw_last_chunk_extension, - sw_last_chunk_extension_almost_done, - sw_trailer, - sw_trailer_almost_done, - sw_trailer_header, - sw_trailer_header_almost_done - } state; - - ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); - - if (ctx == NULL) { - return NGX_ERROR; - } - - state = ctx->state; - - if (state == sw_chunk_data && ctx->size == 0) { - state = sw_after_data; - } - - rc = NGX_AGAIN; - - for (pos = buf->pos; pos < buf->last; pos++) { - - ch = *pos; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http proxy chunked byte: %02Xd s:%d", ch, state); - - switch (state) { - - case sw_chunk_start: - if (ch >= '0' && ch <= '9') { - state = sw_chunk_size; - ctx->size = ch - '0'; - break; - } - - c = (u_char) (ch | 0x20); - - if (c >= 'a' && c <= 'f') { - state = sw_chunk_size; - ctx->size = c - 'a' + 10; - break; - } - - goto invalid; - - case sw_chunk_size: - if (ch >= '0' && ch <= '9') { - ctx->size = ctx->size * 16 + (ch - '0'); - break; - } - - c = (u_char) (ch | 0x20); - - if (c >= 'a' && c <= 'f') { - ctx->size = ctx->size * 16 + (c - 'a' + 10); - break; - } - - if (ctx->size == 0) { - - switch (ch) { - case CR: - state = sw_last_chunk_extension_almost_done; - break; - case LF: - state = sw_trailer; - break; - case ';': - case ' ': - case '\t': - state = sw_last_chunk_extension; - break; - default: - goto invalid; - } - - break; - } - - switch (ch) { - case CR: - state = sw_chunk_extension_almost_done; - break; - case LF: - state = sw_chunk_data; - break; - case ';': - case ' ': - case '\t': - state = sw_chunk_extension; - break; - default: - goto invalid; - } - - break; - - case sw_chunk_extension: - switch (ch) { - case CR: - state = sw_chunk_extension_almost_done; - break; - case LF: - state = sw_chunk_data; - } - break; - - case sw_chunk_extension_almost_done: - if (ch == LF) { - state = sw_chunk_data; - break; - } - goto invalid; - - case sw_chunk_data: - rc = NGX_OK; - goto data; - - case sw_after_data: - switch (ch) { - case CR: - state = sw_after_data_almost_done; - break; - case LF: - state = sw_chunk_start; - } - break; - - case sw_after_data_almost_done: - if (ch == LF) { - state = sw_chunk_start; - break; - } - goto invalid; - - case sw_last_chunk_extension: - switch (ch) { - case CR: - state = sw_last_chunk_extension_almost_done; - break; - case LF: - state = sw_trailer; - } - break; - - case sw_last_chunk_extension_almost_done: - if (ch == LF) { - state = sw_trailer; - break; - } - goto invalid; - - case sw_trailer: - switch (ch) { - case CR: - state = sw_trailer_almost_done; - break; - case LF: - goto done; - default: - state = sw_trailer_header; - } - break; - - case sw_trailer_almost_done: - if (ch == LF) { - goto done; - } - goto invalid; - - case sw_trailer_header: - switch (ch) { - case CR: - state = sw_trailer_header_almost_done; - break; - case LF: - state = sw_trailer; - } - break; - - case sw_trailer_header_almost_done: - if (ch == LF) { - state = sw_trailer; - break; - } - goto invalid; - - } - } - -data: - - ctx->state = state; - buf->pos = pos; - - switch (state) { - - case sw_chunk_start: - ctx->length = 3 /* "0" LF LF */; - break; - case sw_chunk_size: - ctx->length = 2 /* LF LF */ - + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */ : 0); - break; - case sw_chunk_extension: - case sw_chunk_extension_almost_done: - ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */; - break; - case sw_chunk_data: - ctx->length = ctx->size + 4 /* LF "0" LF LF */; - break; - case sw_after_data: - case sw_after_data_almost_done: - ctx->length = 4 /* LF "0" LF LF */; - break; - case sw_last_chunk_extension: - case sw_last_chunk_extension_almost_done: - ctx->length = 2 /* LF LF */; - break; - case sw_trailer: - case sw_trailer_almost_done: - ctx->length = 1 /* LF */; - break; - case sw_trailer_header: - case sw_trailer_header_almost_done: - ctx->length = 2 /* LF LF */; - break; - - } - - return rc; - -done: - - return NGX_DONE; - -invalid: - - return NGX_ERROR; -} - - static ngx_int_t ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) { @@ -1901,7 +1639,7 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) for ( ;; ) { - rc = ngx_http_proxy_parse_chunked(r, buf); + rc = ngx_http_parse_chunked(r, buf, &ctx->chunked); if (rc == NGX_OK) { @@ -1952,16 +1690,16 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, "input buf #%d %p", b->num, b->pos); - if (buf->last - buf->pos >= ctx->size) { + if (buf->last - buf->pos >= ctx->chunked.size) { - buf->pos += ctx->size; + buf->pos += ctx->chunked.size; b->last = buf->pos; - ctx->size = 0; + ctx->chunked.size = 0; continue; } - ctx->size -= buf->last - buf->pos; + ctx->chunked.size -= buf->last - buf->pos; buf->pos = buf->last; b->last = buf->last; @@ -1982,7 +1720,7 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) /* set p->length, minimal amount of data we want to see */ - p->length = ctx->length; + p->length = ctx->chunked.length; break; } @@ -1997,7 +1735,7 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http proxy chunked state %d, length %d", - ctx->state, p->length); + ctx->chunked.state, p->length); if (b) { b->shadow = buf; @@ -2094,7 +1832,7 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes) for ( ;; ) { - rc = ngx_http_proxy_parse_chunked(r, buf); + rc = ngx_http_parse_chunked(r, buf, &ctx->chunked); if (rc == NGX_OK) { @@ -2116,13 +1854,13 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes) b->pos = buf->pos; b->tag = u->output.tag; - if (buf->last - buf->pos >= ctx->size) { - buf->pos += ctx->size; + if (buf->last - buf->pos >= ctx->chunked.size) { + buf->pos += ctx->chunked.size; b->last = buf->pos; - ctx->size = 0; + ctx->chunked.size = 0; } else { - ctx->size -= buf->last - buf->pos; + ctx->chunked.size -= buf->last - buf->pos; buf->pos = buf->last; b->last = buf->last; } diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index f152006a0..f1d02bdd2 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -18,6 +18,7 @@ typedef struct ngx_http_upstream_s ngx_http_upstream_t; typedef struct ngx_http_cache_s ngx_http_cache_t; typedef struct ngx_http_file_cache_s ngx_http_file_cache_t; typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; +typedef struct ngx_http_chunked_s ngx_http_chunked_t; typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); @@ -52,6 +53,13 @@ struct ngx_http_log_ctx_s { }; +struct ngx_http_chunked_s { + ngx_uint_t state; + off_t size; + off_t length; +}; + + typedef struct { ngx_uint_t http_version; ngx_uint_t code; @@ -92,6 +100,8 @@ ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value); void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args); +ngx_int_t ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b, + ngx_http_chunked_t *ctx); ngx_int_t ngx_http_find_server_conf(ngx_http_request_t *r); diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index be750edd0..cbc412c74 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -1818,3 +1818,256 @@ ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args) args->len = 0; } } + + +ngx_int_t +ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b, + ngx_http_chunked_t *ctx) +{ + u_char *pos, ch, c; + ngx_int_t rc; + enum { + sw_chunk_start = 0, + sw_chunk_size, + sw_chunk_extension, + sw_chunk_extension_almost_done, + sw_chunk_data, + sw_after_data, + sw_after_data_almost_done, + sw_last_chunk_extension, + sw_last_chunk_extension_almost_done, + sw_trailer, + sw_trailer_almost_done, + sw_trailer_header, + sw_trailer_header_almost_done + } state; + + state = ctx->state; + + if (state == sw_chunk_data && ctx->size == 0) { + state = sw_after_data; + } + + rc = NGX_AGAIN; + + for (pos = b->pos; pos < b->last; pos++) { + + ch = *pos; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http chunked byte: %02Xd s:%d", ch, state); + + switch (state) { + + case sw_chunk_start: + if (ch >= '0' && ch <= '9') { + state = sw_chunk_size; + ctx->size = ch - '0'; + break; + } + + c = (u_char) (ch | 0x20); + + if (c >= 'a' && c <= 'f') { + state = sw_chunk_size; + ctx->size = c - 'a' + 10; + break; + } + + goto invalid; + + case sw_chunk_size: + if (ch >= '0' && ch <= '9') { + ctx->size = ctx->size * 16 + (ch - '0'); + break; + } + + c = (u_char) (ch | 0x20); + + if (c >= 'a' && c <= 'f') { + ctx->size = ctx->size * 16 + (c - 'a' + 10); + break; + } + + if (ctx->size == 0) { + + switch (ch) { + case CR: + state = sw_last_chunk_extension_almost_done; + break; + case LF: + state = sw_trailer; + break; + case ';': + case ' ': + case '\t': + state = sw_last_chunk_extension; + break; + default: + goto invalid; + } + + break; + } + + switch (ch) { + case CR: + state = sw_chunk_extension_almost_done; + break; + case LF: + state = sw_chunk_data; + break; + case ';': + case ' ': + case '\t': + state = sw_chunk_extension; + break; + default: + goto invalid; + } + + break; + + case sw_chunk_extension: + switch (ch) { + case CR: + state = sw_chunk_extension_almost_done; + break; + case LF: + state = sw_chunk_data; + } + break; + + case sw_chunk_extension_almost_done: + if (ch == LF) { + state = sw_chunk_data; + break; + } + goto invalid; + + case sw_chunk_data: + rc = NGX_OK; + goto data; + + case sw_after_data: + switch (ch) { + case CR: + state = sw_after_data_almost_done; + break; + case LF: + state = sw_chunk_start; + } + break; + + case sw_after_data_almost_done: + if (ch == LF) { + state = sw_chunk_start; + break; + } + goto invalid; + + case sw_last_chunk_extension: + switch (ch) { + case CR: + state = sw_last_chunk_extension_almost_done; + break; + case LF: + state = sw_trailer; + } + break; + + case sw_last_chunk_extension_almost_done: + if (ch == LF) { + state = sw_trailer; + break; + } + goto invalid; + + case sw_trailer: + switch (ch) { + case CR: + state = sw_trailer_almost_done; + break; + case LF: + goto done; + default: + state = sw_trailer_header; + } + break; + + case sw_trailer_almost_done: + if (ch == LF) { + goto done; + } + goto invalid; + + case sw_trailer_header: + switch (ch) { + case CR: + state = sw_trailer_header_almost_done; + break; + case LF: + state = sw_trailer; + } + break; + + case sw_trailer_header_almost_done: + if (ch == LF) { + state = sw_trailer; + break; + } + goto invalid; + + } + } + +data: + + ctx->state = state; + b->pos = pos; + + switch (state) { + + case sw_chunk_start: + ctx->length = 3 /* "0" LF LF */; + break; + case sw_chunk_size: + ctx->length = 2 /* LF LF */ + + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */ : 0); + break; + case sw_chunk_extension: + case sw_chunk_extension_almost_done: + ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */; + break; + case sw_chunk_data: + ctx->length = ctx->size + 4 /* LF "0" LF LF */; + break; + case sw_after_data: + case sw_after_data_almost_done: + ctx->length = 4 /* LF "0" LF LF */; + break; + case sw_last_chunk_extension: + case sw_last_chunk_extension_almost_done: + ctx->length = 2 /* LF LF */; + break; + case sw_trailer: + case sw_trailer_almost_done: + ctx->length = 1 /* LF */; + break; + case sw_trailer_header: + case sw_trailer_header_almost_done: + ctx->length = 2 /* LF LF */; + break; + + } + + return rc; + +done: + + return NGX_DONE; + +invalid: + + return NGX_ERROR; +}