# HG changeset patch # User Igor Sysoev # Date 1187121600 -14400 # Node ID 56675f0026009a26443839e63df99df2e299c4ba # Parent 6762c33c7da8f2842a7ce40ea258b38697be83f9 nginx 0.5.31 *) Feature: named locations. *) Feature: the "proxy_store" and "fastcgi_store" directives. *) Feature: the "proxy_store_access" and "fastcgi_store_access" directives. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,14 @@ +Changes with nginx 0.5.31 15 Aug 2007 + + *) Feature: named locations. + + *) Feature: the "proxy_store" and "fastcgi_store" directives. + + *) Feature: the "proxy_store_access" and "fastcgi_store_access" + directives. + + Changes with nginx 0.5.30 30 Jul 2007 *) Feature: the $args variable can be set with the "set" directive. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,13 @@ +Изменения в nginx 0.5.31 15.08.2007 + + *) Добавление: именованные location'ы. + + *) Добавление: директивы proxy_store и fastcgi_store. + + *) Добавление: директивы proxy_store_access и fastcgi_store_access. + + Изменения в nginx 0.5.30 30.07.2007 *) Добавление: переменную $args можно устанавливать с помощью set. diff --git a/auto/cc/sunc b/auto/cc/sunc --- a/auto/cc/sunc +++ b/auto/cc/sunc @@ -155,3 +155,6 @@ fi # stop on warning CFLAGS="$CFLAGS -errwarn=%all" + +# debug +CFLAGS="$CFLAGS -g" diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.5.30" +#define NGINX_VERSION "0.5.31" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -293,6 +293,68 @@ ngx_conf_set_path_slot(ngx_conf_t *cf, n } +char * +ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *confp = conf; + + u_char *p; + ngx_str_t *value; + ngx_uint_t i, right, shift, *access; + + access = (ngx_uint_t *) (confp + cmd->offset); + + if (*access != NGX_CONF_UNSET_UINT) { + return "is duplicate"; + } + + value = cf->args->elts; + + *access = 0600; + + for (i = 1; i < cf->args->nelts; i++) { + + p = value[i].data; + + if (ngx_strncmp(p, "user:", sizeof("user:") - 1) == 0) { + shift = 6; + p += sizeof("user:") - 1; + + } else if (ngx_strncmp(p, "group:", sizeof("group:") - 1) == 0) { + shift = 3; + p += sizeof("group:") - 1; + + } else if (ngx_strncmp(p, "all:", sizeof("all:") - 1) == 0) { + shift = 0; + p += sizeof("all:") - 1; + + } else { + goto invalid; + } + + if (ngx_strcmp(p, "rw") == 0) { + right = 6; + + } else if (ngx_strcmp(p, "r") == 0) { + right = 4; + + } else { + goto invalid; + } + + *access |= right << shift; + } + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[i]); + + return NGX_CONF_ERROR; +} + + ngx_int_t ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot) { diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h --- a/src/core/ngx_file.h +++ b/src/core/ngx_file.h @@ -95,6 +95,7 @@ void ngx_init_temp_number(void); ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision); char *ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); #define ngx_conf_merge_path_value(curr, prev, path, l1, l2, l3, clean, cf) \ diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c --- a/src/http/modules/ngx_http_dav_module.c +++ b/src/http/modules/ngx_http_dav_module.c @@ -57,8 +57,6 @@ static ngx_int_t ngx_http_dav_depth(ngx_ static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err, ngx_int_t not_found, char *failed, u_char *path); static ngx_int_t ngx_http_dav_location(ngx_http_request_t *r, u_char *path); -static char *ngx_http_dav_access(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); static void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); @@ -94,9 +92,9 @@ static ngx_command_t ngx_http_dav_comma { ngx_string("dav_access"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123, - ngx_http_dav_access, + ngx_conf_set_access_slot, NGX_HTTP_LOC_CONF_OFFSET, - 0, + offsetof(ngx_http_dav_loc_conf_t, access), NULL }, ngx_null_command @@ -1106,66 +1104,6 @@ ngx_http_dav_location(ngx_http_request_t } -static char * -ngx_http_dav_access(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_dav_loc_conf_t *lcf = conf; - - u_char *p; - ngx_str_t *value; - ngx_uint_t i, right, shift; - - if (lcf->access != NGX_CONF_UNSET_UINT) { - return "is duplicate"; - } - - value = cf->args->elts; - - lcf->access = 0600; - - for (i = 1; i < cf->args->nelts; i++) { - - p = value[i].data; - - if (ngx_strncmp(p, "user:", sizeof("user:") - 1) == 0) { - shift = 6; - p += sizeof("user:") - 1; - - } else if (ngx_strncmp(p, "group:", sizeof("group:") - 1) == 0) { - shift = 3; - p += sizeof("group:") - 1; - - } else if (ngx_strncmp(p, "all:", sizeof("all:") - 1) == 0) { - shift = 0; - p += sizeof("all:") - 1; - - } else { - goto invalid; - } - - if (ngx_strcmp(p, "rw") == 0) { - right = 6; - - } else if (ngx_strcmp(p, "r") == 0) { - right = 4; - - } else { - goto invalid; - } - - lcf->access |= right << shift; - } - - return NGX_CONF_OK; - -invalid: - - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid value \"%V\"", &value[i]); - return NGX_CONF_ERROR; -} - - static void * ngx_http_dav_create_loc_conf(ngx_conf_t *cf) { diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -124,6 +124,8 @@ static ngx_int_t ngx_http_fastcgi_script static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data); @@ -200,6 +202,20 @@ static ngx_command_t ngx_http_fastcgi_c offsetof(ngx_http_fastcgi_loc_conf_t, index), NULL }, + { ngx_string("fastcgi_store"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_fastcgi_store, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("fastcgi_store_access"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123, + ngx_conf_set_access_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store_access), + NULL }, + { ngx_string("fastcgi_ignore_client_abort"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -1628,11 +1644,15 @@ ngx_http_fastcgi_create_loc_conf(ngx_con * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; + * conf->upstream.store_lengths = NULL; + * conf->upstream.store_values = NULL; * * conf->index.len = 0; * conf->index.data = NULL; */ + conf->upstream.store = NGX_CONF_UNSET; + conf->upstream.store_access = NGX_CONF_UNSET_UINT; conf->upstream.buffering = NGX_CONF_UNSET; conf->upstream.ignore_client_abort = NGX_CONF_UNSET; @@ -1677,6 +1697,19 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf ngx_http_script_compile_t sc; ngx_http_script_copy_code_t *copy; + if (conf->upstream.store != 0) { + ngx_conf_merge_value(conf->upstream.store, + prev->upstream.store, 0); + + if (conf->upstream.store_lengths == NULL) { + conf->upstream.store_lengths = prev->upstream.store_lengths; + conf->upstream.store_values = prev->upstream.store_values; + } + } + + ngx_conf_merge_uint_value(conf->upstream.store_access, + prev->upstream.store_access, 0600); + ngx_conf_merge_value(conf->upstream.buffering, prev->upstream.buffering, 1); @@ -2148,6 +2181,52 @@ ngx_http_fastcgi_pass(ngx_conf_t *cf, ng static char * +ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_fastcgi_loc_conf_t *flcf = conf; + + ngx_str_t *value; + ngx_http_script_compile_t sc; + + if (flcf->upstream.store != NGX_CONF_UNSET || flcf->upstream.store_lengths) + { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "on") == 0) { + flcf->upstream.store = 1; + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + flcf->upstream.store = 0; + return NGX_CONF_OK; + } + + /* include the terminating '\0' into script */ + value[1].len++; + + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = &value[1]; + sc.lengths = &flcf->upstream.store_lengths; + sc.values = &flcf->upstream.store_values; + sc.variables = ngx_http_script_variables_count(&value[1]);; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data) { #if (NGX_FREEBSD) diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -105,6 +105,8 @@ static char *ngx_http_proxy_pass(ngx_con void *conf); static char *ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data); @@ -154,6 +156,20 @@ static ngx_command_t ngx_http_proxy_com 0, NULL }, + { ngx_string("proxy_store"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_proxy_store, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("proxy_store_access"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123, + ngx_conf_set_access_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.store_access), + NULL }, + { ngx_string("proxy_buffering"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -1490,6 +1506,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; + * conf->upstream.store_lengths = NULL; + * conf->upstream.store_values = NULL; * * conf->method = NULL; * conf->headers_source = NULL; @@ -1502,6 +1520,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ * conf->rewrite_locations = NULL; */ + conf->upstream.store = NGX_CONF_UNSET; + conf->upstream.store_access = NGX_CONF_UNSET_UINT; conf->upstream.buffering = NGX_CONF_UNSET; conf->upstream.ignore_client_abort = NGX_CONF_UNSET; @@ -1553,6 +1573,19 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t ngx_http_script_compile_t sc; ngx_http_script_copy_code_t *copy; + if (conf->upstream.store != 0) { + ngx_conf_merge_value(conf->upstream.store, + prev->upstream.store, 0); + + if (conf->upstream.store_lengths == NULL) { + conf->upstream.store_lengths = prev->upstream.store_lengths; + conf->upstream.store_values = prev->upstream.store_values; + } + } + + ngx_conf_merge_uint_value(conf->upstream.store_access, + prev->upstream.store_access, 0600); + ngx_conf_merge_value(conf->upstream.buffering, prev->upstream.buffering, 1); @@ -2242,13 +2275,17 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_ plcf->upstream.location = clcf->name; + if (clcf->named #if (NGX_PCRE) - - if (clcf->regex || clcf->noname) { + || clcf->regex +#endif + || clcf->noname) + { if (plcf->upstream.uri.len) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"proxy_pass\" may not have URI part in " "location given by regular expression, " + "or inside named location, " "or inside the \"if\" statement, " "or inside the \"limit_except\" block"); return NGX_CONF_ERROR; @@ -2257,8 +2294,6 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_ plcf->upstream.location.len = 0; } -#endif - plcf->upstream.url = *url; if (clcf->name.data[clcf->name.len - 1] == '/') { @@ -2360,6 +2395,52 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, static char * +ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_proxy_loc_conf_t *plcf = conf; + + ngx_str_t *value; + ngx_http_script_compile_t sc; + + if (plcf->upstream.store != NGX_CONF_UNSET || plcf->upstream.store_lengths) + { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "on") == 0) { + plcf->upstream.store = 1; + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + plcf->upstream.store = 0; + return NGX_CONF_OK; + } + + /* include the terminating '\0' into script */ + value[1].len++; + + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = &value[1]; + sc.lengths = &plcf->upstream.store_lengths; + sc.values = &plcf->upstream.store_values; + sc.variables = ngx_http_script_variables_count(&value[1]);; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data) { #if (NGX_FREEBSD) diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -47,7 +47,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.5.30'; +our $VERSION = '0.5.31'; require XSLoader; XSLoader::load('nginx', $VERSION); 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 @@ -402,6 +402,7 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1; + cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1; find_config_index = 0; use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0; use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0; @@ -443,6 +444,14 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma continue; + case NGX_HTTP_REWRITE_PHASE: + if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) { + cmcf->phase_engine.location_rewrite_index = n; + } + checker = ngx_http_core_generic_phase; + + break; + case NGX_HTTP_POST_REWRITE_PHASE: if (use_rewrite) { ph->checker = ngx_http_core_post_rewrite_phase; 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 @@ -933,25 +933,29 @@ ngx_http_core_find_location(ngx_http_req ngx_array_t *locations, ngx_uint_t regex_start, size_t len) { ngx_int_t n, rc; - ngx_uint_t i, found, noregex; + ngx_uint_t i, found; ngx_http_core_loc_conf_t *clcf, **clcfp; +#if (NGX_PCRE) + ngx_uint_t noregex; +#endif ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "find location for \"%V\"", &r->uri); found = 0; +#if (NGX_PCRE) noregex = 0; +#endif clcfp = locations->elts; for (i = 0; i < locations->nelts; i++) { + if (clcfp[i]->noname #if (NGX_PCRE) - if (clcfp[i]->regex) { - break; - } + || clcfp[i]->regex #endif - - if (clcfp[i]->noname) { + || clcfp[i]->named) + { break; } @@ -999,9 +1003,12 @@ ngx_http_core_find_location(ngx_http_req break; } + found = 1; + r->loc_conf = clcfp[i]->loc_conf; +#if (NGX_PCRE) noregex = clcfp[i]->noregex; - found = 1; +#endif } } @@ -1028,7 +1035,7 @@ ngx_http_core_find_location(ngx_http_req for (i = regex_start; i < locations->nelts; i++) { - if (clcfp[i]->noname) { + if (!clcfp[i]->regex) { break; } @@ -1513,6 +1520,51 @@ ngx_http_internal_redirect(ngx_http_requ } +ngx_int_t +ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name) +{ + ngx_uint_t i; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_loc_conf_t **clcfp; + ngx_http_core_main_conf_t *cmcf; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + clcfp = cscf->locations.elts; + + for (i = cscf->named_start; i < cscf->locations.nelts; i++) { + + if (name->len != clcfp[i]->name.len + || ngx_strncmp(name->data, clcfp[i]->name.data, name->len) != 0) + { + continue; + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "named location: %V \"%V?%V\"", name, &r->uri, &r->args); + + r->internal = 1; + + r->loc_conf = clcfp[i]->loc_conf; + + ngx_http_update_location_config(r); + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + r->phase_handler = cmcf->phase_engine.location_rewrite_index; + ngx_http_core_run_phases(r); + + return NGX_DONE; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "could not find name location \"%V\"", name); + + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_DONE; +} + + ngx_http_cleanup_t * ngx_http_cleanup_add(ngx_http_request_t *r, size_t size) { @@ -1557,10 +1609,8 @@ ngx_http_core_server(ngx_conf_t *cf, ngx ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx, *http_ctx; ngx_http_core_srv_conf_t *cscf, **cscfp; + ngx_http_core_loc_conf_t **clcfp; ngx_http_core_main_conf_t *cmcf; -#if (NGX_PCRE) - ngx_http_core_loc_conf_t **clcfp; -#endif ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); if (ctx == NULL) { @@ -1644,10 +1694,11 @@ ngx_http_core_server(ngx_conf_t *cf, ngx ngx_sort(cscf->locations.elts, (size_t) cscf->locations.nelts, sizeof(ngx_http_core_loc_conf_t *), ngx_http_core_cmp_locations); + clcfp = cscf->locations.elts; + #if (NGX_PCRE) cscf->regex_start = cscf->locations.nelts; - clcfp = cscf->locations.elts; for (i = 0; i < cscf->locations.nelts; i++) { if (clcfp[i]->regex) { @@ -1658,6 +1709,15 @@ ngx_http_core_server(ngx_conf_t *cf, ngx #endif + cscf->named_start = cscf->locations.nelts; + + for (i = 0; i < cscf->locations.nelts; i++) { + if (clcfp[i]->named) { + cscf->named_start = i; + break; + } + } + return rv; } @@ -1758,7 +1818,12 @@ ngx_http_core_location(ngx_conf_t *cf, n } } else { + clcf->name = value[1]; + + if (value[1].data[0] == '@') { + clcf->named = 1; + } } pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index]; @@ -1784,6 +1849,14 @@ ngx_http_core_location(ngx_conf_t *cf, n return NGX_CONF_ERROR; } + if (pclcf->named) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "location \"%V\" could not be inside " + "the named location \"%V\"", + &clcf->name, &pclcf->name); + return NGX_CONF_ERROR; + } + #if (NGX_PCRE) if (clcf->regex == NULL && ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len) @@ -1861,6 +1934,20 @@ ngx_http_core_cmp_locations(const void * first = *(ngx_http_core_loc_conf_t **) one; second = *(ngx_http_core_loc_conf_t **) two; + if (first->named && !second->named) { + /* shift named locations to the end */ + return 1; + } + + if (!first->named && second->named) { + /* shift named locations to the end */ + return -1; + } + + if (first->named && second->named) { + return ngx_strcmp(first->name.data, second->name.data); + } + if (first->noname && !second->noname) { /* shift no named locations to the end */ return 1; @@ -2706,6 +2793,14 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c return NGX_CONF_ERROR; } + if (lcf->named && alias) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"alias\" directive may not be used " + "inside named location"); + + return NGX_CONF_ERROR; + } + #if (NGX_PCRE) if (lcf->regex && alias) { 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 @@ -79,6 +79,7 @@ struct ngx_http_phase_handler_s { typedef struct { ngx_http_phase_handler_t *handlers; ngx_uint_t server_rewrite_index; + ngx_uint_t location_rewrite_index; } ngx_http_phase_engine_t; @@ -117,7 +118,8 @@ typedef struct { */ ngx_array_t locations; - unsigned regex_start:16; + unsigned regex_start:15; + unsigned named_start:15; unsigned wildcard:1; /* array of the ngx_http_listen_t, "listen" directive */ @@ -211,9 +213,10 @@ struct ngx_http_core_loc_conf_s { ngx_regex_t *regex; #endif - unsigned regex_start:16; + unsigned regex_start:15; - unsigned noname:1; /* "if () {}" block */ + unsigned noname:1; /* "if () {}" block or limit_except */ + unsigned named:1; unsigned exact_match:1; unsigned noregex:1; @@ -313,6 +316,8 @@ ngx_int_t ngx_http_subrequest(ngx_http_r ngx_http_post_subrequest_t *psr, ngx_uint_t flags); ngx_int_t ngx_http_internal_redirect(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args); +ngx_int_t ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name); + ngx_http_cleanup_t *ngx_http_cleanup_add(ngx_http_request_t *r, size_t size); 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 @@ -409,6 +409,10 @@ ngx_http_special_response_handler(ngx_ht return ngx_http_internal_redirect(r, uri, NULL); } + if (uri->data[0] == '@') { + return ngx_http_named_location(r, uri); + } + r->headers_out.location = ngx_list_push(&r->headers_out.headers); diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -33,6 +33,8 @@ static ngx_int_t ngx_http_upstream_non_b ssize_t bytes); static void ngx_http_upstream_process_downstream(ngx_http_request_t *r); static void ngx_http_upstream_process_body(ngx_event_t *ev); +static void ngx_http_upstream_store(ngx_http_request_t *r, + ngx_http_upstream_t *u); static void ngx_http_upstream_dummy_handler(ngx_event_t *wev); static void ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_uint_t ft_type); @@ -116,6 +118,12 @@ ngx_http_upstream_header_t ngx_http_ups ngx_http_upstream_copy_header_line, offsetof(ngx_http_headers_out_t, date), 0 }, + { ngx_string("Last-Modified"), + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, last_modified), + ngx_http_upstream_copy_header_line, + offsetof(ngx_http_headers_out_t, last_modified), 0 }, + { ngx_string("Server"), ngx_http_upstream_process_header_line, offsetof(ngx_http_upstream_headers_in_t, server), @@ -364,6 +372,8 @@ ngx_http_upstream_init(ngx_http_request_ cln->data = r; u->cleanup = &cln->handler; + u->store = (u->conf->store || u->conf->store_lengths); + ngx_http_upstream_connect(r, u); } @@ -424,7 +434,7 @@ ngx_http_upstream_check_broken_connectio ev->error = 1; } - if (!u->cachable && u->peer.connection) { + if (!u->cachable && !u->store && u->peer.connection) { ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, "kevent() reported that client closed prematurely " "connection, so upstream connection is closed too"); @@ -490,7 +500,7 @@ ngx_http_upstream_check_broken_connectio ev->eof = 1; c->error = 1; - if (!u->cachable && u->peer.connection) { + if (!u->cachable && !u->store && u->peer.connection) { ngx_log_error(NGX_LOG_INFO, ev->log, err, "client closed prematurely connection, " "so upstream connection is closed too"); @@ -1523,7 +1533,7 @@ ngx_http_upstream_send_response(ngx_http p->pool = r->pool; p->log = c->log; - p->cachable = u->cachable; + p->cachable = u->cachable || u->store; p->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); if (p->temp_file == NULL) { @@ -1536,8 +1546,9 @@ ngx_http_upstream_send_response(ngx_http p->temp_file->path = u->conf->temp_path; p->temp_file->pool = r->pool; - if (u->cachable) { + if (u->cachable || u->store) { p->temp_file->persistent = 1; + } else { p->temp_file->log_level = NGX_LOG_WARN; p->temp_file->warn = "an upstream response is buffered " @@ -1552,6 +1563,7 @@ ngx_http_upstream_send_response(ngx_http ngx_http_upstream_finalize_request(r, u, 0); return; } + p->preread_bufs->buf = &u->buffer; p->preread_bufs->next = NULL; u->buffer.recycled = 1; @@ -1559,11 +1571,13 @@ ngx_http_upstream_send_response(ngx_http p->preread_size = u->buffer.last - u->buffer.pos; if (u->cachable) { + p->buf_to_file = ngx_calloc_buf(r->pool); if (p->buf_to_file == NULL) { ngx_http_upstream_finalize_request(r, u, 0); return; } + p->buf_to_file->pos = u->buffer.start; p->buf_to_file->last = u->buffer.pos; p->buf_to_file->temporary = 1; @@ -1910,6 +1924,27 @@ ngx_http_upstream_process_body(ngx_event if (u->peer.connection) { + if (u->store) { + + if (p->upstream_eof && u->headers_in.status_n == NGX_HTTP_OK) { + + ngx_http_upstream_store(r, u); + + } else if ((p->upstream_error + || (p->upstream_eof + && u->headers_in.status_n != NGX_HTTP_OK)) + && u->pipe->temp_file->file.fd != NGX_INVALID_FILE) + { + if (ngx_delete_file(u->pipe->temp_file->file.name.data) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", + u->pipe->temp_file->file.name.data); + } + } + } + #if (NGX_HTTP_FILE_CACHE) if (p->upstream_done && u->cachable) { @@ -1955,6 +1990,143 @@ ngx_http_upstream_process_body(ngx_event static void +ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + char *failed; + u_char *name; + size_t root; + time_t lm; + ngx_err_t err; + ngx_str_t *temp, path, *last_modified; + ngx_temp_file_t *tf; + + if (u->pipe->temp_file->file.fd == NGX_INVALID_FILE) { + + /* create file for empty 200 response */ + + tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); + if (tf == NULL) { + return; + } + + tf->file.fd = NGX_INVALID_FILE; + tf->file.log = r->connection->log; + tf->path = u->conf->temp_path; + tf->pool = r->pool; + tf->persistent = 1; + + if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, + tf->persistent, tf->clean, tf->access) + != NGX_OK) + { + return; + } + + u->pipe->temp_file = tf; + } + + temp = &u->pipe->temp_file->file.name; + +#if !(NGX_WIN32) + + if (ngx_change_file_access(temp->data, u->conf->store_access) + == NGX_FILE_ERROR) + { + err = ngx_errno; + failed = ngx_change_file_access_n; + name = temp->data; + + goto failed; + } + +#endif + + if (r->upstream->headers_in.last_modified) { + + last_modified = &r->upstream->headers_in.last_modified->value; + + lm = ngx_http_parse_time(last_modified->data, last_modified->len); + + if (lm != NGX_ERROR) { + if (ngx_set_file_time(temp->data, u->pipe->temp_file->file.fd, lm) + != NGX_OK) + { + err = ngx_errno; + failed = ngx_set_file_time_n; + name = temp->data; + + goto failed; + } + } + } + + if (u->conf->store_lengths == NULL) { + + ngx_http_map_uri_to_path(r, &path, &root, 0); + + } else { + if (ngx_http_script_run(r, &path, u->conf->store_lengths->elts, 0, + u->conf->store_values->elts) + == NULL) + { + return; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "upstream stores \"%s\" to \"%s\"", temp->data, path.data); + + failed = ngx_rename_file_n; + name = path.data; + + if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) { + return; + } + + err = ngx_errno; + + if (err == NGX_ENOENT) { + + err = ngx_create_full_path(path.data, + ngx_dir_access(u->conf->store_access)); + if (err == 0) { + if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) { + return; + } + + err = ngx_errno; + } + } + +#if (NGX_WIN32) + + if (err == NGX_EEXIST) { + if (ngx_win32_rename_file(temp, &path, r->pool) != NGX_ERROR) { + + if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) { + return; + } + } + + err = ngx_errno; + } + +#endif + +failed: + + if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", + temp->data); + } + + ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, + "%s \"%s\" failed", failed, name); +} + + +static void ngx_http_upstream_dummy_handler(ngx_event_t *wev) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -118,6 +118,7 @@ typedef struct { size_t temp_file_write_size_conf; ngx_uint_t next_upstream; + ngx_uint_t store_access; ngx_bufs_t bufs; @@ -140,6 +141,10 @@ typedef struct { ngx_str_t location; ngx_str_t url; /* used in proxy_rewrite_location */ + ngx_array_t *store_lengths; + ngx_array_t *store_values; + + signed store:2; unsigned intercept_404:1; unsigned change_buffering:1; @@ -237,6 +242,7 @@ struct ngx_http_upstream_s { ngx_http_cleanup_pt *cleanup; + unsigned store:1; unsigned cachable:1; unsigned accel:1;