diff --git a/src/core/ngx_conf_file.h b/src/core/ngx_conf_file.h index 4bf674751..c1e2c010e 100644 --- a/src/core/ngx_conf_file.h +++ b/src/core/ngx_conf_file.h @@ -150,6 +150,8 @@ typedef struct { } ngx_conf_num_bounds_t; +#define NGX_CONF_BITMASK_SET 1 + typedef struct { ngx_str_t name; int mask; diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c index 84e86fffc..80ad9dc8a 100644 --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -12,7 +12,7 @@ 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, + rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool, tf->persistent); if (rc == NGX_ERROR || rc == NGX_AGAIN) { @@ -24,7 +24,7 @@ int ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain) } } - return ngx_write_chain_to_file(&tf->file, chain, tf->file.offset, tf->pool); + return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool); } diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h index 660750a4c..8cf4c7db0 100644 --- a/src/core/ngx_file.h +++ b/src/core/ngx_file.h @@ -28,7 +28,8 @@ typedef struct { typedef struct { ngx_file_t file; - ngx_path_t path; + off_t offset; + ngx_path_t *path; ngx_pool_t *pool; char *warn; diff --git a/src/core/ngx_hunk.h b/src/core/ngx_hunk.h index e68ba6d22..2c6664b60 100644 --- a/src/core/ngx_hunk.h +++ b/src/core/ngx_hunk.h @@ -20,7 +20,7 @@ #define NGX_HUNK_RECYCLED 0x0010 /* the hunk is in file */ -#define NGX_HUNK_FILE 0x0100 +#define NGX_HUNK_FILE 0x0020 #define NGX_HUNK_STORAGE (NGX_HUNK_IN_MEMORY \ |NGX_HUNK_TEMP|NGX_HUNK_MEMORY|NGX_HUNK_MMAP \ @@ -30,9 +30,12 @@ /* in thread state flush means to write the hunk completely before return */ /* in event state flush means to start to write the hunk */ -#define NGX_HUNK_FLUSH 0x1000 +#define NGX_HUNK_FLUSH 0x0100 /* last hunk */ -#define NGX_HUNK_LAST 0x2000 +#define NGX_HUNK_LAST 0x0200 + + +#define NGX_HUNK_PREREAD 0x2000 #define NGX_HUNK_LAST_SHADOW 0x4000 #define NGX_HUNK_TEMP_FILE 0x8000 diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c index 69ed5bc55..f196937ef 100644 --- a/src/core/ngx_output_chain.c +++ b/src/core/ngx_output_chain.c @@ -169,8 +169,12 @@ ngx_inline static int ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t *ctx, return 0; } - if (!ctx->sendfile && (!(hunk->type & NGX_HUNK_IN_MEMORY))) { - return 1; + if (!ctx->sendfile) { + if (!(hunk->type & NGX_HUNK_IN_MEMORY)) { + return 1; + } + + hunk->type &= ~NGX_HUNK_FILE; } if (ctx->need_in_memory && (!(hunk->type & NGX_HUNK_IN_MEMORY))) { diff --git a/src/event/modules/ngx_kqueue_module.c b/src/event/modules/ngx_kqueue_module.c index 26e109b1a..c896a8380 100644 --- a/src/event/modules/ngx_kqueue_module.c +++ b/src/event/modules/ngx_kqueue_module.c @@ -380,7 +380,9 @@ static int ngx_kqueue_process_events(ngx_log_t *log) for (i = 0; i < events; i++) { #if (NGX_DEBUG_EVENT) - if (event_list[i].ident > 0x8000000) { + if (event_list[i].ident > 0x8000000 + && event_list[i].ident != (unsigned) -1) + { ngx_log_debug(log, "kevent: %08x: ft:%d fl:%08x ff:%08x d:%d ud:%08x" _ event_list[i].ident _ event_list[i].filter _ diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h index 629e83d14..7e384693f 100644 --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h @@ -22,6 +22,7 @@ typedef struct { struct ngx_event_s { void *data; + /* TODO rename to handler, move flags to struct start */ void (*event_handler)(ngx_event_t *ev); void *context; diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c index 0d6e89159..6f3d136a1 100644 --- a/src/event/ngx_event_pipe.c +++ b/src/event/ngx_event_pipe.c @@ -42,24 +42,28 @@ int ngx_event_pipe(ngx_event_pipe_t *p, int do_write) do_write = 1; } - rev = p->upstream->read; + if (p->upstream->fd != -1) { + rev = p->upstream->read; - if (ngx_handle_read_event(rev, (rev->eof || rev->error)) == NGX_ERROR) { - return NGX_ABORT; + if (ngx_handle_read_event(rev, (rev->eof || rev->error)) == NGX_ERROR) { + return NGX_ABORT; + } + + if (rev->active) { + ngx_add_timer(rev, p->read_timeout); + } } - if (rev->active) { - ngx_add_timer(rev, p->read_timeout); - } + if (p->downstream->fd != -1) { + wev = p->downstream->write; - wev = p->downstream->write; + if (ngx_handle_write_event(wev, p->send_lowat) == NGX_ERROR) { + return NGX_ABORT; + } - if (ngx_handle_write_event(wev, p->send_lowat) == NGX_ERROR) { - return NGX_ABORT; - } - - if (wev->active) { - ngx_add_timer(wev, p->send_timeout); + if (wev->active) { + ngx_add_timer(wev, p->send_timeout); + } } return NGX_OK; @@ -166,7 +170,9 @@ int ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) break; - } else if (p->cachable || p->temp_offset < p->max_temp_file_size) { + } else if (p->cachable + || p->temp_file->offset < p->max_temp_file_size) + { /* * if it's allowed then save some hunks from r->in @@ -175,7 +181,7 @@ int ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) rc = ngx_event_pipe_write_chain_to_temp_file(p); - ngx_log_debug(p->log, "temp offset: %d" _ p->temp_offset); + ngx_log_debug(p->log, "temp offset: %d" _ p->temp_file->offset); if (rc == NGX_AGAIN) { if (ngx_event_flags & NGX_USE_LEVEL_EVENT @@ -414,8 +420,8 @@ int ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p) /* reset p->temp_offset if all hunks had been sent */ - if (cl->hunk->file_last == p->temp_offset) { - p->temp_offset = 0; + if (cl->hunk->file_last == p->temp_file->offset) { + p->temp_file->offset = 0; } } } @@ -428,37 +434,29 @@ int ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p) static int ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p) { int rc, size, hsize; + char *save_pos; ngx_hunk_t *h; ngx_chain_t *cl, *tl, *next, *out, **ll, **last_free; ngx_log_debug(p->log, "write to file"); - if (p->temp_file->fd == NGX_INVALID_FILE) { - rc = ngx_create_temp_file(p->temp_file, p->temp_path, p->pool, - p->cachable); - - if (rc == NGX_ERROR) { - return NGX_ABORT; - } - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (!p->cachable && p->temp_file_warn) { - ngx_log_error(NGX_LOG_WARN, p->log, 0, p->temp_file_warn); - } - } - out = p->in; + if (out->hunk->type & NGX_HUNK_PREREAD) { + save_pos = out->hunk->pos; + out->hunk->pos = out->hunk->start; + + } else { + save_pos = NULL; + } + if (!p->cachable) { size = 0; cl = p->in; ll = NULL; -ngx_log_debug(p->log, "offset: %d" _ p->temp_offset); +ngx_log_debug(p->log, "offset: %d" _ p->temp_file->offset); do { hsize = cl->hunk->last - cl->hunk->pos; @@ -466,7 +464,7 @@ ngx_log_debug(p->log, "offset: %d" _ p->temp_offset); ngx_log_debug(p->log, "hunk size: %d" _ hsize); if ((size + hsize > p->temp_file_write_size) - || (p->temp_offset + size + hsize > p->max_temp_file_size)) + || (p->temp_file->offset + size + hsize > p->max_temp_file_size)) { break; } @@ -493,8 +491,7 @@ ngx_log_debug(p->log, "size: %d" _ size); p->last_in = &p->in; } - if (ngx_write_chain_to_file(p->temp_file, out, p->temp_offset, - p->pool) == NGX_ERROR) { + if (ngx_write_chain_to_temp_file(p->temp_file, out) == NGX_ERROR) { return NGX_ABORT; } @@ -505,15 +502,21 @@ ngx_log_debug(p->log, "size: %d" _ size); /* void */ } + if (out->hunk->type & NGX_HUNK_PREREAD) { + p->temp_file->offset += save_pos - out->hunk->pos; + out->hunk->pos = save_pos; + out->hunk->type &= ~NGX_HUNK_PREREAD; + } + for (cl = out; cl; cl = next) { next = cl->next; cl->next = NULL; h = cl->hunk; - h->file = p->temp_file; - h->file_pos = p->temp_offset; - p->temp_offset += h->last - h->pos; - h->file_last = p->temp_offset; + h->file = &p->temp_file->file; + h->file_pos = p->temp_file->offset; + p->temp_file->offset += h->last - h->pos; + h->file_last = p->temp_file->offset; if (p->cachable) { h->type |= NGX_HUNK_FILE; diff --git a/src/event/ngx_event_pipe.h b/src/event/ngx_event_pipe.h index 662d65f70..7eb4c57e2 100644 --- a/src/event/ngx_event_pipe.h +++ b/src/event/ngx_event_pipe.h @@ -54,7 +54,6 @@ struct ngx_event_pipe_s { size_t busy_size; - off_t temp_offset; off_t max_temp_file_size; int temp_file_write_size; @@ -71,9 +70,8 @@ struct ngx_event_pipe_s { ngx_chain_t *preread_hunks; int preread_size; - ngx_file_t *temp_file; - ngx_path_t *temp_path; - char *temp_file_warn; + ngx_temp_file_t *temp_file; + /* STUB */ int num; }; diff --git a/src/http/modules/ngx_http_static_handler.c b/src/http/modules/ngx_http_static_handler.c index 3b87923ed..ad2669be6 100644 --- a/src/http/modules/ngx_http_static_handler.c +++ b/src/http/modules/ngx_http_static_handler.c @@ -39,6 +39,7 @@ ngx_module_t ngx_http_static_module = { int ngx_http_static_translate_handler(ngx_http_request_t *r) { + int rc, level; char *location, *last; ngx_err_t err; ngx_http_core_loc_conf_t *clcf; @@ -116,18 +117,24 @@ ngx_log_debug(r->connection->log, "HTTP filename: '%s'" _ r->file.name.data); if (r->file.fd == NGX_INVALID_FILE) { err = ngx_errno; - ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno, - ngx_open_file_n " \"%s\" failed", r->file.name.data); if (err == NGX_ENOENT || err == NGX_ENOTDIR) { - return NGX_HTTP_NOT_FOUND; + level = NGX_LOG_ERR; + rc = NGX_HTTP_NOT_FOUND; } else if (err == NGX_EACCES) { - return NGX_HTTP_FORBIDDEN; + level = NGX_LOG_ERR; + rc = NGX_HTTP_FORBIDDEN; } else { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + level = NGX_LOG_CRIT; + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } + + ngx_log_error(level, r->connection->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", r->file.name.data); + + return rc; } ngx_log_debug(r->connection->log, "FILE: %d" _ r->file.fd); diff --git a/src/http/modules/proxy/ngx_http_proxy_cache.c b/src/http/modules/proxy/ngx_http_proxy_cache.c new file mode 100644 index 000000000..bfa135c50 --- /dev/null +++ b/src/http/modules/proxy/ngx_http_proxy_cache.c @@ -0,0 +1,235 @@ + +#include +#include +#include +#include + + +int ngx_http_proxy_get_cached_response(ngx_http_proxy_ctx_t *p) +{ + int rc; + char *last; + ngx_http_request_t *r; + ngx_http_proxy_cache_t *c; + ngx_http_proxy_upstream_t *u; + + r = p->request; + + if (!(c = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_cache_t)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + c->ctx.file.fd = NGX_INVALID_FILE; + c->ctx.file.log = r->connection->log; + c->ctx.path = p->lcf->cache_path; + + u = p->lcf->upstream; + + c->ctx.key.len = u->url.len + r->uri.len - u->location->len + r->args.len; + if (!(c->ctx.key.data = ngx_palloc(r->pool, c->ctx.key.len + 1))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + last = ngx_cpymem(c->ctx.key.data, u->url.data, u->url.len); + + last = ngx_cpymem(last, r->uri.data + u->location->len, + r->uri.len - u->location->len); + + if (r->args.len > 0) { + *(last++) = '?'; + last = ngx_cpymem(last, r->args.data, r->args.len); + } + *last = '\0'; + + p->header_in = ngx_create_temp_hunk(r->pool, p->lcf->header_buffer_size); + if (p->header_in == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + p->header_in->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module; + + c->ctx.buf = p->header_in; + p->cache = c; + + rc = ngx_http_cache_get_file(r, &c->ctx); + + if (rc == NGX_OK || rc == NGX_STALE) { + p->header_in->pos += c->ctx.header.size; + + } else if (rc == NGX_DECLINED) { + p->header_in->pos += c->ctx.header.size; + p->header_in->last = p->header_in->pos; + } + + return rc; +} + + +int ngx_http_proxy_process_cached_response(ngx_http_proxy_ctx_t *p) +{ + int rc, i; + ngx_table_elt_t *h; + ngx_http_request_t *r; + ngx_http_proxy_cache_t *c; + + rc = ngx_http_proxy_parse_status_line(p); + + c = p->cache; + r = p->request; + + if (rc == NGX_AGAIN) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "\"proxy_header_buffer_size\" " + "is too small to read header from \"%s\"", + c->ctx.file.name.data); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no valid HTTP/1.0 header in \"%s\"", + c->ctx.file.name.data); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* rc == NGX_OK */ + + c->status = p->status; + c->status_line.len = p->status_end - p->status_start; + c->status_line.data = ngx_palloc(r->pool, c->status_line.len + 1); + if (c->status_line.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_cpystrn(c->status_line.data, p->status_start, c->status_line.len + 1); + + ngx_log_debug(r->connection->log, "http cache status %d '%s'" _ + c->status _ c->status_line.data); + + c->headers_in.headers = ngx_create_table(r->pool, 20); + + for ( ;; ) { + rc = ngx_http_parse_header_line(r, p->header_in); + + if (rc == NGX_OK) { + + /* a header line has been parsed successfully */ + + h = ngx_http_add_header(&c->headers_in, ngx_http_proxy_headers_in); + if (h == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + h->key.len = r->header_name_end - r->header_name_start; + h->value.len = r->header_end - r->header_start; + + h->key.data = ngx_palloc(r->pool, + h->key.len + 1 + h->value.len + 1); + if (h->key.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + h->value.data = h->key.data + h->key.len + 1; + ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1); + ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1); + + for (i = 0; ngx_http_proxy_headers_in[i].name.len != 0; i++) { + if (ngx_http_proxy_headers_in[i].name.len != h->key.len) { + continue; + } + + if (ngx_strcasecmp(ngx_http_proxy_headers_in[i].name.data, + h->key.data) == 0) + { + *((ngx_table_elt_t **) ((char *) &c->headers_in + + ngx_http_proxy_headers_in[i].offset)) = h; + break; + } + } + + ngx_log_debug(r->connection->log, "HTTP cache header: '%s: %s'" _ + h->key.data _ h->value.data); + + continue; + + } else if (rc == NGX_HTTP_PARSE_HEADER_DONE) { + + /* a whole header has been parsed successfully */ + + ngx_log_debug(r->connection->log, "HTTP header done"); + + return ngx_http_proxy_send_cached_response(p); + + } else if (rc == NGX_HTTP_PARSE_INVALID_HEADER) { + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "invalid header in \"%s\"", + c->ctx.file.name.data); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* rc == NGX_AGAIN || rc == NGX_HTTP_PARSE_TOO_LONG_HEADER */ + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "\"proxy_header_buffer_size\" " + "is too small to read header from \"%s\"", + c->ctx.file.name.data); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } +} + + +int ngx_http_proxy_send_cached_response(ngx_http_proxy_ctx_t *p) +{ + int rc; + ngx_hunk_t *h; + ngx_chain_t out; + ngx_http_request_t *r; + + r = p->request; + + r->headers_out.status = p->status; + +#if 0 + r->headers_out.content_length_n = -1; + r->headers_out.content_length = NULL; +#endif + + /* copy an cached header to r->headers_out */ + + if (ngx_http_proxy_copy_header(p, &p->cache->headers_in) == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* we need to allocate all before the header would be sent */ + + if (!((h = ngx_calloc_hunk(r->pool)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (!((h->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t))))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rc = ngx_http_send_header(r); + + /* NEEDED ??? */ p->header_sent = 1; + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + + /* TODO: part in p->header_in */ + + h->type = r->main ? NGX_HUNK_FILE : NGX_HUNK_FILE|NGX_HUNK_LAST; + + h->file_pos = p->header_in->pos - p->header_in->start; + h->file_last = h->file_pos + p->cache->ctx.header.length; + + h->file->fd = p->cache->ctx.file.fd; + h->file->log = r->connection->log; + + out.hunk = h; + out.next = NULL; + + return ngx_http_output_filter(r, &out); +} diff --git a/src/http/modules/proxy/ngx_http_proxy_handler.c b/src/http/modules/proxy/ngx_http_proxy_handler.c index 800039415..afa99d9b2 100644 --- a/src/http/modules/proxy/ngx_http_proxy_handler.c +++ b/src/http/modules/proxy/ngx_http_proxy_handler.c @@ -1,26 +1,24 @@ #include #include -#include -#include -#include #include #include - static int ngx_http_proxy_handler(ngx_http_request_t *r); +static int ngx_http_proxy_request_upstream(ngx_http_proxy_ctx_t *p); 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_connect(ngx_http_proxy_ctx_t *p); static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p); static void ngx_http_proxy_process_upstream_status_line(ngx_event_t *rev); static void ngx_http_proxy_process_upstream_headers(ngx_event_t *rev); static ssize_t ngx_http_proxy_read_upstream_header(ngx_http_proxy_ctx_t *); static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p); static void ngx_http_proxy_process_body(ngx_event_t *ev); +static int ngx_http_proxy_update_cache(ngx_http_proxy_ctx_t *p); -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, int ft_type); static int ngx_http_proxy_log_state(ngx_http_proxy_ctx_t *p, int status); static void ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int rc); @@ -42,8 +40,20 @@ static char *ngx_http_proxy_parse_upstream(ngx_str_t *url, static ngx_conf_bitmask_t next_upstream_masks[] = { { ngx_string("error"), NGX_HTTP_PROXY_FT_ERROR }, { ngx_string("timeout"), NGX_HTTP_PROXY_FT_TIMEOUT }, - { ngx_string("http_header"), NGX_HTTP_PROXY_FT_HTTP_HEADER }, + { ngx_string("invalid_header"), NGX_HTTP_PROXY_FT_INVALID_HEADER }, { ngx_string("http_500"), NGX_HTTP_PROXY_FT_HTTP_500 }, + { ngx_string("http_404"), NGX_HTTP_PROXY_FT_HTTP_404 }, + { ngx_null_string, 0 } +}; + + +static ngx_conf_bitmask_t use_stale_masks[] = { + { ngx_string("error"), NGX_HTTP_PROXY_FT_ERROR }, + { ngx_string("timeout"), NGX_HTTP_PROXY_FT_TIMEOUT }, + { ngx_string("invalid_header"), NGX_HTTP_PROXY_FT_INVALID_HEADER }, + { ngx_string("http_500"), NGX_HTTP_PROXY_FT_HTTP_500 }, + { ngx_string("busy_lock"), NGX_HTTP_PROXY_FT_BUSY_LOCK }, + { ngx_string("max_waiting"), NGX_HTTP_PROXY_FT_MAX_WAITING }, { ngx_null_string, 0 } }; @@ -105,6 +115,13 @@ static ngx_command_t ngx_http_proxy_commands[] = { offsetof(ngx_http_proxy_loc_conf_t, busy_buffers_size), NULL }, + { ngx_string("proxy_cache_path"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234, + ngx_conf_set_path_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, cache_path), + NULL }, + { ngx_string("proxy_temp_path"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234, ngx_conf_set_path_slot, @@ -119,6 +136,13 @@ static ngx_command_t ngx_http_proxy_commands[] = { offsetof(ngx_http_proxy_loc_conf_t, temp_file_write_size), NULL }, + { ngx_string("proxy_cache"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, cache), + NULL }, + { ngx_string("proxy_pass_server"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -133,6 +157,13 @@ static ngx_command_t ngx_http_proxy_commands[] = { offsetof(ngx_http_proxy_loc_conf_t, next_upstream), &next_upstream_masks }, + { ngx_string("proxy_use_stale"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, use_stale), + &use_stale_masks }, + ngx_null_command }; @@ -172,7 +203,7 @@ static char *upstream_header_errors[] = { }; -static ngx_http_header_t headers_in[] = { +ngx_http_header_t ngx_http_proxy_headers_in[] = { { ngx_string("Date"), offsetof(ngx_http_proxy_headers_in_t, date) }, { ngx_string("Server"), offsetof(ngx_http_proxy_headers_in_t, server) }, { ngx_string("Connection"), @@ -198,13 +229,50 @@ static char connection_close_header[] = "Connection: close" CRLF; static int ngx_http_proxy_handler(ngx_http_request_t *r) { - ngx_http_proxy_ctx_t *p; + int rc; + char *last; + ngx_http_cache_ctx_t *cctx; + ngx_http_proxy_ctx_t *p; + ngx_http_proxy_upstream_t *u; ngx_http_create_ctx(r, p, ngx_http_proxy_module, sizeof(ngx_http_proxy_ctx_t), NGX_HTTP_INTERNAL_SERVER_ERROR); p->lcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + p->request = r; + + /* TODO: we currently support reverse proxy only */ + p->accel = 1; + + if (!p->lcf->cache || r->bypass_cache) { + return ngx_http_proxy_request_upstream(p); + } + + rc = ngx_http_proxy_get_cached_response(p); + + if (rc == NGX_OK) { + return ngx_http_proxy_process_cached_response(p); + } + + if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { + return rc; + } + + if (rc == NGX_DECLINED || rc == NGX_STALE) { + return ngx_http_proxy_request_upstream(p); + } + + return NGX_DONE; +} + + +static int ngx_http_proxy_request_upstream(ngx_http_proxy_ctx_t *p) +{ + ngx_http_request_t *r; + + r = p->request; + p->upstream.peers = p->lcf->peers; p->upstream.tries = p->lcf->peers->number; @@ -212,29 +280,28 @@ static int ngx_http_proxy_handler(ngx_http_request_t *r) sizeof(ngx_http_proxy_state_t), NGX_HTTP_INTERNAL_SERVER_ERROR); - p->request = r; p->method = r->method; - /* TODO: we currently support reverse proxy only */ - p->accel = 1; + /* STUB */ p->cachable = p->lcf->cache; 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); + if (!(r->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)))) { + return 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->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->temp_file->persistent = 0; */ r->request_body_handler = ngx_http_proxy_init_request; r->data = p; - /* TODO: we ignore return value of ngx_http_read_client_request_body */ + /* TODO: we ignore return value of ngx_http_read_client_request_body, + probably we should not return anything */ ngx_http_read_client_request_body(r, p->lcf->request_buffer_size); return NGX_DONE; @@ -255,7 +322,6 @@ static void ngx_http_proxy_init_request(void *data) ngx_output_chain_ctx_t *octx; ngx_chain_writer_ctx_t *wctx; - r = p->request; ngx_log_debug(r->connection->log, "timer_set: %d" _ @@ -283,40 +349,27 @@ ngx_log_debug(r->connection->log, "timer_set: %d" _ r->connection->log->handler = ngx_http_proxy_log_error; p->action = "connecting to upstream"; - octx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t)); - if (octx == NULL) { + if (!(octx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t)))) { ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } p->output_chain_ctx = octx; - - if (r->request_body_hunk) { - octx->free = ngx_alloc_chain_link(r->pool); - if (octx->free == NULL) { - ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - octx->free->hunk = r->request_body_hunk; - octx->free->next = NULL; - } - octx->sendfile = r->sendfile; octx->pool = r->pool; octx->bufs.num = 1; octx->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module; octx->output_filter = (ngx_output_chain_filter_pt) ngx_chain_writer; - wctx = ngx_pcalloc(r->pool, sizeof(ngx_chain_writer_ctx_t)); - if (wctx == NULL) { + if (!(wctx = ngx_palloc(r->pool, sizeof(ngx_chain_writer_ctx_t)))) { ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } octx->output_ctx = wctx; wctx->pool = r->pool; - wctx->last = &wctx->out; - ngx_http_proxy_send_request(p); + ngx_http_proxy_connect(p); } @@ -441,106 +494,135 @@ static void ngx_http_proxy_send_request_handler(ngx_event_t *wev) c = wev->data; p = c->data; - p->action = "sending request to upstream"; - if (wev->timedout) { - p->timedout = 1; + p->action = "sending request to upstream"; ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT); return; } ngx_http_proxy_send_request(p); - - return; } -#if 0 - -static int ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p) +static void ngx_http_proxy_dummy_handler(ngx_event_t *wev) { - int rc; - ngx_chain_t *cl; - ngx_connection_t *c; + ngx_log_debug(wev->log, "dummy handler"); +} - for ( ;; ) { - p->action = "connecting to upstream"; - rc = ngx_event_connect_peer(&p->upstream); +static void ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p) +{ + int rc; + ngx_chain_t *cl; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_output_chain_ctx_t *octx; - if (rc == NGX_ERROR) { - ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_DONE; - } + p->action = "connecting to upstream"; - if (rc == NGX_CONNECT_ERROR) { - ngx_event_connect_peer_failed(&p->upstream); + rc = ngx_event_connect_peer(&p->upstream); - if (ngx_http_proxy_log_state(p, NGX_HTTP_BAD_GATEWAY) == NGX_ERROR) - { - ngx_http_proxy_finalize_request(p, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_DONE; - } + if (rc == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } - if (p->upstream.tries == 0) { - ngx_http_proxy_finalize_request(p, NGX_HTTP_BAD_GATEWAY); - return NGX_DONE; - } + if (rc == NGX_CONNECT_ERROR) { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); + return; + } - continue; - } - - p->upstream.connection->data = p; - p->upstream.connection->write->event_handler = + p->upstream.connection->data = p; + p->upstream.connection->write->event_handler = ngx_http_proxy_send_request_handler; - p->upstream.connection->read->event_handler = + p->upstream.connection->read->event_handler = ngx_http_proxy_process_upstream_status_line; - c = p->upstream.connection; - c->pool = p->request->pool; - c->read->log = c->write->log = c->log = p->request->connection->log; + r = p->request; + c = p->upstream.connection; + c->pool = r->pool; + c->read->log = c->write->log = c->log = r->connection->log; - if (p->upstream.tries > 1 && p->request_sent) { + octx = p->output_chain_ctx; - /* reinit the request chain */ - - for (cl = p->request->request_hunks; cl; cl = cl->next) { - cl->hunk->pos = cl->hunk->start; - } - } - - p->request_sent = 0; - p->timedout = 0; - - if (rc == NGX_OK) { - return ngx_http_proxy_send_request0(p); - } - - /* rc == NGX_AGAIN */ - - ngx_add_timer(c->write, p->lcf->connect_timeout); - - return NGX_AGAIN; + if (p->upstream.tries > 1 && p->request_sent) { + ngx_http_proxy_reinit_upstream(p); } + + /* init or reinit ngx_output_chain() context */ + + if (r->request_body_hunk) { + if (!(octx->free = ngx_alloc_chain_link(r->pool))) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + octx->free->hunk = r->request_body_hunk; + octx->free->next = NULL; + octx->hunks = 1; + + r->request_body_hunk->pos = r->request_body_hunk->start; + r->request_body_hunk->last = r->request_body_hunk->start; + } + + p->request_sent = 0; + + if (rc == NGX_AGAIN) { + ngx_add_timer(c->write, p->lcf->connect_timeout); + return; + } + + /* rc == NGX_OK */ + +#if 1 /* test only */ + + if (c->read->ready) { + /* post aio operation */ + ngx_http_proxy_process_upstream_status_line(c->read); + return; + } +#endif + + ngx_http_proxy_send_request(p); } -static int ngx_http_proxy_send_request0(ngx_http_proxy_ctx_t *p) +static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p) { + int rc; ngx_connection_t *c; ngx_chain_writer_ctx_t *wctx; c = p->upstream.connection; +#if (HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) + && !p->request_sent + && c->write->kq_eof) + { + ngx_log_error(NGX_LOG_ERR, c->log, c->write->kq_errno, + "connect() failed"); + + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); + return; + } + +#endif + p->action = "sending request to upstream"; + wctx = p->output_chain_ctx->output_ctx; + wctx->out = NULL; + wctx->last = &wctx->out; wctx->connection = c; + rc = ngx_output_chain(p->output_chain_ctx, - !p->request_sent ? p->request->request_hunks: - NULL); + p->request_sent ? NULL : p->request->request_hunks); + if (rc == NGX_ERROR) { - return ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); + return; } p->request_sent = 1; @@ -558,188 +640,45 @@ static int ngx_http_proxy_send_request0(ngx_http_proxy_ctx_t *p) return; } - return NGX_AGAIN; + return; } /* rc == NGX_OK */ - if (c->read->ready) { - /* post aio operation */ - ngx_http_proxy_process_upstream_status_line(c->read); - } - - if (ngx_handle_level_write_event(c->write) == NGX_ERROR) { - ngx_http_proxy_finalize_request(p, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - 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; - } + 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; } - ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); +#if 0 + if (c->read->ready) { - return NGX_OK; -} + /* post aio operation */ + /* + * although we can post aio operation just in the end + * of ngx_http_proxy_connect() CHECK IT !!! + * it's better to do here because we postpone header buffer allocation + */ + ngx_http_proxy_process_upstream_status_line(c->read); + return; + } #endif + p->upstream.connection->write->event_handler = ngx_http_proxy_dummy_handler; -static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p) -{ - int rc; - ngx_chain_t *cl; - ngx_connection_t *c; - ngx_chain_writer_ctx_t *wctx; - - c = p->upstream.connection; - - for ( ;; ) { - - if (c) { - p->action = "sending request to upstream"; - wctx = p->output_chain_ctx->output_ctx; - wctx->connection = c; - rc = ngx_output_chain(p->output_chain_ctx, - !p->request_sent ? p->request->request_hunks: - NULL); - - if (rc != NGX_ERROR) { - p->request_sent = 1; - - if (c->write->timer_set) { - ngx_del_timer(c->write); - } - - if (rc == NGX_AGAIN) { - ngx_add_timer(c->write, p->lcf->send_timeout); - - if (ngx_handle_write_event(c->write, /* STUB: lowat */ 0) - == NGX_ERROR) - { - ngx_http_proxy_finalize_request(p, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - } else { - - /* rc == NGX_OK */ - - if (c->read->ready) { - /* post aio operation */ - ngx_http_proxy_process_upstream_status_line(c->read); - } - - if (ngx_handle_level_write_event(c->write) == NGX_ERROR) { - ngx_http_proxy_finalize_request(p, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - 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; - } - - ngx_event_connect_peer_failed(&p->upstream); - ngx_http_proxy_close_connection(c); - - if (p->upstream.tries == 0 - || !(p->lcf->next_upstream & NGX_HTTP_PROXY_FT_ERROR)) - { - ngx_http_proxy_finalize_request(p, - p->timedout ? NGX_HTTP_GATEWAY_TIME_OUT: - NGX_HTTP_BAD_GATEWAY); - return; - } - - if (!p->fatal_error) { - ngx_http_proxy_send_request(p); - return; - } - } - - for ( ;; ) { - p->action = "connecting to upstream"; - - rc = ngx_event_connect_peer(&p->upstream); - - if (rc == NGX_ERROR) { - ngx_http_proxy_finalize_request(p, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - if (rc == NGX_CONNECT_ERROR) { - ngx_event_connect_peer_failed(&p->upstream); - - if (p->upstream.tries == 0) { - ngx_http_proxy_finalize_request(p, NGX_HTTP_BAD_GATEWAY); - return; - } - - continue; - } - - p->upstream.connection->data = p; - p->upstream.connection->write->event_handler = - ngx_http_proxy_send_request_handler; - p->upstream.connection->read->event_handler = - ngx_http_proxy_process_upstream_status_line; - - c = p->upstream.connection; - c->pool = p->request->pool; - c->read->log = c->write->log = c->log = p->request->connection->log; - - if (p->upstream.tries > 1 && p->request_sent) { - - /* reinit the request chain */ - - for (cl = p->request->request_hunks; cl; cl = cl->next) { - cl->hunk->pos = cl->hunk->start; - } - } - - p->request_sent = 0; - p->timedout = 0; - - if (rc == NGX_OK) { - break; - } - - /* rc == NGX_AGAIN */ - - ngx_add_timer(c->write, p->lcf->connect_timeout); - - return; - } + if (ngx_handle_level_write_event(c->write) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; } } @@ -753,13 +692,11 @@ static void ngx_http_proxy_process_upstream_status_line(ngx_event_t *rev) 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_FT_TIMEOUT); return; } @@ -772,18 +709,30 @@ static void ngx_http_proxy_process_upstream_status_line(ngx_event_t *rev) return; } p->header_in->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module; + + if (p->cache) { + p->header_in->pos += p->cache->ctx.header.size; + p->header_in->last = p->header_in->pos; + } } n = ngx_http_proxy_read_upstream_header(p); - if (n == NGX_ERROR) { + if (n == NGX_AGAIN) { + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream prematurely closed connection"); + } + + if (n == NGX_ERROR || n == 0) { ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); return; } - if (n == NGX_AGAIN) { - return; - } + p->upstream.cached = 0; rc = ngx_http_proxy_parse_status_line(p); @@ -791,9 +740,8 @@ static void ngx_http_proxy_process_upstream_status_line(ngx_event_t *rev) if (p->header_in->pos == p->header_in->last) { ngx_log_error(NGX_LOG_ERR, rev->log, 0, "upstream sent too long status line"); - ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER); } - return; } @@ -802,7 +750,7 @@ static void ngx_http_proxy_process_upstream_status_line(ngx_event_t *rev) "upstream sent no valid HTTP/1.0 header"); if (p->accel) { - ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER); } else { p->request->http_version = NGX_HTTP_VERSION_9; @@ -815,12 +763,25 @@ static void ngx_http_proxy_process_upstream_status_line(ngx_event_t *rev) /* 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; + if (p->status == NGX_HTTP_INTERNAL_SERVER_ERROR) { + + if (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; + } + + if (p->upstream.tries == 0 + && p->stale + && (p->lcf->use_stale & NGX_HTTP_PROXY_FT_HTTP_500)) + { + /* + * TODO: use stale cached response if it exists and enabled + */ + + return; + } } p->status_line.len = p->status_end - p->status_start; @@ -842,8 +803,6 @@ static void ngx_http_proxy_process_upstream_status_line(ngx_event_t *rev) c->read->event_handler = ngx_http_proxy_process_upstream_headers; ngx_http_proxy_process_upstream_headers(rev); - - return; } @@ -859,13 +818,11 @@ static void ngx_http_proxy_process_upstream_headers(ngx_event_t *rev) c = rev->data; 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_FT_TIMEOUT); return; } @@ -876,7 +833,12 @@ static void ngx_http_proxy_process_upstream_headers(ngx_event_t *rev) if (rc == NGX_AGAIN) { n = ngx_http_proxy_read_upstream_header(p); - if (n == NGX_ERROR) { + if (n == 0) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream prematurely closed connection"); + } + + if (n == NGX_ERROR || n == 0) { ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); return; } @@ -892,7 +854,8 @@ static void ngx_http_proxy_process_upstream_headers(ngx_event_t *rev) /* a header line has been parsed successfully */ - if (!(h = ngx_http_add_header(&p->headers_in, headers_in))) { + h = ngx_http_add_header(&p->headers_in, ngx_http_proxy_headers_in); + if (h == NULL) { ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -913,14 +876,16 @@ static void ngx_http_proxy_process_upstream_headers(ngx_event_t *rev) ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1); ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1); - for (i = 0; headers_in[i].name.len != 0; i++) { - if (headers_in[i].name.len != h->key.len) { + for (i = 0; ngx_http_proxy_headers_in[i].name.len != 0; i++) { + if (ngx_http_proxy_headers_in[i].name.len != h->key.len) { continue; } - 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; + if (ngx_strcasecmp(ngx_http_proxy_headers_in[i].name.data, + h->key.data) == 0) + { + *((ngx_table_elt_t **) ((char *) &p->headers_in + + ngx_http_proxy_headers_in[i].offset)) = h; break; } } @@ -937,7 +902,6 @@ static void ngx_http_proxy_process_upstream_headers(ngx_event_t *rev) ngx_log_debug(c->log, "HTTP header done"); ngx_http_proxy_send_response(p); - return; } else if (rc != NGX_AGAIN) { @@ -947,7 +911,7 @@ static void ngx_http_proxy_process_upstream_headers(ngx_event_t *rev) 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); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER); return; } @@ -957,7 +921,7 @@ static void ngx_http_proxy_process_upstream_headers(ngx_event_t *rev) ngx_log_error(NGX_LOG_ERR, rev->log, 0, "upstream sent too big header"); - ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER); return; } } @@ -1008,86 +972,44 @@ static ssize_t ngx_http_proxy_read_upstream_header(ngx_http_proxy_ctx_t *p) static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p) { - int rc, i; - ngx_table_elt_t *ho, *h; - ngx_event_pipe_t *ep; - ngx_http_request_t *r; - ngx_http_core_loc_conf_t *clcf; + int rc, i; + ngx_table_elt_t *ho, *h; + ngx_event_pipe_t *ep; + ngx_http_request_t *r; + ngx_http_bin_cache_t *header; + ngx_http_core_loc_conf_t *clcf; r = p->request; r->headers_out.status = p->status; +#if 0 r->headers_out.content_length_n = -1; r->headers_out.content_length = NULL; +#endif /* copy an upstream header to r->headers_out */ - h = p->headers_in.headers->elts; - for (i = 0; i < p->headers_in.headers->nelts; i++) { - - if (&h[i] == p->headers_in.connection) { - continue; - } - - if (p->accel) { - if (&h[i] == p->headers_in.date - || &h[i] == p->headers_in.accept_ranges) { - continue; - } - - if (&h[i] == p->headers_in.server && !p->lcf->pass_server) { - continue; - } - } - - if (&h[i] == p->headers_in.content_type) { - r->headers_out.content_type = &h[i]; - r->headers_out.content_type->key.len = 0; - continue; - } - - if (!(ho = ngx_http_add_header(&r->headers_out, ngx_http_headers_out))) - { - ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - *ho = h[i]; - - /* - * ngx_http_header_filter() output the following headers - * from r->headers_out.headers if they are set: - * r->headers_out.server, - * r->headers_out.date, - * r->headers_out.content_length - */ - - if (&h[i] == p->headers_in.server) { - r->headers_out.server = ho; - continue; - } - - if (&h[i] == p->headers_in.date) { - r->headers_out.date = ho; - continue; - } - - if (&h[i] == p->headers_in.content_length) { - r->headers_out.content_length = ho; - r->headers_out.content_length_n = ngx_atoi(ho->value.data, - ho->value.len); - continue; - } + if (ngx_http_proxy_copy_header(p, &p->headers_in) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; } - /* TODO: preallocate event_pipe hunks, look "Content-Length" */ rc = ngx_http_send_header(r); p->header_sent = 1; + if (p->cache) { + header = (ngx_http_bin_cache_t *) p->header_in->start; + header->type = 0x42424242; /* "BBBB" */ + header->header.length = r->headers_out.content_length_n; + header->key_len = p->cache->ctx.key.len; + ngx_memcpy(&header->key, p->cache->ctx.key.data, header->key_len); + header->key[header->key_len] = LF; + } + ep = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t)); if (ep == NULL) { ngx_http_proxy_finalize_request(p, 0); @@ -1105,21 +1027,21 @@ static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p) ep->downstream = r->connection; ep->pool = r->pool; ep->log = r->connection->log; - ep->temp_path = p->lcf->temp_path; - ep->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); - if (ep->temp_file == NULL) { + if (!(ep->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)))) { ngx_http_proxy_finalize_request(p, 0); return; } - ep->temp_file->fd = NGX_INVALID_FILE; - ep->temp_file->log = r->connection->log; + ep->temp_file->file.fd = NGX_INVALID_FILE; + ep->temp_file->file.log = r->connection->log; + ep->temp_file->path = p->lcf->temp_path; + ep->temp_file->pool = r->pool; + ep->temp_file->warn = "an upstream response is buffered " + "to a temporary file"; ep->max_temp_file_size = p->lcf->max_temp_file_size; ep->temp_file_write_size = p->lcf->temp_file_write_size; - ep->temp_file_warn = "an upstream response is buffered " - "to a temporary file"; ep->preread_hunks = ngx_alloc_chain_link(r->pool); if (ep->preread_hunks == NULL) { @@ -1128,6 +1050,7 @@ static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p) } ep->preread_hunks->hunk = p->header_in; ep->preread_hunks->next = NULL; + p->header_in->type |= NGX_HUNK_PREREAD; ep->preread_size = p->header_in->last - p->header_in->pos; @@ -1146,7 +1069,7 @@ static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p) */ p->header_in->last = p->header_in->pos; - /* STUB */ ep->cachable = 0; + ep->cachable = p->cachable; if (p->lcf->cyclic_temp_file) { @@ -1226,10 +1149,19 @@ static void ngx_http_proxy_process_body(ngx_event_t *ev) if (p->upstream.connection) { if (ep->upstream_done) { - /* TODO: update cache */ + if (ngx_http_proxy_update_cache(p) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, 0); + return; + } } else if (ep->upstream_eof) { + /* TODO: check length & update cache */ + + if (ngx_http_proxy_update_cache(p) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, 0); + return; + } } if (ep->upstream_done || ep->upstream_eof || ep->upstream_error) { @@ -1259,202 +1191,14 @@ 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 int ngx_http_proxy_update_cache(ngx_http_proxy_ctx_t *p) { - char ch; - char *pos; - enum { - sw_start = 0, - sw_H, - sw_HT, - sw_HTT, - sw_HTTP, - sw_first_major_digit, - sw_major_digit, - sw_first_minor_digit, - sw_minor_digit, - sw_status, - sw_space_after_status, - sw_status_text, - sw_almost_done, - sw_done - } state; - - state = p->state; - pos = p->header_in->pos; - - while (pos < p->header_in->last && state < sw_done) { - ch = *pos++; - - switch (state) { - - /* "HTTP/" */ - case sw_start: - switch (ch) { - case 'H': - state = sw_H; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - case sw_H: - switch (ch) { - case 'T': - state = sw_HT; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - case sw_HT: - switch (ch) { - case 'T': - state = sw_HTT; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - case sw_HTT: - switch (ch) { - case 'P': - state = sw_HTTP; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - case sw_HTTP: - switch (ch) { - case '/': - state = sw_first_major_digit; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - /* the first digit of major HTTP version */ - case sw_first_major_digit: - if (ch < '1' || ch > '9') { - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - - state = sw_major_digit; - break; - - /* the major HTTP version or dot */ - case sw_major_digit: - if (ch == '.') { - state = sw_first_minor_digit; - break; - } - - if (ch < '0' || ch > '9') { - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - - break; - - /* the first digit of minor HTTP version */ - case sw_first_minor_digit: - if (ch < '0' || ch > '9') { - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - - state = sw_minor_digit; - break; - - /* the minor HTTP version or the end of the request line */ - case sw_minor_digit: - if (ch == ' ') { - state = sw_status; - break; - } - - if (ch < '0' || ch > '9') { - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - - break; - - /* HTTP status code */ - case sw_status: - if (ch < '0' || ch > '9') { - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - - p->status = p->status * 10 + ch - '0'; - - if (++p->status_count == 3) { - state = sw_space_after_status; - p->status_start = pos - 3; - } - - break; - - /* space or end of line */ - case sw_space_after_status: - switch (ch) { - case ' ': - state = sw_status_text; - break; - case CR: - state = sw_almost_done; - break; - case LF: - state = sw_done; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - /* any text until end of line */ - case sw_status_text: - switch (ch) { - case CR: - state = sw_almost_done; - - break; - case LF: - state = sw_done; - break; - } - break; - - /* end of request line */ - case sw_almost_done: - p->status_end = pos - 2; - switch (ch) { - case LF: - state = sw_done; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - } - } - - p->header_in->pos = pos; - - if (state == sw_done) { - if (p->status_end == NULL) { - p->status_end = pos - 1; - } - - p->state = sw_start; + if (p->cache == NULL) { return NGX_OK; } - p->state = state; - return NGX_AGAIN; + return ngx_http_cache_update_file(p->request, &p->cache->ctx, + &p->event_pipe->temp_file->file.name); } @@ -1462,43 +1206,69 @@ static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p, int ft_type) { int status; - ngx_event_connect_peer_failed(&p->upstream); +ngx_log_debug(p->request->connection->log, "next upstream: %d" _ ft_type); - if (p->timedout) { + if (ft_type != NGX_HTTP_PROXY_FT_HTTP_404) { + ngx_event_connect_peer_failed(&p->upstream); + } + + if (ft_type == NGX_HTTP_PROXY_FT_TIMEOUT) { ngx_log_error(NGX_LOG_ERR, p->request->connection->log, NGX_ETIMEDOUT, "upstream timed out"); } + if (p->upstream.cached && ft_type == NGX_HTTP_PROXY_FT_ERROR) { + status = 0; + + } else { + switch(ft_type) { + case NGX_HTTP_PROXY_FT_TIMEOUT: + status = NGX_HTTP_GATEWAY_TIME_OUT; + break; + + case NGX_HTTP_PROXY_FT_HTTP_500: + status = NGX_HTTP_INTERNAL_SERVER_ERROR; + break; + + case NGX_HTTP_PROXY_FT_HTTP_404: + status = NGX_HTTP_NOT_FOUND; + break; + + /* + * NGX_HTTP_PROXY_FT_BUSY_LOCK and NGX_HTTP_PROXY_FT_MAX_WAITING + * never reach here + */ + + default: + status = NGX_HTTP_BAD_GATEWAY; + } + } + if (p->upstream.connection) { ngx_http_proxy_close_connection(p->upstream.connection); p->upstream.connection = NULL; } - if (ft_type == NGX_HTTP_PROXY_FT_TIMEOUT) { - status = NGX_HTTP_GATEWAY_TIME_OUT; + if (status) { + if (ngx_http_proxy_log_state(p, status) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } - } else { - status = NGX_HTTP_BAD_GATEWAY; + if (p->upstream.tries == 0 || !(p->lcf->next_upstream & ft_type)) { + + if (p->stale && (p->lcf->use_stale & ft_type)) { + /* + * TODO: use stale cached response if it exists and enabled + */ + } + + ngx_http_proxy_finalize_request(p, status); + return; + } } - if (ngx_http_proxy_log_state(p, status) == NGX_ERROR) { - ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - if (p->upstream.tries == 0 || !(p->lcf->next_upstream & ft_type)) { - ngx_http_proxy_finalize_request(p, status); - return; - } - - if (!p->fatal_error) { - ngx_http_proxy_send_request(p); - return; - } - -ngx_log_debug(p->request->connection->log, "FATAL ERROR IN NEXT UPSTREAM"); - - return; + ngx_http_proxy_connect(p); } @@ -1538,16 +1308,12 @@ static void ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int rc) p->request->connection->log->handler = p->saved_handler; ngx_http_finalize_request(p->request, rc); - - p->fatal_error = 1; - - return; } static void ngx_http_proxy_close_connection(ngx_connection_t *c) { - ngx_log_debug(c->log, "close connection: %d" _ c->fd); + ngx_log_debug(c->log, "proxy close connection: %d" _ c->fd); if (c->fd == -1) { #if 0 @@ -1585,8 +1351,6 @@ static void ngx_http_proxy_close_connection(ngx_connection_t *c) } c->fd = -1; - - return; } @@ -1622,10 +1386,14 @@ static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) conf->path = NULL; conf->next_upstream = 0; + conf->use_stale = 0; conf->upstreams = NULL; conf->peers = NULL; + conf->cache_path = NULL; + conf->temp_path = NULL; + */ conf->request_buffer_size = NGX_CONF_UNSET; @@ -1646,6 +1414,8 @@ static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) /* "proxy_cyclic_temp_file" is disabled */ conf->cyclic_temp_file = 0; + conf->cache = NGX_CONF_UNSET; + conf->pass_server = NGX_CONF_UNSET; return conf; @@ -1681,11 +1451,21 @@ static char *ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, prev->temp_file_write_size, 16384); ngx_conf_merge_bitmask_value(conf->next_upstream, prev->next_upstream, - (NGX_HTTP_PROXY_FT_ERROR|NGX_HTTP_PROXY_FT_TIMEOUT)); + (NGX_CONF_BITMASK_SET + |NGX_HTTP_PROXY_FT_ERROR + |NGX_HTTP_PROXY_FT_TIMEOUT)); + + ngx_conf_merge_bitmask_value(conf->use_stale, prev->use_stale, + NGX_CONF_BITMASK_SET); + + ngx_conf_merge_path_value(conf->cache_path, prev->cache_path, + "cache", 1, 2, 0, cf->pool); ngx_conf_merge_path_value(conf->temp_path, prev->temp_path, "temp", 1, 2, 0, cf->pool); + ngx_conf_merge_value(conf->cache, prev->cache, 0); + ngx_conf_merge_value(conf->pass_server, prev->pass_server, 0); return NULL; @@ -1717,6 +1497,12 @@ static char *ngx_http_proxy_set_pass(ngx_conf_t *cf, ngx_command_t *cmd, ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_upstream_t)), NGX_CONF_ERROR); + lcf->upstream->url.len = value[1].len; + if (!(lcf->upstream->url.data = ngx_palloc(cf->pool, value[1].len + 1))) { + return NGX_CONF_ERROR; + } + ngx_cpystrn(lcf->upstream->url.data, value[1].data, value[1].len + 1); + value[1].data += 7; value[1].len -= 7; diff --git a/src/http/modules/proxy/ngx_http_proxy_handler.h b/src/http/modules/proxy/ngx_http_proxy_handler.h index c211afbba..ed515b84c 100644 --- a/src/http/modules/proxy/ngx_http_proxy_handler.h +++ b/src/http/modules/proxy/ngx_http_proxy_handler.h @@ -5,10 +5,13 @@ #include #include #include +#include +#include #include typedef struct { + ngx_str_t url; ngx_str_t host; ngx_str_t uri; ngx_str_t host_header; @@ -19,51 +22,62 @@ typedef struct { typedef struct { - ssize_t request_buffer_size; - ngx_msec_t connect_timeout; - ngx_msec_t send_timeout; - ssize_t header_buffer_size; - ngx_msec_t read_timeout; + ssize_t request_buffer_size; + ngx_msec_t connect_timeout; + ngx_msec_t send_timeout; + ssize_t header_buffer_size; + ngx_msec_t read_timeout; - ngx_bufs_t bufs; - ssize_t busy_buffers_size; + ngx_bufs_t bufs; + ssize_t busy_buffers_size; - ssize_t max_temp_file_size; - ssize_t temp_file_write_size; - int cyclic_temp_file; + ssize_t max_temp_file_size; + ssize_t temp_file_write_size; + int cyclic_temp_file; - int pass_server; + int cache; + int pass_server; - int next_upstream; + int next_upstream; + int use_stale; - ngx_path_t *temp_path; + ngx_path_t *cache_path; + ngx_path_t *temp_path; - ngx_http_proxy_upstream_t *upstream; - ngx_peers_t *peers; + ngx_http_proxy_upstream_t *upstream; + ngx_peers_t *peers; } ngx_http_proxy_loc_conf_t; typedef struct { - int status; - ngx_str_t *peer; + int status; + ngx_str_t *peer; } ngx_http_proxy_state_t; typedef struct { - ngx_table_t *headers; /* it must be first field */ + ngx_table_t *headers; /* it must be first field */ - ngx_table_elt_t *date; - ngx_table_elt_t *server; - ngx_table_elt_t *connection; - ngx_table_elt_t *content_type; - ngx_table_elt_t *content_length; - ngx_table_elt_t *last_modified; - ngx_table_elt_t *accept_ranges; + ngx_table_elt_t *date; + ngx_table_elt_t *server; + ngx_table_elt_t *connection; + ngx_table_elt_t *content_type; + ngx_table_elt_t *content_length; + ngx_table_elt_t *last_modified; + ngx_table_elt_t *accept_ranges; - off_t content_length_n; + off_t content_length_n; } ngx_http_proxy_headers_in_t; +typedef struct { + ngx_http_cache_ctx_t ctx; + int status; + ngx_str_t status_line; + ngx_http_proxy_headers_in_t headers_in; +} ngx_http_proxy_cache_t; + + typedef struct ngx_http_proxy_ctx_s ngx_http_proxy_ctx_t; struct ngx_http_proxy_ctx_s { @@ -72,45 +86,69 @@ struct ngx_http_proxy_ctx_s { ngx_http_request_t *request; ngx_http_proxy_loc_conf_t *lcf; + ngx_http_proxy_cache_t *cache; ngx_http_proxy_headers_in_t headers_in; - ngx_hunk_t *header_in; - int status; - ngx_str_t status_line; + ngx_hunk_t *header_in; + int status; + ngx_str_t status_line; - ngx_output_chain_ctx_t *output_chain_ctx; + ngx_output_chain_ctx_t *output_chain_ctx; - int method; + int method; - ngx_event_pipe_t *event_pipe; + ngx_event_pipe_t *event_pipe; - unsigned accel:1; - unsigned cachable:1; - unsigned fatal_error:1; - unsigned request_sent:1; - unsigned timedout:1; - unsigned header_sent:1; + unsigned accel:1; + + unsigned cachable:1; + unsigned stale:1; + + unsigned request_sent:1; + unsigned header_sent:1; /* used to parse an upstream HTTP header */ - char *status_start; - char *status_end; - int status_count; - int state; + char *status_start; + char *status_end; + int status_count; + int state; - ngx_array_t states; /* of ngx_http_proxy_state_t */ + ngx_array_t states; /* of ngx_http_proxy_state_t */ - char *action; - ngx_http_log_ctx_t *saved_ctx; - ngx_log_handler_pt saved_handler; + 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_STALE 1 + +#define NGX_HTTP_PROXY_PARSE_NO_HEADER 20 + +#define NGX_HTTP_PROXY_FT_ERROR 2 +#define NGX_HTTP_PROXY_FT_TIMEOUT 4 +#define NGX_HTTP_PROXY_FT_INVALID_HEADER 8 +#define NGX_HTTP_PROXY_FT_HTTP_500 16 +#define NGX_HTTP_PROXY_FT_HTTP_404 32 +#define NGX_HTTP_PROXY_FT_BUSY_LOCK 64 +#define NGX_HTTP_PROXY_FT_MAX_WAITING 128 + + +void ngx_http_proxy_reinit_upstream(ngx_http_proxy_ctx_t *p); + +int ngx_http_proxy_get_cached_response(ngx_http_proxy_ctx_t *p); +int ngx_http_proxy_process_cached_response(ngx_http_proxy_ctx_t *p); +int ngx_http_proxy_send_cached_response(ngx_http_proxy_ctx_t *p); + +int ngx_http_proxy_parse_status_line(ngx_http_proxy_ctx_t *p); +int ngx_http_proxy_copy_header(ngx_http_proxy_ctx_t *p, + ngx_http_proxy_headers_in_t *headers_in); + + + +extern ngx_module_t ngx_http_proxy_module; +extern ngx_http_header_t ngx_http_proxy_headers_in[]; -#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/modules/proxy/ngx_http_proxy_header.c b/src/http/modules/proxy/ngx_http_proxy_header.c new file mode 100644 index 000000000..18e27976a --- /dev/null +++ b/src/http/modules/proxy/ngx_http_proxy_header.c @@ -0,0 +1,75 @@ + +#include +#include +#include +#include + + +int ngx_http_proxy_copy_header(ngx_http_proxy_ctx_t *p, + ngx_http_proxy_headers_in_t *headers_in) +{ + int i; + ngx_table_elt_t *ho, *h; + ngx_http_request_t *r; + + r = p->request; + + h = headers_in->headers->elts; + for (i = 0; i < headers_in->headers->nelts; i++) { + + if (&h[i] == headers_in->connection) { + continue; + } + + if (p->accel) { + if (&h[i] == headers_in->date + || &h[i] == headers_in->accept_ranges) { + continue; + } + + if (&h[i] == headers_in->server && !p->lcf->pass_server) { + continue; + } + } + + if (&h[i] == headers_in->content_type) { + r->headers_out.content_type = &h[i]; + r->headers_out.content_type->key.len = 0; + continue; + } + + if (!(ho = ngx_http_add_header(&r->headers_out, ngx_http_headers_out))) + { + return NGX_ERROR; + } + + *ho = h[i]; + + /* + * ngx_http_header_filter() does not handle specially + * the following headers if they are set: + * r->headers_out.server, + * r->headers_out.date, + * r->headers_out.content_length + */ + + if (&h[i] == headers_in->server) { + r->headers_out.server = ho; + continue; + } + + if (&h[i] == headers_in->date) { + r->headers_out.date = ho; + continue; + } + + if (&h[i] == headers_in->content_length) { + r->headers_out.content_length = ho; + r->headers_out.content_length_n = ngx_atoi(ho->value.data, + ho->value.len); + continue; + } + } + + return NGX_OK; +} diff --git a/src/http/modules/proxy/ngx_http_proxy_parse.c b/src/http/modules/proxy/ngx_http_proxy_parse.c new file mode 100644 index 000000000..85c406ea7 --- /dev/null +++ b/src/http/modules/proxy/ngx_http_proxy_parse.c @@ -0,0 +1,204 @@ + +#include +#include +#include +#include + + +int ngx_http_proxy_parse_status_line(ngx_http_proxy_ctx_t *p) +{ + char ch; + char *pos; + enum { + sw_start = 0, + sw_H, + sw_HT, + sw_HTT, + sw_HTTP, + sw_first_major_digit, + sw_major_digit, + sw_first_minor_digit, + sw_minor_digit, + sw_status, + sw_space_after_status, + sw_status_text, + sw_almost_done, + sw_done + } state; + + state = p->state; + pos = p->header_in->pos; + + while (pos < p->header_in->last && state < sw_done) { + ch = *pos++; + + switch (state) { + + /* "HTTP/" */ + case sw_start: + switch (ch) { + case 'H': + state = sw_H; + break; + default: + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + break; + + case sw_H: + switch (ch) { + case 'T': + state = sw_HT; + break; + default: + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + break; + + case sw_HT: + switch (ch) { + case 'T': + state = sw_HTT; + break; + default: + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + break; + + case sw_HTT: + switch (ch) { + case 'P': + state = sw_HTTP; + break; + default: + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + break; + + case sw_HTTP: + switch (ch) { + case '/': + state = sw_first_major_digit; + break; + default: + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + break; + + /* the first digit of major HTTP version */ + case sw_first_major_digit: + if (ch < '1' || ch > '9') { + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + + state = sw_major_digit; + break; + + /* the major HTTP version or dot */ + case sw_major_digit: + if (ch == '.') { + state = sw_first_minor_digit; + break; + } + + if (ch < '0' || ch > '9') { + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + + break; + + /* the first digit of minor HTTP version */ + case sw_first_minor_digit: + if (ch < '0' || ch > '9') { + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + + state = sw_minor_digit; + break; + + /* the minor HTTP version or the end of the request line */ + case sw_minor_digit: + if (ch == ' ') { + state = sw_status; + break; + } + + if (ch < '0' || ch > '9') { + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + + break; + + /* HTTP status code */ + case sw_status: + if (ch < '0' || ch > '9') { + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + + p->status = p->status * 10 + ch - '0'; + + if (++p->status_count == 3) { + state = sw_space_after_status; + p->status_start = pos - 3; + } + + break; + + /* space or end of line */ + case sw_space_after_status: + switch (ch) { + case ' ': + state = sw_status_text; + break; + case CR: + state = sw_almost_done; + break; + case LF: + state = sw_done; + break; + default: + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + break; + + /* any text until end of line */ + case sw_status_text: + switch (ch) { + case CR: + state = sw_almost_done; + + break; + case LF: + state = sw_done; + break; + } + break; + + /* end of request line */ + case sw_almost_done: + p->status_end = pos - 2; + switch (ch) { + case LF: + state = sw_done; + break; + default: + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + break; + } + } + + p->header_in->pos = pos; + + if (state == sw_done) { + if (p->status_end == NULL) { + p->status_end = pos - 1; + } + + p->state = sw_start; + return NGX_OK; + } + + p->state = state; + return NGX_AGAIN; +} diff --git a/src/http/modules/proxy/ngx_http_proxy_upstream.c b/src/http/modules/proxy/ngx_http_proxy_upstream.c new file mode 100644 index 000000000..532e313d6 --- /dev/null +++ b/src/http/modules/proxy/ngx_http_proxy_upstream.c @@ -0,0 +1,43 @@ + +#include +#include +#include +#include +#include +#include +#include + + +void ngx_http_proxy_reinit_upstream(ngx_http_proxy_ctx_t *p) +{ + ngx_chain_t *cl; + ngx_output_chain_ctx_t *octx; + + octx = p->output_chain_ctx; + + /* reinit the request chain */ + + for (cl = p->request->request_hunks; cl; cl = cl->next) { + cl->hunk->pos = cl->hunk->start; + } + + /* reinit ngx_output_chain() context */ + + octx->hunk = NULL; + octx->in = NULL; + octx->free = NULL; + octx->busy = NULL; + + /* reinit r->header_in buffer */ + + if (p->header_in) { + if (p->cache) { + p->header_in->pos = p->header_in->start + p->cache->ctx.header.size; + p->header_in->last = p->header_in->pos; + + } else { + p->header_in->pos = p->header_in->start; + p->header_in->last = p->header_in->start; + } + } +} diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index 90a47f4a8..b964377bf 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/src/http/ngx_http_cache.c b/src/http/ngx_http_cache.c index e29241b57..261229361 100644 --- a/src/http/ngx_http_cache.c +++ b/src/http/ngx_http_cache.c @@ -1,4 +1,161 @@ +#include +#include +#include + +#include + + +int ngx_http_cache_get_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx) +{ + int small; + ssize_t n, len; + MD5_CTX md5; + ngx_err_t err; + ngx_str_t key; + ngx_http_bin_cache_t *h; + + ctx->file.name.len = ctx->path->name.len + 1 + ctx->path->len + 32; + if (!(ctx->file.name.data = ngx_palloc(r->pool, ctx->file.name.len + 1))) { + return NGX_ERROR; + } + + ngx_memcpy(ctx->file.name.data, ctx->path->name.data, ctx->path->name.len); + + MD5Init(&md5); + MD5Update(&md5, (u_char *) ctx->key.data, ctx->key.len); + MD5End(&md5, + ctx->file.name.data + ctx->path->name.len + 1 + ctx->path->len); + +ngx_log_debug(r->connection->log, "URL: %s, md5: %s" _ ctx->key.data _ + ctx->file.name.data + ctx->path->name.len + 1 + ctx->path->len); + + ngx_create_hashed_filename(&ctx->file, ctx->path); + +ngx_log_debug(r->connection->log, "FILE: %s" _ ctx->file.name.data); + + /* TODO: look open files cache */ + + ctx->file.fd = ngx_open_file(ctx->file.name.data, + NGX_FILE_RDONLY, NGX_FILE_OPEN); + + if (ctx->file.fd == NGX_INVALID_FILE) { + err = ngx_errno; + + if (err == NGX_ENOENT || err == NGX_ENOTDIR) { + + /* TODO: text size */ + + ctx->header.size = 2 * sizeof(ssize_t) + + sizeof(ngx_http_cache_header_t) + + ctx->key.len + 1; + + return NGX_DECLINED; + } + + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", ctx->file.name.data); + return NGX_ERROR; + } + + n = ngx_read_file(&ctx->file, ctx->buf->pos, + ctx->buf->end - ctx->buf->last, 0); + + if (n == NGX_ERROR || n == NGX_AGAIN) { + return n; + } + + len = 0; + small = 1; + + if (n > 1) { + if (ctx->buf->pos[0] == 'T') { + /* STUB */ + return NGX_ERROR; + + } else if (ctx->buf->pos[0] == 'B') { + + len = sizeof(ngx_http_bin_cache_t); + + if (n > len) { + h = (ngx_http_bin_cache_t *) ctx->buf->pos; + key.len = h->key_len; + + if (n >= len + (ssize_t) key.len + 1) { + ctx->header = h->header; + key.data = h->key; + + small = 0; + } + } + + } else { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "unknown type of cache file \"%s\"", + ctx->file.name.data); + return NGX_ERROR; + } + + } + + if (small) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "cache file \"%s\" is to small", ctx->file.name.data); + return NGX_ERROR; + } + + if (key.len != ctx->key.len + || ngx_strncmp(key.data, ctx->key.data, key.len) != 0) + { + key.data[key.len] = '\0'; + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "md5 collision: \"%s\" and \"%s\"", + key.data, ctx->key.data); + return NGX_DECLINED; + } + + ctx->header.size = len + key.len + 1; + ctx->buf->last += n; + + return NGX_OK; +} + + +int ngx_http_cache_update_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx, + ngx_str_t *temp_file) +{ + int retry; + ngx_err_t err; + + retry = 0; + + for ( ;; ) { + if (ngx_rename_file(temp_file->data, ctx->file.name.data) + != NGX_FILE_ERROR) + { + return NGX_OK; + } + + err = ngx_errno; + + if (retry || (err != NGX_ENOENT && err != NGX_ENOTDIR)) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_rename_file_n "(\"%s\", \"%s\") failed", + temp_file->data, ctx->file.name.data); + + return NGX_ERROR; + } + + if (ngx_create_path(&ctx->file, ctx->path) == NGX_ERROR) { + return NGX_ERROR; + } + + retry = 1; + } +} + + +#if 0 /* * small file in malloc()ed memory, mmap()ed file, file descriptor only, @@ -94,3 +251,5 @@ int ngx_crc(char *data, size_t len) return sum; } + +#endif diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h new file mode 100644 index 000000000..47dd582a4 --- /dev/null +++ b/src/http/ngx_http_cache.h @@ -0,0 +1,71 @@ +#ifndef _NGX_HTTP_CACHE_H_INCLUDED_ +#define _NGX_HTTP_CACHE_H_INCLUDED_ + + +#include +#include + + +typedef struct { + time_t expires; + time_t last_modified; + off_t length; + + size_t size; +} ngx_http_cache_header_t; + + +typedef struct { + ssize_t type; + ngx_http_cache_header_t header; + ssize_t key_len; + char key[0]; +} ngx_http_bin_cache_t; + + +typedef struct { + char type; + char space0; + char expires[8]; + char space1; + char last_modified[8]; + char space2; + char length[16]; + char space3; + char lf; + char key_len[0]; +} ngx_http_text_cache_t; + + +typedef struct { + u_int32_t crc; + ngx_str_t key; + ngx_fd_t fd; + off_t size; + void *data; /* mmap, memory */ + time_t accessed; + time_t last_modified; + time_t updated; /* no needed with kqueue */ + int refs; + int flags; +} ngx_http_cache_entry_t; + + +typedef struct { + ngx_file_t file; + ngx_str_t key; + ngx_path_t *path; + ngx_hunk_t *buf; + ngx_http_cache_header_t header; +} ngx_http_cache_ctx_t; + + +int ngx_http_cache_get_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx); +int ngx_http_cache_update_file(ngx_http_request_t *r,ngx_http_cache_ctx_t *ctx, + ngx_str_t *temp_file); + + + + + +#endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */ diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 7ef20aca5..6c50b6b32 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -376,10 +376,7 @@ ngx_log_debug(r->connection->log, "trans: %s" _ clcfp[i]->name.data); continue; } - rc = ngx_strncmp(r->uri.data, clcfp[i]->name.data, - clcfp[i]->name.len); - -ngx_log_debug(r->connection->log, "rc: %d" _ rc); + rc = ngx_strncmp(r->uri.data, clcfp[i]->name.data, clcfp[i]->name.len); if (rc < 0) { break; diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 5c7ffb726..7f05b40b5 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -197,6 +197,8 @@ struct ngx_http_request_s { unsigned header_timeout_set:1; unsigned proxy:1; + unsigned bypass_cache:1; + #if 0 unsigned cachable:1; #endif diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index eb82af05c..2fcdd5087 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -95,6 +95,8 @@ static void ngx_http_read_client_request_body_handler(ngx_event_t *rev) return; } + r->temp_file->offset += n; + r->request_body_hunk->pos = r->request_body_hunk->start; r->request_body_hunk->last = r->request_body_hunk->start; } diff --git a/src/os/unix/ngx_files.c b/src/os/unix/ngx_files.c index adafc17cf..f07ef5444 100644 --- a/src/os/unix/ngx_files.c +++ b/src/os/unix/ngx_files.c @@ -50,9 +50,10 @@ ssize_t ngx_write_file(ngx_file_t *file, char *buf, size_t size, off_t offset) } -ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce, +ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset, ngx_pool_t *pool) { + char *prev; size_t size; ssize_t n; struct iovec *iov; @@ -61,20 +62,39 @@ ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce, /* use pwrite() if there's the only hunk in a chain */ - if (ce->next == NULL) { - return ngx_write_file(file, ce->hunk->pos, - ce->hunk->last - ce->hunk->pos, offset); + if (cl->next == NULL) { + return ngx_write_file(file, cl->hunk->pos, + cl->hunk->last - cl->hunk->pos, offset); } - ngx_init_array(io, pool, 10, sizeof(struct iovec), NGX_ERROR); + prev = NULL; + iov = NULL; size = 0; - while (ce) { - ngx_test_null(iov, ngx_push_array(&io), NGX_ERROR); - iov->iov_base = ce->hunk->pos; - iov->iov_len = ce->hunk->last - ce->hunk->pos; - size += ce->hunk->last - ce->hunk->pos; - ce = ce->next; + ngx_init_array(io, pool, 10, sizeof(struct iovec), NGX_ERROR); + + /* create the iovec and coalesce the neighbouring hunks */ + + while (cl) { + if (prev == cl->hunk->pos) { + iov->iov_len += cl->hunk->last - cl->hunk->pos; + + } else { + ngx_test_null(iov, ngx_push_array(&io), NGX_ERROR); + iov->iov_base = cl->hunk->pos; + iov->iov_len = cl->hunk->last - cl->hunk->pos; + } + + size += cl->hunk->last - cl->hunk->pos; + prev = cl->hunk->last; + cl = cl->next; + } + + /* use pwrite() if there's the only iovec buffer */ + + if (io.nelts == 1) { + iov = io.elts; + return ngx_write_file(file, iov[0].iov_base, iov[0].iov_len, offset); } if (file->offset != offset) { @@ -84,7 +104,7 @@ ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce, } } - n = writev(file->fd, (struct iovec *) io.elts, io.nelts); + n = writev(file->fd, io.elts, io.nelts); if (n == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "writev() failed"); diff --git a/src/os/unix/ngx_files.h b/src/os/unix/ngx_files.h index e2af7222c..494ec55b1 100644 --- a/src/os/unix/ngx_files.h +++ b/src/os/unix/ngx_files.h @@ -45,6 +45,10 @@ ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce, off_t offset, ngx_pool_t *pool); +#define ngx_rename_file rename +#define ngx_rename_file_n "rename" + + #define ngx_mkdir(name) mkdir(name, 0700) #define ngx_mkdir_n "mkdir()" diff --git a/src/os/unix/ngx_freebsd_sendfile_chain.c b/src/os/unix/ngx_freebsd_sendfile_chain.c index c24149928..65dfc03f6 100644 --- a/src/os/unix/ngx_freebsd_sendfile_chain.c +++ b/src/os/unix/ngx_freebsd_sendfile_chain.c @@ -31,13 +31,28 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in) struct sf_hdtr hdtr; ngx_err_t err; ngx_array_t header, trailer; + ngx_event_t *wev; ngx_hunk_t *file; ngx_chain_t *cl, *tail; - if (!c->write->ready) { + wev = c->write; + + if (!wev->ready) { return in; } +#if (HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && wev->kq_eof) { + ngx_log_error(NGX_LOG_ERR, c->log, wev->kq_errno, + "kevent() reported about closed connection"); + + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + do { cl = in; file = NULL; @@ -181,7 +196,7 @@ ngx_log_debug(c->log, "NOPUSH"); "sendfile() sent only %qd bytes", sent); } else { - c->write->error = 1; + wev->error = 1; ngx_log_error(NGX_LOG_CRIT, c->log, err, "sendfile() failed"); return NGX_CHAIN_ERROR; @@ -194,7 +209,7 @@ ngx_log_debug(c->log, "NOPUSH"); #endif } else { - rc = writev(c->fd, (struct iovec *) header.elts, header.nelts); + rc = writev(c->fd, header.elts, header.nelts); if (rc == -1) { err = ngx_errno; @@ -206,7 +221,7 @@ ngx_log_debug(c->log, "NOPUSH"); ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EINTR"); } else { - c->write->error = 1; + wev->error = 1; ngx_log_error(NGX_LOG_CRIT, c->log, err, "writev() failed"); return NGX_CHAIN_ERROR; } @@ -268,7 +283,7 @@ ngx_log_debug(c->log, "NOPUSH"); * return EAGAIN right away and would not send anything */ - c->write->ready = 0; + wev->ready = 0; break; } @@ -277,7 +292,7 @@ ngx_log_debug(c->log, "NOPUSH"); } while ((tail && tail == in) || eintr); if (in) { - c->write->ready = 0; + wev->ready = 0; } return in; diff --git a/src/os/unix/ngx_writev_chain.c b/src/os/unix/ngx_writev_chain.c index 70141e341..6ead65f3a 100644 --- a/src/os/unix/ngx_writev_chain.c +++ b/src/os/unix/ngx_writev_chain.c @@ -11,34 +11,34 @@ ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in) off_t sent; struct iovec *iov; ngx_err_t err; - ngx_array_t iovecs; - ngx_chain_t *ce; + ngx_array_t io; + ngx_chain_t *cl; if (!c->write->ready) { return in; } - ngx_init_array(iovecs, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR); + ngx_init_array(io, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR); prev = NULL; iov = NULL; - /* create the iovec and coalesce the neighbouring chain entries */ - for (ce = in; ce; ce = ce->next) { + /* create the iovec and coalesce the neighbouring hunks */ + for (cl = in; cl; cl = cl->next) { - if (prev == ce->hunk->pos) { - iov->iov_len += ce->hunk->last - ce->hunk->pos; - prev = ce->hunk->last; + if (prev == cl->hunk->pos) { + iov->iov_len += cl->hunk->last - cl->hunk->pos; + prev = cl->hunk->last; } else { - ngx_test_null(iov, ngx_push_array(&iovecs), NGX_CHAIN_ERROR); - iov->iov_base = ce->hunk->pos; - iov->iov_len = ce->hunk->last - ce->hunk->pos; - prev = ce->hunk->last; + ngx_test_null(iov, ngx_push_array(&io), NGX_CHAIN_ERROR); + iov->iov_base = cl->hunk->pos; + iov->iov_len = cl->hunk->last - cl->hunk->pos; + prev = cl->hunk->last; } } - n = writev(c->fd, iovecs.elts, iovecs.nelts); + n = writev(c->fd, io.elts, io.nelts); if (n == -1) { err = ngx_errno; @@ -62,42 +62,40 @@ ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in) c->sent += sent; - for (ce = in; ce && sent > 0; ce = ce->next) { + for (cl = in; cl && sent > 0; cl = cl->next) { - size = ce->hunk->last - ce->hunk->pos; + size = cl->hunk->last - cl->hunk->pos; ngx_log_debug(c->log, "SIZE: %d" _ size); if (sent >= size) { sent -= size; - if (ce->hunk->type & NGX_HUNK_IN_MEMORY) { - ce->hunk->pos = ce->hunk->last; + if (cl->hunk->type & NGX_HUNK_IN_MEMORY) { + cl->hunk->pos = cl->hunk->last; } #if 0 - if (ce->hunk->type & NGX_HUNK_FILE) { - ce->hunk->file_pos = ce->hunk->file_last; + if (cl->hunk->type & NGX_HUNK_FILE) { + cl->hunk->file_pos = cl->hunk->file_last; } #endif continue; } - if (ce->hunk->type & NGX_HUNK_IN_MEMORY) { - ce->hunk->pos += sent; + if (cl->hunk->type & NGX_HUNK_IN_MEMORY) { + cl->hunk->pos += sent; } #if 0 - if (ce->hunk->type & NGX_HUNK_FILE) { - ce->hunk->file_pos += sent; + if (cl->hunk->type & NGX_HUNK_FILE) { + cl->hunk->file_pos += sent; } #endif break; } - ngx_destroy_array(&iovecs); - - return ce; + return cl; }