# HG changeset patch # User Igor Sysoev # Date 1068408218 0 # Node ID c0552e5ab5673b01e6018b78c9106ce9312c898c # Parent e92c2c647c57eb128a12135ba2a15900231ca2e9 nginx-0.0.1-2003-11-09-23:03:38 import; separate building diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c --- a/src/core/ngx_conf_file.c +++ b/src/core/ngx_conf_file.c @@ -168,7 +168,15 @@ ngx_log_debug(cf->log, "command '%s'" _ } else if (cmd->type & NGX_CONF_1MORE) { - if (cf->args->nelts != 1) { + if (cf->args->nelts > 1) { + valid = 1; + } else { + valid = 0; + } + + } else if (cmd->type & NGX_CONF_2MORE) { + + if (cf->args->nelts > 2) { valid = 1; } else { valid = 0; diff --git a/src/core/ngx_conf_file.h b/src/core/ngx_conf_file.h --- a/src/core/ngx_conf_file.h +++ b/src/core/ngx_conf_file.h @@ -23,14 +23,19 @@ #define NGX_CONF_TAKE8 0x00000100 #define NGX_CONF_TAKE9 0x00000200 +#define NGX_CONF_TAKE12 (NGX_CONF_TAKE1|NGX_CONF_TAKE2) + +#define NGX_CONF_TAKE13 (NGX_CONF_TAKE1|NGX_CONF_TAKE3) + #define NGX_CONF_TAKE1234 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3 \ |NGX_CONF_TAKE4) #define NGX_CONF_ARGS_NUMBER 0x0000ffff -#define NGX_CONF_ANY 0x00010000 -#define NGX_CONF_1MORE 0x00020000 -#define NGX_CONF_BLOCK 0x00040000 -#define NGX_CONF_FLAG 0x00080000 +#define NGX_CONF_BLOCK 0x00010000 +#define NGX_CONF_FLAG 0x00020000 +#define NGX_CONF_ANY 0x00040000 +#define NGX_CONF_1MORE 0x00080000 +#define NGX_CONF_2MORE 0x00100000 #define NGX_MAIN_CONF 0x01000000 diff --git a/src/core/ngx_modules.c b/src/core/ngx_modules.c --- a/src/core/ngx_modules.c +++ b/src/core/ngx_modules.c @@ -27,6 +27,7 @@ extern ngx_module_t ngx_aio_module; extern ngx_module_t ngx_http_module; extern ngx_module_t ngx_http_core_module; +extern ngx_module_t ngx_http_log_module; extern ngx_module_t ngx_http_write_filter_module; extern ngx_module_t ngx_http_output_filter_module; @@ -42,8 +43,6 @@ extern ngx_module_t ngx_http_static_mod extern ngx_module_t ngx_http_index_module; extern ngx_module_t ngx_http_proxy_module; -extern ngx_module_t ngx_http_log_module; - ngx_module_t *ngx_modules[] = { @@ -78,6 +77,7 @@ ngx_module_t *ngx_modules[] = { &ngx_http_module, &ngx_http_core_module, + &ngx_http_log_module, &ngx_http_write_filter_module, &ngx_http_output_filter_module, &ngx_http_header_filter_module, @@ -93,7 +93,5 @@ ngx_module_t *ngx_modules[] = { &ngx_http_index_module, &ngx_http_proxy_module, - &ngx_http_log_module, - NULL }; diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -71,13 +71,15 @@ int ngx_atoi(char *line, size_t n) void ngx_md5_text(char *text, u_char *md5) { - /* STUB */ + int i; + static char hex[] = "0123456789abcdef"; - ngx_snprintf(text, 33, - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - md5[0], md5[1], md5[2], md5[3], md5[4], md5[5], - md5[6], md5[7], md5[8], md5[9], md5[10], md5[11], - md5[12], md5[13], md5[14], md5[15]); + for (i = 0; i < 16; i++) { + *text++ = hex[md5[i] >> 4]; + *text++ = hex[md5[i] & 0xf]; + } + + *text = '\0'; } diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c --- a/src/event/ngx_event_pipe.c +++ b/src/event/ngx_event_pipe.c @@ -417,6 +417,7 @@ int ngx_event_pipe_write_to_downstream(n } /* TODO: free hunk if p->free_bufs && upstream done */ + /* add the free shadow raw hunk to p->free_raw_hunks */ if (cl->hunk->type & NGX_HUNK_LAST_SHADOW) { 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 @@ -158,22 +158,6 @@ 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)) { ngx_log_debug(r->connection->log, "HTTP DIR: '%s'" _ r->file.name.data); @@ -203,6 +187,22 @@ ngx_log_debug(r->connection->log, "HTTP return NGX_HTTP_MOVED_PERMANENTLY; } +#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 + r->content_handler = ngx_http_static_handler; return NGX_OK; diff --git a/src/http/modules/proxy/ngx_http_proxy_cache.c b/src/http/modules/proxy/ngx_http_proxy_cache.c --- a/src/http/modules/proxy/ngx_http_proxy_cache.c +++ b/src/http/modules/proxy/ngx_http_proxy_cache.c @@ -5,12 +5,14 @@ #include +static int ngx_http_proxy_process_cached_response(ngx_http_proxy_ctx_t *p, + int rc); static int ngx_http_proxy_process_cached_header(ngx_http_proxy_ctx_t *p); +static void ngx_http_proxy_cache_look_complete_request(ngx_http_proxy_ctx_t *p); 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; @@ -54,44 +56,69 @@ int ngx_http_proxy_get_cached_response(n c->ctx.buf = p->header_in; - rc = ngx_http_cache_get_file(r, &c->ctx); + return ngx_http_proxy_process_cached_response(p, + ngx_http_cache_get_file(r, &c->ctx)); +} - switch (rc) { - case NGX_HTTP_CACHE_STALE: - p->stale = 1; - p->state->cache = NGX_HTTP_PROXY_CACHE_EXPR; - break; - - case NGX_HTTP_CACHE_AGED: - p->stale = 1; - p->state->cache = NGX_HTTP_PROXY_CACHE_AGED; - break; - case NGX_OK: - p->state->cache = NGX_HTTP_PROXY_CACHE_HIT; - break; +static int ngx_http_proxy_process_cached_response(ngx_http_proxy_ctx_t *p, + int rc) +{ + if (rc == NGX_OK) { + p->state->cache_state = NGX_HTTP_PROXY_CACHE_HIT; + p->header_in->pos = p->header_in->start + p->cache->ctx.header_size; - default: - p->state->cache = NGX_HTTP_PROXY_CACHE_MISS; - } - - if (rc == NGX_OK - || rc == NGX_HTTP_CACHE_STALE - || rc == NGX_HTTP_CACHE_AGED) - { - p->header_in->pos = p->header_in->start + c->ctx.header_size; if (ngx_http_proxy_process_cached_header(p) == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - p->header_in->pos = p->header_in->start + c->ctx.header_size; + + p->valid_header_in = 1; + + return ngx_http_proxy_send_cached_response(p); + } + + if (rc == NGX_HTTP_CACHE_STALE) { + p->state->cache_state = NGX_HTTP_PROXY_CACHE_EXPR; + + } else if (rc == NGX_HTTP_CACHE_AGED) { + p->state->cache_state = NGX_HTTP_PROXY_CACHE_AGED; + } + + if (rc == NGX_HTTP_CACHE_STALE || rc == NGX_HTTP_CACHE_AGED) { + p->header_in->pos = p->header_in->start + p->cache->ctx.header_size; + + if (ngx_http_proxy_process_cached_header(p) == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + p->header_in->pos = p->header_in->start + p->cache->ctx.header_size; p->header_in->last = p->header_in->pos; + p->stale = 1; + p->valid_header_in = 1; + } else if (rc == NGX_DECLINED) { - p->header_in->pos = p->header_in->start + c->ctx.header_size; + p->state->cache_state = NGX_HTTP_PROXY_CACHE_MISS; + p->header_in->pos = p->header_in->start + p->cache->ctx.header_size; p->header_in->last = p->header_in->pos; } - return rc; + if (p->lcf->busy_lock) { + p->try_busy_lock = 1; + + p->header_in->pos = p->header_in->start; + p->header_in->last = p->header_in->start; + + p->busy_lock.time = 0; + p->busy_lock.event = p->request->connection->read; + p->busy_lock.event_handler = ngx_http_proxy_busy_lock_handler; + p->busy_lock.md5 = p->cache->ctx.md5; + + ngx_http_proxy_cache_busy_lock(p); + return NGX_DONE; + } + + return ngx_http_proxy_request_upstream(p); } @@ -141,6 +168,7 @@ static int ngx_http_proxy_process_cached ngx_log_debug(r->connection->log, "http cache status %d '%s'" _ c->status _ c->status_line.data); + /* TODO: ngx_init_table */ c->headers_in.headers = ngx_create_table(r->pool, 20); for ( ;; ) { @@ -216,29 +244,110 @@ static int ngx_http_proxy_process_cached } -#if 0 +void ngx_http_proxy_cache_busy_lock(ngx_http_proxy_ctx_t *p) +{ + int rc, ft_type; -static void ngx_http_proxy_cache_busy_lock(ngx_http_proxy_ctx_t *p) -{ - rc = ngx_http_busy_lock(p->lcf->busy_lock, p->cache->ctx.md5); + rc = ngx_http_busy_lock_cachable(p->lcf->busy_lock, &p->busy_lock, + p->try_busy_lock); if (rc == NGX_OK) { - ngx_http_proxy_request_upstream(p); + if (p->try_busy_lock) { + p->busy_locked = 1; + p->header_in->pos = p->header_in->start + p->cache->ctx.header_size; + p->header_in->last = p->header_in->pos; + + ngx_http_proxy_request_upstream(p); + return; + } + + ngx_http_proxy_cache_look_complete_request(p); + return; } + p->try_busy_lock = 0; + + if (p->cache->ctx.file.fd != NGX_INVALID_FILE + && !p->cache->ctx.file.info_valid) + { + if (ngx_stat_fd(p->cache->ctx.file.fd, &p->cache->ctx.file.info) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_CRIT, p->request->connection->log, ngx_errno, + ngx_stat_fd_n " \"%s\" failed", + p->cache->ctx.file.name.data); + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + p->cache->ctx.file.info_valid = 1; + } + + if (rc == NGX_AGAIN) { - if (p->busy_lock_time) { - ngx_add_timer(p->request->connection->read, 1000); - return; + return; + } + + if (rc == NGX_DONE) { + ft_type = NGX_HTTP_PROXY_FT_BUSY_LOCK; + + } else { + /* rc == NGX_ERROR */ + ft_type = NGX_HTTP_PROXY_FT_MAX_WAITING; + } + + if (p->stale && (p->lcf->use_stale & ft_type)) { + ngx_http_proxy_finalize_request(p, + ngx_http_proxy_send_cached_response(p)); + return; + } + + ngx_http_proxy_finalize_request(p, NGX_HTTP_SERVICE_UNAVAILABLE); +} + + +static void ngx_http_proxy_cache_look_complete_request(ngx_http_proxy_ctx_t *p) +{ + int rc; + ngx_http_cache_ctx_t *ctx; + + if (!(ctx = ngx_pcalloc(p->request->pool, sizeof(ngx_http_cache_ctx_t)))) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + *ctx = p->cache->ctx; + + rc = ngx_http_cache_open_file(p->request, ctx, + ngx_file_uniq(p->cache->ctx.file.info)); + + if (rc == NGX_HTTP_CACHE_THE_SAME) { + p->try_busy_lock = 1; + p->busy_lock.time = 0; + ngx_http_proxy_cache_busy_lock(p); + return; + } + +ngx_log_debug(p->request->connection->log, "OLD: %d, NEW: %d" _ + p->cache->ctx.file.fd _ ctx->file.fd); + + if (p->cache->ctx.file.fd != NGX_INVALID_FILE) { + if (ngx_close_file(p->cache->ctx.file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, p->request->connection->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + p->cache->ctx.file.name.data); } } - rc == NGX_ERROR - check waitn + p->cache->ctx = *ctx; + + p->status = 0; + p->status_count = 0; + + ngx_http_proxy_finalize_request(p, + ngx_http_proxy_process_cached_response(p, rc)); } -#endif - int ngx_http_proxy_send_cached_response(ngx_http_proxy_ctx_t *p) { @@ -435,8 +544,8 @@ int ngx_http_proxy_is_cachable(ngx_http_ /* FIXME: time_t == int_64_t, we can use fpu */ p->state->reason = NGX_HTTP_PROXY_CACHE_LMF; - p->cache->ctx.expires = ngx_time() - + (((int64_t) (date - last_modified)) * p->lcf->lm_factor) / 100; + p->cache->ctx.expires = (time_t) (ngx_time() + + (((int64_t) (date - last_modified)) * p->lcf->lm_factor) / 100); return 1; } @@ -460,6 +569,9 @@ int ngx_http_proxy_update_cache(ngx_http ep = p->upstream->event_pipe; +ngx_log_debug(p->request->connection->log, "LEN: " OFF_FMT ", " OFF_FMT _ + p->cache->ctx.length _ ep->read_length); + if (p->cache->ctx.length == -1) { /* TODO: test rc */ ngx_write_file(&ep->temp_file->file, 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 @@ -132,7 +132,7 @@ static ngx_command_t ngx_http_proxy_com { ngx_string("proxy_busy_lock"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE3, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, ngx_http_set_busy_lock_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_proxy_loc_conf_t, busy_lock), @@ -240,9 +240,31 @@ ngx_http_header_t ngx_http_proxy_headers }; +static ngx_str_t cache_states[] = { + ngx_string("PASS"), + ngx_string("BYPASS"), + ngx_string("AUTH"), + ngx_string("PGNC"), + ngx_string("MISS"), + ngx_string("EXPR"), + ngx_string("AGED"), + ngx_string("HIT") +}; + + +static ngx_str_t cache_reason[] = { + ngx_string("BPS"), + ngx_string("XAE"), + ngx_string("CTL"), + ngx_string("EXP"), + ngx_string("MVD"), + ngx_string("LMF"), + ngx_string("PDE") +}; + + 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, @@ -269,16 +291,16 @@ static int ngx_http_proxy_handler(ngx_ht if (!p->lcf->cache || (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD)) { - p->state->cache = NGX_HTTP_PROXY_CACHE_PASS; + p->state->cache_state = NGX_HTTP_PROXY_CACHE_PASS; } else if (r->bypass_cache) { - p->state->cache = NGX_HTTP_PROXY_CACHE_BYPASS; + p->state->cache_state = NGX_HTTP_PROXY_CACHE_BYPASS; } else if (r->headers_in.authorization) { - p->state->cache = NGX_HTTP_PROXY_CACHE_AUTH; + p->state->cache_state = NGX_HTTP_PROXY_CACHE_AUTH; } else if (r->no_cache) { - p->state->cache = NGX_HTTP_PROXY_CACHE_PGNC; + p->state->cache_state = NGX_HTTP_PROXY_CACHE_PGNC; p->cachable = 1; } else { @@ -286,25 +308,72 @@ static int ngx_http_proxy_handler(ngx_ht } - if (p->state->cache) { + if (p->state->cache_state != 0) { return ngx_http_proxy_request_upstream(p); } - rc = ngx_http_proxy_get_cached_response(p); + return ngx_http_proxy_get_cached_response(p); +} + + +void ngx_http_proxy_busy_lock_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_proxy_ctx_t *p; + + ngx_log_debug(rev->log, "busy lock"); - if (rc == NGX_DONE || rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { - return rc; + c = rev->data; + r = c->data; + p = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + p->action = "waiting upstream in busy lock"; + + if (rev->timedout) { + rev->timedout = 0; + p->busy_lock.time++; + p->state->bl_time = p->busy_lock.time; + if (p->state->cache_state < NGX_HTTP_PROXY_CACHE_MISS) { + ngx_http_proxy_upstream_busy_lock(p); + + } else { + ngx_http_proxy_cache_busy_lock(p); + } + + return; } - p->valid_header_in = 1; + ngx_log_debug(rev->log, "client sent while busy lock"); + + /* + * TODO: kevent() notify about error, otherwise we need to + * call ngx_peek(): recv(MGS_PEEK) to get errno. THINK about aio + * if there's no error we need to disable event. + */ + +#if (HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && rev->kq_eof) { + p->lcf->busy_lock->waiting--; - if (rc == NGX_OK) { - return ngx_http_proxy_send_cached_response(p); + ngx_del_timer(rev); + + ngx_log_error(NGX_LOG_ERR, c->log, rev->kq_errno, + "client() closed connection"); + + if (ngx_del_event(rev, NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + /* we have not HTTP code for the case when a client cancels a request */ + + ngx_http_proxy_finalize_request(p, 0); + return; } - /* rc == NGX_DECLINED || NGX_HTTP_CACHE_STALE || NGX_HTTP_CACHE_AGED */ +#endif - return ngx_http_proxy_request_upstream(p); } @@ -313,7 +382,7 @@ void ngx_http_proxy_finalize_request(ngx ngx_log_debug(p->request->connection->log, "finalize http proxy request"); - if (p->upstream->peer.connection) { + if (p->upstream && p->upstream->peer.connection) { ngx_http_proxy_close_connection(p); } @@ -323,8 +392,10 @@ void ngx_http_proxy_finalize_request(ngx rc = 0; } - p->request->connection->log->data = p->saved_ctx; - p->request->connection->log->handler = p->saved_handler; + if (p->saved_ctx) { + p->request->connection->log->data = p->saved_ctx; + p->request->connection->log->handler = p->saved_handler; + } ngx_http_finalize_request(p->request, rc); } @@ -338,7 +409,7 @@ void ngx_http_proxy_close_connection(ngx p->upstream->peer.connection = NULL; if (p->lcf->busy_lock) { - p->lcf->busy_lock->conn_n--; + p->lcf->busy_lock->busy--; } ngx_log_debug(c->log, "proxy close connection: %d" _ c->fd); @@ -382,6 +453,12 @@ void ngx_http_proxy_close_connection(ngx } +size_t ngx_http_proxy_log_state(void *data, char *buf, size_t len) +{ + return 0; +} + + size_t ngx_http_proxy_log_error(void *data, char *buf, size_t len) { ngx_http_proxy_ctx_t *p = data; @@ -513,21 +590,21 @@ static char *ngx_http_proxy_merge_loc_co conf->busy_lock = prev->busy_lock; } - if (conf->busy_lock && conf->cache && conf->busy_lock->busy == NULL) { + if (conf->busy_lock && conf->cache && conf->busy_lock->md5 == NULL) { - /* ngx_alloc_shared() */ - conf->busy_lock->busy_mask = - ngx_palloc(cf->pool, (conf->busy_lock->max_conn + 7) / 8); - if (conf->busy_lock->busy_mask == NULL) { + /* ngx_calloc_shared() */ + conf->busy_lock->md5_mask = + ngx_pcalloc(cf->pool, (conf->busy_lock->max_busy + 7) / 8); + if (conf->busy_lock->md5_mask == NULL) { return NGX_CONF_ERROR; } /* 16 bytes are 128 bits of the md5 */ /* ngx_alloc_shared() */ - conf->busy_lock->busy = ngx_palloc(cf->pool, - 16 * conf->busy_lock->max_conn); - if (conf->busy_lock->busy == NULL) { + conf->busy_lock->md5 = ngx_palloc(cf->pool, + 16 * conf->busy_lock->max_busy); + if (conf->busy_lock->md5 == NULL) { return NGX_CONF_ERROR; } } 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 @@ -80,10 +80,24 @@ typedef struct { } ngx_http_proxy_loc_conf_t; +/* + * "EXPR/10/5/- 200/EXP/60 4" + * "MISS/-/-/B 503/-/- -" + * "EXPR/10/20/SB HIT/-/- -" + * "EXPR/10/15/NB HIT/-/- -" + */ + typedef struct { - ngx_http_proxy_state_e cache; + ngx_http_proxy_state_e cache_state; + time_t expired; + time_t bl_time; + int bl_state; + + int status; ngx_http_proxy_reason_e reason; - int status; + time_t time; + time_t expires; + ngx_str_t *peer; } ngx_http_proxy_state_t; @@ -140,12 +154,14 @@ struct ngx_http_proxy_ctx_s { ngx_hunk_t *header_in; - time_t busy_lock_time; + ngx_http_busy_lock_ctx_t busy_lock; unsigned accel:1; unsigned cachable:1; unsigned stale:1; + unsigned try_busy_lock:1; + unsigned busy_locked:1; unsigned valid_header_in:1; unsigned request_sent:1; @@ -187,6 +203,10 @@ int ngx_http_proxy_send_cached_response( int ngx_http_proxy_is_cachable(ngx_http_proxy_ctx_t *p); int ngx_http_proxy_update_cache(ngx_http_proxy_ctx_t *p); +void ngx_http_proxy_busy_lock_handler(ngx_event_t *rev); +void ngx_http_proxy_cache_busy_lock(ngx_http_proxy_ctx_t *p); +void ngx_http_proxy_upstream_busy_lock(ngx_http_proxy_ctx_t *p); + size_t ngx_http_proxy_log_error(void *data, char *buf, size_t len); void ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int rc); void ngx_http_proxy_close_connection(ngx_http_proxy_ctx_t *p); diff --git a/src/http/modules/proxy/ngx_http_proxy_upstream.c b/src/http/modules/proxy/ngx_http_proxy_upstream.c --- a/src/http/modules/proxy/ngx_http_proxy_upstream.c +++ b/src/http/modules/proxy/ngx_http_proxy_upstream.c @@ -11,8 +11,6 @@ static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p); static void ngx_http_proxy_init_upstream(void *data); static void ngx_http_proxy_reinit_upstream(ngx_http_proxy_ctx_t *p); -static void ngx_http_proxy_upstream_busy_lock(ngx_http_proxy_ctx_t *p); -static void ngx_http_proxy_upstream_busy_lock_handler(ngx_event_t *rev); 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_send_request_handler(ngx_event_t *wev); @@ -266,7 +264,7 @@ ngx_log_debug(r->connection->log, "timer wctx->pool = r->pool; - if (p->lcf->busy_lock) { + if (p->lcf->busy_lock && !p->busy_locked) { ngx_http_proxy_upstream_busy_lock(p); } else { ngx_http_proxy_connect(p); @@ -312,41 +310,31 @@ static void ngx_http_proxy_reinit_upstre } -static void ngx_http_proxy_upstream_busy_lock(ngx_http_proxy_ctx_t *p) +void ngx_http_proxy_upstream_busy_lock(ngx_http_proxy_ctx_t *p) { - int ft_type; + int rc, ft_type; - if (p->lcf->busy_lock->conn_n < p->lcf->busy_lock->max_conn) { - p->lcf->busy_lock->conn_n++; + if (p->busy_lock.time == 0) { + p->busy_lock.event = p->request->connection->read; + p->busy_lock.event_handler = ngx_http_proxy_busy_lock_handler; + } - if (p->busy_lock_time) { - p->busy_lock_time = 0; - p->lcf->busy_lock->waiting_n--; - } + rc = ngx_http_busy_lock(p->lcf->busy_lock, &p->busy_lock); + if (rc == NGX_AGAIN) { + return; + } + + if (rc == NGX_OK) { ngx_http_proxy_connect(p); return; } - if (p->busy_lock_time) { - if (p->busy_lock_time < p->lcf->busy_lock->timeout) { - ngx_add_timer(p->request->connection->read, 1000); - return; - } - - p->lcf->busy_lock->waiting_n--; + if (rc == NGX_DONE) { ft_type = NGX_HTTP_PROXY_FT_BUSY_LOCK; } else { - if (p->lcf->busy_lock->waiting_n < p->lcf->busy_lock->max_waiting) { - p->lcf->busy_lock->waiting_n++; - ngx_add_timer(p->request->connection->read, 1000); - p->request->connection->read->event_handler = - ngx_http_proxy_upstream_busy_lock_handler; - /* TODO: ngx_handle_level_read_event() */ - return; - } - + /* rc == NGX_ERROR */ ft_type = NGX_HTTP_PROXY_FT_MAX_WAITING; } @@ -357,61 +345,6 @@ static void ngx_http_proxy_upstream_busy } ngx_http_proxy_finalize_request(p, NGX_HTTP_SERVICE_UNAVAILABLE); - return; -} - - -static void ngx_http_proxy_upstream_busy_lock_handler(ngx_event_t *rev) -{ - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_proxy_ctx_t *p; - - ngx_log_debug(rev->log, "busy lock"); - - c = rev->data; - r = c->data; - p = ngx_http_get_module_ctx(r, ngx_http_proxy_module); - p->action = "waiting upstream in busy lock"; - - if (rev->timedout) { - rev->timedout = 0; - p->busy_lock_time++; - ngx_http_proxy_upstream_busy_lock(p); - return; - } - - ngx_log_debug(rev->log, "client sent while busy lock"); - - /* - * TODO: kevent() notify about error, otherwise we need to - * call ngx_peek(): recv(MGS_PEEK) to get errno. THINK about aio - * if there's no error we need to disable event. - */ - -#if (HAVE_KQUEUE) - - if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && rev->kq_eof) { - p->lcf->busy_lock->waiting_n--; - - ngx_del_timer(rev); - - ngx_log_error(NGX_LOG_ERR, c->log, rev->kq_errno, - "client() closed connection"); - - if (ngx_del_event(rev, NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR) { - ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - /* we have not HTTP code for the case when a client cancels a request */ - - ngx_http_proxy_finalize_request(p, 0); - return; - } - -#endif - } @@ -724,6 +657,16 @@ static void ngx_http_proxy_process_upstr } } + if (p->status == NGX_HTTP_NOT_FOUND + && p->upstream->peer.tries > 1 + && p->lcf->next_upstream & NGX_HTTP_PROXY_FT_HTTP_404) + { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_404); + return; + } + + /* TODO: "proxy_error_page" */ + p->upstream->status_line.len = p->status_end - p->status_start; p->upstream->status_line.data = ngx_palloc(p->request->pool, p->upstream->status_line.len + 1); @@ -740,6 +683,7 @@ static void ngx_http_proxy_process_upstr if (p->upstream->headers_in.headers) { p->upstream->headers_in.headers->nelts = 0; } else { + /* TODO: ngx_init_table */ p->upstream->headers_in.headers = ngx_create_table(p->request->pool, 20); } @@ -956,8 +900,8 @@ static void ngx_http_proxy_send_response header->expires = p->cache->ctx.expires; header->last_modified = p->cache->ctx.last_modified; header->date = p->cache->ctx.date; - /* TODO: r->headers_out.content_length_n == -1 */ header->length = r->headers_out.content_length_n; + p->cache->ctx.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); @@ -1105,18 +1049,24 @@ static void ngx_http_proxy_process_body( if (p->upstream->peer.connection) { if (ep->upstream_done && p->cachable) { if (ngx_http_proxy_update_cache(p) == NGX_ERROR) { + ngx_http_busy_unlock_cachable(p->lcf->busy_lock, &p->busy_lock); ngx_http_proxy_finalize_request(p, 0); return; } + ngx_http_busy_unlock_cachable(p->lcf->busy_lock, &p->busy_lock); + } else if (ep->upstream_eof && p->cachable) { /* TODO: check length & update cache */ if (ngx_http_proxy_update_cache(p) == NGX_ERROR) { + ngx_http_busy_unlock_cachable(p->lcf->busy_lock, &p->busy_lock); ngx_http_proxy_finalize_request(p, 0); return; } + + ngx_http_busy_unlock_cachable(p->lcf->busy_lock, &p->busy_lock); } if (ep->upstream_done || ep->upstream_eof || ep->upstream_error) { @@ -1206,7 +1156,7 @@ ngx_log_debug(p->request->connection->lo } } - if (p->lcf->busy_lock) { + if (p->lcf->busy_lock && !p->busy_locked) { ngx_http_proxy_upstream_busy_lock(p); } else { ngx_http_proxy_connect(p); diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -123,7 +123,6 @@ static char *ngx_http_block(ngx_conf_t * } } - /* parse inside the http{} block */ pcf = *cf; @@ -131,14 +130,16 @@ static char *ngx_http_block(ngx_conf_t * cf->module_type = NGX_HTTP_MODULE; cf->cmd_type = NGX_HTTP_MAIN_CONF; rv = ngx_conf_parse(cf, NULL); - *cf = pcf; - if (rv != NGX_CONF_OK) + if (rv != NGX_CONF_OK) { + *cf = pcf; return rv; - + } - /* init http{} main_conf's, merge the server{}s' srv_conf's - and its location{}s' loc_conf's */ + /* + * init http{} main_conf's, merge the server{}s' srv_conf's + * and its location{}s' loc_conf's + */ cmcf = ctx->main_conf[ngx_http_core_module.ctx_index]; cscfp = cmcf->servers.elts; @@ -556,5 +557,6 @@ ngx_log_debug(cf->log, "%s %08x" _ s_nam } /**/ + *cf = pcf; return NGX_CONF_OK; } 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 @@ -9,6 +9,7 @@ #include #include #include +#include #include diff --git a/src/http/ngx_http_busy_lock.c b/src/http/ngx_http_busy_lock.c --- a/src/http/ngx_http_busy_lock.c +++ b/src/http/ngx_http_busy_lock.c @@ -4,42 +4,151 @@ #include -int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, u_char *md5) + +static int ngx_http_busy_lock_look_cachable(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc, + int lock); + + +int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc) +{ + if (bl->busy < bl->max_busy) { + bl->busy++; + + if (bc->time) { + bc->time = 0; + bl->waiting--; + } + + return NGX_OK; + } + + if (bc->time) { + if (bc->time < bl->timeout) { + ngx_add_timer(bc->event, 1000); + return NGX_AGAIN; + } + + bl->waiting--; + return NGX_DONE; + + } + + if (bl->timeout == 0) { + return NGX_DONE; + } + + if (bl->waiting < bl->max_waiting) { + bl->waiting++; + ngx_add_timer(bc->event, 1000); + bc->event->event_handler = bc->event_handler; + + /* TODO: ngx_handle_level_read_event() */ + + return NGX_AGAIN; + } + + return NGX_ERROR; +} + + +int ngx_http_busy_lock_cachable(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc, int lock) { - int i, b, busy, free; + int rc; + + rc = ngx_http_busy_lock_look_cachable(bl, bc, lock); + +ngx_log_debug(bc->event->log, "BUSYLOCK: %d" _ rc); + + if (rc == NGX_OK) { /* no the same request, there's free slot */ + return NGX_OK; + } + + if (rc == NGX_ERROR && !lock) { /* no the same request, no free slot */ + return NGX_OK; + } + + /* rc == NGX_AGAIN: the same request */ + + if (bc->time) { + if (bc->time < bl->timeout) { + ngx_add_timer(bc->event, 1000); + return NGX_AGAIN; + } + + bl->waiting--; + return NGX_DONE; + + } + + if (bl->timeout == 0) { + return NGX_DONE; + } + + if (bl->waiting < bl->max_waiting) { + bl->waiting++; + ngx_add_timer(bc->event, 1000); + bc->event->event_handler = bc->event_handler; + + /* TODO: ngx_handle_level_read_event() */ + + return NGX_AGAIN; + } + + return NGX_ERROR; +} + + +void ngx_http_busy_unlock_cachable(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc) +{ + bl->md5_mask[bc->slot / 8] &= ~(1 << (bc->slot & 7)); + bl->cachable--; + bl->busy--; +} + + +static int ngx_http_busy_lock_look_cachable(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc, + int lock) +{ + int i, b, cachable, free; u_int mask; b = 0; - busy = 0; + cachable = 0; free = -1; #if (NGX_SUPPRESS_WARN) mask = 0; #endif - for (i = 0; i < bl->max_conn; i++) { + for (i = 0; i < bl->max_busy; i++) { if ((b & 7) == 0) { - mask = bl->busy_mask[i / 8]; + mask = bl->md5_mask[i / 8]; } if (mask & 1) { - if (ngx_memcmp(&bl->busy[i * 16], md5, 16) == 0) { + if (ngx_memcmp(&bl->md5[i * 16], bc->md5, 16) == 0) { return NGX_AGAIN; } - busy++; + cachable++; } else if (free == -1) { free = i; } - if (busy == bl->busy_n) { - if (busy < bl->max_conn) { +#if 1 + if (cachable == bl->cachable) { + if (free == -1 && cachable < bl->max_busy) { free = i + 1; } break; } +#endif mask >>= 1; b++; @@ -49,11 +158,18 @@ int ngx_http_busy_lock(ngx_http_busy_loc return NGX_ERROR; } - ngx_memcpy(&bl->busy[free * 16], md5, 16); - bl->busy_mask[free / 8] |= free % 8; + if (lock) { + if (bl->busy == bl->max_busy) { + return NGX_ERROR; + } - bl->busy_n++; - bl->conn_n++; + ngx_memcpy(&bl->md5[free * 16], bc->md5, 16); + bl->md5_mask[free / 8] |= 1 << (free & 7); + bc->slot = free; + + bl->cachable++; + bl->busy++; + } return NGX_OK; } @@ -64,8 +180,8 @@ char *ngx_http_set_busy_lock_slot(ngx_co { char *p = conf; - int i; - ngx_str_t *value; + int i, dup, invalid; + ngx_str_t *value, line; ngx_http_busy_lock_t *bl, **blp; blp = (ngx_http_busy_lock_t **) (p + cmd->offset); @@ -79,57 +195,86 @@ char *ngx_http_set_busy_lock_slot(ngx_co } *blp = bl; + dup = 0; + invalid = 0; value = (ngx_str_t *) cf->args->elts; - for (i = 1; i < 4; i++) { + for (i = 1; i < cf->args->nelts; i++) { - if (value[i].len > 2 && ngx_strncasecmp(value[i].data, "c:", 2) == 0) { - if (bl->max_conn) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "duplicate \"%s\"", value[i].data); - return NGX_CONF_ERROR; + if (value[i].data[1] != '=') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[i].data); + return NGX_CONF_ERROR; + } + + switch (value[i].data[0]) { + + case 'b': + if (bl->max_busy) { + dup = 1; + break; } - bl->max_conn = ngx_atoi(value[i].data + 2, value[i].len - 2); - if (bl->max_conn == NGX_ERROR) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid value \"%s\"", value[i].data); - return NGX_CONF_ERROR; + bl->max_busy = ngx_atoi(value[i].data + 2, value[i].len - 2); + if (bl->max_busy == NGX_ERROR) { + invalid = 1; + break; } continue; - } - if (value[i].len > 2 && ngx_strncasecmp(value[i].data, "w:", 2) == 0) { + case 'w': if (bl->max_waiting) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "duplicate \"%s\"", value[i].data); - return NGX_CONF_ERROR; + dup = 1; + break; } bl->max_waiting = ngx_atoi(value[i].data + 2, value[i].len - 2); if (bl->max_waiting == NGX_ERROR) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid value \"%s\"", value[i].data); - return NGX_CONF_ERROR; + invalid = 1; + break; } continue; + + case 't': + if (bl->timeout) { + dup = 1; + break; + } + + line.len = value[i].len - 2; + line.data = value[i].data + 2; + + bl->timeout = ngx_parse_time(&line, 1); + if (bl->timeout == NGX_ERROR) { + invalid = 1; + break; + } + + continue; + + default: + invalid = 1; } - if (bl->timeout) { + if (dup) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "duplicate timeout \"%s\"", value[i].data); + "duplicate value \"%s\"", value[i].data); return NGX_CONF_ERROR; } - bl->timeout = ngx_parse_time(&value[1], 1); - if (bl->timeout == NGX_ERROR) { + if (invalid) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid timeout \"%s\"", value[i].data); + "invalid value \"%s\"", value[i].data); return NGX_CONF_ERROR; } } + if (bl->timeout == 0 && bl->max_waiting) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "busy lock waiting is useless with zero timeout"); + } + return NGX_CONF_OK; } diff --git a/src/http/ngx_http_busy_lock.h b/src/http/ngx_http_busy_lock.h --- a/src/http/ngx_http_busy_lock.h +++ b/src/http/ngx_http_busy_lock.h @@ -4,19 +4,20 @@ #include #include +#include #include typedef struct { - u_char *busy_mask; - char *busy; - int busy_n; + u_char *md5_mask; + char *md5; + int cachable; - int waiting_n; - int max_waiting; + int busy; + int max_busy; - int conn_n; - int max_conn; + int waiting; + int max_waiting; time_t timeout; @@ -25,7 +26,21 @@ typedef struct { } ngx_http_busy_lock_t; -int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, u_char *md5); +typedef struct { + time_t time; + ngx_event_t *event; + void (*event_handler)(ngx_event_t *ev); + u_char *md5; + int slot; +} ngx_http_busy_lock_ctx_t; + + +int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc); +int ngx_http_busy_lock_cachable(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc, int lock); +void ngx_http_busy_unlock_cachable(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc); + char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); diff --git a/src/http/ngx_http_cache.c b/src/http/ngx_http_cache.c --- a/src/http/ngx_http_cache.c +++ b/src/http/ngx_http_cache.c @@ -8,10 +8,7 @@ int ngx_http_cache_get_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx) { - ssize_t n; - MD5_CTX md5; - ngx_err_t err; - ngx_http_cache_header_t *h; + MD5_CTX md5; ctx->header_size = sizeof(ngx_http_cache_header_t) + ctx->key.len + 1; @@ -38,6 +35,19 @@ ngx_log_debug(r->connection->log, "FILE: /* TODO: look open files cache */ + return ngx_http_cache_open_file(r, ctx, 0); +} + + +/* TODO: Win32 inode analogy */ + +int ngx_http_cache_open_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx, + ngx_file_uniq_t uniq) +{ + ssize_t n; + ngx_err_t err; + ngx_http_cache_header_t *h; + ctx->file.fd = ngx_open_file(ctx->file.name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN); @@ -53,6 +63,25 @@ ngx_log_debug(r->connection->log, "FILE: return NGX_ERROR; } + if (uniq) { + if (ngx_stat_fd(ctx->file.fd, &ctx->file.info) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_stat_fd_n " \"%s\" failed", ctx->file.name.data); + + return NGX_ERROR; + } + + if (ngx_file_uniq(ctx->file.info) == uniq) { + if (ngx_close_file(ctx->file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + ctx->file.name.data); + } + + return NGX_HTTP_CACHE_THE_SAME; + } + } + n = ngx_read_file(&ctx->file, ctx->buf->pos, ctx->buf->end - ctx->buf->last, 0); diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -13,7 +13,7 @@ typedef struct { time_t date; off_t length; size_t key_len; - char key[0]; + char key[1]; } ngx_http_cache_header_t; @@ -46,11 +46,14 @@ typedef struct { } ngx_http_cache_ctx_t; -#define NGX_HTTP_CACHE_STALE 1 -#define NGX_HTTP_CACHE_AGED 2 +#define NGX_HTTP_CACHE_STALE 1 +#define NGX_HTTP_CACHE_AGED 2 +#define NGX_HTTP_CACHE_THE_SAME 3 int ngx_http_cache_get_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx); +int ngx_http_cache_open_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx, + ngx_file_uniq_t uniq); int ngx_http_cache_update_file(ngx_http_request_t *r,ngx_http_cache_ctx_t *ctx, ngx_str_t *temp_file); diff --git a/src/http/ngx_http_config.h b/src/http/ngx_http_config.h --- a/src/http/ngx_http_config.h +++ b/src/http/ngx_http_config.h @@ -47,6 +47,8 @@ typedef struct { #define ngx_http_get_module_srv_conf(r, module) r->srv_conf[module.ctx_index] #define ngx_http_get_module_loc_conf(r, module) r->loc_conf[module.ctx_index] +#define ngx_http_conf_module_main_conf(cf, module) \ + ((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index] extern int (*ngx_http_top_header_filter) (ngx_http_request_t *r); 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 @@ -31,6 +31,7 @@ static char *ngx_set_type(ngx_conf_t *cf static char *ngx_set_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_set_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_set_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_lowat_check(ngx_conf_t *cf, void *post, void *data); @@ -192,6 +193,13 @@ static ngx_command_t ngx_http_core_comm offsetof(ngx_http_core_loc_conf_t, msie_padding), NULL}, + {ngx_string("error_page"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE, + ngx_set_error_page, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL}, + {ngx_string("error_log"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_set_error_log, @@ -631,8 +639,8 @@ static char *ngx_location_block(ngx_conf char *rv; ngx_str_t *location; ngx_http_module_t *module; - ngx_conf_t pcf; - ngx_http_conf_ctx_t *ctx, *pctx; + ngx_conf_t pvcf; + ngx_http_conf_ctx_t *ctx, *pvctx; ngx_http_core_srv_conf_t *cscf; ngx_http_core_loc_conf_t *clcf, **clcfp; @@ -640,9 +648,9 @@ static char *ngx_location_block(ngx_conf ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)), NGX_CONF_ERROR); - pctx = (ngx_http_conf_ctx_t *) cf->ctx; - ctx->main_conf = pctx->main_conf; - ctx->srv_conf = pctx->srv_conf; + pvctx = (ngx_http_conf_ctx_t *) cf->ctx; + ctx->main_conf = pvctx->main_conf; + ctx->srv_conf = pvctx->srv_conf; ngx_test_null(ctx->loc_conf, ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module), @@ -672,11 +680,11 @@ static char *ngx_location_block(ngx_conf ngx_test_null(clcfp, ngx_push_array(&cscf->locations), NGX_CONF_ERROR); *clcfp = clcf; - pcf = *cf; + pvcf = *cf; cf->ctx = ctx; cf->cmd_type = NGX_HTTP_LOC_CONF; rv = ngx_conf_parse(cf, NULL); - *cf = pcf; + *cf = pvcf; return rv; } @@ -856,6 +864,7 @@ static void *ngx_http_core_create_loc_co lcf->default_type.len = 0; lcf->default_type.data = NULL; lcf->err_log = NULL; + lcf->error_pages = NULL; */ @@ -929,6 +938,10 @@ static char *ngx_http_core_merge_loc_con } } + if (conf->error_pages == NULL && prev->error_pages) { + conf->error_pages = prev->error_pages; + } + ngx_conf_merge_str_value(conf->default_type, prev->default_type, "text/plain"); @@ -1061,6 +1074,47 @@ static char *ngx_set_server_name(ngx_con } +static char *ngx_set_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *lcf = conf; + + int i; + ngx_str_t *value; + ngx_http_err_page_t *err; + + if (lcf->error_pages == NULL) { + lcf->error_pages = ngx_create_array(cf->pool, 5, + sizeof(ngx_http_err_page_t)); + if (lcf->error_pages == NULL) { + return NGX_CONF_ERROR; + } + } + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts - 1; i++) { + ngx_test_null(err, ngx_push_array(lcf->error_pages), NGX_CONF_ERROR); + err->code = ngx_atoi(value[i].data, value[i].len); + if (err->code == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[i].data); + return NGX_CONF_ERROR; + } + + if (err->code < 400 || err->code > 599) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "value \"%s\" must be between 400 and 599", + value[i].data); + return NGX_CONF_ERROR; + } + + err->uri = value[cf->args->nelts - 1]; + } + + return NGX_CONF_OK; +} + + static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *lcf = conf; @@ -1079,9 +1133,9 @@ static char *ngx_set_error_log(ngx_conf_ static char *ngx_http_lowat_check(ngx_conf_t *cf, void *post, void *data) { - int *np = data; +#if (HAVE_LOWAT_EVENT) -#if (HAVE_LOWAT_EVENT) + int *np = data; if (*np >= ngx_freebsd_net_inet_tcp_sendspace) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -100,6 +100,12 @@ typedef struct { typedef struct { + int code; + ngx_str_t uri; +} ngx_http_err_page_t; + + +typedef struct { ngx_str_t name; /* location name */ void **loc_conf ; /* pointer to the modules' loc_conf */ @@ -120,6 +126,7 @@ typedef struct { ngx_msec_t lingering_timeout; /* lingering_timeout */ int msie_padding; /* msie_padding */ + ngx_array_t *error_pages; /* error_page */ ngx_log_t *err_log; } ngx_http_core_loc_conf_t; diff --git a/src/http/ngx_http_header_filter.c b/src/http/ngx_http_header_filter.c --- a/src/http/ngx_http_header_filter.c +++ b/src/http/ngx_http_header_filter.c @@ -90,8 +90,9 @@ static ngx_str_t http_codes[] = { static int ngx_http_header_filter(ngx_http_request_t *r) { int len, status, i; + char *p; ngx_hunk_t *h; - ngx_chain_t *ch; + ngx_chain_t *ln; ngx_table_elt_t *header; if (r->http_version < NGX_HTTP_VERSION_10) { @@ -149,7 +150,7 @@ static int ngx_http_header_filter(ngx_ht len += r->headers_out.date->key.len + r->headers_out.date->value.len + 2; } else { - len += sizeof("Date: Mon, 28 Sep 1970 00:00:00 GMT" CRLF) - 1; + len += sizeof("Date: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; } if (r->headers_out.content_length == NULL) { @@ -174,7 +175,7 @@ static int ngx_http_header_filter(ngx_ht && r->headers_out.location->value.data[0] == '/') { r->headers_out.location->key.len = 0; - len += sizeof("Location: http://") - 1, + len += sizeof("Location: http://") - 1 + r->server_name->len + r->headers_out.location->value.len + 2; if (r->port != 80) { @@ -187,7 +188,7 @@ static int ngx_http_header_filter(ngx_ht + r->headers_out.last_modified->value.len + 2; } else if (r->headers_out.last_modified_time != -1) { - len += sizeof("Last-Modified: Mon, 28 Sep 1970 00:00:00 GMT" CRLF) - 1; + len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; } if (r->chunked) { @@ -210,7 +211,9 @@ static int ngx_http_header_filter(ngx_ht len += header[i].key.len + 2 + header[i].value.len + 2; } - ngx_test_null(h, ngx_create_temp_hunk(r->pool, len), NGX_ERROR); + if (!(h = ngx_create_temp_hunk(r->pool, len))) { + return NGX_ERROR; + } /* "HTTP/1.x " */ h->last = ngx_cpymem(h->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1); @@ -232,30 +235,70 @@ static int ngx_http_header_filter(ngx_ht if (!(r->headers_out.date && r->headers_out.date->key.len)) { h->last = ngx_cpymem(h->last, "Date: ", sizeof("Date: ") - 1); - h->last += ngx_http_get_time(h->last, time(NULL)); +#if (NGX_HTTP_LOG_ALL_HEADERS_OUT) + p = h->last; +#endif + h->last += ngx_http_get_time(h->last, ngx_time()); + +#if (NGX_HTTP_LOG_ALL_HEADERS_OUT) + r->headers_out.date = ngx_palloc(r->pool, sizeof(ngx_table_elt_t)); + if (r->headers_out.date == NULL) { + return NGX_ERROR; + } + + r->headers_out.date->key.len = 0; + r->headers_out.date->key.data = NULL; + r->headers_out.date->value.len = h->last - p; + r->headers_out.date->value.data = p; +#endif + *(h->last++) = CR; *(h->last++) = LF; } if (r->headers_out.content_length == NULL) { if (r->headers_out.content_length_n >= 0) { +#if (NGX_HTTP_LOG_ALL_HEADERS_OUT) + p = h->last + sizeof("Content-Length: ") - 1; +#endif h->last += ngx_snprintf(h->last, /* 2^64 */ sizeof("Content-Length: 18446744073709551616" CRLF), "Content-Length: " OFF_FMT CRLF, r->headers_out.content_length_n); + +#if (NGX_HTTP_LOG_ALL_HEADERS_OUT) + r->headers_out.content_length = ngx_palloc(r->pool, + sizeof(ngx_table_elt_t)); + if (r->headers_out.content_length == NULL) { + return NGX_ERROR; + } + + r->headers_out.content_length->key.len = 0; + r->headers_out.content_length->key.data = NULL; + r->headers_out.content_length->value.len = h->last - p - 2; + r->headers_out.content_length->value.data = p; +#endif } } if (r->headers_out.content_type && r->headers_out.content_type->value.len) { h->last = ngx_cpymem(h->last, "Content-Type: ", sizeof("Content-Type: ") - 1); +#if (NGX_HTTP_LOG_ALL_HEADERS_OUT) + p = h->last; +#endif h->last = ngx_cpymem(h->last, r->headers_out.content_type->value.data, r->headers_out.content_type->value.len); +#if (NGX_HTTP_LOG_ALL_HEADERS_OUT) if (r->headers_out.charset.len) { h->last = ngx_cpymem(h->last, "; charset=", sizeof("; charset=") - 1); h->last = ngx_cpymem(h->last, r->headers_out.charset.data, r->headers_out.charset.len); + + r->headers_out.content_type->value.len = h->last - p; + r->headers_out.content_type->value.data = p; +#endif } *(h->last++) = CR; *(h->last++) = LF; @@ -265,6 +308,7 @@ static int ngx_http_header_filter(ngx_ht && r->headers_out.location->value.len && r->headers_out.location->value.data[0] == '/') { + p = h->last + sizeof("Location: ") - 1; h->last = ngx_cpymem(h->last, "Location: http://", sizeof("Location: http://") - 1); h->last = ngx_cpymem(h->last, r->server_name->data, @@ -277,6 +321,9 @@ static int ngx_http_header_filter(ngx_ht h->last = ngx_cpymem(h->last, r->headers_out.location->value.data, r->headers_out.location->value.len); + r->headers_out.location->value.len = h->last - p; + r->headers_out.location->value.data = p; + *(h->last++) = CR; *(h->last++) = LF; } @@ -285,8 +332,25 @@ static int ngx_http_header_filter(ngx_ht { h->last = ngx_cpymem(h->last, "Last-Modified: ", sizeof("Last-Modified: ") - 1); +#if (NGX_HTTP_LOG_ALL_HEADERS_OUT) + p = h->last; +#endif h->last += ngx_http_get_time(h->last, - r->headers_out.last_modified_time); + r->headers_out.last_modified_time); + +#if (NGX_HTTP_LOG_ALL_HEADERS_OUT) + r->headers_out.last_modified = ngx_palloc(r->pool, + sizeof(ngx_table_elt_t)); + if (r->headers_out.last_modified == NULL) { + return NGX_ERROR; + } + + r->headers_out.last_modified->key.len = 0; + r->headers_out.last_modified->key.data = NULL; + r->headers_out.last_modified->value.len = h->last - p; + r->headers_out.last_modified->value.data = p; +#endif + *(h->last++) = CR; *(h->last++) = LF; } @@ -329,12 +393,14 @@ static int ngx_http_header_filter(ngx_ht h->type |= NGX_HUNK_LAST; } - ngx_test_null(ch, ngx_palloc(r->pool, sizeof(ngx_chain_t)), NGX_ERROR); + if (!(ln = ngx_alloc_chain_link(r->pool))) { + return NGX_ERROR; + } - ch->hunk = h; - ch->next = NULL; + ln->hunk = h; + ln->next = NULL; - return ngx_http_write_filter(r, ch); + return ngx_http_write_filter(r, ln); } diff --git a/src/http/ngx_http_headers.c b/src/http/ngx_http_headers.c --- a/src/http/ngx_http_headers.c +++ b/src/http/ngx_http_headers.c @@ -10,6 +10,7 @@ ngx_http_header_t ngx_http_headers_in[] { ngx_string("If-Modified-Since"), offsetof(ngx_http_headers_in_t, if_modified_since) }, { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent) }, + { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer) }, { ngx_string("Content-Length"), offsetof(ngx_http_headers_in_t, content_length) }, diff --git a/src/http/modules/ngx_http_log_handler.c b/src/http/ngx_http_log_handler.c rename from src/http/modules/ngx_http_log_handler.c rename to src/http/ngx_http_log_handler.c --- a/src/http/modules/ngx_http_log_handler.c +++ b/src/http/ngx_http_log_handler.c @@ -2,22 +2,55 @@ #include #include #include - - -typedef struct { - ngx_open_file_t *file; -} ngx_http_log_conf_t; +#include -static void *ngx_http_log_create_conf(ngx_conf_t *cf); -static char *ngx_http_log_merge_conf(ngx_conf_t *cf, void *parent, void *child); +static char *ngx_http_log_addr(ngx_http_request_t *r, char *buf, + uintptr_t data); +static char *ngx_http_log_connection(ngx_http_request_t *r, char *buf, + uintptr_t data); +static char *ngx_http_log_pipe(ngx_http_request_t *r, char *buf, + uintptr_t data); +static char *ngx_http_log_time(ngx_http_request_t *r, char *buf, + uintptr_t data); +static char *ngx_http_log_request(ngx_http_request_t *r, char *buf, + uintptr_t data); +static char *ngx_http_log_status(ngx_http_request_t *r, char *buf, + uintptr_t data); +static char *ngx_http_log_length(ngx_http_request_t *r, char *buf, + uintptr_t data); +static char *ngx_http_log_header_in(ngx_http_request_t *r, char *buf, + uintptr_t data); +static char *ngx_http_log_unknown_header_in(ngx_http_request_t *r, char *buf, + uintptr_t data); +static char *ngx_http_log_header_out(ngx_http_request_t *r, char *buf, + uintptr_t data); +static char *ngx_http_log_unknown_header_out(ngx_http_request_t *r, char *buf, + uintptr_t data); + +static void *ngx_http_log_create_main_conf(ngx_conf_t *cf); +static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent, + void *child); static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static int ngx_http_log_parse_format(ngx_conf_t *cf, ngx_array_t *ops, + ngx_str_t *line); -static ngx_command_t ngx_http_log_commands[] = { + +static ngx_command_t ngx_http_log_commands[] = { + + {ngx_string("log_format"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE, + ngx_http_log_set_format, + NGX_HTTP_MAIN_CONF_OFFSET, + 0, + NULL}, {ngx_string("access_log"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, ngx_http_log_set_log, NGX_HTTP_LOC_CONF_OFFSET, 0, @@ -28,14 +61,14 @@ static ngx_command_t ngx_http_log_comman ngx_http_module_t ngx_http_log_module_ctx = { - NULL, /* create main configuration */ + ngx_http_log_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ - ngx_http_log_create_conf, /* create location configration */ - ngx_http_log_merge_conf /* merge location configration */ + ngx_http_log_create_loc_conf, /* create location configration */ + ngx_http_log_merge_loc_conf /* merge location configration */ }; @@ -55,130 +88,444 @@ static ngx_str_t http_access_log = ngx_s static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +static ngx_str_t ngx_http_combined_fmt = + ngx_string("%addr - - [%time] \"%request\" %status %length " + "\"%{Referer}i\" %{User-Agent}i\""); + + +static ngx_http_log_op_name_t ngx_http_log_fmt_ops[] = { + { ngx_string("addr"), INET_ADDRSTRLEN - 1, ngx_http_log_addr }, + { ngx_string("conn"), NGX_INT32_LEN, ngx_http_log_connection }, + { ngx_string("pipe"), 1, ngx_http_log_pipe }, + { ngx_string("time"), sizeof("28/Sep/1970:12:00:00") - 1, + ngx_http_log_time }, + { ngx_string("request"), 0, ngx_http_log_request }, + { ngx_string("status"), 3, ngx_http_log_status }, + { ngx_string("length"), NGX_OFF_LEN, ngx_http_log_length }, + { ngx_string("i"), NGX_HTTP_LOG_ARG, ngx_http_log_header_in }, + { ngx_string("o"), NGX_HTTP_LOG_ARG, ngx_http_log_header_out }, + { ngx_null_string, 0, NULL } +}; + int ngx_http_log_handler(ngx_http_request_t *r) { - char *line, *p; - size_t len; - ngx_tm_t tm; - ngx_http_log_conf_t *lcf; + int i, l; + u_int data; + char *line, *p; + size_t len; + ngx_http_log_t *log; + ngx_http_log_op_t *op; + ngx_http_log_loc_conf_t *lcf; #if (WIN32) - int written; + u_int written; #endif ngx_log_debug(r->connection->log, "log handler"); lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module); - /* 10:%con, 1:%pipe, 22:%date, 2:"%r", 3:%status, 20:%bytes, 2:%user-agent, - 7*" ", 2/1: "\r\n" */ + log = lcf->logs->elts; + for (l = 0; l < lcf->logs->nelts; l++) { + + len = 0; + op = log[l].ops->elts; + for (i = 0; i < log[l].ops->nelts; i++) { + if (op[i].len == 0) { + len += (size_t) op[i].op(r, NULL, op[i].data); + + } else { + len += op[i].len; + } + } + #if (WIN32) - len = 10 + 1 + 22 + 2 + 3 + 20 + 2 + 7 + 2; + len += 2; #else - len = 10 + 1 + 22 + 2 + 3 + 20 + 2 + 7 + 1; + len++; #endif - len += r->connection->addr_text.len; - len += r->request_line.len; - if (r->headers_in.user_agent) { - len += r->headers_in.user_agent->value.len; - } - - ngx_test_null(line, ngx_palloc(r->pool, len), NGX_ERROR); - p = line; - - ngx_memcpy(p, r->connection->addr_text.data, r->connection->addr_text.len); - p += r->connection->addr_text.len; - - *p++ = ' '; - - p += ngx_snprintf(p, 21, "%u", r->connection->number); - - *p++ = ' '; - - if (r->pipeline) { - *p++ = 'p'; - } else { - *p++ = '.'; - } - - *p++ = ' '; - - ngx_localtime(&tm); + ngx_test_null(line, ngx_palloc(r->pool, len), NGX_ERROR); + p = line; - *p++ = '['; - p += ngx_snprintf(p, 21, "%02d/%s/%d:%02d:%02d:%02d", - tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1], - tm.ngx_tm_year, - tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec); - - *p++ = ']'; - - *p++ = ' '; - - *p++ = '"'; - ngx_memcpy(p, r->request_line.data, r->request_line.len); - p += r->request_line.len; - *p++ = '"'; + for (i = 0; i < log[l].ops->nelts; i++) { + if (op[i].op == NGX_HTTP_LOG_COPY_SHORT) { + len = op[i].len; + data = op[i].data; + while (len--) { + *p++ = data & 0xff; + data >>= 8; + } - *p++ = ' '; - - p += ngx_snprintf(p, 4, "%d", r->headers_out.status); - - *p++ = ' '; - - p += ngx_snprintf(p, 21, OFF_FMT, r->connection->sent); + } else if (op[i].op == NGX_HTTP_LOG_COPY_LONG) { + p = ngx_cpymem(p, (void *) op[i].data, op[i].len); - *p++ = ' '; - - *p++ = '"'; - if (r->headers_in.user_agent) { - ngx_memcpy(p, r->headers_in.user_agent->value.data, - r->headers_in.user_agent->value.len); - p += r->headers_in.user_agent->value.len; - } - *p++ = '"'; + } else { + p = op[i].op(r, p, op[i].data); + } + } #if (WIN32) - - *p++ = CR; *p++ = LF; - WriteFile(lcf->file->fd, line, p - line, &written, NULL); - + *p++ = CR; *p++ = LF; + WriteFile(log[l].file->fd, line, p - line, &written, NULL); #else - - *p++ = LF; - write(lcf->file->fd, line, p - line); - + *p++ = LF; + write(log[l].file->fd, line, p - line); #endif - + } return NGX_OK; } -static void *ngx_http_log_create_conf(ngx_conf_t *cf) +static char *ngx_http_log_addr(ngx_http_request_t *r, char *buf, uintptr_t data) +{ + return ngx_cpymem(buf, r->connection->addr_text.data, + r->connection->addr_text.len); +} + + +static char *ngx_http_log_connection(ngx_http_request_t *r, char *buf, + uintptr_t data) +{ + return buf + ngx_snprintf(buf, NGX_INT32_LEN + 1, "%u", + r->connection->number); +} + + +static char *ngx_http_log_pipe(ngx_http_request_t *r, char *buf, uintptr_t data) +{ + if (r->pipeline) { + *buf = 'p'; + } else { + *buf = '.'; + } + + return buf + 1; +} + + +static char *ngx_http_log_time(ngx_http_request_t *r, char *buf, uintptr_t data) +{ + ngx_tm_t tm; + + ngx_localtime(&tm); + + return buf + ngx_snprintf(buf, sizeof("28/Sep/1970:12:00:00"), + "%02d/%s/%d:%02d:%02d:%02d", + tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1], + tm.ngx_tm_year, + tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec); +} + + +static char *ngx_http_log_request(ngx_http_request_t *r, char *buf, + uintptr_t data) +{ + if (buf == NULL) { + /* find the request line length */ + return (char *) r->request_line.len; + } + + return ngx_cpymem(buf, r->request_line.data, r->request_line.len); +} + + +static char *ngx_http_log_status(ngx_http_request_t *r, char *buf, + uintptr_t data) +{ + return buf + ngx_snprintf(buf, 4, "%d", r->headers_out.status); +} + + +static char *ngx_http_log_length(ngx_http_request_t *r, char *buf, + uintptr_t data) +{ + return buf + ngx_snprintf(buf, NGX_OFF_LEN + 1, OFF_FMT, + r->connection->sent); +} + + +static char *ngx_http_log_header_in(ngx_http_request_t *r, char *buf, + uintptr_t data) +{ + int i; + ngx_str_t *s; + ngx_table_elt_t *h; + ngx_http_log_op_t *op; + + if (r) { + h = *(ngx_table_elt_t **) ((char *) &r->headers_in + data); + + if (h == NULL) { + + /* no header */ + + if (buf) { + *buf = '-'; + } + + return buf + 1; + } + + if (buf == NULL) { + /* find the header length */ + return (char *) h->value.len; + } + + return ngx_cpymem(buf, h->value.data, h->value.len); + } + + /* find an offset while a format string compilation */ + + op = (ngx_http_log_op_t *) buf; + s = (ngx_str_t *) data; + + op->len = 0; + + for (i = 0; ngx_http_headers_in[i].name.len != 0; i++) { + if (ngx_http_headers_in[i].name.len != s->len) { + continue; + } + + if (ngx_strncasecmp(ngx_http_headers_in[i].name.data, s->data, s->len) + == 0) + { + op->op = ngx_http_log_header_in; + op->data = ngx_http_headers_in[i].offset; + return NULL; + } + } + + op->op = ngx_http_log_unknown_header_in; + op->data = (uintptr_t) s; + + return NULL; +} + + +static char *ngx_http_log_unknown_header_in(ngx_http_request_t *r, char *buf, + uintptr_t data) { - ngx_http_log_conf_t *conf; + int i; + ngx_str_t *s; + ngx_table_elt_t *h; + + s = (ngx_str_t *) data; + + h = r->headers_in.headers->elts; + for (i = 0; i < r->headers_in.headers->nelts; i++) { + if (h[i].key.len != s->len) { + continue; + } + + if (ngx_strncasecmp(h[i].key.data, s->data, s->len) == 0) { + if (buf == NULL) { + /* find the header length */ + return (char *) h[i].value.len; + } + + return ngx_cpymem(buf, h[i].value.data, h[i].value.len); + } + } + + /* no header */ + + if (buf) { + *buf = '-'; + } + + return buf + 1; +} + + +static char *ngx_http_log_header_out(ngx_http_request_t *r, char *buf, + uintptr_t data) +{ + int i; + ngx_str_t *s; + ngx_table_elt_t *h; + ngx_http_log_op_t *op; + + if (r) { + h = *(ngx_table_elt_t **) ((char *) &r->headers_out + data); + + if (h == NULL) { + + /* no header */ + + if (data == offsetof(ngx_http_headers_out_t, server)) { + if (buf == NULL) { + return (char *) (sizeof(NGINX_VER) - 1); + } + return ngx_cpymem(buf, NGINX_VER, sizeof(NGINX_VER) - 1); + } + + if (buf) { + *buf = '-'; + } + + return buf + 1; + } + + if (buf == NULL) { + /* find the header length */ + return (char *) h->value.len; + } + + return ngx_cpymem(buf, h->value.data, h->value.len); + } + + /* find an offset while a format string compilation */ + + op = (ngx_http_log_op_t *) buf; + s = (ngx_str_t *) data; + + op->len = 0; + + for (i = 0; ngx_http_headers_out[i].name.len != 0; i++) { + if (ngx_http_headers_out[i].name.len != s->len) { + continue; + } + + if (ngx_strncasecmp(ngx_http_headers_out[i].name.data, s->data, s->len) + == 0) + { + op->op = ngx_http_log_header_out; + op->data = ngx_http_headers_out[i].offset; + return NULL; + } + } - ngx_test_null(conf, ngx_pcalloc(cf->pool, sizeof(ngx_http_log_conf_t)), + op->op = ngx_http_log_unknown_header_out; + op->data = (uintptr_t) s; + + return NULL; +} + + +static char *ngx_http_log_unknown_header_out(ngx_http_request_t *r, char *buf, + uintptr_t data) +{ + int i; + ngx_str_t *s; + ngx_table_elt_t *h; + + s = (ngx_str_t *) data; + + h = r->headers_out.headers->elts; + for (i = 0; i < r->headers_out.headers->nelts; i++) { + if (h[i].key.len != s->len) { + continue; + } + + if (ngx_strncasecmp(h[i].key.data, s->data, s->len) == 0) { + if (buf == NULL) { + /* find the header length */ + return (char *) h[i].value.len; + } + + return ngx_cpymem(buf, h[i].value.data, h[i].value.len); + } + } + + /* no header */ + + if (buf) { + *buf = '-'; + } + + return buf + 1; +} + + +static void *ngx_http_log_create_main_conf(ngx_conf_t *cf) +{ + ngx_http_log_main_conf_t *conf; + + char *rc; + ngx_str_t *value; + + if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t)))) { + return NGX_CONF_ERROR; + } + + ngx_init_array(conf->formats, cf->pool, 5, sizeof(ngx_http_log_fmt_t), + NGX_CONF_ERROR); + + cf->args->nelts = 0; + + if (!(value = ngx_push_array(cf->args))) { + return NGX_CONF_ERROR; + } + + if (!(value = ngx_push_array(cf->args))) { + return NGX_CONF_ERROR; + } + + value->len = sizeof("combined") - 1; + value->data = "combined"; + + if (!(value = ngx_push_array(cf->args))) { + return NGX_CONF_ERROR; + } + + *value = ngx_http_combined_fmt; + + rc = ngx_http_log_set_format(cf, NULL, conf); + if (rc != NGX_CONF_OK) { + return rc; + } + + return conf; +} + + +static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_log_loc_conf_t *conf; + + ngx_test_null(conf, ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t)), NGX_CONF_ERROR); return conf; } -static char *ngx_http_log_merge_conf(ngx_conf_t *cf, void *parent, void *child) +static char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent, + void *child) { - ngx_http_log_conf_t *prev = parent; - ngx_http_log_conf_t *conf = child; + ngx_http_log_loc_conf_t *prev = parent; + ngx_http_log_loc_conf_t *conf = child; + + ngx_http_log_t *log; + ngx_http_log_fmt_t *fmt; + ngx_http_log_main_conf_t *lmcf; + + if (conf->logs == NULL) { + if (prev->logs) { + conf->logs = prev->logs; + + } else { - if (conf->file == NULL) { - if (prev->file) { - conf->file = prev->file; - } else { - ngx_test_null(conf->file, - ngx_conf_open_file(cf->cycle, &http_access_log), - NGX_CONF_ERROR); + conf->logs = ngx_create_array(cf->pool, 2, sizeof(ngx_http_log_t)); + if (conf->logs == NULL) { + return NGX_CONF_ERROR; + } + + if (!(log = ngx_push_array(conf->logs))) { + return NGX_CONF_ERROR; + } + + log->file = ngx_conf_open_file(cf->cycle, &http_access_log); + if (log->file == NULL) { + return NGX_CONF_ERROR; + } + + lmcf = ngx_http_conf_module_main_conf(cf, ngx_http_log_module); + fmt = lmcf->formats.elts; + /* the default "combined" format */ + log->ops = fmt[0].ops; } } @@ -189,14 +536,238 @@ static char *ngx_http_log_merge_conf(ngx static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_log_conf_t *lcf = conf; + ngx_http_log_loc_conf_t *llcf = conf; - ngx_str_t *value; + int i; + ngx_str_t *value, name; + ngx_http_log_t *log; + ngx_http_log_fmt_t *fmt; + ngx_http_log_main_conf_t *lmcf; + + if (llcf->logs == NULL) { + if (!(llcf->logs = ngx_create_array(cf->pool, 2, + sizeof(ngx_http_log_t)))) { + return NGX_CONF_ERROR; + } + } value = cf->args->elts; + lmcf = ngx_http_conf_module_main_conf(cf, ngx_http_log_module); - ngx_test_null(lcf->file, ngx_conf_open_file(cf->cycle, &value[1]), - NGX_CONF_ERROR); + if (!(log = ngx_push_array(llcf->logs))) { + return NGX_CONF_ERROR; + } + + if (!(log->file = ngx_conf_open_file(cf->cycle, &value[1]))) { + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 3) { + name = value[2]; + } else { + name.len = sizeof("combined") - 1; + name.data = "combined"; + } + + fmt = lmcf->formats.elts; + for (i = 0; i < lmcf->formats.nelts; i++) { + if (fmt[i].name.len == name.len + && ngx_strcasecmp(fmt[i].name.data, name.data) == 0) + { + log->ops = fmt[i].ops; + return NGX_CONF_OK; + } + } return NGX_CONF_OK; } + + +static char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_http_log_main_conf_t *lmcf = conf; + + int s, f, invalid; + char *data, *p, *fname; + size_t i, len, fname_len; + ngx_str_t *value, arg, *a; + ngx_http_log_op_t *op; + ngx_http_log_fmt_t *fmt; + ngx_http_log_op_name_t *name; + + value = cf->args->elts; +#if 0 + lmcf = ngx_http_conf_module_main_conf(cf, ngx_http_log_module); +#endif + + fmt = lmcf->formats.elts; + for (f = 0; f < lmcf->formats.nelts; f++) { + if (fmt[f].name.len == value[1].len + && ngx_strcmp(fmt->name.data, value[1].data) == 0) + { + return "duplicate \"log_format\" name"; + } + } + + if (!(fmt = ngx_push_array(&lmcf->formats))) { + return NGX_CONF_ERROR; + } + + fmt->name = value[1]; + + if (!(fmt->ops = ngx_create_array(cf->pool, 20, + sizeof(ngx_http_log_op_t)))) { + return NGX_CONF_ERROR; + } + + invalid = 0; + data = NULL; + + for (s = 2; s < cf->args->nelts && !invalid; s++) { + + i = 0; + + while (i < value[s].len) { + + if (!(op = ngx_push_array(fmt->ops))) { + return NGX_CONF_ERROR; + } + + data = &value[s].data[i]; + + if (value[s].data[i] == '%') { + i++; + + if (i == value[s].len) { + invalid = 1; + break; + } + + if (value[s].data[i] == '{') { + i++; + + arg.data = &value[s].data[i]; + + while (i < value[s].len && value[s].data[i] != '}') { + i++; + } + + arg.len = &value[s].data[i] - arg.data; + + if (i == value[s].len || arg.len == 0) { + invalid = 1; + break; + } + + i++; + + } else { + arg.len = 0; + } + + fname = &value[s].data[i]; + + while (i < value[s].len + && value[s].data[i] >= 'a' + && value[s].data[i] <= 'z') + { + i++; + } + + fname_len = &value[s].data[i] - fname; + + if (fname_len == 0) { + invalid = 1; + break; + } + + for (name = ngx_http_log_fmt_ops; name->name.len; name++) { + if (name->name.len == fname_len + && ngx_strncmp(name->name.data, fname, fname_len) == 0) + { + if (name->len != NGX_HTTP_LOG_ARG) { + if (arg.len) { + fname[fname_len] = '\0'; + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%s\" must not have argument", + data); + return NGX_CONF_ERROR; + } + + op->len = name->len; + op->op = name->op; + op->data = 0; + + break; + } + + if (arg.len == 0) { + fname[fname_len] = '\0'; + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%s\" requires argument", + data); + return NGX_CONF_ERROR; + } + + if (!(a = ngx_palloc(cf->pool, sizeof(ngx_str_t)))) { + return NGX_CONF_ERROR; + } + + *a = arg; + name->op(NULL, (char *) op, (uintptr_t) a); + + break; + } + } + + if (name->name.len == 0) { + invalid = 1; + break; + } + + } else { + i++; + + while (i < value[s].len && value[s].data[i] != '%') { + i++; + } + + len = &value[s].data[i] - data; + + if (len) { + + op->len = len; + + if (len <= sizeof(uintptr_t)) { + op->op = NGX_HTTP_LOG_COPY_SHORT; + op->data = 0; + + while (len--) { + op->data <<= 8; + op->data |= data[len]; + } + + } else { + op->op = NGX_HTTP_LOG_COPY_LONG; + + if (!(p = ngx_palloc(cf->pool, len))) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(p, data, len); + op->data = (uintptr_t) p; + } + } + } + } + } + + if (invalid) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%s\"", data); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} diff --git a/src/http/modules/ngx_http_log_handler.h b/src/http/ngx_http_log_handler.h rename from src/http/modules/ngx_http_log_handler.h rename to src/http/ngx_http_log_handler.h --- a/src/http/modules/ngx_http_log_handler.h +++ b/src/http/ngx_http_log_handler.h @@ -7,35 +7,53 @@ #include -typedef struct { - int dummy; -} ngx_http_log_conf_t; +typedef char *(*ngx_http_log_op_pt) (ngx_http_request_t *r, char *buf, + uintptr_t data); + +#define NGX_HTTP_LOG_COPY_SHORT (ngx_http_log_op_pt) 0 +#define NGX_HTTP_LOG_COPY_LONG (ngx_http_log_op_pt) -1 + +#define NGX_HTTP_LOG_ARG (u_int) -1 + +/* STUB */ +#define NGX_INT32_LEN sizeof("4294967296") - 1 +#define NGX_OFF_LEN sizeof("18446744073709551616") - 1 -typedef enum { - - NGX_HTTP_LOG_HANDLER = 0, - -#if 0 - /* the ngx_str_t field of the request */ - NGX_HTTP_LOG_REQUEST_STR_FIELD, - - /* the ngx_str_t field of the r->headers_in */ - NGX_HTTP_LOG_REQUEST_HEADER_IN_FIELD, - - /* the ngx_str_t field of the r->headers_out */ - NGX_HTTP_LOG_REQUEST_HEADER_OUT_FIELD, -#endif - -} ngx_http_log_code_e; +typedef struct { + size_t len; + ngx_http_log_op_pt op; + uintptr_t data; +} ngx_http_log_op_t; typedef struct { - int type; - int size; - char *(*handler) (ngx_http_request_t *r, char *p); - int offset; -} ngx_http_log_code_t; + ngx_str_t name; + ngx_array_t *ops; /* array of ngx_http_log_op_t */ +} ngx_http_log_fmt_t; + + +typedef struct { + ngx_str_t name; + size_t len; + ngx_http_log_op_pt op; +} ngx_http_log_op_name_t; + + +typedef struct { + ngx_array_t formats; /* array of ngx_http_log_fmt_t */ +} ngx_http_log_main_conf_t; + + +typedef struct { + ngx_open_file_t *file; + ngx_array_t *ops; /* array of ngx_http_log_op_t */ +} ngx_http_log_t; + + +typedef struct { + ngx_array_t *logs; /* array of ngx_http_log_t */ +} ngx_http_log_loc_conf_t; #endif /* _NGX_HTTP_LOG_HANDLER_H_INCLUDED_ */ 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 @@ -225,6 +225,7 @@ ngx_log_debug(rev->log, "IN: %08x" _ in_ return; } + /* TODO: ngx_init_table */ if (!(r->headers_out.headers = ngx_create_table(r->pool, 20))) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); ngx_http_close_connection(c); @@ -416,6 +417,7 @@ static void ngx_http_process_request_lin lctx = c->log->data; lctx->action = "reading client request headers"; lctx->url = r->unparsed_uri.data; + /* TODO: ngx_init_table */ r->headers_in.headers = ngx_create_table(r->pool, 20); if (cscf->large_client_header @@ -1068,7 +1070,8 @@ static void ngx_http_set_keepalive(ngx_h if (h->pos < h->last) { - /* Pipelined request. + /* + * Pipelined request. * * We do not know here whether a pipelined request is complete * so if the large client headers are not enabled 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 @@ -74,6 +74,7 @@ typedef struct { ngx_table_elt_t *connection; ngx_table_elt_t *if_modified_since; ngx_table_elt_t *user_agent; + ngx_table_elt_t *referer; ngx_table_elt_t *content_length; ngx_table_elt_t *accept_encoding; @@ -202,6 +203,8 @@ struct ngx_http_request_s { unsigned bypass_cache:1; unsigned no_cache:1; + unsigned error_page:1; + #if 0 unsigned cachable:1; #endif diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -168,9 +168,10 @@ static ngx_str_t error_pages[] = { int ngx_http_special_response_handler(ngx_http_request_t *r, int error) { - int err, rc; + int err, rc, i; ngx_hunk_t *h; ngx_chain_t *out, **ll, *cl; + ngx_http_err_page_t *err_page; ngx_http_core_loc_conf_t *clcf; rc = ngx_http_discard_body(r); @@ -181,19 +182,6 @@ int ngx_http_special_response_handler(ng r->headers_out.status = error; - if (error < NGX_HTTP_BAD_REQUEST) { - /* 3XX */ - err = error - NGX_HTTP_MOVED_PERMANENTLY; - - } else if (error < NGX_HTTP_INTERNAL_SERVER_ERROR) { - /* 4XX */ - err = error - NGX_HTTP_BAD_REQUEST + 3; - - } else { - /* 5XX */ - err = error - NGX_HTTP_INTERNAL_SERVER_ERROR + 3 + 17; - } - if (r->keepalive != 0) { switch (error) { case NGX_HTTP_BAD_REQUEST: @@ -213,6 +201,31 @@ int ngx_http_special_response_handler(ng } } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (!r->error_page && clcf->error_pages) { + err_page = clcf->error_pages->elts; + for (i = 0; i < clcf->error_pages->nelts; i++) { + if (err_page[i].code == error) { + r->error_page = 1; + return ngx_http_internal_redirect(r, &err_page[i].uri, NULL); + } + } + } + + if (error < NGX_HTTP_BAD_REQUEST) { + /* 3XX */ + err = error - NGX_HTTP_MOVED_PERMANENTLY; + + } else if (error < NGX_HTTP_INTERNAL_SERVER_ERROR) { + /* 4XX */ + err = error - NGX_HTTP_BAD_REQUEST + 3; + + } else { + /* 5XX */ + err = error - NGX_HTTP_INTERNAL_SERVER_ERROR + 3 + 17; + } + if (error_pages[err].len) { r->headers_out.content_length_n = error_pages[err].len + sizeof(error_tail) - 1 @@ -272,8 +285,6 @@ int ngx_http_special_response_handler(ng ngx_alloc_link_and_set_hunk(cl, h, r->pool, NGX_ERROR); ngx_chain_add_link(out, ll, cl); - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (clcf->msie_padding && r->http_version >= NGX_HTTP_VERSION_10 && error >= NGX_HTTP_BAD_REQUEST diff --git a/src/os/unix/ngx_files.h b/src/os/unix/ngx_files.h --- a/src/os/unix/ngx_files.h +++ b/src/os/unix/ngx_files.h @@ -63,6 +63,7 @@ ssize_t ngx_write_chain_to_file(ngx_file #define ngx_is_file(sb) (S_ISREG(sb.st_mode)) #define ngx_file_size(sb) sb.st_size #define ngx_file_mtime(sb) sb.st_mtime +#define ngx_file_uniq(sb) sb.st_ino #endif /* _NGX_FILES_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_types.h b/src/os/unix/ngx_types.h --- a/src/os/unix/ngx_types.h +++ b/src/os/unix/ngx_types.h @@ -10,6 +10,8 @@ typedef int ngx_fd_t; typedef struct stat ngx_file_info_t; +typedef ino_t ngx_file_uniq_t; + #endif /* _NGX_TYPES_H_INCLUDED_ */ diff --git a/src/os/win32/ngx_files.h b/src/os/win32/ngx_files.h --- a/src/os/win32/ngx_files.h +++ b/src/os/win32/ngx_files.h @@ -6,7 +6,7 @@ #include -/* INVALID_FILE_ATTRIBUTES specified but never defined at least in VC6SP2 */ +/* INVALID_FILE_ATTRIBUTES specified but not defined at least in MSVC6SP2 */ #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF #endif @@ -67,6 +67,7 @@ int ngx_file_type(char *filename, ngx_fi #define ngx_file_size(fi) \ (((off_t) fi.nFileSizeHigh << 32) | fi.nFileSizeLow) +#define ngx_file_uniq(fi) (*(ngx_file_uniq_t *) &fi.nFileIndexHigh) /* There are 134774 days between 1 Jan 1970 and 1 Jan 1601, 11644473600 seconds or 11644473600,000,000,0 100-nanosecond intervals */ diff --git a/src/os/win32/ngx_stat.c b/src/os/win32/ngx_stat.c --- a/src/os/win32/ngx_stat.c +++ b/src/os/win32/ngx_stat.c @@ -1,7 +1,7 @@ #include +#include -#include int ngx_file_type(char *file, ngx_file_info_t *sb) { diff --git a/src/os/win32/ngx_stat.h b/src/os/win32/ngx_stat.h deleted file mode 100644 --- a/src/os/win32/ngx_stat.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _NGX_STAT_H_INCLUDED_ -#define _NGX_STAT_H_INCLUDED_ - - -#include - -/* INVALID_FILE_ATTRIBUTES specified but never defined at least in VC6SP2 */ -#ifndef INVALID_FILE_ATTRIBUTES -#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF -#endif - -typedef BY_HANDLE_FILE_INFORMATION ngx_file_info_t; - - -#define ngx_file_type_n "GetFileAttributes" - -#define ngx_is_dir(fi) (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - -#define ngx_stat_n "GetFileAttributes" - -#define ngx_fstat(file, fd, sb) ngx_stat(file, sb) -#define ngx_fstat_n "GetFileAttributes" - - -/* -int ngx_stat(char *file, ngx_stat_t *sb); -*/ - - -#endif /* _NGX_STAT_H_INCLUDED_ */ diff --git a/src/os/win32/ngx_types.h b/src/os/win32/ngx_types.h --- a/src/os/win32/ngx_types.h +++ b/src/os/win32/ngx_types.h @@ -6,21 +6,9 @@ #include -typedef unsigned __int32 u_int32_t; -typedef __int64 int64_t; - -typedef int ssize_t; -typedef long time_t; - typedef HANDLE ngx_fd_t; -typedef unsigned __int64 off_t; typedef BY_HANDLE_FILE_INFORMATION ngx_file_info_t; - - -#define OFF_FMT "%I64d" -#define SIZE_FMT "%d" -#define SIZEX_FMT "%x" -#define PID_FMT "%d" +typedef uint64_t ngx_file_uniq_t; #endif /* _NGX_TYPES_H_INCLUDED_ */ diff --git a/src/os/win32/ngx_win32_config.h b/src/os/win32/ngx_win32_config.h --- a/src/os/win32/ngx_win32_config.h +++ b/src/os/win32/ngx_win32_config.h @@ -18,6 +18,30 @@ #define ngx_inline __inline +#if 0 +typedef unsigned __int32 uint32_t; +#else +typedef unsigned int uint32_t; +#endif +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +typedef u_int uintptr_t; + +typedef int ssize_t; +typedef long time_t; +typedef unsigned __int64 off_t; + + +#define OFF_FMT "%I64d" +#define SIZE_FMT "%d" +#define SIZEX_FMT "%x" +#define PID_FMT "%d" + + +/* STUB */ +typedef uint32_t u_int32_t; + + #ifndef HAVE_INHERITED_NONBLOCK #define HAVE_INHERITED_NONBLOCK 1 #endif