# HG changeset patch # User Igor Sysoev # Date 1067244829 0 # Node ID e7e094d34162e01c6f8a36d25d993a33ab4120a2 # Parent 981e4af2a4256a1a898b1549b4f62b75b37baf3f nginx-0.0.1-2003-10-27-11:53:49 import diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -7,6 +7,27 @@ static int ngx_temp_number; static int ngx_random; +int ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain) +{ + int rc; + + if (tf->file.fd == NGX_INVALID_FILE) { + rc = ngx_create_temp_file(&tf->file, &tf->path, tf->pool, + tf->persistent); + + if (rc == NGX_ERROR || rc == NGX_AGAIN) { + return rc; + } + + if (!tf->persistent && tf->warn) { + ngx_log_error(NGX_LOG_WARN, tf->file.log, 0, tf->warn); + } + } + + return ngx_write_chain_to_file(&tf->file, chain, tf->file.offset, tf->pool); +} + + int ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool, int persistent) { diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h --- a/src/core/ngx_file.h +++ b/src/core/ngx_file.h @@ -26,6 +26,17 @@ typedef struct { } ngx_path_t; +typedef struct { + ngx_file_t file; + ngx_path_t path; + ngx_pool_t *pool; + char *warn; + + unsigned persistent:1; +} ngx_temp_file_t; + + +int ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain); int ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool, int persistent); void ngx_create_hashed_filename(ngx_file_t *file, ngx_path_t *path); @@ -46,7 +57,7 @@ char *ngx_conf_set_path_slot(ngx_conf_t conf->level[0] = l1; \ conf->level[1] = l2; \ conf->level[2] = l3; \ - conf->len = l1 + l2 + l3 + l1 ? 1:0 + l2 ? 1:0 + l3 ? 1:0; \ + conf->len = l1 + l2 + l3 + (l1 ? 1:0) + (l2 ? 1:0) + (l3 ? 1:0); \ } else { \ conf = prev; \ } \ diff --git a/src/core/ngx_hunk.c b/src/core/ngx_hunk.c --- a/src/core/ngx_hunk.c +++ b/src/core/ngx_hunk.c @@ -26,6 +26,48 @@ ngx_hunk_t *ngx_create_temp_hunk(ngx_poo return h; } + +ngx_chain_t *ngx_create_chain_of_hunks(ngx_pool_t *pool, ngx_bufs_t *bufs) +{ + int i; + char *p; + ngx_hunk_t *h; + ngx_chain_t *chain, *cl, **ll; + + ngx_test_null(p, ngx_palloc(pool, bufs->num * bufs->size), NULL); + + ll = &chain; + + for (i = 0; i < bufs->num; i++) { + ngx_test_null(h, ngx_alloc_hunk(pool), NULL); + + h->pos = p; + h->last = p; + h->file_pos = 0; + h->file_last = 0; + + h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP; + + h->start = p; + p += bufs->size; + h->end = p; + + h->file = NULL; + h->shadow = NULL; + h->tag = 0; + + ngx_test_null(cl, ngx_alloc_chain_link(pool), NULL); + cl->hunk = h; + *ll = cl; + ll = &cl->next; + } + + *ll = NULL; + + return chain; +} + + ngx_hunk_t *ngx_create_hunk_before(ngx_pool_t *pool, ngx_hunk_t *hunk, int size) { ngx_hunk_t *h; @@ -61,6 +103,7 @@ ngx_hunk_t *ngx_create_hunk_before(ngx_p return h; } + ngx_hunk_t *ngx_create_hunk_after(ngx_pool_t *pool, ngx_hunk_t *hunk, int size) { ngx_hunk_t *h; diff --git a/src/core/ngx_hunk.h b/src/core/ngx_hunk.h --- a/src/core/ngx_hunk.h +++ b/src/core/ngx_hunk.h @@ -73,6 +73,29 @@ typedef struct { } ngx_bufs_t; +typedef int (*ngx_output_chain_filter_pt)(void *ctx, ngx_chain_t *out); + +typedef struct { + ngx_hunk_t *hunk; + ngx_chain_t *in; + ngx_chain_t *free; + ngx_chain_t *busy; + + unsigned sendfile; + unsigned need_in_memory; + unsigned need_in_temp; + unsigned copy_chain; + + ngx_pool_t *pool; + int hunks; + ngx_bufs_t bufs; + ngx_hunk_tag_t tag; + + ngx_output_chain_filter_pt output_filter; + void *output_ctx; +} ngx_output_chain_ctx_t; + + #define NGX_CHAIN_ERROR (ngx_chain_t *) NGX_ERROR @@ -120,6 +143,7 @@ ngx_hunk_t *ngx_create_temp_hunk(ngx_poo last = &cl->next +int ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in); int ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in); void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy, ngx_chain_t **out, ngx_hunk_tag_t tag); diff --git a/src/core/ngx_log.h b/src/core/ngx_log.h --- a/src/core/ngx_log.h +++ b/src/core/ngx_log.h @@ -63,11 +63,14 @@ typedef enum { */ +typedef size_t (*ngx_log_handler_pt) (void *ctx, char *buf, size_t len); + + typedef struct { - int log_level; - ngx_open_file_t *file; - void *data; - size_t (*handler)(void *ctx, char *buf, size_t len); + int log_level; + ngx_open_file_t *file; + void *data; + ngx_log_handler_pt handler; #if 0 /* STUB */ diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_output_chain.c @@ -0,0 +1,252 @@ + +#include +#include + + +#define NGX_NONE 1 + + +ngx_inline static int ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t *ctx, + ngx_hunk_t *hunk); +static int ngx_output_chain_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src, + int sendfile); + + +int ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in) +{ + int rc, last; + ssize_t size, hsize; + ngx_chain_t *cl, *out, **last_out; + + /* + * the short path for the case when the chain ctx->in is empty + * and the incoming chain is empty too or it has the single hunk + * that does not require the copy + */ + + if (ctx->in == NULL) { + + if (in == NULL) { + return ctx->output_filter(ctx->output_ctx, in); + } + + if (!ctx->copy_chain + && in->next == NULL + && (!ngx_output_chain_need_to_copy(ctx, in->hunk))) + { + return ctx->output_filter(ctx->output_ctx, in); + } + } + + /* add the incoming hunk to the chain ctx->in */ + + if (in) { + if (ngx_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) { + return NGX_ERROR; + } + } + + last = NGX_NONE; + last_out = &out; + + for ( ;; ) { + + while (ctx->in) { + + if (!ngx_output_chain_need_to_copy(ctx, ctx->in->hunk)) { + + /* move the chain link to the chain out */ + + cl = ctx->in; + ctx->in = cl->next; + + *last_out = cl; + last_out = &cl->next; + cl->next = NULL; + + continue; + } + + if (ctx->hunk == NULL) { + + /* get the free hunk */ + + if (ctx->free) { + ctx->hunk = ctx->free->hunk; + ctx->free = ctx->free->next; + + } else if (ctx->hunks < ctx->bufs.num) { + + size = ctx->bufs.size; + + if (ctx->in->hunk->type & NGX_HUNK_LAST) { + + hsize = ngx_hunk_size(ctx->in->hunk); + + if (hsize < ctx->bufs.size) { + + /* + * allocate small temp hunk for the small last hunk + * or its small last part + */ + + size = hsize; + + } else if (ctx->bufs.num == 1 + && (hsize < ctx->bufs.size + + (ctx->bufs.size >> 2))) + { + /* + * allocate a temp hunk that equals + * to the last hunk if the last hunk size is lesser + * than 1.25 of bufs.size and a temp hunk is single + */ + + size = hsize; + } + } + + ngx_test_null(ctx->hunk, + ngx_create_temp_hunk(ctx->pool, size, 0, 0), + NGX_ERROR); + ctx->hunk->tag = ctx->tag; + ctx->hunk->type |= NGX_HUNK_RECYCLED; + ctx->hunks++; + + } else { + break; + } + } + + rc = ngx_output_chain_copy_hunk(ctx->hunk, ctx->in->hunk, + ctx->sendfile); + + if (rc == NGX_ERROR) { + return rc; + } + + if (rc == NGX_AGAIN) { + if (out) { + break; + } + return rc; + } + + /* delete the completed hunk from the chain ctx->in */ + + if (ngx_hunk_size(ctx->in->hunk) == 0) { + ctx->in = ctx->in->next; + } + + ngx_alloc_link_and_set_hunk(cl, ctx->hunk, ctx->pool, NGX_ERROR); + *last_out = cl; + last_out = &cl->next; + ctx->hunk = NULL; + + if (ctx->free == NULL) { + break; + } + } + + if (out == NULL && last != NGX_NONE) { + return last; + } + + last = ctx->output_filter(ctx->output_ctx, out); + + ngx_chain_update_chains(&ctx->free, &ctx->busy, &out, ctx->tag); + last_out = &out; + } +} + + +ngx_inline static int ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t *ctx, + ngx_hunk_t *hunk) +{ + if (ngx_hunk_special(hunk)) { + return 0; + } + + if (!ctx->sendfile) { + return 1; + } + + if (ctx->need_in_memory && (!(hunk->type & NGX_HUNK_IN_MEMORY))) { + return 1; + } + + + if (ctx->need_in_temp && (hunk->type & (NGX_HUNK_MEMORY|NGX_HUNK_MMAP))) { + return 1; + } + + return 0; +} + + +static int ngx_output_chain_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src, + int sendfile) +{ + ssize_t n, size; + + size = ngx_hunk_size(src); + + if (size > (dst->end - dst->pos)) { + size = dst->end - dst->pos; + } + + if (src->type & NGX_HUNK_IN_MEMORY) { + ngx_memcpy(dst->pos, src->pos, size); + src->pos += size; + dst->last += size; + + if (src->type & NGX_HUNK_FILE) { + src->file_pos += size; + } + + if ((src->type & NGX_HUNK_LAST) && src->pos == src->last) { + dst->type |= NGX_HUNK_LAST; + } + + } else { + n = ngx_read_file(src->file, dst->pos, size, src->file_pos); + +if (n == 0) { +ngx_log_debug(src->file->log, "READ: %qd:%qd %X:%X %X:%X" _ + src->file_pos _ src->file_last _ + dst->pos _ dst->last _ dst->start _ dst->end); +} + + if (n == NGX_ERROR) { + return n; + } + +#if (NGX_FILE_AIO_READ) + if (n == NGX_AGAIN) { + return n; + } +#endif + + if (n != size) { + ngx_log_error(NGX_LOG_ALERT, src->file->log, 0, + ngx_read_file_n " reads only %d of %d from file", + n, size); + if (n == 0) { + return NGX_ERROR; + } + } + + src->file_pos += n; + dst->last += n; + + if (!sendfile) { + dst->type &= ~NGX_HUNK_FILE; + } + + if ((src->type & NGX_HUNK_LAST) && src->file_pos == src->file_last) { + dst->type |= NGX_HUNK_LAST; + } + } + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_gzip_filter.c b/src/http/modules/ngx_http_gzip_filter.c --- a/src/http/modules/ngx_http_gzip_filter.c +++ b/src/http/modules/ngx_http_gzip_filter.c @@ -168,7 +168,6 @@ static int ngx_http_gzip_header_filter(n if (!conf->enable || r->headers_out.status != NGX_HTTP_OK || r->header_only - || r->main /* TODO: conf->http_version */ || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) diff --git a/src/http/modules/ngx_http_range_filter.c b/src/http/modules/ngx_http_range_filter.c --- a/src/http/modules/ngx_http_range_filter.c +++ b/src/http/modules/ngx_http_range_filter.c @@ -4,6 +4,41 @@ #include +/* + * the single part format: + * + * "HTTP/1.0 206 Partial Content" CRLF + * ... header ... + * "Content-Type: image/jpeg" CRLF + * "Content-Length: SIZE" CRLF + * "Content-Range: bytes START-END/SIZE" CRLF + * CRLF + * ... data ... + * + * + * the mutlipart format: + * + * "HTTP/1.0 206 Partial Content" CRLF + * ... header ... + * "Content-Type: multipart/byteranges; boundary=0123456789" CRLF + * CRLF + * CRLF + * "--0123456789" CRLF + * "Content-Type: image/jpeg" CRLF + * "Content-Range: bytes START0-END0/SIZE" CRLF + * CRLF + * ... data ... + * CRLF + * "--0123456789" CRLF + * "Content-Type: image/jpeg" CRLF + * "Content-Range: bytes START1-END1/SIZE" CRLF + * CRLF + * ... data ... + * CRLF + * "--0123456789--" CRLF + */ + + typedef struct { ngx_str_t boundary_header; } ngx_http_range_filter_ctx_t; @@ -46,11 +81,12 @@ static int ngx_http_range_header_filter( ngx_http_range_t *range; ngx_http_range_filter_ctx_t *ctx; - if (r->main - || r->http_version < NGX_HTTP_VERSION_10 + if (r->http_version < NGX_HTTP_VERSION_10 || r->headers_out.status != NGX_HTTP_OK || r->headers_out.content_length_n == -1 + /* STUB: we currently support ranges for file hunks only */ + || !r->sendfile || r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY) { return ngx_http_next_header_filter(r); @@ -75,11 +111,8 @@ static int ngx_http_range_header_filter( ngx_init_array(r->headers_out.ranges, r->pool, 5, sizeof(ngx_http_range_t), NGX_ERROR); -#if (NGX_SUPPRESS_WARN) + rc = 0; range = NULL; -#endif - - rc = 0; p = r->headers_in.range->value.data + 6; for ( ;; ) { @@ -156,6 +189,9 @@ static int ngx_http_range_header_filter( } if (rc) { + + /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */ + r->headers_out.status = rc; r->headers_out.ranges.nelts = 0; @@ -189,6 +225,8 @@ static int ngx_http_range_header_filter( ngx_palloc(r->pool, 6 + 20 + 1 + 20 + 1 + 20 + 1), NGX_ERROR); + /* "Content-Range: bytes SSSS-EEEE/TTTT" header */ + r->headers_out.content_range->value.len = ngx_snprintf(r->headers_out.content_range->value.data, 6 + 20 + 1 + 20 + 1 + 20 + 1, @@ -222,6 +260,14 @@ static int ngx_http_range_header_filter( boundary = ngx_next_temp_number(0); + /* + * The boundary header of the range: + * CRLF + * "--0123456789" CRLF + * "Content-Type: image/jpeg" CRLF + * "Content-Range: bytes " + */ + if (r->headers_out.charset.len) { ctx->boundary_header.len = ngx_snprintf(ctx->boundary_header.data, len, @@ -248,30 +294,34 @@ static int ngx_http_range_header_filter( ngx_palloc(r->pool, 31 + 10 + 1), NGX_ERROR); + /* "Content-Type: multipart/byteranges; boundary=0123456789" */ + r->headers_out.content_type->value.len = ngx_snprintf(r->headers_out.content_type->value.data, 31 + 10 + 1, "multipart/byteranges; boundary=%010u", boundary); - /* the last "CRLF--BOUNDARY--CRLF" */ + /* the size of the last boundary CRLF "--0123456789--" CRLF */ len = 4 + 10 + 4; range = r->headers_out.ranges.elts; for (i = 0; i < r->headers_out.ranges.nelts; i++) { - ngx_test_null(range[i].content_range.data, - ngx_palloc(r->pool, 20 + 1 + 20 + 1 + 20 + 5), - NGX_ERROR); + ngx_test_null(range[i].content_range.data, + ngx_palloc(r->pool, 20 + 1 + 20 + 1 + 20 + 5), + NGX_ERROR); - range[i].content_range.len = + /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */ + + range[i].content_range.len = ngx_snprintf(range[i].content_range.data, 20 + 1 + 20 + 1 + 20 + 5, OFF_FMT "-" OFF_FMT "/" OFF_FMT CRLF CRLF, range[i].start, range[i].end - 1, r->headers_out.content_length_n); - len += ctx->boundary_header.len + range[i].content_range.len - + (size_t) (range[i].end - range[i].start); + len += ctx->boundary_header.len + range[i].content_range.len + + (size_t) (range[i].end - range[i].start); } r->headers_out.content_length_n = len; @@ -304,8 +354,9 @@ static int ngx_http_range_body_filter(ng && in->hunk->type & NGX_HUNK_FILE && in->hunk->type & NGX_HUNK_LAST) { + range = r->headers_out.ranges.elts; + if (r->headers_out.ranges.nelts == 1) { - range = r->headers_out.ranges.elts; in->hunk->file_pos = range->start; in->hunk->file_last = range->end; @@ -315,9 +366,16 @@ static int ngx_http_range_body_filter(ng ctx = ngx_http_get_module_ctx(r, ngx_http_range_filter_module); ll = &out; - range = r->headers_out.ranges.elts; for (i = 0; i < r->headers_out.ranges.nelts; i++) { + /* + * The boundary header of the range: + * CRLF + * "--0123456789" CRLF + * "Content-Type: image/jpeg" CRLF + * "Content-Range: bytes " + */ + ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR); h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_MEMORY; h->pos = ctx->boundary_header.data; @@ -326,6 +384,8 @@ static int ngx_http_range_body_filter(ng ngx_test_null(hcl, ngx_alloc_chain_link(r->pool), NGX_ERROR); hcl->hunk = h; + /* "SSSS-EEEE/TTTT" CRLF CRLF */ + ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR); h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP; h->pos = range[i].content_range.data; @@ -334,6 +394,8 @@ static int ngx_http_range_body_filter(ng ngx_test_null(rcl, ngx_alloc_chain_link(r->pool), NGX_ERROR); rcl->hunk = h; + /* the range data */ + ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR); h->type = NGX_HUNK_FILE; h->file_pos = range[i].start; @@ -348,6 +410,8 @@ static int ngx_http_range_body_filter(ng ll = &dcl->next; } + /* the last boundary CRLF "--0123456789--" CRLF */ + ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR); h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP|NGX_HUNK_LAST; ngx_test_null(h->pos, ngx_palloc(r->pool, 4 + 10 + 4), NGX_ERROR); diff --git a/src/http/modules/ngx_http_static_handler.c b/src/http/modules/ngx_http_static_handler.c --- a/src/http/modules/ngx_http_static_handler.c +++ b/src/http/modules/ngx_http_static_handler.c @@ -88,7 +88,7 @@ ngx_log_debug(r->connection->log, "HTTP /* * There is no way to open a file or a directory in Win9X with * one syscall: Win9X has no FILE_FLAG_BACKUP_SEMANTICS flag. - * so we need to check its type before the opening + * So we need to check its type before the opening */ r->file.info.dwFileAttributes = GetFileAttributes(r->file.name.data); @@ -151,6 +151,21 @@ ngx_log_debug(r->connection->log, "FILE: r->file.info_valid = 1; } + +#if !(WIN32) /* the not regular files are probably Unix specific */ + + if (!ngx_is_file(r->file.info)) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + "%s is not a regular file", r->file.name.data); + + if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR) + ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, + ngx_close_file_n " %s failed", r->file.name.data); + + return NGX_HTTP_NOT_FOUND; + } + +#endif #endif if (ngx_is_dir(r->file.info)) { @@ -163,6 +178,7 @@ ngx_log_debug(r->connection->log, "HTTP } r->file.fd = NGX_INVALID_FILE; + r->file.info_valid = 0; #endif ngx_test_null(h, ngx_push_table(r->headers_out.headers), @@ -203,7 +219,7 @@ static int ngx_http_static_handler(ngx_h } ctx = r->connection->log->data; - ctx->action = "sending response"; + ctx->action = "sending response to client"; if (r->file.fd == NGX_INVALID_FILE) { r->file.fd = ngx_open_file(r->file.name.data, @@ -242,21 +258,6 @@ static int ngx_http_static_handler(ngx_h r->file.info_valid = 1; } -#if !(WIN32) /* the not regular files are probably Unix specific */ - - if (!ngx_is_file(r->file.info)) { - ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, - "%s is not regular file", r->file.name.data); - - if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR) - ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, - ngx_close_file_n " %s failed", r->file.name.data); - - return NGX_HTTP_NOT_FOUND; - } - -#endif - r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = ngx_file_size(r->file.info); r->headers_out.last_modified_time = ngx_file_mtime(r->file.info); diff --git a/src/http/modules/proxy/ngx_http_proxy_handler.c b/src/http/modules/proxy/ngx_http_proxy_handler.c --- a/src/http/modules/proxy/ngx_http_proxy_handler.c +++ b/src/http/modules/proxy/ngx_http_proxy_handler.c @@ -10,6 +10,7 @@ static int ngx_http_proxy_handler(ngx_http_request_t *r); +static void ngx_http_proxy_init_request(void *data); static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p); static void ngx_http_proxy_send_request_handler(ngx_event_t *wev); static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p); @@ -20,7 +21,7 @@ static void ngx_http_proxy_send_response static void ngx_http_proxy_process_body(ngx_event_t *ev); static int ngx_http_proxy_parse_status_line(ngx_http_proxy_ctx_t *p); -static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p); +static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p, int ft_type); static void ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int rc); static void ngx_http_proxy_close_connection(ngx_connection_t *c); @@ -46,6 +47,13 @@ static ngx_command_t ngx_http_proxy_comm 0, NULL}, + {ngx_string("proxy_request_buffer_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, request_buffer_size), + NULL}, + {ngx_string("proxy_connect_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, @@ -135,13 +143,10 @@ static ngx_str_t http_methods[] = { }; -#if 0 -static char *header_errors[] = { - "upstream sent too long status line", +static char *upstream_header_errors[] = { "upstream sent invalid header", "upstream sent too long header line" }; -#endif static ngx_http_header_t headers_in[] = { @@ -170,6 +175,7 @@ static char connection_close_header[] = static int ngx_http_proxy_handler(ngx_http_request_t *r) { + int rc; ngx_http_proxy_ctx_t *p; ngx_http_create_ctx(r, p, ngx_http_proxy_module, @@ -186,20 +192,89 @@ static int ngx_http_proxy_handler(ngx_ht /* TODO: we currently support reverse proxy only */ p->accel = 1; - ngx_test_null(p->request_hunks, ngx_http_proxy_create_request(p), - NGX_HTTP_INTERNAL_SERVER_ERROR); + if (r->headers_in.content_length_n > 0) { + ngx_test_null(r->temp_file, + ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)), + NGX_HTTP_INTERNAL_SERVER_ERROR); + + r->temp_file->file.fd = NGX_INVALID_FILE; + r->temp_file->file.log = r->connection->log; + r->temp_file->path = *p->lcf->temp_path; + r->temp_file->pool = r->pool; + r->temp_file->warn = "a client request body is buffered " + "to a temporary file"; + /* STUB */ r->temp_file->persistent = 1; + + r->request_body_handler = ngx_http_proxy_init_request; + r->data = p; + + rc = ngx_http_read_client_request_body(r, p->lcf->request_buffer_size); + + if (rc != NGX_OK) { + return rc; + } + } + + ngx_http_proxy_init_request(p); - /* TODO: read request body */ + return NGX_DONE; +} + + +static void ngx_http_proxy_init_request(void *data) +{ + ngx_http_proxy_ctx_t *p = data; + + ngx_chain_t *cl; + ngx_http_request_t *r; + ngx_output_chain_ctx_t *ctx; + + + r = p->request; + + cl = ngx_http_proxy_create_request(p); + if (cl == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (r->request_hunks) { + cl->next = r->request_hunks; + } + + r->request_hunks = cl; p->upstream.log = r->connection->log; p->saved_ctx = r->connection->log->data; - r->connection->log->data = p;; + p->saved_handler = r->connection->log->handler; + r->connection->log->data = p; r->connection->log->handler = ngx_http_proxy_log_error; p->action = "connecting to upstream"; - ngx_http_proxy_send_request(p); + ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t)); + if (ctx == NULL) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + p->output_chain_ctx = ctx; - return NGX_DONE; + if (r->request_body_hunk) { + ctx->free = ngx_alloc_chain_link(r->pool); + if (ctx->free == NULL) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + ctx->free->hunk = r->request_body_hunk; + ctx->free->next = NULL; + } + + ctx->sendfile = r->sendfile; + ctx->copy_chain = 1; + ctx->pool = r->pool; + ctx->bufs.num = 1; + ctx->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module; + ctx->output_filter = (ngx_output_chain_filter_pt) ngx_write_chain; + + ngx_http_proxy_send_request(p); } @@ -324,9 +399,11 @@ static void ngx_http_proxy_send_request_ c = wev->data; p = c->data; + p->action = "sending request to upstream"; + if (wev->timedout) { p->timedout = 1; - ngx_http_proxy_next_upstream(p); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT); return; } @@ -338,30 +415,43 @@ static void ngx_http_proxy_send_request_ static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p) { - int rc; - ngx_chain_t *chain, *cl, *tl, **ll; - ngx_connection_t *c; + int rc; + ngx_chain_t *cl; + ngx_connection_t *c; c = p->upstream.connection; for ( ;; ) { if (c) { - chain = ngx_write_chain(c, p->work_request_hunks); + p->output_chain_ctx->output_ctx = c; + rc = ngx_output_chain(p->output_chain_ctx, + p->request->request_hunks); - if (chain != NGX_CHAIN_ERROR) { - p->work_request_hunks = chain; + if (rc != NGX_ERROR) { p->request_sent = 1; if (c->write->timer_set) { ngx_del_timer(c->write); } - if (chain) { + if (rc == NGX_AGAIN) { ngx_add_timer(c->write, p->lcf->send_timeout); } else { /* TODO: del event */ + + if (c->tcp_nopush) { + if (ngx_tcp_push(c->fd) == NGX_ERROR) { + ngx_log_error(NGX_LOG_CRIT, c->log, + ngx_socket_errno, + ngx_tcp_push_n " failed"); + ngx_http_proxy_finalize_request(p, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + c->tcp_nopush = 0; + } } return; @@ -404,34 +494,9 @@ static void ngx_http_proxy_send_request( /* reinit the request chain */ - p->work_request_hunks = ngx_alloc_chain_link(p->request->pool); - if (p->work_request_hunks == NULL) { - ngx_http_proxy_finalize_request(p, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; + for (cl = p->request->request_hunks; cl; cl = cl->next) { + cl->hunk->pos = cl->hunk->start; } - - tl = p->work_request_hunks; - ll = &p->work_request_hunks; - - for (cl = p->request_hunks; cl; cl = cl->next) { - tl->hunk = cl->hunk; - *ll = tl; - ll = &tl->next; - cl->hunk->pos = cl->hunk->start; - - tl = ngx_alloc_chain_link(p->request->pool); - if (tl == NULL) { - ngx_http_proxy_finalize_request(p, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - } - - *ll = NULL; - - } else { - p->work_request_hunks = p->request_hunks; } p->request_sent = 0; @@ -461,11 +526,13 @@ static void ngx_http_proxy_process_upstr c = rev->data; p = c->data; + p->action = "reading upstream status line"; + ngx_log_debug(rev->log, "http proxy process status line"); if (rev->timedout) { p->timedout = 1; - ngx_http_proxy_next_upstream(p); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT); return; } @@ -483,7 +550,7 @@ static void ngx_http_proxy_process_upstr n = ngx_http_proxy_read_upstream_header(p); if (n == NGX_ERROR) { - ngx_http_proxy_next_upstream(p); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); return; } @@ -496,21 +563,39 @@ static void ngx_http_proxy_process_upstr if (rc == NGX_AGAIN) { if (p->header_in->pos == p->header_in->last) { ngx_log_error(NGX_LOG_ERR, rev->log, 0, - "upstream sent too big header"); - ngx_http_proxy_finalize_request(p, NGX_HTTP_BAD_GATEWAY); - return; + "upstream sent too long status line"); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER); } return; } if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) { - /* TODO: HTTP/0.9 */ + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream sent no valid HTTP/1.0 header"); + + if (p->accel) { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER); + + } else { + p->request->http_version = NGX_HTTP_VERSION_9; + p->status = NGX_HTTP_OK; + ngx_http_proxy_send_response(p); + } + return; } /* rc == NGX_OK */ + if (p->status == NGX_HTTP_INTERNAL_SERVER_ERROR + && p->upstream.tries > 1 + && (p->lcf->next_upstream & NGX_HTTP_PROXY_FT_HTTP_500)) + { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_500); + return; + } + p->status_line.len = p->status_end - p->status_start; p->status_line.data = ngx_palloc(p->request->pool, p->status_line.len + 1); if (p->status_line.data == NULL) { @@ -522,7 +607,6 @@ static void ngx_http_proxy_process_upstr ngx_log_debug(rev->log, "http proxy status %d '%s'" _ p->status _ p->status_line.data); - if (p->headers_in.headers) { p->headers_in.headers->nelts = 0; } else { @@ -549,11 +633,13 @@ static void ngx_http_proxy_process_upstr p = c->data; r = p->request; + p->action = "reading upstream headers"; + ngx_log_debug(rev->log, "http proxy process header line"); if (rev->timedout) { p->timedout = 1; - ngx_http_proxy_next_upstream(p); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT); return; } @@ -564,7 +650,7 @@ static void ngx_http_proxy_process_upstr n = ngx_http_proxy_read_upstream_header(p); if (n == NGX_ERROR) { - ngx_http_proxy_next_upstream(p); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); return; } @@ -609,6 +695,7 @@ static void ngx_http_proxy_process_upstr if (ngx_strcasecmp(headers_in[i].name.data, h->key.data) == 0) { *((ngx_table_elt_t **) ((char *) &p->headers_in + headers_in[i].offset)) = h; + break; } } @@ -631,7 +718,10 @@ static void ngx_http_proxy_process_upstr /* there was error while a header line parsing */ - ngx_http_proxy_finalize_request(p, NGX_HTTP_BAD_GATEWAY); + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + upstream_header_errors[rc - NGX_HTTP_PARSE_HEADER_ERROR]); + + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER); return; } @@ -640,7 +730,8 @@ static void ngx_http_proxy_process_upstr if (p->header_in->last == p->header_in->end) { ngx_log_error(NGX_LOG_ERR, rev->log, 0, "upstream sent too big header"); - ngx_http_proxy_finalize_request(p, NGX_HTTP_BAD_GATEWAY); + + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER); return; } } @@ -679,7 +770,7 @@ static ssize_t ngx_http_proxy_read_upstr } if (n == 0) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, + ngx_log_error(NGX_LOG_ERR, rev->log, 0, "upstream closed prematurely connection"); } @@ -834,11 +925,6 @@ static void ngx_http_proxy_send_response p->event_pipe = ep; -#if 0 - lcx = p->log->data; - lcx->action = "reading an upstream"; -#endif - p->upstream.connection->read->event_handler = ngx_http_proxy_process_body; r->connection->write->event_handler = ngx_http_proxy_process_body; @@ -861,11 +947,13 @@ static void ngx_http_proxy_process_body( ngx_log_debug(ev->log, "http proxy process downstream"); r = c->data; p = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + p->action = "sending to client"; } else { ngx_log_debug(ev->log, "http proxy process upstream"); p = c->data; r = p->request; + p->action = "reading upstream body"; } ep = p->event_pipe; @@ -1123,7 +1211,7 @@ static int ngx_http_proxy_parse_status_l } -static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p) +static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p, int ft_type) { ngx_event_connect_peer_failed(&p->upstream); @@ -1137,7 +1225,7 @@ static void ngx_http_proxy_next_upstream p->upstream.connection = NULL; } - if (p->upstream.tries == 0) { + if (p->upstream.tries == 0 || !(p->lcf->next_upstream & ft_type)) { ngx_http_proxy_finalize_request(p, p->timedout ? NGX_HTTP_GATEWAY_TIME_OUT: NGX_HTTP_BAD_GATEWAY); @@ -1146,8 +1234,11 @@ static void ngx_http_proxy_next_upstream if (!p->fatal_error) { ngx_http_proxy_send_request(p); + return; } +ngx_log_debug(p->request->connection->log, "FATAL ERROR IN NEXT UPSTREAM"); + return; } @@ -1164,6 +1255,9 @@ static void ngx_http_proxy_finalize_requ rc = 0; } + p->request->connection->log->data = p->saved_ctx; + p->request->connection->log->handler = p->saved_handler; + ngx_http_finalize_request(p->request, rc); p->fatal_error = 1; @@ -1223,11 +1317,15 @@ static size_t ngx_http_proxy_log_error(v ngx_http_proxy_ctx_t *p = data; return ngx_snprintf(buf, len, - " while %s, upstream: %s, client: %s, URL: %s", + " while %s, client: %s, URL: %s, upstream: %s%s%s%s%s", p->action, + p->request->connection->addr_text.data, + p->request->unparsed_uri.data, p->upstream.peers->peers[p->upstream.cur_peer].addr_port_text.data, - p->request->connection->addr_text.data, - p->request->unparsed_uri.data); + p->lcf->upstream->uri.data, + p->request->uri.data + p->lcf->upstream->location->len, + p->request->args.len ? "?" : "", + p->request->args.len ? p->request->args.data : ""); } @@ -1250,6 +1348,7 @@ static void *ngx_http_proxy_create_loc_c */ + conf->request_buffer_size = NGX_CONF_UNSET; conf->connect_timeout = NGX_CONF_UNSET; conf->send_timeout = NGX_CONF_UNSET; conf->header_buffer_size = NGX_CONF_UNSET; @@ -1267,6 +1366,8 @@ static void *ngx_http_proxy_create_loc_c /* "proxy_cyclic_temp_file" is disabled */ conf->cyclic_temp_file = 0; + conf->next_upstream = NGX_CONF_UNSET; + return conf; } @@ -1277,6 +1378,8 @@ static char *ngx_http_proxy_merge_loc_co ngx_http_proxy_loc_conf_t *prev = parent; ngx_http_proxy_loc_conf_t *conf = child; + ngx_conf_merge_size_value(conf->request_buffer_size, + prev->request_buffer_size, 8192); ngx_conf_merge_msec_value(conf->connect_timeout, prev->connect_timeout, 60000); ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 30000); @@ -1297,6 +1400,9 @@ static char *ngx_http_proxy_merge_loc_co ngx_conf_merge_size_value(conf->temp_file_write_size, prev->temp_file_write_size, 16384); + ngx_conf_merge_value(conf->next_upstream, prev->next_upstream, + (NGX_HTTP_PROXY_FT_ERROR|NGX_HTTP_PROXY_FT_TIMEOUT)); + ngx_conf_merge_path_value(conf->temp_path, prev->temp_path, "temp", 1, 2, 0, cf->pool); diff --git a/src/http/modules/proxy/ngx_http_proxy_handler.h b/src/http/modules/proxy/ngx_http_proxy_handler.h --- a/src/http/modules/proxy/ngx_http_proxy_handler.h +++ b/src/http/modules/proxy/ngx_http_proxy_handler.h @@ -19,6 +19,7 @@ typedef struct { typedef struct { + ssize_t request_buffer_size; ngx_msec_t connect_timeout; ngx_msec_t send_timeout; ssize_t header_buffer_size; @@ -31,6 +32,8 @@ typedef struct { ssize_t temp_file_write_size; int cyclic_temp_file; + int next_upstream; + ngx_path_t *temp_path; ngx_http_proxy_upstream_t *upstream; @@ -67,8 +70,7 @@ struct ngx_http_proxy_ctx_s { int status; ngx_str_t status_line; - ngx_chain_t *work_request_hunks; - ngx_chain_t *request_hunks; + ngx_output_chain_ctx_t *output_chain_ctx; int method; @@ -89,10 +91,16 @@ struct ngx_http_proxy_ctx_s { char *action; ngx_http_log_ctx_t *saved_ctx; + ngx_log_handler_pt saved_handler; }; #define NGX_HTTP_PROXY_PARSE_NO_HEADER 20 +#define NGX_HTTP_PROXY_FT_ERROR 1 +#define NGX_HTTP_PROXY_FT_TIMEOUT 2 +#define NGX_HTTP_PROXY_FT_HTTP_HEADER 4 +#define NGX_HTTP_PROXY_FT_HTTP_500 8 + #endif /* _NGX_HTTP_PROXY_HANDLER_H_INCLUDED_ */ diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -51,12 +51,8 @@ void ngx_http_close_request(ngx_http_req void ngx_http_close_connection(ngx_connection_t *c); - -int ngx_http_init_client_request_body(ngx_http_request_t *r, int size); -int ngx_http_read_client_request_body(ngx_http_request_t *r); -int ngx_http_init_client_request_body_chain(ngx_http_request_t *r); -void ngx_http_reinit_client_request_body_hunks(ngx_http_request_t *r); - +int ngx_http_read_client_request_body(ngx_http_request_t *r, + int request_buffer_size); int ngx_http_send_header(ngx_http_request_t *r); int ngx_http_special_response_handler(ngx_http_request_t *r, int error); diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -232,7 +232,7 @@ void ngx_http_handler(ngx_http_request_t break; } -#if 1 +#if 0 /* TEST STUB */ r->http_version = NGX_HTTP_VERSION_10; /* TEST STUB */ r->keepalive = 0; #endif @@ -295,11 +295,6 @@ static void ngx_http_run_phases(ngx_http return; } - /* TODO THINK: is it dupliate NGX_DONE ??? */ - if (r->closed) { - return; - } - if (rc == NGX_DECLINED) { continue; } @@ -403,6 +398,10 @@ ngx_log_debug(r->connection->log, "rc: % int ngx_http_send_header(ngx_http_request_t *r) { + if (r->main) { + return NGX_OK; + } + return (*ngx_http_top_header_filter)(r); } diff --git a/src/http/ngx_http_output_filter.c b/src/http/ngx_http_output_filter.c --- a/src/http/ngx_http_output_filter.c +++ b/src/http/ngx_http_output_filter.c @@ -9,33 +9,6 @@ typedef struct { } ngx_http_output_filter_conf_t; -typedef struct { - - /* - * NOTE: we do not need now to store hunk in ctx, - * it's needed for the future NGX_FILE_AIO_READ support only - */ - - ngx_hunk_t *hunk; - - ngx_chain_t *in; - - /* TODO: out and last_out should be local variables */ - ngx_chain_t *out; - ngx_chain_t **last_out; - /* */ - - ngx_chain_t *free; - ngx_chain_t *busy; - - int hunks; -} ngx_http_output_filter_ctx_t; - - -ngx_inline static int ngx_http_output_filter_need_to_copy(ngx_http_request_t *r, - ngx_hunk_t *hunk); -static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src, - int sendfile); static void *ngx_http_output_filter_create_conf(ngx_conf_t *cf); static char *ngx_http_output_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child); @@ -79,244 +52,34 @@ ngx_module_t ngx_http_output_filter_mod int ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in) { - int rc, last; - ssize_t size; - ngx_chain_t *cl; - ngx_http_output_filter_ctx_t *ctx; + ngx_output_chain_ctx_t *ctx; ngx_http_output_filter_conf_t *conf; ctx = ngx_http_get_module_ctx(r->main ? r->main : r, ngx_http_output_filter_module); if (ctx == NULL) { - ngx_http_create_ctx(r, ctx, ngx_http_output_filter_module, - sizeof(ngx_http_output_filter_ctx_t), NGX_ERROR); - ctx->last_out = &ctx->out; - } + conf = ngx_http_get_module_loc_conf(r->main ? r->main : r, + ngx_http_output_filter_module); - /* - * the short path for the case when the chain ctx->in is empty - * and the incoming chain is empty too or it has the single hunk - * that does not require the copy - */ - - if (ctx->in == NULL) { + ngx_http_create_ctx(r, ctx, ngx_http_output_filter_module, + sizeof(ngx_output_chain_ctx_t), NGX_ERROR); - if (in == NULL) { - return ngx_http_top_body_filter(r, in); - } + ctx->sendfile = r->sendfile; + ctx->need_in_memory = r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY; + ctx->need_in_temp = r->filter & NGX_HTTP_FILTER_NEED_TEMP; - if (in->next == NULL - && (!ngx_http_output_filter_need_to_copy(r, in->hunk))) - { - return ngx_http_top_body_filter(r, in); - } - } + ctx->pool = r->pool; + ctx->bufs = conf->bufs; + ctx->tag = (ngx_hunk_tag_t) &ngx_http_output_filter_module; - /* add the incoming hunk to the chain ctx->in */ + ctx->output_filter = (ngx_output_chain_filter_pt) + ngx_http_top_body_filter; + ctx->output_ctx = r; - if (in) { - if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { - return NGX_ERROR; - } } - conf = ngx_http_get_module_loc_conf(r->main ? r->main : r, - ngx_http_output_filter_module); - - last = NGX_NONE; - - for ( ;; ) { - - while (ctx->in) { - - if (!ngx_http_output_filter_need_to_copy(r, ctx->in->hunk)) { - - /* move the chain link to the chain ctx->out */ - - cl = ctx->in; - ctx->in = cl->next; - - *ctx->last_out = cl; - ctx->last_out = &cl->next; - cl->next = NULL; - - continue; - } - - if (ctx->hunk == NULL) { - - /* get the free hunk */ - - if (ctx->free) { - ctx->hunk = ctx->free->hunk; - ctx->free = ctx->free->next; - - } else if (ctx->hunks < conf->bufs.num) { - ngx_test_null(ctx->hunk, - ngx_create_temp_hunk(r->pool, conf->bufs.size, - 0, 0), - NGX_ERROR); - ctx->hunk->tag = (ngx_hunk_tag_t) - &ngx_http_output_filter_module; - ctx->hunk->type |= NGX_HUNK_RECYCLED; - ctx->hunks++; - - } else { - break; - } - } - - rc = ngx_http_output_filter_copy_hunk(ctx->hunk, ctx->in->hunk, - r->sendfile); - - if (rc == NGX_ERROR) { - return rc; - } - -#if (NGX_FILE_AIO_READ) - if (rc == NGX_AGAIN) { - if (ctx->out) { - break; - } - return rc; - } -#endif - - if (ctx->in->hunk->type & NGX_HUNK_IN_MEMORY) { - size = ctx->in->hunk->last - ctx->in->hunk->pos; - - } else { - size = (size_t) (ctx->in->hunk->file_last - - ctx->in->hunk->file_pos); - } - - /* delete the completed hunk from the chain ctx->in */ - - if (size == 0) { - ctx->in = ctx->in->next; - } - - ngx_alloc_link_and_set_hunk(cl, ctx->hunk, r->pool, NGX_ERROR); - *ctx->last_out = cl; - ctx->last_out = &cl->next; - ctx->hunk = NULL; - - if (ctx->free == NULL) { - break; - } - } - - if (ctx->out == NULL && last != NGX_NONE) { - return last; - } - - last = ngx_http_top_body_filter(r, ctx->out); - - ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, - (ngx_hunk_tag_t) &ngx_http_output_filter_module); - ctx->last_out = &ctx->out; - } -} - - -ngx_inline static int ngx_http_output_filter_need_to_copy(ngx_http_request_t *r, - ngx_hunk_t *hunk) -{ - if (ngx_hunk_special(hunk)) { - return 0; - } - - if (!r->sendfile) { - return 1; - } - - if ((r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY) - && (!(hunk->type & NGX_HUNK_IN_MEMORY))) - { - return 1; - } - - - if ((r->filter & NGX_HTTP_FILTER_NEED_TEMP) - && (hunk->type & (NGX_HUNK_MEMORY|NGX_HUNK_MMAP))) - { - return 1; - } - - return 0; -} - - -static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src, - int sendfile) -{ - ssize_t n, size; - - if (src->type & NGX_HUNK_IN_MEMORY) { - size = src->last - src->pos; - } else { - size = (size_t) (src->file_last - src->file_pos); - } - - if (size > (dst->end - dst->pos)) { - size = dst->end - dst->pos; - } - - if (src->type & NGX_HUNK_IN_MEMORY) { - ngx_memcpy(dst->pos, src->pos, size); - src->pos += size; - dst->last += size; - - if (src->type & NGX_HUNK_FILE) { - src->file_pos += size; - } - - if ((src->type & NGX_HUNK_LAST) && src->pos == src->last) { - dst->type |= NGX_HUNK_LAST; - } - - } else { - n = ngx_read_file(src->file, dst->pos, size, src->file_pos); - -if (n == 0) { -ngx_log_debug(src->file->log, "READ: %qd:%qd %X:%X %X:%X" _ - src->file_pos _ src->file_last _ - dst->pos _ dst->last _ dst->start _ dst->end); -} - - if (n == NGX_ERROR) { - return n; - } - -#if (NGX_FILE_AIO_READ) - if (n == NGX_AGAIN) { - return n; - } -#endif - - if (n != size) { - ngx_log_error(NGX_LOG_ALERT, src->file->log, 0, - ngx_read_file_n " reads only %d of %d from file", - n, size); - if (n == 0) { - return NGX_ERROR; - } - } - - src->file_pos += n; - dst->last += n; - - if (!sendfile) { - dst->type &= ~NGX_HUNK_FILE; - } - - if ((src->type & NGX_HUNK_LAST) && src->file_pos == src->file_last) { - dst->type |= NGX_HUNK_LAST; - } - } - - return NGX_OK; + return ngx_output_chain(ctx, in); } @@ -340,7 +103,7 @@ static char *ngx_http_output_filter_merg ngx_http_output_filter_conf_t *prev = parent; ngx_http_output_filter_conf_t *conf = child; - ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 2, 32768); + ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 1, 32768); return NULL; } diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -23,14 +23,14 @@ static void ngx_http_set_lingering_close static void ngx_http_lingering_close_handler(ngx_event_t *ev); static void ngx_http_empty_handler(ngx_event_t *wev); -static void ngx_http_header_parse_error(ngx_http_request_t *r, - int parse_err, int error); +static void ngx_http_client_error(ngx_http_request_t *r, + int client_error, int error); static size_t ngx_http_log_error(void *data, char *buf, size_t len); -/* NGX_HTTP_PARSE_ ... errors */ +/* NGX_HTTP_PARSE_... errors */ -static char *header_errors[] = { +static char *client_header_errors[] = { "client %s sent invalid method", "client %s sent invalid request", "client %s sent too long URI", @@ -66,6 +66,12 @@ static ngx_http_header_t headers_in[] = }; +static void ngx_http_dummy(ngx_event_t *wev) +{ + return; +} + + void ngx_http_init_connection(ngx_connection_t *c) { ngx_event_t *rev; @@ -117,6 +123,16 @@ void ngx_http_init_connection(ngx_connec ngx_http_close_connection(c); return; } + +#if 0 + c->write->ready = 0; + c->write->event_handler = ngx_http_dummy; + + if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } +#endif } @@ -135,6 +151,12 @@ static void ngx_http_init_request(ngx_ev c = rev->data; + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_http_close_connection(c); + return; + } + if (c->data) { r = c->data; ngx_memzero(r, sizeof(ngx_http_request_t)); @@ -281,8 +303,7 @@ static void ngx_http_process_request_lin ngx_log_debug(rev->log, "http process request line"); if (rev->timedout) { - ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); - ngx_http_close_connection(c); + ngx_http_client_error(r, 0, NGX_HTTP_REQUEST_TIME_OUT); return; } @@ -304,8 +325,8 @@ static void ngx_http_process_request_lin r->request_line.data = r->request_start; r->request_line.data[r->request_line.len] = '\0'; - ngx_http_header_parse_error(r, NGX_HTTP_PARSE_INVALID_REQUEST, - NGX_HTTP_BAD_REQUEST); + ngx_http_client_error(r, NGX_HTTP_PARSE_INVALID_REQUEST, + NGX_HTTP_BAD_REQUEST); return; } @@ -317,8 +338,8 @@ static void ngx_http_process_request_lin { /* no space for "\r\n" at the end of the header */ - ngx_http_header_parse_error(r, NGX_HTTP_PARSE_TOO_LONG_URI, - NGX_HTTP_REQUEST_URI_TOO_LARGE); + ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_URI, + NGX_HTTP_REQUEST_URI_TOO_LARGE); return; } @@ -447,7 +468,7 @@ static void ngx_http_process_request_lin /* there was error while a request line parsing */ - ngx_http_header_parse_error(r, rc, NGX_HTTP_BAD_REQUEST); + ngx_http_client_error(r, rc, NGX_HTTP_BAD_REQUEST); return; } @@ -470,8 +491,8 @@ static void ngx_http_process_request_lin offset = r->request_start - r->header_in->start; if (offset == 0) { - ngx_http_header_parse_error(r, NGX_HTTP_PARSE_TOO_LONG_URI, - NGX_HTTP_REQUEST_URI_TOO_LARGE); + ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_URI, + NGX_HTTP_REQUEST_URI_TOO_LARGE); return; } @@ -492,8 +513,8 @@ static void ngx_http_process_request_lin } } else { - ngx_http_header_parse_error(r, NGX_HTTP_PARSE_TOO_LONG_URI, - NGX_HTTP_REQUEST_URI_TOO_LARGE); + ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_URI, + NGX_HTTP_REQUEST_URI_TOO_LARGE); } } @@ -516,8 +537,7 @@ static void ngx_http_process_request_hea ngx_log_debug(rev->log, "http process request header line"); if (rev->timedout) { - ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); - ngx_http_close_connection(c); + ngx_http_client_error(r, 0, NGX_HTTP_REQUEST_TIME_OUT); return; } @@ -605,7 +625,7 @@ static void ngx_http_process_request_hea rc = ngx_http_process_request_header(r); if (rc != NGX_OK) { - ngx_http_header_parse_error(r, rc, NGX_HTTP_BAD_REQUEST); + ngx_http_client_error(r, rc, NGX_HTTP_BAD_REQUEST); return; } @@ -621,7 +641,7 @@ static void ngx_http_process_request_hea /* there was error while a header line parsing */ - ngx_http_header_parse_error(r, rc, NGX_HTTP_BAD_REQUEST); + ngx_http_client_error(r, rc, NGX_HTTP_BAD_REQUEST); return; } @@ -636,9 +656,8 @@ static void ngx_http_process_request_hea offset = r->header_name_start - r->header_in->start; if (offset == 0) { - ngx_http_header_parse_error(r, - NGX_HTTP_PARSE_TOO_LONG_HEADER, - NGX_HTTP_BAD_REQUEST); + ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_HEADER, + NGX_HTTP_BAD_REQUEST); return; } @@ -653,8 +672,8 @@ static void ngx_http_process_request_hea r->header_end -= offset; } else { - ngx_http_header_parse_error(r, NGX_HTTP_PARSE_TOO_LONG_HEADER, - NGX_HTTP_BAD_REQUEST); + ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_HEADER, + NGX_HTTP_BAD_REQUEST); return; } } @@ -798,12 +817,14 @@ static int ngx_http_process_request_head void ngx_http_finalize_request(ngx_http_request_t *r, int rc) { - ngx_log_debug(r->connection->log, "finalize http request"); + /* r can be already destroyed when rc == NGX_DONE */ - if (rc == NGX_DONE || r->main || r->closed) { + if (rc == NGX_DONE || r->main) { return; } + ngx_log_debug(r->connection->log, "finalize http request"); + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { if (r->connection->read->timer_set) { @@ -886,6 +907,17 @@ void ngx_http_writer(ngx_event_t *wev) c = wev->data; r = c->data; +#if 0 /* TODO: THINK */ + if (wev->delayed) { + return; + } +#endif + + if (wev->timedout) { + ngx_http_client_error(r, 0, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + rc = ngx_http_output_filter(r, NULL); ngx_log_debug(c->log, "writer output filter: %d" _ rc); @@ -1356,8 +1388,6 @@ void ngx_http_close_request(ngx_http_req ngx_destroy_pool(r->pool); - r->closed = 1; - return; } @@ -1405,23 +1435,32 @@ void ngx_http_close_connection(ngx_conne } -static void ngx_http_header_parse_error(ngx_http_request_t *r, - int parse_err, int error) +static void ngx_http_client_error(ngx_http_request_t *r, + int client_error, int error) { ngx_http_log_ctx_t *ctx; ctx = r->connection->log->data; + + if (error == NGX_HTTP_REQUEST_TIME_OUT) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT, + "client timed out"); + ngx_http_close_request(r, error); + ngx_http_close_connection(r->connection); + return; + } + r->connection->log->handler = NULL; if (ctx->url) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - header_errors[parse_err - NGX_HTTP_PARSE_INVALID_METHOD], - ctx->client, ctx->url); + client_header_errors[client_error - NGX_HTTP_CLIENT_ERROR], + ctx->client, ctx->url); } else { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - header_errors[parse_err - NGX_HTTP_PARSE_INVALID_METHOD], - ctx->client); + client_header_errors[client_error - NGX_HTTP_CLIENT_ERROR], + ctx->client); } r->connection->log->handler = ngx_http_log_error; diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -18,10 +18,14 @@ #define NGX_HTTP_PARSE_HEADER_DONE 1 + +#define NGX_HTTP_CLIENT_ERROR 10 #define NGX_HTTP_PARSE_INVALID_METHOD 10 #define NGX_HTTP_PARSE_INVALID_REQUEST 11 #define NGX_HTTP_PARSE_TOO_LONG_URI 12 #define NGX_HTTP_PARSE_INVALID_09_METHOD 13 + +#define NGX_HTTP_PARSE_HEADER_ERROR 14 #define NGX_HTTP_PARSE_INVALID_HEADER 14 #define NGX_HTTP_PARSE_TOO_LONG_HEADER 15 #define NGX_HTTP_PARSE_NO_HOST_HEADER 16 @@ -176,6 +180,13 @@ struct ngx_http_request_s { int phase_handler; ngx_http_handler_pt content_handler; + ngx_temp_file_t *temp_file; + ngx_chain_t *request_hunks; + ngx_hunk_t *request_body_hunk; + int request_body_len; + void (*request_body_handler) (void *data); + void *data; + char *discarded_buffer; /* URI is not started with '/' - "GET http://" */ @@ -197,7 +208,9 @@ struct ngx_http_request_s { unsigned header_only:1; unsigned keepalive:1; unsigned lingering_close:1; +#if 0 unsigned closed:1; +#endif /* TODO: use filter or bits ???? */ int filter; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -1,196 +1,156 @@ #include #include +#include #include -int ngx_http_init_client_request_body(ngx_http_request_t *r, int size) -{ - int header_in_part, len; - ngx_hunk_t *h; - ngx_http_request_body_t *rb; +static void ngx_http_read_client_request_body_handler(ngx_event_t *rev); + - ngx_test_null(rb, ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)), - NGX_HTTP_INTERNAL_SERVER_ERROR); +int ngx_http_read_client_request_body(ngx_http_request_t *r, + int request_buffer_size) +{ + ssize_t size; + ngx_hunk_t *h; + ngx_chain_t *cl; - header_in_part = r->header_in->end - r->header_in->pos; - - if (header_in_part) { - rb->header_in_pos = r->header_in->pos; - } + size = r->header_in->last - r->header_in->pos; - if (header_in_part > r->headers_in.content_length_n) { - header_in_part = r->headers_in.content_length_n; - - } else { - len = r->headers_in.content_length_n - header_in_part; - if (len > size) { - len = size; + if (size) { + ngx_test_null(h, ngx_calloc_hunk(r->pool), + NGX_HTTP_INTERNAL_SERVER_ERROR); - } else if (len > NGX_PAGE_SIZE) { - len = ((len + NGX_PAGE_SIZE - 1) / NGX_PAGE_SIZE) * NGX_PAGE_SIZE; - } + h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP; + h->start = h->pos = r->header_in->pos; + h->end = h->last = r->header_in->last; - if (len) { - ngx_test_null(rb->hunk, ngx_create_temp_hunk(r->pool, len, 0, 0), - NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_alloc_link_and_set_hunk(r->request_hunks, h, r->pool, + NGX_HTTP_INTERNAL_SERVER_ERROR); + + if (size >= r->headers_in.content_length_n) { + r->header_in->pos += r->headers_in.content_length_n; + return NGX_OK; } } - r->request_body = rb; + r->request_body_len = r->headers_in.content_length_n - size; + + if (r->request_body_len < request_buffer_size + (request_buffer_size >> 2)) + { + size = r->request_body_len; + + } else { + size = request_buffer_size; + } + + ngx_test_null(r->request_body_hunk, + ngx_create_temp_hunk(r->pool, size, 0, 0), + NGX_HTTP_INTERNAL_SERVER_ERROR); + + r->connection->read->event_handler = + ngx_http_read_client_request_body_handler; + + ngx_http_read_client_request_body_handler(r->connection->read); + + ngx_alloc_link_and_set_hunk(cl, r->request_body_hunk, r->pool, + NGX_HTTP_INTERNAL_SERVER_ERROR); + + if (r->request_hunks) { + r->request_hunks->next = cl; + + } else { + r->request_hunks = cl; + } + + if (r->request_body_len) { + return NGX_AGAIN; + } return NGX_OK; } -int ngx_http_read_client_request_body(ngx_http_request_t *r) +static void ngx_http_read_client_request_body_handler(ngx_event_t *rev) { - int size, n, rc; - ngx_chain_t *entry; - ngx_http_request_body_t *rb; - - rb = r->request_body; - - do { - if (r->header_in->last < r->header_in->end) { - rb->chain[0].hunk = r->header_in; - - if (rb->hunk) { - rb->chain[0].next = &rb->chain[1]; - rb->chain[1].hunk = rb->hunk; - rb->chain[1].next = NULL; + ssize_t n, size; + ngx_hunk_t *h; + ngx_connection_t *c; + ngx_http_request_t *r; - } else { - rb->chain[0].next = NULL; - } - - } else { - rb->chain[0].hunk = rb->hunk; - rb->chain[0].next = NULL; - } + c = rev->data; + r = c->data; - n = ngx_recv_chain(r->connection, rb->chain); - - if (n == NGX_ERROR) { - return NGX_ERROR; - } - - if (n == NGX_AGAIN) { - return NGX_AGAIN; - } + if (r->request_body_hunk->end - r->request_body_hunk->last == 0) { + n = ngx_write_chain_to_temp_file(r->temp_file, + r->request_hunks->next ? r->request_hunks->next: + r->request_hunks); + /* TODO: n == 0 or not complete and level event */ - for (entry = rb->chain; entry; entry = entry->next) { - size = entry->hunk->end - entry->hunk->last; - - if (n >= size) { - n -= size; - entry->hunk->last = entry->hunk->end; + r->request_body_hunk->pos = r->request_body_hunk->start; + r->request_body_hunk->last = r->request_body_hunk->start; + } - continue; - } - - entry->hunk->last += n; - - break; - } + size = r->request_body_hunk->end - r->request_body_hunk->last; - if (rb->hunk && rb->hunk->last == rb->hunk->end) { - if (rb->temp_file.fd == NGX_INVALID_FILE) { - rc = ngx_create_temp_file(&rb->temp_file, rb->temp_path, - r->pool, 0); - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } + if (size > r->request_body_len) { + size = r->request_body_len; + } - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - } + n = ngx_recv(c, r->request_body_hunk->last, size); - n = ngx_write_file(&rb->temp_file, rb->hunk->pos, - rb->hunk->last - rb->hunk->pos, rb->offset); - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - rb->offset += n; - rb->hunk->last = rb->hunk->pos; + if (n == NGX_AGAIN) { + if (ngx_handle_read_event(rev) == NGX_ERROR) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } - } while (r->connection->read->ready); + return; + } - return NGX_OK; -} - + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client closed prematurely connection"); + } -int ngx_http_init_client_request_body_chain(ngx_http_request_t *r) -{ - int i; - ngx_hunk_t *h; - ngx_http_request_body_t *rb; - - rb = r->request_body; + if (n == 0 || n == NGX_ERROR) { + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return; + } - rb->chain[0].hunk = rb->header_out; - i = 0; + r->request_body_hunk->last += n; + r->request_body_len -= n; - if (r->header_in->pos < r->header_in->last) { - rb->chain[i].next = &rb->chain[i + 1]; - i++; - rb->chain[i].hunk = r->header_in; + if (r->request_body_len) { + return; } - if (rb->temp_file.fd != NGX_INVALID_FILE) { - - if (rb->file_hunk == NULL) { - ngx_test_null(h, ngx_alloc_hunk(r->pool), NGX_ERROR); + if (r->temp_file->file.fd != NGX_INVALID_FILE) { - h->type = NGX_HUNK_FILE; - h->pos = h->start = h->pre_start = 0; - h->last = h->end = h->post_end = 0; - h->file_pos = 0; - h->file_last = rb->offset; - h->file = &rb->temp_file; - h->shadow = NULL; - h->tag = 0; + /* save the last part */ + n = ngx_write_chain_to_temp_file(r->temp_file, + r->request_hunks->next ? r->request_hunks->next: + r->request_hunks); + /* TODO: n == 0 or not complete and level event */ - rb->file_hunk = h; + + h = ngx_calloc_hunk(r->pool); + if (h == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; } - rb->chain[i].next = &rb->chain[i + 1]; - i++; - rb->chain[i].hunk = rb->file_hunk; + h->type = NGX_HUNK_FILE; + h->file_pos = 0; + h->file_last = r->temp_file->file.offset; + h->file = &r->temp_file->file; + + if (r->request_hunks->next) { + r->request_hunks->next->hunk = h; + + } else { + r->request_hunks->hunk = h; + } } - if (rb->hunk && rb->hunk->pos < rb->hunk->last) { - rb->chain[i].next = &rb->chain[i + 1]; - i++; - rb->chain[i].hunk = h; - } - - rb->chain[i].next = NULL; - - return NGX_OK; + r->request_body_handler(r->data); } - - -void ngx_http_reinit_client_request_body_hunks(ngx_http_request_t *r) -{ - ngx_http_request_body_t *rb; - - rb = r->request_body; - - if (rb->header_in_pos) { - r->header_in->pos = rb->header_in_pos; - } - - if (rb->file_hunk) { - rb->file_hunk->file_pos = 0; - } - - if (rb->hunk) { - rb->hunk->pos = rb->hunk->start; - } -}