/* * Copyright (C) Igor Sysoev */ #include #include #include #include static void ngx_http_read_client_request_body_handler(ngx_event_t *rev); static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r); ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r) { ssize_t size; ngx_buf_t *b; ngx_chain_t *cl; ngx_http_core_loc_conf_t *clcf; size = r->header_in->last - r->header_in->pos; if (size) { /* there is the pre-read part of the request body */ ngx_test_null(b, ngx_calloc_buf(r->pool), NGX_HTTP_INTERNAL_SERVER_ERROR); b->temporary = 1; b->start = b->pos = r->header_in->pos; b->end = b->last = r->header_in->last; ngx_alloc_link_and_set_buf(r->request_body->bufs, b, r->pool, NGX_HTTP_INTERNAL_SERVER_ERROR); if (size >= r->headers_in.content_length_n) { /* the whole request body was pre-read */ r->header_in->pos += r->headers_in.content_length_n; r->request_body->handler(r->request_body->data); return NGX_OK; } r->header_in->pos = r->header_in->last; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); r->request_body->rest = r->headers_in.content_length_n - size; if (r->request_body->rest < clcf->client_body_buffer_size + (clcf->client_body_buffer_size >> 2)) { size = r->request_body->rest; } else { size = clcf->client_body_buffer_size; } ngx_test_null(r->request_body->buf, ngx_create_temp_buf(r->pool, size), NGX_HTTP_INTERNAL_SERVER_ERROR); ngx_alloc_link_and_set_buf(cl, r->request_body->buf, r->pool, NGX_HTTP_INTERNAL_SERVER_ERROR); if (r->request_body->bufs) { r->request_body->bufs->next = cl; } else { r->request_body->bufs = cl; } r->connection->read->event_handler = ngx_http_read_client_request_body_handler; return ngx_http_do_read_client_request_body(r); } static void ngx_http_read_client_request_body_handler(ngx_event_t *rev) { ngx_int_t rc; ngx_connection_t *c; ngx_http_request_t *r; c = rev->data; r = c->data; if (rev->timedout) { ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; } rc = ngx_http_do_read_client_request_body(r); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { ngx_http_finalize_request(r, 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; ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; c = r->connection; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http read client request body"); for ( ;; ) { if (r->request_body->buf->last == r->request_body->buf->end) { n = ngx_write_chain_to_temp_file(r->request_body->temp_file, r->request_body->bufs->next ? r->request_body->bufs->next: r->request_body->bufs); /* TODO: n == 0 or not complete and level event */ if (n == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->request_body->temp_file->offset += n; r->request_body->buf->pos = r->request_body->buf->start; r->request_body->buf->last = r->request_body->buf->start; } size = r->request_body->buf->end - r->request_body->buf->last; if (size > r->request_body->rest) { size = r->request_body->rest; } n = c->recv(c, r->request_body->buf->last, size); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http client request body recv " SIZE_T_FMT, n); if (n == NGX_AGAIN) { 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_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } return NGX_AGAIN; } if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client closed prematurely connection"); } if (n == 0 || n == NGX_ERROR) { r->closed = 1; return NGX_HTTP_BAD_REQUEST; } r->request_body->buf->last += n; r->request_body->rest -= n; if (r->request_body->rest == 0) { break; } if (r->request_body->buf->last < r->request_body->buf->end) { break; } } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http client request body rest " SIZE_T_FMT, r->request_body->rest); if (r->request_body->rest) { return NGX_AGAIN; } if (r->request_body->temp_file->file.fd != NGX_INVALID_FILE) { /* save the last part */ n = ngx_write_chain_to_temp_file(r->request_body->temp_file, r->request_body->bufs->next ? r->request_body->bufs->next: r->request_body->bufs); /* TODO: n == 0 or not complete and level event */ if (n == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (!(b = ngx_calloc_buf(r->pool))) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b->in_file = 1; b->file_pos = 0; b->file_last = r->request_body->temp_file->file.offset; b->file = &r->request_body->temp_file->file; if (r->request_body->bufs->next) { r->request_body->bufs->next->buf = b; } else { r->request_body->bufs->buf = b; } } r->request_body->handler(r->request_body->data); return NGX_OK; }