ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r) { syslog(LOG_INFO, "[%s:%s:%d]", __FILE__, __func__, __LINE__); ssize_t size; ngx_event_t *rev; if (r != r->main || r->discard_body) { return NGX_OK; } if (ngx_http_test_expect(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rev = r->connection->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); if (rev->timer_set) { ngx_del_timer(rev); } if (r->headers_in.content_length_n <= 0 || r->request_body) { return NGX_OK; } size = r->header_in->last - r->header_in->pos; if (size) { if (r->headers_in.content_length_n > size) { r->header_in->pos += size; r->headers_in.content_length_n -= size; } else { r->header_in->pos += (size_t) r->headers_in.content_length_n; r->headers_in.content_length_n = 0; return NGX_OK; } } if (ngx_http_read_discarded_request_body(r) == NGX_OK) { r->lingering_close = 0; return NGX_OK; } /* == NGX_AGAIN */ r->read_event_handler = ngx_http_discarded_request_body_handler; if (ngx_handle_read_event(rev, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->count++; r->discard_body = 1; return NGX_OK; }
ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r) { ssize_t size; ngx_event_t *rev; if (r != r->main || r->discard_body) { return NGX_OK; } if (ngx_http_test_expect(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rev = r->connection->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); if (rev->timer_set) { ngx_del_timer(rev); } if (r->headers_in.content_length_n <= 0 || r->request_body) { return NGX_OK; } size = r->header_in->last - r->header_in->pos; if (size) { if (r->headers_in.content_length_n > size) { r->headers_in.content_length_n -= size; } else { r->header_in->pos += (size_t) r->headers_in.content_length_n; r->headers_in.content_length_n = 0; return NGX_OK; } } r->discard_body = 1; r->read_event_handler = ngx_http_read_discarded_request_body_handler; if (ngx_handle_read_event(rev, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->count++; (void) ngx_http_read_discarded_request_body(r); return NGX_OK; }
ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r) { ssize_t size; ngx_int_t rc; ngx_event_t *rev; if (r != r->main || r->discard_body || r->request_body) { return NGX_OK; } if (ngx_http_test_expect(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rev = r->connection->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); if (rev->timer_set) { ngx_del_timer(rev); } if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) { return NGX_OK; } size = r->header_in->last - r->header_in->pos; if (size || r->headers_in.chunked) { rc = ngx_http_discard_request_body_filter(r, r->header_in); if (rc != NGX_OK) { return rc; } if (r->headers_in.content_length_n == 0) { return NGX_OK; } } rc = ngx_http_read_discarded_request_body(r); if (rc == NGX_OK) { r->lingering_close = 0; return NGX_OK; } if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } /* rc == NGX_AGAIN */ r->read_event_handler = ngx_http_discarded_request_body_handler; if (ngx_handle_read_event(rev, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->count++; r->discard_body = 1; 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; }
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; }
/* this function's implementation is borrowed from nginx 0.8.20 * and modified a lot to work with the chunked encoding. * copyrighted (C) by Igor Sysoev */ ngx_int_t ngx_http_chunkin_read_chunked_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler) { size_t preread; size_t size; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; ngx_http_chunkin_ctx_t *ctx; ngx_int_t rc; /* r->request_body_in_single_buf = 1; */ /* r->request_body_in_file_only = 1; */ if (r->request_body || r->discard_body) { post_handler(r); return NGX_OK; } if (ngx_http_test_expect(r) != NGX_OK) { 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; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); rb->post_handler = post_handler; ctx = ngx_http_get_module_ctx(r, ngx_http_chunkin_filter_module); if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ctx->count++; ctx->saved_header_in_pos = r->header_in->pos; preread = r->header_in->last - r->header_in->pos; ngx_http_chunkin_init_chunked_parser(r, ctx); if (preread) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "chunkin: http chunked client request body preread %uz", preread); dd("raw chunked body %.*s", preread, r->header_in->pos); rc = ngx_http_chunkin_run_chunked_parser(r, ctx, &r->header_in->pos, r->header_in->last, "preread"); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } if (rc == NGX_ERROR) { /* chunked body parsefail */ return NGX_HTTP_BAD_REQUEST; } if (rc == NGX_OK) { /* TODO: take r->request_body_in_file_only * into account */ dd("we have the whole chunked body in 'prepread'..."); dd("keepalive? %s", r->keepalive ? "yes" : "no"); dd("chunks total size: %d", ctx->chunks_total_size); rc = ngx_http_chunkin_set_content_length_header(r, ctx->chunks_total_size); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } rb->bufs = ctx->chunks; if (rb->bufs) { rb->buf = ctx->chunks->buf; } dd("buf len: %d", rb->buf->last - rb->buf->pos); dd("buf left (len %d): %s", (int) (r->header_in->last - r->header_in->pos), r->header_in->pos); /* r->header_in->pos = r->header_in->last; */ r->request_length += r->header_in->pos - ctx->saved_header_in_pos; ctx->raw_body_size += r->header_in->pos - ctx->saved_header_in_pos; post_handler(r); return NGX_OK; } if (rc != NGX_AGAIN) { /* this is impossible to reach... */ return NGX_HTTP_INTERNAL_SERVER_ERROR; } size = r->header_in->last - r->header_in->pos; if (size) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "chunkin: internal assertion failed: %O bytes " "left in the r->header_in buffer but " "the parser returns NGX_AGAIN", (off_t) size); return NGX_HTTP_INTERNAL_SERVER_ERROR; } dd("we need to read more chunked body in addition to 'prepread'..."); ctx->just_after_preread = 1; /* r->header_in->pos = r->header_in->last; */ r->request_length += preread; ctx->raw_body_size += preread; } size = clcf->client_body_buffer_size; if (ctx->chunks_total_size > size) { size = ctx->chunks_total_size; } dd("chunks total size after preread: %d, buf size: %d", (int)ctx->chunks_total_size, (int)size); rb->buf = ngx_create_temp_buf(r->pool, size); #if 0 /* XXX just for debugging... */ ngx_memzero(rb->buf->start, rb->buf->end - rb->buf->start); #endif if (rb->buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->read_event_handler = ngx_http_chunkin_read_chunked_request_body_handler; rc = ngx_http_chunkin_do_read_chunked_request_body(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) { 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; }
// 要求nginx丢弃请求体数据 // 子请求不与客户端直接通信,不会有请求体的读取 // 已经设置了discard_body标志,表示已经调用了此函数 // request_body指针不空,表示已经调用了此函数 // 这三种情况就无需再启动读取handler,故直接返回成功 // 因为要丢弃数据,所以不需要检查超时,也就是说即使超时也不算是错误 // 如果头里的长度是0且不是chunked // 说明没有请求体数据,那么就无需再读,直接返回成功 // *一直*读数据并解析,检查content_length_n,如果无数据可读就返回NGX_AGAIN // 因为使用的是et模式,所以必须把数据读完 // 调用ngx_http_discard_request_body_filter检查收到的数据 // 使用回调ngx_http_discarded_request_body_handler读取数据 ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r) { ssize_t size; ngx_int_t rc; ngx_event_t *rev; // 子请求不与客户端直接通信,不会有请求体的读取 // 已经设置了discard_body标志,表示已经调用了此函数 // request_body指针不空,表示已经调用了此函数 // 这三种情况就无需再启动读取handler,故直接返回成功 // discard_body在本函数最末尾设置,防止重入 if (r != r->main || r->discard_body || r->request_body) { return NGX_OK; } #if (NGX_HTTP_V2) if (r->stream) { r->stream->skip_data = 1; return NGX_OK; } #endif if (ngx_http_test_expect(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } // 从请求获取连接对象,再获得读事件 rev = r->connection->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); // 因为要丢弃数据,所以不需要检查超时,也就是说即使超时也不算是错误 // 不检查读事件的超时,有数据就读 if (rev->timer_set) { ngx_del_timer(rev); } // 如果头里的长度未设置、或者是0且不是chunked // 说明没有请求体数据,那么就无需再读,直接返回成功 if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) { return NGX_OK; } // 检查缓冲区里在解析完头后是否还有数据 // 也就是说之前可能读取了部分请求体数据 size = r->header_in->last - r->header_in->pos; // 有数据,或者是chunked数据 // 有可能已经读取了一些请求体数据,所以先检查一下 if (size || r->headers_in.chunked) { // 检查请求结构体里的缓冲区数据,丢弃 // 有content_length_n指定确切长度,那么只接收,不处理,移动缓冲区指针 // chunked数据需要解析数据 rc = ngx_http_discard_request_body_filter(r, r->header_in); // 不是ok表示出错,不能再读取数据 if (rc != NGX_OK) { return rc; } // content_length_n==0表示数据已经全部读完 // 就已经完成了丢弃任务,否则就要加入epoll读事件继续读 if (r->headers_in.content_length_n == 0) { return NGX_OK; } } // 走到这里,表明content_length_n>=0,还有数据要读取 // 接下来就读取请求体数据并丢弃 // 使用固定的4k缓冲区接受丢弃的数据 // 一直读数据并解析,检查content_length_n,如果无数据可读就返回NGX_AGAIN // 因为使用的是et模式,所以必须把数据读完 // 需要使用回调ngx_http_discarded_request_body_handler读取数据 rc = ngx_http_read_discarded_request_body(r); if (rc == NGX_OK) { r->lingering_close = 0; return NGX_OK; } // 出错 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } /* rc == NGX_AGAIN */ // 读事件not ready,无数据可读,那么就要在epoll里加入读事件和handler // 注意,不再需要加入定时器 // 之后再有数据来均由ngx_http_discarded_request_body_handler处理 // 里面还是调用ngx_http_read_discarded_request_body读数据 r->read_event_handler = ngx_http_discarded_request_body_handler; // 注意,读事件的handler实际上是ngx_http_request_handler // 但最终会调用r->read_event_handler if (ngx_handle_read_event(rev, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } // 引用计数器增加,表示此请求还有关联的操作,不能直接销毁 r->count++; // 设置丢弃标志,防止再次进入本函数 r->discard_body = 1; return NGX_OK; }
// 要求nginx读取请求体,传入一个post_handler // 引用计数器增加,表示此请求还有关联的操作,不能直接销毁 // 所以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; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; // 引用计数器增加,表示此请求还有关联的操作,不能直接销毁 r->main->count++; // 删除了原spdy的代码 // 子请求不与客户端直接通信,不会有请求体的读取 // 已经设置了discard_body标志,表示已经开始丢弃请求体 // request_body指针不空,表示已经开始读取请求体 if (r != r->main || r->request_body || r->discard_body) { r->request_body_no_buffering = 0; // 不需要再读取数据了,直接回调handler // 相当于触发写事件,继续之前中断的处理流程 post_handler(r); return NGX_OK; } #if (NGX_HTTP_V2) if (r->stream) { rc = ngx_http_v2_read_request_body(r, post_handler); goto done; } #endif // 如果要求不缓存请求体数据 // 那么请求体就不会存在磁盘文件里 // if (r->request_body_no_buffering) { // r->request_body_in_file_only = 0; // } 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; } /* * set by ngx_pcalloc(): * * rb->bufs = NULL; * rb->buf = NULL; * rb->free = NULL; * rb->busy = NULL; * rb->chunked = NULL; */ // -1表示未初始化 rb->rest = -1; // 当读取完毕后的回调函数 // 即ngx_http_read_client_request_body的第二个参数 rb->post_handler = post_handler; r->request_body = rb; // 数据长度不对,直接回调handler if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { r->request_body_no_buffering = 0; // 不需要再读取数据了,直接回调handler // 相当于触发写事件,继续之前中断的处理流程 post_handler(r); return NGX_OK; } // 查看已经读取的数据,即缓冲区里头之后的数据 preread = r->header_in->last - r->header_in->pos; // 已经读取了部分body 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); // 链表的第一个节点指向r->header_in out.buf = r->header_in; out.next = NULL; // 分为chunked和确定长度两种 // 简单起见只研究确定长度,即ngx_http_request_body_length_filter // // 处理确定长度的请求体数据,参数是已经读取的数据链表 // 先看free里是否有空闲节点,有则直接使用 // 如果没有,就从内存池的空闲链表里获取 // 这里只是指针操作,没有内存拷贝 // 调用请求体过滤链表,对数据进行过滤处理 // 实际上是ngx_http_request_body_save_filter // 拷贝in链表里的buf到rb->bufs里,不是直接连接 // 最后把用完的ngx_chaint_t挂到free里供复用,提高效率 rc = ngx_http_request_body_filter(r, &out); if (rc != NGX_OK) { goto done; } // 增加已经读取的数据长度,但因为 // preread = r->header_in->last - r->header_in->pos; // 实际上是没有增加 r->request_length += preread - (r->header_in->last - r->header_in->pos); // 不是chunked,有确定长度 // 还有剩余数据要读取 // header_in缓冲区里还有空间,足够容纳rest字节的数据 // 所以不需要再另外分配内存了,header_in缓冲区可以存下所有请求数据 // 特别优化处理 // 设置读事件handler为ngx_http_read_client_request_body_handler // 要求继续读取 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; } // 指向header_in里的请求头后的所有空间 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; // 设置读事件handler为ngx_http_read_client_request_body_handler // 注意,读事件的handler实际上是ngx_http_request_handler // 但最终会调用r->read_event_handler // 读取请求体的handler // 首先检查超时,实际功能在ngx_http_do_read_client_request_body r->read_event_handler = ngx_http_read_client_request_body_handler; // 写事件阻塞 r->write_event_handler = ngx_http_request_empty_handler; // 在rb->buf里读取数据 // 如果已经读完了所有剩余数据,那么就挂到bufs指针,结束函数 rc = ngx_http_do_read_client_request_body(r); goto done; } // 这里表示rest==0,一次就已经全部读取了header+body // 不需要再关心读事件 // 走下面的if (rb->rest == 0) } else { /* set rb->rest */ // 没有读取body数据 // 分为chunked和确定长度两种 // 简单起见只研究确定长度,即ngx_http_request_body_length_filter // 因为参数是null,所以函数里只会设置rb->rest,即剩余要读取的字节数 if (ngx_http_request_body_filter(r, NULL) != NGX_OK) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } // 走下面的clcf = ngx_http_get_module_loc_conf } // rb->rest == 0 body已经读取完毕 // preread >= content length if (rb->rest == 0) { /* the whole request body was pre-read */ // body已经读取完毕,可以调用post_handler继续处理流程 r->request_body_no_buffering = 0; post_handler(r); return NGX_OK; } // 错误,负数body 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; } // 没读到body数据,但知道了确定的body长度 // 取模块的loc配置 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); // 查看body的缓冲区大小 size = clcf->client_body_buffer_size; // 增加1/4的长度 size += size >> 2; /* TODO: honor r->request_body_in_single_buf */ // 长度确定,且在size里可以容纳剩余字节数 if (!r->headers_in.chunked && rb->rest < size) { size = (ssize_t) rb->rest; // 要求body在一块缓冲区里,长度增加 if (r->request_body_in_single_buf) { size += preread; } } else { size = clcf->client_body_buffer_size; } // 内存池里分配一个缓冲区,大小为size rb->buf = ngx_create_temp_buf(r->pool, size); if (rb->buf == NULL) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; goto done; } // 设置读事件handler为ngx_http_read_client_request_body_handler // 注意,读事件的handler实际上是ngx_http_request_handler // 但最终会调用r->read_event_handler // 读取请求体的handler // 首先检查超时,实际功能在ngx_http_do_read_client_request_body r->read_event_handler = ngx_http_read_client_request_body_handler; // 写事件阻塞 r->write_event_handler = ngx_http_request_empty_handler; // 在rb->buf里读取数据 // 如果已经读完了所有剩余数据,那么就挂到bufs指针,结束函数 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; }
ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r) { ssize_t size; ngx_int_t rc; ngx_event_t *rev; #if (NGX_HTTP_SPDY) if (r->spdy_stream && r == r->main) { r->spdy_stream->skip_data = NGX_SPDY_DATA_DISCARD; return NGX_OK; } #endif /* 首先检查当前请求是一个子请求还是原始请求。为什么要检查这个呢?因为对于子请求而言,它木是来自客户端的请求,所以不存在处理HTTP 请求包体的概念。如果当前请求是原始请求,则继续执行;如果它是子请求,则直接返回NGX_OK表示丢弃包体成功。检查ngx_http_request_t结构 体的request_body成员,如果它已经被赋值过且不再为NULL空指针,则说明已经接收过包体了,这时也需要返回NGX_OK表示成功。 */ if (r != r->main || r->discard_body || r->request_body) { return NGX_OK; } if (ngx_http_test_expect(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rev = r->connection->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); /* 检查请求连接上的读事件是否在定时器中,这是因为丢弃包体不用考虑超时问题(linger_timer例外,本章不考虑此情况)。如果读事件 的timer set标志位为1,则从定时器中移除此事件。还要检查content-length头部,如果它的值小于或等于0,同样意味着可以直接返回 NGX一OK,表示成功丢弃了全部包体。 */ if (rev->timer_set) { ngx_del_timer(rev, NGX_FUNC_LINE); } if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) { return NGX_OK; } size = r->header_in->last - r->header_in->pos; if (size || r->headers_in.chunked) { rc = ngx_http_discard_request_body_filter(r, r->header_in); if (rc != NGX_OK) { return rc; } if (r->headers_in.content_length_n == 0) { return NGX_OK; } } /* 在接收HTTP头部时,还是要检查是否凑巧已经接收到完整的包体(如果包体很小,那么这是非常可能发生的事),如果已经接收到完整的包 体,则直接返回NGX OK,表示丢弃包体成功,否则,说明需要多次的调度才能完成丢弃包体这一动作,此时把请求的read_event_handler 成员设置为ngx_http_discarded_request_body_handler方法。 */ rc = ngx_http_read_discarded_request_body(r); if (rc == NGX_OK) { /* 返回NGX一OK表示已经接收到完整的包体了,这时将请求的lingering_close延时关闭标志位设为0,表示不需要为了包体的接收而 延时关闭了,同时返回NGX―OK表示丢弃包体成功。 */ r->lingering_close = 0; return NGX_OK; } if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } //返回非NGX_OK表示Nginx的事件框架触发事件需要多次调度才能完成丢弃包体这一动作 /* rc == NGX_AGAIN */ r->read_event_handler = ngx_http_discarded_request_body_handler; //下次读事件到来时通过ngx_http_request_handler来调用 if (ngx_handle_read_event(rev, 0, NGX_FUNC_LINE) != NGX_OK) { //调用ngx_handle_read_event方法把读事件添加到epoll中 handle为ngx_http_request_handler return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* 返回非NGX_ OK表示Nginx的事件框架触发事件需要多次调度才能完成丢弃包体这一动作,于是先把引用计数加1,防止这边还在丢弃包体, 而其他事件却已让请求意外销毁,引发严重错误。同时把ngx_http_request_t结构体的discard_body标志位置为1,表示正在丢弃包体,并 返回NGX―OK,当然,达时的NGX OK绝不表示已经成功地接收完包体,只是说明ngx_http_discard_request_body执行完毕而已。 */ r->count++; r->discard_body = 1; 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_int_t ngx_http_discard_request_body(ngx_http_request_t *r) { ssize_t size; ngx_event_t *rev; //子请求不需处理 if (r != r->main || r->discard_body) { return NGX_OK; } if (ngx_http_test_expect(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rev = r->connection->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); if (rev->timer_set) { //删除读事件定时器 ngx_del_timer(rev); } if (r->headers_in.content_length_n <= 0 || r->request_body) { //无请求体或已读完直接返回 return NGX_OK; } size = r->header_in->last - r->header_in->pos; if (size) { //丢弃预读数据 if (r->headers_in.content_length_n > size) { r->header_in->pos += size; r->headers_in.content_length_n -= size; } else { r->header_in->pos += (size_t) r->headers_in.content_length_n; r->headers_in.content_length_n = 0; return NGX_OK; } } //设置读事件回调函数并挂在读事件 r->read_event_handler = ngx_http_discarded_request_body_handler; if (ngx_handle_read_event(rev, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } //读取并丢弃请求体 if (ngx_http_read_discarded_request_body(r) == NGX_OK) { r->lingering_close = 0; } else { //客户端没有一次发完请求体,在下一次读事件是,通过ngx_http_discarded_request_body_handler函数处理 r->count++; //可能在请求处理完后仍未发送完请求体,防止处理完请求后直接释放请求 r->discard_body = 1; } 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); }