static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r) { size_t size; ssize_t n; ngx_buf_t *b, buf; ngx_int_t rc; ngx_connection_t *c; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; c = r->connection; rb = r->request_body; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http read client request body"); for ( ;; ) { for ( ;; ) { if (rb->buf->last == rb->buf->end) { if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) { ngx_http_probe_read_body_abort(r, "write temp file failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; rb->buf->last = rb->buf->start; } size = rb->buf->end - rb->buf->last; if ((off_t) size > rb->rest) { size = (size_t) rb->rest; } n = c->recv(c, rb->buf->last, size); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http client request body recv %z", n); if (n == NGX_AGAIN) { break; } if (n == 0) { ngx_http_probe_read_body_abort(r, "connection closed"); ngx_log_error(NGX_LOG_INFO, c->log, 0, "client prematurely closed connection"); } if (n == 0 || n == NGX_ERROR) { c->error = 1; return NGX_HTTP_BAD_REQUEST; } buf.start = rb->buf->last; buf.pos = rb->buf->last; buf.last = buf.start + n; buf.end = buf.last; rc = ngx_http_top_input_body_filter(r, &buf); if (rc != NGX_OK) { return rc; } rb->buf->last += n; rb->rest -= n; r->request_length += n; if (rb->rest == 0) { break; } if (rb->buf->last < rb->buf->end) { break; } } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http 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; } } if (c->read->timer_set) { ngx_del_timer(c->read); } if (rb->temp_file || r->request_body_in_file_only) { /* save the last part */ if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) { ngx_http_probe_read_body_abort(r, "write temp file failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b->in_file = 1; b->file_pos = 0; b->file_last = rb->temp_file->file.offset; b->file = &rb->temp_file->file; if (rb->bufs->next) { rb->bufs->next->buf = b; } else { rb->bufs->buf = b; } } if (rb->bufs->next && (r->request_body_in_file_only || r->request_body_in_single_buf)) { rb->bufs = rb->bufs->next; } r->read_event_handler = ngx_http_block_reading; ngx_http_probe_read_body_done(r); rb->post_handler(r); return NGX_OK; }
ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler) { size_t preread; ssize_t size; ngx_buf_t *b, buf; ngx_int_t rc; ngx_chain_t *cl, **next; ngx_temp_file_t *tf; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; r->main->count++; if (r->request_body || r->discard_body) { ngx_http_probe_read_body_abort(r, (r->request_body ? "body exists" : "body discarded")); post_handler(r); return NGX_OK; } if (ngx_http_test_expect(r) != NGX_OK) { ngx_http_probe_read_body_abort(r, "test expect failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->request_body = rb; if (r->headers_in.content_length_n < 0) { ngx_http_probe_read_body_abort(r, "no content length"); post_handler(r); return NGX_OK; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->headers_in.content_length_n == 0) { if (r->request_body_in_file_only) { tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); if (tf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } tf->file.fd = NGX_INVALID_FILE; tf->file.log = r->connection->log; tf->path = clcf->client_body_temp_path; tf->pool = r->pool; tf->warn = "a client request body is buffered to a temporary file"; tf->log_level = r->request_body_file_log_level; tf->persistent = r->request_body_in_persistent_file; tf->clean = r->request_body_in_clean_file; if (r->request_body_file_group_access) { tf->access = 0660; } rb->temp_file = tf; if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, tf->persistent, tf->clean, tf->access) != NGX_OK) { ngx_http_probe_read_body_abort(r, "create temp file failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } } ngx_http_probe_read_body_done(r); post_handler(r); return NGX_OK; } rb->post_handler = post_handler; /* * set by ngx_pcalloc(): * * rb->bufs = NULL; * rb->buf = NULL; * rb->rest = 0; */ 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, "http client request body preread %uz", preread); b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b->temporary = 1; b->start = r->header_in->pos; b->pos = r->header_in->pos; b->last = r->header_in->last; b->end = r->header_in->end; buf.start = r->header_in->pos; buf.pos = r->header_in->pos; buf.last = (off_t) preread >= r->headers_in.content_length_n ? r->header_in->pos + (size_t) r->headers_in.content_length_n : r->header_in->last; buf.end = r->header_in->end; rc = ngx_http_top_input_body_filter(r, &buf); if (rc != NGX_OK) { return rc; } rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rb->bufs->buf = b; rb->bufs->next = NULL; rb->buf = b; if ((off_t) preread >= r->headers_in.content_length_n) { /* the whole request body was pre-read */ r->header_in->pos += (size_t) r->headers_in.content_length_n; r->request_length += r->headers_in.content_length_n; b->last = r->header_in->pos; if (r->request_body_in_file_only) { if (ngx_http_write_request_body(r, rb->bufs) != NGX_OK) { ngx_http_probe_read_body_abort(r, "write temp file failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } } ngx_http_probe_read_body_done(r); post_handler(r); return NGX_OK; } /* * to not consider the body as pipelined request in * ngx_http_set_keepalive() */ r->header_in->pos = r->header_in->last; r->request_length += preread; rb->rest = r->headers_in.content_length_n - preread; if (rb->rest <= (off_t) (b->end - b->last)) { /* the whole request body may be placed in r->header_in */ rb->to_write = rb->bufs; r->read_event_handler = ngx_http_read_client_request_body_handler; return ngx_http_do_read_client_request_body(r); } next = &rb->bufs->next; } else { b = NULL; rb->rest = r->headers_in.content_length_n; next = &rb->bufs; } size = clcf->client_body_buffer_size; size += size >> 2; if (rb->rest < size) { size = (ssize_t) rb->rest; if (r->request_body_in_single_buf) { size += preread; } } else { size = clcf->client_body_buffer_size; /* disable copying buffer for r->request_body_in_single_buf */ b = NULL; } rb->buf = ngx_create_temp_buf(r->pool, size); if (rb->buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cl->buf = rb->buf; cl->next = NULL; if (b && r->request_body_in_single_buf) { size = b->last - b->pos; ngx_memcpy(rb->buf->pos, b->pos, size); rb->buf->last += size; next = &rb->bufs; } *next = cl; if (r->request_body_in_file_only || r->request_body_in_single_buf) { rb->to_write = rb->bufs; } else { rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; } r->read_event_handler = ngx_http_read_client_request_body_handler; return ngx_http_do_read_client_request_body(r); }
ngx_int_t ngx_http_do_read_non_buffered_client_request_body(ngx_http_request_t *r) { size_t size; ssize_t n; ngx_buf_t buf; ngx_int_t rc; ngx_connection_t *c; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; c = r->connection; rb = r->request_body; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http read no buffered client request body"); for ( ;; ) { for ( ;; ) { if ((rb->buf == NULL) || (rb->buf->end == rb->buf->last)) { rc = ngx_http_request_body_get_buf(r); if (rc == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } else if (rc == NGX_DECLINED) { /* The buffers are full */ return NGX_DECLINED; } } size = rb->buf->end - rb->buf->last; if ((off_t) size > rb->rest) { size = (size_t) rb->rest; } n = c->recv(c, rb->buf->last, size); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http no buffered client request body recv %z", n); if (n == NGX_AGAIN) { break; } if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client prematurely closed connection"); } if (n == 0 || n == NGX_ERROR) { c->error = 1; return NGX_HTTP_BAD_REQUEST; } if (rb->last) { *rb->last_out = rb->last; rb->last_out = &rb->last->next; rb->last = NULL; } buf.start = rb->buf->last; buf.pos = rb->buf->last; buf.last = buf.start + n; buf.end = buf.last; rc = ngx_http_top_input_body_filter(r, &buf); if (rc != NGX_OK) { return rc; } rb->buf->last += n; rb->rest -= n; r->request_length += n; rb->postpone_size += n; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http no buffered client request body " "request_length: %O, rest: %uz, postpone_size: %O", r->request_length, rb->rest, rb->postpone_size); if (rb->rest == 0) { rb->buf->last_buf = 1; goto read_ok; } if (rb->postpone_size >= (off_t) clcf->client_body_postpone_size) { rb->flush = 1; if (rb->buffered) { goto read_ok; } return NGX_DECLINED; } } if (!c->read->ready) { 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; } } read_ok: /* * All the request body is read or the postpone_size is larger than the * client_body_postpone_size, we should call the post_handler if this * request is still buffered. */ if (c->read->timer_set) { ngx_del_timer(c->read); } r->read_event_handler = ngx_http_block_reading; rb->buffered = 0; return NGX_OK; }
static ngx_int_t ngx_http_read_non_buffered_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler) { size_t preread; ngx_buf_t *b, buf; ngx_int_t rc; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->headers_in.content_length_n == 0) { post_handler(r); return NGX_OK; } rb = r->request_body; rb->post_handler = post_handler; 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, "http client no buffered request body preread %uz", preread); b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b->temporary = 1; b->start = r->header_in->pos; b->pos = r->header_in->pos; b->last = r->header_in->last; b->end = r->header_in->end; buf.start = r->header_in->pos; buf.pos = r->header_in->pos; buf.last = (off_t) preread >= r->headers_in.content_length_n ? r->header_in->pos + (size_t) r->headers_in.content_length_n : r->header_in->last; buf.end = r->header_in->end; rc = ngx_http_top_input_body_filter(r, &buf); if (rc != NGX_OK) { return rc; } rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rb->bufs->buf = b; rb->bufs->next = NULL; rb->buf = b; if ((off_t) preread >= r->headers_in.content_length_n) { /* the whole request body was pre-read */ r->header_in->pos += (size_t) r->headers_in.content_length_n; r->request_length += r->headers_in.content_length_n; b->last = r->header_in->pos; b->last_buf = 1; post_handler(r); return NGX_OK; } /* * to not consider the body as pipelined request in * ngx_http_set_keepalive() */ r->header_in->pos = r->header_in->last; r->request_length += preread; rb->rest = r->headers_in.content_length_n - preread; if (rb->rest <= (off_t) (b->end - b->last)) { /* the whole request body could be placed in r->header_in */ goto read_body; } rb->last_out = &rb->bufs->next; } else { rb->rest = r->headers_in.content_length_n; rb->last_out = &rb->bufs; } read_body: rb->buffered = 1; rb->postpone_size = preread; rc = ngx_http_do_read_non_buffered_client_request_body(r); if (rc == NGX_AGAIN) { if (rb->buffered) { r->read_event_handler = ngx_http_read_non_buffered_client_request_body_handler; } } else if (rc == NGX_OK || rc == NGX_DECLINED) { post_handler(r); } return rc; }
ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler) { size_t preread; ssize_t size; ngx_buf_t *b, buf; ngx_int_t rc; ngx_chain_t *cl, **next; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; r->main->count++; if (r->request_body || r->discard_body) { post_handler(r); return NGX_OK; } if (ngx_http_test_expect(r) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } r->request_body = rb; if (r->headers_in.content_length_n < 0) { post_handler(r); return NGX_OK; } if (!r->request_buffering) { return ngx_http_read_non_buffered_client_request_body(r, post_handler); } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->headers_in.content_length_n == 0) { if (r->request_body_in_file_only) { if (ngx_http_write_request_body(r, NULL) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } } post_handler(r); return NGX_OK; } rb->post_handler = post_handler; /* * set by ngx_pcalloc(): * * rb->bufs = NULL; * rb->buf = NULL; * rb->rest = 0; * rb->postpone_size = 0; * rb->num = 0; */ 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, "http client request body preread %uz", preread); b = ngx_calloc_buf(r->pool); if (b == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } b->temporary = 1; b->start = r->header_in->pos; b->pos = r->header_in->pos; b->last = r->header_in->last; b->end = r->header_in->end; ngx_memzero(&buf, sizeof(ngx_buf_t)); buf.memory = 1; buf.start = r->header_in->pos; buf.pos = r->header_in->pos; buf.last = (off_t) preread >= r->headers_in.content_length_n ? r->header_in->pos + (size_t) r->headers_in.content_length_n : r->header_in->last; buf.end = r->header_in->end; rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } rb->bufs->buf = b; rb->bufs->next = NULL; rb->buf = b; rc = ngx_http_top_input_body_filter(r, &buf); if (rc != NGX_OK) { if (rc > NGX_OK && rc < NGX_HTTP_SPECIAL_RESPONSE) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "input filter: return code 1xx or 2xx " "will cause trouble and is converted to 500"); } /** * NGX_OK: success and continue; * NGX_ERROR: failed and exit; * NGX_AGAIN: not ready and retry later. */ if (rc < NGX_HTTP_SPECIAL_RESPONSE && rc != NGX_AGAIN) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } return rc; } if ((off_t) preread >= r->headers_in.content_length_n) { /* the whole request body was pre-read */ r->header_in->pos += (size_t) r->headers_in.content_length_n; r->request_length += r->headers_in.content_length_n; b->last = r->header_in->pos; if (r->request_body_in_file_only) { if (ngx_http_write_request_body(r, rb->bufs) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } } post_handler(r); return NGX_OK; } /* * to not consider the body as pipelined request in * ngx_http_set_keepalive() */ r->header_in->pos = r->header_in->last; r->request_length += preread; rb->rest = r->headers_in.content_length_n - preread; if (rb->rest <= (off_t) (b->end - b->last)) { /* the whole request body may be placed in r->header_in */ rb->to_write = rb->bufs; r->read_event_handler = ngx_http_read_client_request_body_handler; rc = ngx_http_do_read_client_request_body(r); goto done; } next = &rb->bufs->next; } else { b = NULL; rb->rest = r->headers_in.content_length_n; next = &rb->bufs; } size = clcf->client_body_buffer_size; size += size >> 2; if (rb->rest < size) { size = (ssize_t) rb->rest; if (r->request_body_in_single_buf) { size += preread; } } else { size = clcf->client_body_buffer_size; /* disable copying buffer for r->request_body_in_single_buf */ b = NULL; } rb->buf = ngx_create_temp_buf(r->pool, size); if (rb->buf == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } cl->buf = rb->buf; cl->next = NULL; if (b && r->request_body_in_single_buf) { size = b->last - b->pos; ngx_memcpy(rb->buf->pos, b->pos, size); rb->buf->last += size; next = &rb->bufs; } *next = cl; if (r->request_body_in_file_only || r->request_body_in_single_buf) { rb->to_write = rb->bufs; } else { rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; } r->read_event_handler = ngx_http_read_client_request_body_handler; rc = ngx_http_do_read_client_request_body(r); done: if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { r->main->count--; } return rc; }
static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; ngx_chain_t *cl; ngx_http_request_body_t *rb; rb = r->request_body; #if (NGX_DEBUG) for (cl = rb->bufs; cl; cl = cl->next) { ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, "http body old buf t:%d f:%d %p, pos %p, size: %z " "file: %O, size: %z", 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); } for (cl = in; cl; cl = cl->next) { ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, "http body new buf t:%d f:%d %p, pos %p, size: %z " "file: %O, size: %z", 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); } #endif /* TODO: coalesce neighbouring buffers */ if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } for (cl = in; cl; cl = cl->next) { rc = ngx_http_top_input_body_filter(r, cl->buf); if (rc != NGX_OK) { if (rc > NGX_OK && rc < NGX_HTTP_SPECIAL_RESPONSE) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "input filter: return code 1xx or 2xx " "will cause trouble and is converted to 500"); } /** * NGX_OK: success and continue; * NGX_ERROR: failed and exit; * NGX_AGAIN: not ready and retry later. */ if (rc < NGX_HTTP_SPECIAL_RESPONSE && rc != NGX_AGAIN) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } return rc; } } return NGX_OK; }