mirror of
https://github.com/nginx/nginx.git
synced 2025-07-23 05:41:07 +08:00
HTTP/3: refactored request body parser.
The change reduces diff to the default branch for src/http/ngx_http_request_body.c. Also, client Content-Length, if present, is now checked against the real body size sent by client.
This commit is contained in:
parent
a7cf99b10d
commit
6f3c821d1f
@ -66,9 +66,6 @@ struct ngx_http_chunked_s {
|
||||
ngx_uint_t state;
|
||||
off_t size;
|
||||
off_t length;
|
||||
#if (NGX_HTTP_V3)
|
||||
void *h3_parse;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
@ -87,6 +87,13 @@ ngx_http_read_client_request_body(ngx_http_request_t *r,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NGX_HTTP_V3)
|
||||
if (r->http_version == NGX_HTTP_VERSION_30) {
|
||||
rc = ngx_http_v3_read_request_body(r);
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
preread = r->header_in->last - r->header_in->pos;
|
||||
|
||||
if (preread) {
|
||||
@ -229,6 +236,18 @@ ngx_http_read_unbuffered_request_body(ngx_http_request_t *r)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NGX_HTTP_V3)
|
||||
if (r->http_version == NGX_HTTP_VERSION_30) {
|
||||
rc = ngx_http_v3_read_unbuffered_request_body(r);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
r->reading_body = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (r->connection->read->timedout) {
|
||||
r->connection->timedout = 1;
|
||||
return NGX_HTTP_REQUEST_TIME_OUT;
|
||||
@ -333,10 +352,11 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
rb->buf->last_buf = 1;
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"client prematurely closed connection");
|
||||
}
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
if (n == 0 || n == NGX_ERROR) {
|
||||
c->error = 1;
|
||||
return NGX_HTTP_BAD_REQUEST;
|
||||
}
|
||||
@ -583,8 +603,8 @@ ngx_http_discard_request_body(ngx_http_request_t *r)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NGX_HTTP_QUIC)
|
||||
if (r->connection->quic) {
|
||||
#if (NGX_HTTP_V3)
|
||||
if (r->http_version == NGX_HTTP_VERSION_30) {
|
||||
return NGX_OK;
|
||||
}
|
||||
#endif
|
||||
@ -956,15 +976,6 @@ ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
break;
|
||||
}
|
||||
|
||||
size = cl->buf->last - cl->buf->pos;
|
||||
|
||||
if (cl->buf->last_buf && (off_t) size < rb->rest) {
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
"client prematurely closed connection");
|
||||
r->connection->error = 1;
|
||||
return NGX_HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
tl = ngx_chain_get_free_buf(r->pool, &rb->free);
|
||||
if (tl == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
@ -982,6 +993,8 @@ ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
b->end = cl->buf->end;
|
||||
b->flush = r->request_body_no_buffering;
|
||||
|
||||
size = cl->buf->last - cl->buf->pos;
|
||||
|
||||
if ((off_t) size < rb->rest) {
|
||||
cl->buf->pos = cl->buf->last;
|
||||
rb->rest -= size;
|
||||
@ -1053,16 +1066,7 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
cl->buf->file_pos,
|
||||
cl->buf->file_last - cl->buf->file_pos);
|
||||
|
||||
switch (r->http_version) {
|
||||
#if (NGX_HTTP_V3)
|
||||
case NGX_HTTP_VERSION_30:
|
||||
rc = ngx_http_v3_parse_request_body(r, cl->buf, rb->chunked);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default: /* HTTP/1.x */
|
||||
rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
|
||||
}
|
||||
rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
|
||||
@ -1146,20 +1150,6 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN && cl->buf->last_buf) {
|
||||
|
||||
/* last body buffer */
|
||||
|
||||
if (rb->chunked->length > 0) {
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
"client prematurely closed connection");
|
||||
r->connection->error = 1;
|
||||
return NGX_HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
rc = NGX_DONE;
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
|
||||
/* a whole response has been parsed successfully */
|
||||
|
@ -133,8 +133,8 @@ typedef struct {
|
||||
|
||||
|
||||
void ngx_http_v3_init(ngx_connection_t *c);
|
||||
ngx_int_t ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b,
|
||||
ngx_http_chunked_t *ctx);
|
||||
ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r);
|
||||
|
||||
uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value);
|
||||
uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value,
|
||||
|
@ -19,6 +19,10 @@ static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r,
|
||||
ngx_str_t *name, ngx_str_t *value);
|
||||
static ngx_int_t ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_v3_process_request_header(ngx_http_request_t *r);
|
||||
static void ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_v3_request_body_filter(ngx_http_request_t *r,
|
||||
ngx_chain_t *in);
|
||||
|
||||
|
||||
static const struct {
|
||||
@ -625,12 +629,18 @@ failed:
|
||||
static ngx_int_t
|
||||
ngx_http_v3_process_request_header(ngx_http_request_t *r)
|
||||
{
|
||||
ssize_t n;
|
||||
ngx_buf_t *b;
|
||||
ngx_connection_t *c;
|
||||
|
||||
c = r->connection;
|
||||
|
||||
if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (r->headers_in.server.len == 0) {
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"client sent neither \":authority\" nor \"Host\" header");
|
||||
goto failed;
|
||||
}
|
||||
@ -642,7 +652,7 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r)
|
||||
r->headers_in.server.len)
|
||||
!= 0)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"client sent \":authority\" and \"Host\" headers "
|
||||
"with different values");
|
||||
goto failed;
|
||||
@ -655,10 +665,32 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r)
|
||||
r->headers_in.content_length->value.len);
|
||||
|
||||
if (r->headers_in.content_length_n == NGX_ERROR) {
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"client sent invalid \"Content-Length\" header");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
} else {
|
||||
b = r->header_in;
|
||||
n = b->last - b->pos;
|
||||
|
||||
if (n == 0) {
|
||||
n = c->recv(c, b->start, b->end - b->start);
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
b->pos = b->start;
|
||||
b->last = b->start + n;
|
||||
}
|
||||
}
|
||||
|
||||
if (n != 0) {
|
||||
r->headers_in.chunked = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
@ -671,74 +703,457 @@ failed:
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b,
|
||||
ngx_http_chunked_t *ctx)
|
||||
ngx_http_v3_read_request_body(ngx_http_request_t *r)
|
||||
{
|
||||
size_t preread;
|
||||
ngx_int_t rc;
|
||||
ngx_chain_t *cl, out;
|
||||
ngx_http_request_body_t *rb;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
rb = r->request_body;
|
||||
|
||||
preread = r->header_in->last - r->header_in->pos;
|
||||
|
||||
if (preread) {
|
||||
|
||||
/* there is the pre-read part of the request body */
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http3 client request body preread %uz", preread);
|
||||
|
||||
out.buf = r->header_in;
|
||||
out.next = NULL;
|
||||
cl = &out;
|
||||
|
||||
} else {
|
||||
cl = NULL;
|
||||
}
|
||||
|
||||
rc = ngx_http_v3_request_body_filter(r, cl);
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (rb->rest == 0) {
|
||||
/* the whole request body was pre-read */
|
||||
r->request_body_no_buffering = 0;
|
||||
rb->post_handler(r);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (rb->rest < 0) {
|
||||
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
||||
"negative request body rest");
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
rb->buf = ngx_create_temp_buf(r->pool, clcf->client_body_buffer_size);
|
||||
if (rb->buf == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
r->read_event_handler = ngx_http_v3_read_client_request_body_handler;
|
||||
r->write_event_handler = ngx_http_request_empty_handler;
|
||||
|
||||
return ngx_http_v3_do_read_client_request_body(r);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
|
||||
if (r->connection->read->timedout) {
|
||||
r->connection->timedout = 1;
|
||||
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = ngx_http_v3_do_read_client_request_body(r);
|
||||
|
||||
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
|
||||
ngx_http_finalize_request(r, rc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
|
||||
if (r->connection->read->timedout) {
|
||||
r->connection->timedout = 1;
|
||||
return NGX_HTTP_REQUEST_TIME_OUT;
|
||||
}
|
||||
|
||||
rc = ngx_http_v3_do_read_client_request_body(r);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
r->reading_body = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r)
|
||||
{
|
||||
off_t rest;
|
||||
size_t size;
|
||||
ssize_t n;
|
||||
ngx_int_t rc;
|
||||
ngx_chain_t out;
|
||||
ngx_connection_t *c;
|
||||
ngx_http_v3_parse_data_t *st;
|
||||
enum {
|
||||
sw_start = 0,
|
||||
sw_skip
|
||||
};
|
||||
ngx_http_request_body_t *rb;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
c = r->connection;
|
||||
st = ctx->h3_parse;
|
||||
rb = r->request_body;
|
||||
|
||||
if (st == NULL) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http3 parse request body");
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http3 read client request body");
|
||||
|
||||
st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_data_t));
|
||||
if (st == NULL) {
|
||||
goto failed;
|
||||
for ( ;; ) {
|
||||
for ( ;; ) {
|
||||
if (rb->buf->last == rb->buf->end) {
|
||||
|
||||
/* update chains */
|
||||
|
||||
rc = ngx_http_v3_request_body_filter(r, NULL);
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (rb->busy != NULL) {
|
||||
if (r->request_body_no_buffering) {
|
||||
if (c->read->timer_set) {
|
||||
ngx_del_timer(c->read);
|
||||
}
|
||||
|
||||
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"busy buffers after request body flush");
|
||||
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
rb->buf->pos = rb->buf->start;
|
||||
rb->buf->last = rb->buf->start;
|
||||
}
|
||||
|
||||
size = rb->buf->end - rb->buf->last;
|
||||
rest = rb->rest - (rb->buf->last - rb->buf->pos);
|
||||
|
||||
if ((off_t) size > rest) {
|
||||
size = (size_t) rest;
|
||||
}
|
||||
|
||||
n = c->recv(c, rb->buf->last, size);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http3 client request body recv %z", n);
|
||||
|
||||
if (n == NGX_AGAIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
rb->buf->last_buf = 1;
|
||||
}
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
c->error = 1;
|
||||
return NGX_HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
rb->buf->last += n;
|
||||
|
||||
/* pass buffer to request body filter chain */
|
||||
|
||||
out.buf = rb->buf;
|
||||
out.next = NULL;
|
||||
|
||||
rc = ngx_http_v3_request_body_filter(r, &out);
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (rb->rest == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (rb->buf->last < rb->buf->end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->h3_parse = st;
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http3 client request body rest %O", rb->rest);
|
||||
|
||||
if (rb->rest == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!c->read->ready) {
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
ngx_add_timer(c->read, clcf->client_body_timeout);
|
||||
|
||||
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
while (b->pos < b->last && ctx->size == 0) {
|
||||
|
||||
rc = ngx_http_v3_parse_data(c, st, *b->pos++);
|
||||
|
||||
if (rc > 0) {
|
||||
ngx_http_v3_finalize_connection(c, rc,
|
||||
"could not parse request body");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
ctx->state = sw_skip;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
return NGX_DONE;
|
||||
}
|
||||
|
||||
/* rc == NGX_OK */
|
||||
|
||||
ctx->size = st->length;
|
||||
ctx->state = sw_start;
|
||||
if (c->read->timer_set) {
|
||||
ngx_del_timer(c->read);
|
||||
}
|
||||
|
||||
if (ctx->state == sw_skip) {
|
||||
ctx->length = 1;
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (b->pos == b->last) {
|
||||
ctx->length = ctx->size;
|
||||
return NGX_AGAIN;
|
||||
if (!r->request_body_no_buffering) {
|
||||
r->read_event_handler = ngx_http_block_reading;
|
||||
rb->post_handler(r);
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
failed:
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
{
|
||||
off_t max;
|
||||
size_t size;
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b;
|
||||
ngx_uint_t last;
|
||||
ngx_chain_t *cl, *out, *tl, **ll;
|
||||
ngx_http_request_body_t *rb;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
ngx_http_core_srv_conf_t *cscf;
|
||||
ngx_http_v3_parse_data_t *st;
|
||||
|
||||
rb = r->request_body;
|
||||
st = r->h3_parse;
|
||||
|
||||
if (rb->rest == -1) {
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http3 request body filter");
|
||||
|
||||
st = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_parse_data_t));
|
||||
if (st == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
r->h3_parse = st;
|
||||
|
||||
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
|
||||
|
||||
rb->rest = cscf->large_client_header_buffers.size;
|
||||
}
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
max = r->headers_in.content_length_n;
|
||||
|
||||
if (max == -1 && clcf->client_max_body_size) {
|
||||
max = clcf->client_max_body_size;
|
||||
}
|
||||
|
||||
out = NULL;
|
||||
ll = &out;
|
||||
last = 0;
|
||||
|
||||
for (cl = in; cl; cl = cl->next) {
|
||||
|
||||
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
|
||||
"http3 body buf "
|
||||
"t:%d f:%d %p, pos %p, size: %z file: %O, size: %O",
|
||||
cl->buf->temporary, cl->buf->in_file,
|
||||
cl->buf->start, cl->buf->pos,
|
||||
cl->buf->last - cl->buf->pos,
|
||||
cl->buf->file_pos,
|
||||
cl->buf->file_last - cl->buf->file_pos);
|
||||
|
||||
if (cl->buf->last_buf) {
|
||||
last = 1;
|
||||
}
|
||||
|
||||
b = NULL;
|
||||
|
||||
while (cl->buf->pos < cl->buf->last) {
|
||||
|
||||
if (st->length == 0) {
|
||||
r->request_length++;
|
||||
|
||||
rc = ngx_http_v3_parse_data(r->connection, st, *cl->buf->pos++);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
last = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (rc > 0) {
|
||||
ngx_http_v3_finalize_connection(r->connection, rc,
|
||||
"client sent invalid body");
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"client sent invalid body");
|
||||
return NGX_HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
ngx_http_v3_finalize_connection(r->connection,
|
||||
NGX_HTTP_V3_ERR_INTERNAL_ERROR,
|
||||
"internal error");
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
/* rc == NGX_OK */
|
||||
}
|
||||
|
||||
if (max != -1 && (uint64_t) (max - rb->received) < st->length) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"client intended to send too large "
|
||||
"body: %O+%uL bytes",
|
||||
rb->received, st->length);
|
||||
|
||||
return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
|
||||
}
|
||||
|
||||
if (b
|
||||
&& st->length <= 128
|
||||
&& (uint64_t) (cl->buf->last - cl->buf->pos) >= st->length)
|
||||
{
|
||||
rb->received += st->length;
|
||||
r->request_length += st->length;
|
||||
|
||||
if (st->length < 8) {
|
||||
|
||||
while (st->length) {
|
||||
*b->last++ = *cl->buf->pos++;
|
||||
st->length--;
|
||||
}
|
||||
|
||||
} else {
|
||||
ngx_memmove(b->last, cl->buf->pos, st->length);
|
||||
b->last += st->length;
|
||||
cl->buf->pos += st->length;
|
||||
st->length = 0;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
tl = ngx_chain_get_free_buf(r->pool, &rb->free);
|
||||
if (tl == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
b = tl->buf;
|
||||
|
||||
ngx_memzero(b, sizeof(ngx_buf_t));
|
||||
|
||||
b->temporary = 1;
|
||||
b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
|
||||
b->start = cl->buf->pos;
|
||||
b->pos = cl->buf->pos;
|
||||
b->last = cl->buf->last;
|
||||
b->end = cl->buf->end;
|
||||
b->flush = r->request_body_no_buffering;
|
||||
|
||||
*ll = tl;
|
||||
ll = &tl->next;
|
||||
|
||||
size = cl->buf->last - cl->buf->pos;
|
||||
|
||||
if (size > st->length) {
|
||||
cl->buf->pos += (size_t) st->length;
|
||||
rb->received += st->length;
|
||||
r->request_length += st->length;
|
||||
st->length = 0;
|
||||
|
||||
} else {
|
||||
st->length -= size;
|
||||
rb->received += size;
|
||||
r->request_length += size;
|
||||
cl->buf->pos = cl->buf->last;
|
||||
}
|
||||
|
||||
b->last = cl->buf->pos;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
if (last) {
|
||||
|
||||
if (st->length > 0) {
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
"client prematurely closed stream");
|
||||
r->connection->error = 1;
|
||||
return NGX_HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if (r->headers_in.content_length_n == -1) {
|
||||
r->headers_in.content_length_n = rb->received;
|
||||
|
||||
} else if (r->headers_in.content_length_n != rb->received) {
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
"client sent less body data than expected: "
|
||||
"%O out of %O bytes of request body received",
|
||||
rb->received, r->headers_in.content_length_n);
|
||||
return NGX_HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
rb->rest = 0;
|
||||
|
||||
tl = ngx_chain_get_free_buf(r->pool, &rb->free);
|
||||
if (tl == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
b = tl->buf;
|
||||
|
||||
ngx_memzero(b, sizeof(ngx_buf_t));
|
||||
|
||||
b->last_buf = 1;
|
||||
|
||||
*ll = tl;
|
||||
ll = &tl->next;
|
||||
|
||||
} else {
|
||||
|
||||
/* set rb->rest, amount of data we want to see next time */
|
||||
|
||||
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
|
||||
|
||||
rb->rest = (off_t) cscf->large_client_header_buffers.size;
|
||||
}
|
||||
|
||||
rc = ngx_http_top_request_body_filter(r, out);
|
||||
|
||||
ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
|
||||
(ngx_buf_tag_t) &ngx_http_read_client_request_body);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user