//通过ngx_http_top_request_body_filter调用 ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) { #if (NGX_DEBUG) ngx_chain_t *cl; #endif 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: %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); } 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: %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); } #endif /* TODO: coalesce neighbouring buffers */ if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } //当一个rb->buf填满后就会通过ngx_http_write_request_body把bufs链表中的所有ngx_chain_t->ngx_buf_t中指向的数据 //写入到临时文件,并把ngx_buf_t结构加入poll->chain,通过poll统一释放他们 if (rb->rest > 0 && rb->buf && rb->buf->last == rb->buf->end && !r->request_body_no_buffering) { //需要缓存数据,并且rb->buf数据已经解析完毕,并且buf已经满了,但是包体还没有读完,那么就可以把buf中的数据写入临时文件, //这样改buf指向的内存空间在该函数退出后可以继续用来读取数据 if (ngx_http_write_request_body(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } return NGX_OK; }
ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) { #if (NGX_DEBUG) ngx_chain_t *cl; #endif 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: %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); } 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: %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); } #endif /* TODO: coalesce neighbouring buffers */ if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (rb->rest > 0 && rb->buf && rb->buf->last == rb->buf->end && !r->request_body_no_buffering) { if (ngx_http_write_request_body(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } return NGX_OK; }
static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r) { off_t rest; size_t size; ssize_t n; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *cl, out; 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 (rb->buf->pos != rb->buf->last) { /* pass buffer to request body filter chain */ out.buf = rb->buf; out.next = NULL; rc = ngx_http_request_body_filter(r, &out); if (rc != NGX_OK) { return rc; } } else { /* update chains */ rc = ngx_http_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; } 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, "http 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; } rb->buf->last += n; r->request_length += n; if (n == rest) { /* pass buffer to request body filter chain */ out.buf = rb->buf; out.next = NULL; rc = ngx_http_request_body_filter(r, &out); if (rc != NGX_OK) { return rc; } } 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) { if (r->request_body_no_buffering && rb->buf->pos != rb->buf->last) { /* pass buffer to request body filter chain */ out.buf = rb->buf; out.next = NULL; rc = ngx_http_request_body_filter(r, &out); if (rc != NGX_OK) { return rc; } } 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) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (rb->temp_file->file.offset != 0) { cl = ngx_chain_get_free_buf(r->pool, &rb->free); if (cl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b = cl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->in_file = 1; b->file_last = rb->temp_file->file.offset; b->file = &rb->temp_file->file; rb->bufs = cl; } else { rb->bufs = NULL; } } if (!r->request_body_no_buffering) { r->read_event_handler = ngx_http_block_reading; 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_int_t rc; ngx_buf_t *b; ngx_chain_t out, *cl; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; r->main->count++; if (r != r->main || r->request_body || r->discard_body) { r->request_body_no_buffering = 0; post_handler(r); return NGX_OK; } if (ngx_http_test_expect(r) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } if (r->request_body_no_buffering) { r->request_body_in_file_only = 0; } rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } /* * set by ngx_pcalloc(): * * rb->bufs = NULL; * rb->buf = NULL; * rb->free = NULL; * rb->busy = NULL; * rb->chunked = NULL; */ rb->rest = -1; rb->post_handler = post_handler; r->request_body = rb; if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { r->request_body_no_buffering = 0; post_handler(r); return NGX_OK; } 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); out.buf = r->header_in; out.next = NULL; rc = ngx_http_request_body_filter(r, &out); if (rc != NGX_OK) { goto done; } r->request_length += preread - (r->header_in->last - r->header_in->pos); if (!r->headers_in.chunked && rb->rest > 0 && rb->rest <= (off_t) (r->header_in->end - r->header_in->last)) { /* the whole request body may be placed in r->header_in */ 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; rb->buf = b; r->read_event_handler = ngx_http_read_client_request_body_handler; r->write_event_handler = ngx_http_request_empty_handler; rc = ngx_http_do_read_client_request_body(r); goto done; } } else { /* set rb->rest */ if (ngx_http_request_body_filter(r, NULL) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } } if (rb->rest == 0) { /* the whole request body was pre-read */ if (r->request_body_in_file_only) { if (ngx_http_write_request_body(r) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } if (rb->temp_file->file.offset != 0) { cl = ngx_chain_get_free_buf(r->pool, &rb->free); if (cl == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } b = cl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->in_file = 1; b->file_last = rb->temp_file->file.offset; b->file = &rb->temp_file->file; rb->bufs = cl; } else { rb->bufs = NULL; } } r->request_body_no_buffering = 0; post_handler(r); return NGX_OK; } if (rb->rest < 0) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "negative request body rest"); rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); size = clcf->client_body_buffer_size; size += size >> 2; /* TODO: honor r->request_body_in_single_buf */ if (!r->headers_in.chunked && rb->rest < size) { size = (ssize_t) rb->rest; if (r->request_body_in_single_buf) { size += preread; } } else { size = clcf->client_body_buffer_size; } rb->buf = ngx_create_temp_buf(r->pool, size); if (rb->buf == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } r->read_event_handler = ngx_http_read_client_request_body_handler; r->write_event_handler = ngx_http_request_empty_handler; rc = ngx_http_do_read_client_request_body(r); done: if (r->request_body_no_buffering && (rc == NGX_OK || rc == NGX_AGAIN)) { if (rc == NGX_OK) { r->request_body_no_buffering = 0; } else { /* rc == NGX_AGAIN */ r->reading_body = 1; } r->read_event_handler = ngx_http_block_reading; post_handler(r); } if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { r->main->count--; } return rc; }
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_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; }
/* mostly based on the ngx_http_do_read_client_request_body * function in ngx_http_request_body.c of nginx 0.8.20. * copyrighted by Igor Sysoev. */ static ngx_int_t ngx_http_chunkin_do_read_chunked_request_body(ngx_http_request_t *r) { ngx_int_t rc; size_t size; ssize_t n; ngx_buf_t *b; ngx_connection_t *c; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; ngx_http_chunkin_ctx_t *ctx; ngx_flag_t done; ngx_chain_t *cl, *pending_chunk; u_char *p; ngx_http_chunkin_conf_t *conf; c = r->connection; rb = r->request_body; ctx = ngx_http_get_module_ctx(r, ngx_http_chunkin_filter_module); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "chunkin: http chunkin read chunked client request body"); done = 0; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (ctx->just_after_preread) { ctx->just_after_preread = 0; dd("Just after preread and ctx->chunks defined (bytes read: %d, " "chunk size: %d, last chars %c %c %c)", ctx->chunk_bytes_read, ctx->chunk_size, *(r->header_in->pos - 2), *(r->header_in->pos - 1), *r->header_in->pos); for (cl = ctx->chunks; cl; cl = cl->next) { b = cl->buf; dd("before ngx_copy..."); p = rb->buf->last; rb->buf->last = ngx_copy(rb->buf->last, b->pos, b->last - b->pos); dd("after ngx_copy..."); b->pos = p; b->last = rb->buf->last; } } conf = ngx_http_get_module_loc_conf(r, ngx_http_chunkin_filter_module); for ( ;; ) { for ( ;; ) { /* dd("client_max_body_size: %d, raw_body_size: %d", (int)clcf->client_max_body_size, (int)ctx->raw_body_size); */ if (clcf->client_max_body_size && clcf->client_max_body_size < ctx->raw_body_size) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "chunkin: client intended to send too large body: " "%O bytes", (off_t) ctx->raw_body_size); return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } /* dd("rb->buf pos: %d", (int) (rb->buf->last - rb->buf->pos)); */ if (rb->buf->last == rb->buf->end || ctx->chunks_count > conf->max_chunks_per_buf) { if (ctx->chunks_count > conf->max_chunks_per_buf) { dd("too many chounks already: %d (max %d, buf last %c)", (int) ctx->chunks_count, (int) conf->max_chunks_per_buf, *(rb->buf->last - 2)); } if (ctx->chunks == NULL || (ctx->chunks_total_size && ctx->chunks_total_size <= ctx->chunks_written_size)) { ngx_log_error(NGX_LOG_WARN, c->log, 0, "chunkin: the chunkin_max_chunks_per_buf or " "max_client_body_size setting seems rather small " "(chunks %snull, total decoded %d, " "total written %d)", (u_char *) (ctx->chunks ? "not " : ""), (int) ctx->chunks_total_size, (int) ctx->chunks_written_size); } else { dd("save exceeding part to disk (%d bytes), buf size: %d, " "chunks count: %d", ctx->chunks_total_size - ctx->chunks_written_size, rb->buf->end - rb->buf->start, ctx->chunks_count); rc = ngx_http_write_request_body(r, ctx->chunks, ctx->chunks_count); if (rc != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } if (ctx->last_complete_chunk) { pending_chunk = ctx->last_complete_chunk->next; /* add ctx->chunks ~ ctx->last_complete_chunk * into ctx->free_bufs */ ctx->last_complete_chunk->next = ctx->free_bufs; ctx->free_bufs = ctx->chunks; ctx->last_complete_chunk = NULL; } else { pending_chunk = ctx->chunks; } if (pending_chunk) { ctx->next_chunk = &pending_chunk->next; ctx->chunks = pending_chunk; ctx->chunk = pending_chunk; ctx->chunk->buf->pos = rb->buf->start; ctx->chunk->buf->last = rb->buf->start; ctx->chunks_count = 1; ctx->chunks_written_size = ctx->chunks_total_size - ngx_buf_size(pending_chunk->buf); } else { ctx->next_chunk = NULL; ctx->chunks = NULL; ctx->chunk = NULL; ctx->chunks_count = 0; ctx->chunks_written_size = ctx->chunks_total_size; } dd("reset rb->buf"); rb->buf->last = rb->buf->start; #if 0 /* XXX just for debugging... */ ngx_memzero(rb->buf->start, rb->buf->end - rb->buf->start); #endif } size = rb->buf->end - rb->buf->last; n = c->recv(c, rb->buf->last, size); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "chunkin: http chunked client request body recv %z", n); if (n == NGX_AGAIN) { dd("NGX_AGAIN caught"); break; } if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "chunkin: client closed prematurely connection"); } if (n == 0 || n == NGX_ERROR) { c->error = 1; return NGX_HTTP_BAD_REQUEST; } /* save the original pos */ p = rb->buf->last; rc = ngx_http_chunkin_run_chunked_parser(r, ctx, &rb->buf->last, rb->buf->last + n, "main handler"); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } if (rc == NGX_ERROR) { /* chunked body parsefail */ return NGX_HTTP_BAD_REQUEST; } /* rb->buf->last += n; */ r->request_length += n; ctx->raw_body_size += n; if (rc == NGX_OK) { dd("successfully done the parsing"); dd("keepalive? %s", r->keepalive ? "yes" : "no"); if (r->keepalive) { dd("cleaning the buffers for pipelined reqeusts (if any)"); size = p + n - rb->buf->last; if (size) { dd("found remaining data for pipelined requests"); if (size > (size_t) (r->header_in->end - ctx->saved_header_in_pos)) { /* XXX enlarge the r->header_in buffer... */ r->keepalive = 0; } else { r->header_in->pos = ctx->saved_header_in_pos; r->header_in->last = ngx_copy(r->header_in->pos, rb->buf->last, size); } } } done = 1; break; } /* rc == NGX_AGAIN */ if (rb->buf->last < rb->buf->end) { break; } } if (done) { break; } 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; } } if (!done) { return NGX_HTTP_BAD_REQUEST; } if (c->read->timer_set) { ngx_del_timer(c->read); } if (rb->temp_file || r->request_body_in_file_only) { size = ctx->chunks_total_size - ctx->chunks_written_size; dd("save the last part to disk...(%d bytes left)", size); if (size == 0) { ctx->chunks = NULL; } #if 0 n = 0; for (cl = ctx->chunks; cl != NULL; cl = cl->next) { /* dd("chunks %d found buf %c", n, *cl->buf->start); */ n++; } #endif dd("for total %d chunks found", n); /* save the last part */ rc = ngx_http_write_request_body(r, ctx->chunks, ctx->chunks_count); if (rc != NGX_OK) { 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; rb->bufs = ngx_http_chunkin_get_buf(r->pool, ctx); if (rb->bufs == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rb->bufs->buf = b; } else if ( r->request_body_in_single_buf ) { dd("request body in single buf"); /* XXX we may not have to allocate a big buffer here */ size = 0; for (cl = ctx->chunks; cl != NULL; cl = cl->next) { size += ngx_buf_size(cl->buf); } rb->buf = ngx_create_temp_buf(r->pool, size); if (rb->buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } for (cl = ctx->chunks; cl != NULL; cl = cl->next) { size = ngx_buf_size(cl->buf); dd("copy buf ...(size %d)", size); rb->buf->last = ngx_cpymem(rb->buf->last, cl->buf->pos, size); } rb->bufs = ngx_http_chunkin_get_buf(r->pool, ctx); if (rb->bufs == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rb->bufs->buf = rb->buf; } else { rb->bufs = ctx->chunks; } #if 0 if (rb->bufs) { int i; for (i = 0, cl = rb->bufs; cl; cl = cl->next, i++) { if (cl->buf->memory && cl->buf->pos == cl->buf->last) { dd("Found zero size buf in chain pos %d", i); } } } #endif dd("last minute, chunks count: %d, chunks_total_size: %d", ctx->chunks_count, ctx->chunks_total_size); #if 0 size = 0; for (cl = rb->bufs; cl; cl = cl->next) { size += ngx_buf_size(cl->buf); } dd("data size: %d", size); #endif rc = ngx_http_chunkin_set_content_length_header(r, ctx->chunks_total_size); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } rb->post_handler(r); return NGX_OK; }
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; ngx_connection_t *c; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; int upload = 0; char bdbuf[128]; c = r->connection; rb = r->request_body; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http read client request body"); if (r->headers_in.content_type->value.len > s_multipart.len && !memcmp(r->headers_in.content_type->value.data, s_multipart.data, s_multipart.len)) { memset(bdbuf, 0, sizeof(bdbuf)); memcpy(bdbuf, r->headers_in.content_type->value.data+s_multipart.len, r->headers_in.content_type->value.len - s_multipart.len); upload = 1; } for ( ;; ) { for ( ;; ) { if (rb->buf->last == rb->buf->end) { /*if (1! = upload&&ngx_http_write_request_body(r, rb->to_write) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }*/ writeUploadFile(r,upload,0); 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_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; } rb->buf->last += n; rb->rest -= n; r->request_length += n; writeUploadFile(r,upload,n); if (r->request_length >= r->headers_in.content_length_n) { break; } 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 (r->request_length >= r->headers_in.content_length_n) { 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) { 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; 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) { syslog(LOG_INFO, "[%s:%s:%d]", __FILE__, __func__, __LINE__); size_t preread; ssize_t size; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *cl, **next; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; r->main->count++; //原始请求的引用计数加1 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; } 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; */ 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; 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; 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; }
// 参数in实际上是ngx_http_request_body_length_filter里的out,即读取到的数据 // 从内存池里分配节点 // 拷贝in链表里的buf到rb->bufs里,不是直接连接 // 同样是指针操作,没有内存拷贝 // 如果要求写磁盘文件,那么调用ngx_http_write_request_body ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_buf_t *b; 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: %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); } 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: %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); } #endif /* TODO: coalesce neighbouring buffers */ // 从内存池里分配节点 // 拷贝in链表里的buf到rb->bufs里,不是直接连接 // 同样是指针操作,没有内存拷贝 if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (r->request_body_no_buffering) { return NGX_OK; } if (rb->rest > 0) { if (rb->buf && rb->buf->last == rb->buf->end && ngx_http_write_request_body(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } return NGX_OK; } /* rb->rest == 0 */ // 如果要求写磁盘文件,那么调用ngx_http_write_request_body if (rb->temp_file || r->request_body_in_file_only) { if (ngx_http_write_request_body(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (rb->temp_file->file.offset != 0) { cl = ngx_chain_get_free_buf(r->pool, &rb->free); if (cl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b = cl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->in_file = 1; b->file_last = rb->temp_file->file.offset; b->file = &rb->temp_file->file; rb->bufs = cl; } } return NGX_OK; }
//只有在连接后端服务器的时候才会读取客户端请求包体,见ngx_http_xxx_handler(proxy fastcgi等) //post_handler在ngx_http_do_read_client_request_body接收完所有包体后执行,或者在本函数能读取完包体后也会执行 //post_handler方法被回调时,务必调用类似ngx_http_finalize_request的方法去结束请求,否则引用计数会始终无法清零,从而导致请求无法释放。 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_int_t rc; ngx_buf_t *b; ngx_chain_t out, *cl; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; /* 首先把该请求对应的原始请求的引用计数加1。这同时是在要求每一个HTTP模块在传入的post_handler方法被回调时,务必调用类似 ngx_http_finalize_request的方法去结束请求,否则引用计数会始终无法清零,从而导致请求无法释放。 */ r->main->count++; #if (NGX_HTTP_SPDY) if (r->spdy_stream && r == r->main) { r->request_body_no_buffering = 0; rc = ngx_http_spdy_read_request_body(r, post_handler); goto done; } #endif /* 检查请求ngx_http_request_t结构体中的request_body成员,如果它已经被分配过了,证明已经读取过HTTP包体了,不需要再次读取一遍; 再检查请求ngx_http_request_t结构体中的discard_body标志位,如果discard_body为1,则证明曾经执行过丢弃包体的方法,现在包体正在被丢弃中。 只有这两个条件都不满足,才说明真正需要接收HTTP包体。 */ if (r != r->main || r->request_body || r->discard_body) { r->request_body_no_buffering = 0; post_handler(r); //直接执行各HTTP模块提供的post_handler回调方法 return NGX_OK; } if (ngx_http_test_expect(r) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } if (r->request_body_no_buffering) { //如果不缓存包体,request_body_no_buffering和request_body_in_file_only是互斥的 r->request_body_in_file_only = 0; //设置为不缓存包体,则就不能把包体写道文件中 } /* 分配请求的ngx_http_request_t结构体中的request_body成员(之前request_body是NULL空指针),准备接收包体。 */ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } /* * set by ngx_pcalloc(): * * rb->bufs = NULL; * rb->buf = NULL; * rb->free = NULL; * rb->busy = NULL; * rb->chunked = NULL; */ rb->rest = -1; rb->post_handler = post_handler; r->request_body = rb; //把创建的ngx_http_request_body_t空间赋值给request_body /* 检查请求的content-length头部,如果指定了包体长度的content-length字段小于或等于0,当然不用继续接收包体: 如果content-length大于0,则意味着继续执行,但HTTP模块定义的post_handler方法不会知道在哪一次事件的触发中会被回调, 所以先把它设置到request_body结构体的post_handler成员中。 */ if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { r->request_body_no_buffering = 0; post_handler(r); return NGX_OK; } /* * 接收HTTP头部的流程中,是有可能接收到HTTP包体的。 * 首先我们需要检查在header_in缓冲区中已经接收到的包体长度,确定其是否大于或者等于content-length头部指定的长度, * 如果大干或等于则说明已经接收到完整的包体 */ preread = r->header_in->last - r->header_in->pos; if (preread) { //注意在ngx_http_wait_request_handler中第一次读的时候默认是读1024字节,有可能ngx_http_wait_request_handler已经把包体读了 /* 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); out.buf = r->header_in;// out.next = NULL; //把最新读取到的buf数据添加到r->request_body->bufs中,并且让free指向该bufs中所有数据中已经解析了的数据节点信息(重复利用ngx_buf_t) //busy链表中的ngx_buf_t节点指向bufs中所有数据中还没有解析完毕的数据 rc = ngx_http_request_body_filter(r, &out); if (rc != NGX_OK) { goto done; } r->request_length += preread - (r->header_in->last - r->header_in->pos); /* 当上述条件不满足时,再检查header_in缓冲区里的剩余空闲空间是否可以存放下全部的包体(content-length头部指定),如果可以,就不用分配新的包体缓冲区浪费内存了 */ if (!r->headers_in.chunked && rb->rest > 0 //还需要读取rb->rest才能保证包体读完 && rb->rest <= (off_t) (r->header_in->end - r->header_in->last)) //判断header_in指向的剩余未用空间是否足够存取剩余的rest字节数据 { /* the whole request body may be placed in r->header_in */ //header_in中剩余的未用空间足够,例如还差rest = 1000字节才能读取完包体,但是header_in中剩余空间end - last超过1000,则不需要从新开辟空间 //直接使用header_in剩余空间,开辟新的ngx_buf_t空间,使用新的ngx_buf_t中的各个指针指向header_in中剩余未用空间,用来继续读取 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; rb->buf = b; r->read_event_handler = ngx_http_read_client_request_body_handler; r->write_event_handler = ngx_http_request_empty_handler; rc = ngx_http_do_read_client_request_body(r); goto done; } } else { /* set rb->rest */ if (ngx_http_request_body_filter(r, NULL) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } } //包体一次读取完毕 if (rb->rest == 0) { /* the whole request body was pre-read */ //如果配置"client_body_in_file_only" on | clean 表示包体存储在磁盘文件中 if (r->request_body_in_file_only) { if (ngx_http_write_request_body(r) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } if (rb->temp_file->file.offset != 0) { cl = ngx_chain_get_free_buf(r->pool, &rb->free); if (cl == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } b = cl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->in_file = 1; b->file_last = rb->temp_file->file.offset; b->file = &rb->temp_file->file; rb->bufs = cl; //如果包体存入临时文件中,则读取包体完成后,bufs指向的ngx_chain_t中的各个指针指向文件中的相关偏移 } else { rb->bufs = NULL; } } r->request_body_no_buffering = 0; post_handler(r); return NGX_OK; } //包体一次没有读取完毕 if (rb->rest < 0) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "negative request body rest"); rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); size = clcf->client_body_buffer_size; size += size >> 2; //实际上就是四分之五5/4个client_body_buffer_size /* TODO: honor r->request_body_in_single_buf */ //走到这里之前至少在ngx_http_wait_request_handler函数中读取过一次,也就是读取头部的时候,可能会读取一部分包体,在读取头部的时候 //读取的最大报文长度为client_header_buffer_size,所以包体有可能在那里读取后处理了头部行后,会走到本函数处理包体,这时候可能包体没有读完 if (!r->headers_in.chunked && rb->rest < size) { size = (ssize_t) rb->rest; if (r->request_body_in_single_buf) { //需要缓存到同一个buf中,那么开辟的空间就必须一次分配完,这样可以存储后面所有的。 size += preread; //如果是把读取的网络数据存到同一个single buffer中,则本次读到preread字节,但是还有size字节没读,所以需要相加,表示一共需要这么多空间, } } else { size = clcf->client_body_buffer_size; //如果不是缓存到同一个buf,则一次最多开辟这么多空间,这样可能需要多个buf才能读取完 } /* 说明确实需要分配用于接收包体的缓冲区了。缓冲区长度由nginx.conf丈件中的client_body_buffer_size配置项指定,缓冲区就在ngx_http_request_body_t 结构体的buf成员中存放着,同时,bufs和to_ write这两个缓冲区链表首部也指向该buf。 */ rb->buf = ngx_create_temp_buf(r->pool, size); //这个是为下次读取准备的 if (rb->buf == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } /* 设置请求ngx_http_request_t结构体的read_ event_ handler成员为上面介绍过的ngx_http_read_client_request_body_handler方法, 它意味着如果epoll再次检测到可读事件或者读事件的定时器超时,HTTP框架将调用ngx_http_read_client_request_body_handler方法处理 */ r->read_event_handler = ngx_http_read_client_request_body_handler; r->write_event_handler = ngx_http_request_empty_handler; /* 调用ngx_http_do_read_client_request_body方法接收包体。该方法的意义在于把客户端与Nginx之间TCP连接上套接字缓冲区中的当前字符流全 部读出来,并判断是否需要写入文件,以及是否接收到全部的包体,同时在接收到完整的包体后激活post_handler回调方法 */ rc = ngx_http_do_read_client_request_body(r);//这里面添加ngx_handle_read_event的时候,对应的handler为ngx_http_read_client_request_body_handler done: if (r->request_body_no_buffering && (rc == NGX_OK || rc == NGX_AGAIN)) { if (rc == NGX_OK) { r->request_body_no_buffering = 0; } else { /* rc == NGX_AGAIN */ r->reading_body = 1; } r->read_event_handler = ngx_http_block_reading; post_handler(r); } if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {//如果返回出错 r->main->count--; //该函数处理结束后-1,因为该函数开始处理的时候有+1 } return rc; }
/* 调用ngx_http_do_read_client_request_body方法接收包体。该方法的意义在于把客户端与Nginx之间TCP连接上套接字缓冲区中的当前字符流全 部读出来,并判断是否需要写入文件,以及是否接收到全部的包体,同时在接收到完整的包体后激活post_handler回调方法 负责具体的读取包体工作,该函数会在for循环中会反复读直到包体读取完毕,如果内核已经没有数据并且包体还没有读完,则添加读事件,并退出循环,这样HTTP模块还能继续作用其他功能,避免阻塞 读取的时候一个buf装满后,会把buf中存储的数据写到临时文件中(不管有没有配置request_body_in_file_only),然后继续使用该buf读取数据, 存储数据的内存分配地方有两个:1.在读取报文头部的时候ngx_http_wait_request_handler 2.如果在1中读到的内容里面不包括完整包体,则需要在 ngx_http_read_client_request_body中会重新分配内存读取,触发再次读取的地方未ngx_http_read_client_request_body中为读取到完整包体的时候添加的ngx_handle_read_event */ static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r) { off_t rest; size_t size; ssize_t n; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *cl, out; 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 ( ;; ) { /* 首先检查请求的request_body成员中的buf缓冲区,如果缓冲区还有空闲的空间,则跳过该if{}去读取内核中套接字缓冲区里的TCP字符流; 如果缓冲区已经写满,则调用ngx_http_write_request_body方法把缓冲区中的字符流写入文件。不管有没有配置request_body_in_file_only置1 */ if (rb->buf->last == rb->buf->end) { if (rb->buf->pos != rb->buf->last) { /* pass buffer to request body filter chain */ out.buf = rb->buf; out.next = NULL; //这里肯定会调用ngx_http_request_body_save_filter->ngx_http_write_request_body写该buf中的内容到临时文件,因为该buf指向的空间 //会重复利用来读取包体内容 rc = ngx_http_request_body_filter(r, &out); if (rc != NGX_OK) { return rc; } } else { /* update chains */ rc = ngx_http_request_body_filter(r, NULL); if (rc != NGX_OK) { return rc; } } if (rb->busy != NULL) { //如果头部行中的content-length:LEN中的len长度表示后面的包体大小,如果后面的包体数据长度实际比头部中的LEN大,则会走这里, if (r->request_body_no_buffering) { if (c->read->timer_set) { ngx_del_timer(c->read, NGX_FUNC_LINE); } if (ngx_handle_read_event(c->read, 0, NGX_FUNC_LINE) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } return NGX_AGAIN; } return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* 为什么能下次还可以直接利用rb->buf空间来读取数据呢? 当一个rb->buf填满后就会通过ngx_http_write_request_body把bufs链表中的所有ngx_chain_t->ngx_buf_t中指向的数据写入到临时文件,因此rb->buf中的内存就可以再次使用了 */ //只需要把缓冲区ngx_buf_t结构体的last指针指向start指针,缓冲区即可复用。 rb->buf->pos = rb->buf->start; rb->buf->last = rb->buf->start; } size = rb->buf->end - rb->buf->last; //buf中还剩余这么多空间 rest = rb->rest - (rb->buf->last - rb->buf->pos); //还有多少字节包体没有读取 if ((off_t) size > rest) { //说明空间够用来存储剩余的没有读取的字节数 size = (size_t) rest; } //负责具体的读取包体工作,该函数会在for循环中会反复读直到包体读取完毕,如果内核已经没有数据并且包体还没有读完,则添加读事件,并退出循环,这样HTTP模块还能继续作用其他功能,避免阻塞 //调用封装了recv的方法从套接字缓冲区中读取包体到缓冲区中。 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) {//如果recv方法返回错误,或者客户端主动关闭了连接 ngx_log_error(NGX_LOG_INFO, c->log, 0, "client prematurely closed connection"); } if (n == 0 || n == NGX_ERROR) {//如果recv方法返回错误,或者客户端主动关闭了连接 c->error = 1; return NGX_HTTP_BAD_REQUEST; } /* 根据接收到的TCP流长度,修改缓冲区参数。例如,把缓冲区ngx_buf_t结构体的last揩针加上接收到的长度,同时更新request_body结 构体中表示待接收的剩余包体长度的rest成员、更新ngx_http_request_t结构体中表示已接收请求长度的request_length成员。 */ //从这里可以看出在多次读取包体的时候,需要先把前面开辟空间buf中没有填充的部分填满,如果buf填满了,则重新利用该buf读取数据 //之前读取到填满buf中的数据取出来存放到临时文件中,参考前面的if (rb->buf->last == rb->buf->end) rb->buf->last += n; r->request_length += n; if (n == rest) {//根据rest成员检查是否接收到完整的包体 /* pass buffer to request body filter chain */ out.buf = rb->buf; out.next = NULL; rc = ngx_http_request_body_filter(r, &out); if (rc != NGX_OK) { return rc; } } if (rb->rest == 0) { break; //所有包体读取处理完毕,则退出for } 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; } /* 如果当前已经没有可读的字符流,同时还没有接收到完整的包体,则说明需要把读事件添加到事件模块,等待可读事件发生时,事件框架可以再次 调度到这个方法接收包体。这一步是调用ngx_add_timer方法将读事件添加到定时器中,超时时间以nginx.conf文件中的client_body_timeout配置项参数为准。 */ //说明前面的 n = c->recv(c, rb->buf->last, size);返回的是NGX_AGAIN,所以在recv中会把ready置0 if (!c->read->ready) { if (r->request_body_no_buffering && rb->buf->pos != rb->buf->last) { /* pass buffer to request body filter chain */ out.buf = rb->buf; out.next = NULL; rc = ngx_http_request_body_filter(r, &out); if (rc != NGX_OK) { return rc; } } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); //当读取到完整的包体后,会删除该定时器,见后面的ngx_del_timer(c->read); ngx_add_timer(c->read, clcf->client_body_timeout, NGX_FUNC_LINE);//handle应该是ngx_http_request_handler /* 这个请求连接上的读事件触发时的回调方法ngx_http_request_handler,从而会调用read_event_handler方法(ngx_http_read_client_request_body_handler) */ if (ngx_handle_read_event(c->read, 0, NGX_FUNC_LINE) != NGX_OK) { //handle应该是ngx_http_request_handler,通过这里触发再次读取包体 return NGX_HTTP_INTERNAL_SERVER_ERROR; } return NGX_AGAIN; //把控制器交给HTTP框架,由框架感知读事件,当读事件发生,也就是数据到来,继续读取包体 } } //只有包体读取完毕,才会从上面的for()循环中退出 /* 表明已经接收到完整的包体,需要做一些收尾工作了。首先不需要检查是否接收HTTP包体超时了,要把读事件从定时器中取出,防止不必要的定时器触发。 这一步会检查读事件的timer set标志位,如果为1,则调用ngx_del_timer方法把读事件从定时器中移除。 */ if (c->read->timer_set) { ngx_del_timer(c->read, NGX_FUNC_LINE); } //如果缓冲区中还有未写入文件的内容,调用ngx_http_write_request_body方法把最后的包体内容也写入文件。 if (rb->temp_file || r->request_body_in_file_only) { //只要之前的内存有写入文件,那么剩余的部分也要写入文件 /* save the last part */ if (ngx_http_write_request_body(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (rb->temp_file->file.offset != 0) { cl = ngx_chain_get_free_buf(r->pool, &rb->free); if (cl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b = cl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->in_file = 1; b->file_last = rb->temp_file->file.offset; b->file = &rb->temp_file->file; rb->bufs = cl; //读取客户包体即使是存入临时文件中,当所有包体读取完毕后(ngx_http_do_read_client_request_body),还是会让r->request_body->bufs指向文件中的相关偏移内存地址 } else { rb->bufs = NULL; } } /* 在之前read_event_handler成员设置为ngx_http_read_client_request_body_handler方法,现在既然已经接收到完整的包体了,就会把 read_event_handler设为ngx_http_block_reading方法,表示连接上再有读事件将不做任何处理。 */ if (!r->request_body_no_buffering) { r->read_event_handler = ngx_http_block_reading; rb->post_handler(r); //执行ngx_http_read_client_request_body的第二个参数 } 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; ngx_chain_t *cl, **next; ngx_temp_file_t *tf; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; //主请求引用+1 r->main->count++; //请求体已经被读取或丢弃 if (r->request_body || r->discard_body) { post_handler(r); return NGX_OK; } //检查是否有Expect: 100-continue头,该请求头表示客户端期望发送请求体,服务器回复“HTTP/1.1 100 Continue”允许客户端发送请求体 if (ngx_http_test_expect(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } //分配ngx_http_request_body_t结构 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) { post_handler(r); return NGX_OK; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); //请求体长度为0 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) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } 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; //header_in中有未处理数据,表明预读了请求体 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; 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) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } 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); }