# HG changeset patch # User Igor Sysoev # Date 1223841600 -14400 # Node ID b246022ef4543cf779e3a0e040922d069c109171 # Parent b453a4324c60689e0def20c4fcd1311726726640 nginx 0.7.18 *) Change: the "underscores_in_headers" directive; now nginx does not allows underscores in a client request header line names. *) Feature: the ngx_http_secure_link_module. *) Feature: the "real_ip_header" directive supports any header. *) Feature: the "log_subrequest" directive. *) Feature: the $realpath_root variable. *) Feature: the "http_502" and "http_504" parameters of the "proxy_next_upstream" directive. *) Bugfix: the "http_503" parameter of the "proxy_next_upstream" or "fastcgi_next_upstream" directives did not work. *) Bugfix: nginx might send a "Transfer-Encoding: chunked" heaer line for HEAD requests. *) Bugfix: now accept threshold depends on worker_connections. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,29 @@ +Changes with nginx 0.7.18 13 Oct 2008 + + *) Change: the "underscores_in_headers" directive; now nginx does not + allows underscores in a client request header line names. + + *) Feature: the ngx_http_secure_link_module. + + *) Feature: the "real_ip_header" directive supports any header. + + *) Feature: the "log_subrequest" directive. + + *) Feature: the $realpath_root variable. + + *) Feature: the "http_502" and "http_504" parameters of the + "proxy_next_upstream" directive. + + *) Bugfix: the "http_503" parameter of the "proxy_next_upstream" or + "fastcgi_next_upstream" directives did not work. + + *) Bugfix: nginx might send a "Transfer-Encoding: chunked" heaer line + for HEAD requests. + + *) Bugfix: now accept threshold depends on worker_connections. + + Changes with nginx 0.7.17 15 Sep 2008 *) Feature: now the "directio" directive works on Linux. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,30 @@ +Изменения в nginx 0.7.18 13.10.2008 + + *) Изменение: директива underscores_in_headers; теперь nginx по + умолчанию не разрешает подчёркивания в именах строк в заголовке + запроса клиента. + + *) Добавление: модуль ngx_http_secure_link_module. + + *) Добавление: директива real_ip_header поддерживает любой заголовок. + + *) Добавление: директива log_subrequest. + + *) Добавление: переменная $realpath_root. + + *) Добавление: параметры http_502 и http_504 в директиве + proxy_next_upstream. + + *) Исправление: параметр http_503 в директивах proxy_next_upstream или + fastcgi_next_upstream не работал. + + *) Исправление: nginx мог выдавать строку "Transfer-Encoding: chunked" + для запросов HEAD. + + *) Исправление: теперь accept-лимит зависит от числа worker_connections. + + Изменения в nginx 0.7.17 15.09.2008 *) Добавление: директива directio теперь работает на Linux. diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -284,6 +284,12 @@ if [ $HTTP_BROWSER = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_BROWSER_SRCS" fi +if [ $HTTP_SECURE_LINK = YES ]; then + USE_MD5=YES + HTTP_MODULES="$HTTP_MODULES $HTTP_SECURE_LINK_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_SECURE_LINK_SRCS" +fi + if [ $HTTP_FLV = YES ]; then HTTP_MODULES="$HTTP_MODULES $HTTP_FLV_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_FLV_SRCS" diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -77,6 +77,7 @@ HTTP_MEMCACHED=YES HTTP_LIMIT_ZONE=YES HTTP_EMPTY_GIF=YES HTTP_BROWSER=YES +HTTP_SECURE_LINK=NO HTTP_FLV=NO HTTP_GZIP_STATIC=NO HTTP_UPSTREAM_IP_HASH=YES @@ -173,6 +174,7 @@ do --with-http_flv_module) HTTP_FLV=YES ;; --with-http_gzip_static_module) HTTP_GZIP_STATIC=YES ;; --with-http_random_index_module) HTTP_RANDOM_INDEX=YES ;; + --with-http_secure_link_module) HTTP_SECURE_LINK=YES ;; --without-http_charset_module) HTTP_CHARSET=NO ;; --without-http_gzip_module) HTTP_GZIP=NO ;; @@ -290,6 +292,7 @@ cat << END --with-http_flv_module enable ngx_http_flv_module --with-http_gzip_static_module enable ngx_http_gzip_static_module --with-http_random_index_module enable ngx_http_random_index_module + --with-http_secure_link_module enable ngx_http_secure_link_module --with-http_stub_status_module enable ngx_http_stub_status_module --without-http_charset_module disable ngx_http_charset_module diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -425,6 +425,10 @@ HTTP_BROWSER_MODULE=ngx_http_browser_mod HTTP_BROWSER_SRCS=src/http/modules/ngx_http_browser_module.c +HTTP_SECURE_LINK_MODULE=ngx_http_secure_link_module +HTTP_SECURE_LINK_SRCS=src/http/modules/ngx_http_secure_link_module.c + + HTTP_FLV_MODULE=ngx_http_flv_module HTTP_FLV_SRCS=src/http/modules/ngx_http_flv_module.c 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.7.17" +#define NGINX_VERSION "0.7.19" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_config.h b/src/core/ngx_config.h --- a/src/core/ngx_config.h +++ b/src/core/ngx_config.h @@ -110,8 +110,6 @@ typedef intptr_t ngx_flag_t; #define ngx_inline inline #endif -#define NGX_ACCEPT_THRESHOLD 100 - #ifndef INADDR_NONE /* Solaris */ #define INADDR_NONE ((unsigned int) -1) #endif diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -593,7 +593,7 @@ ngx_get_connection(ngx_socket_t s, ngx_l if (c == NULL) { ngx_log_error(NGX_LOG_ALERT, log, 0, - "%ui worker_connections is not enough", + "%ui worker_connections are not enough", ngx_cycle->connection_n); /* ngx_mutex_unlock */ diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c --- a/src/event/ngx_event_accept.c +++ b/src/event/ngx_event_accept.c @@ -82,7 +82,7 @@ ngx_event_accept(ngx_event_t *ev) ngx_atomic_fetch_add(ngx_stat_accepted, 1); #endif - ngx_accept_disabled = NGX_ACCEPT_THRESHOLD + ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n; c = ngx_get_connection(s, ev->log); diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -53,7 +53,8 @@ ngx_http_chunked_header_filter(ngx_http_ if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED || r->headers_out.status == NGX_HTTP_NO_CONTENT || r->headers_out.status == NGX_HTTP_CREATED - || r != r->main) + || r != r->main + || (r->method & NGX_HTTP_HEAD)) { return ngx_http_next_header_filter(r); } 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 @@ -1031,7 +1031,7 @@ ngx_http_fastcgi_process_header(ngx_http part_start = u->buffer.pos; - rc = ngx_http_parse_header_line(r, &u->buffer); + rc = ngx_http_parse_header_line(r, &u->buffer, 1); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http fastcgi parser: %d", rc); @@ -1076,7 +1076,7 @@ ngx_http_fastcgi_process_header(ngx_http f->split_parts->nelts = 0; - rc = ngx_http_parse_header_line(r, &buf); + rc = ngx_http_parse_header_line(r, &buf, 1); h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; diff --git a/src/http/modules/ngx_http_log_module.c b/src/http/modules/ngx_http_log_module.c --- a/src/http/modules/ngx_http_log_module.c +++ b/src/http/modules/ngx_http_log_module.c @@ -28,6 +28,7 @@ struct ngx_http_log_op_s { typedef struct { ngx_str_t name; + ngx_array_t *flushes; ngx_array_t *ops; /* array of ngx_http_log_op_t */ } ngx_http_log_fmt_t; @@ -49,7 +50,7 @@ typedef struct { ngx_http_log_script_t *script; time_t disk_full_time; time_t error_log_time; - ngx_array_t *ops; /* array of ngx_http_log_op_t */ + ngx_http_log_fmt_t *format; } ngx_http_log_t; @@ -113,7 +114,7 @@ static char *ngx_http_log_set_log(ngx_co static char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_log_compile_format(ngx_conf_t *cf, - ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s); + ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s); static char *ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_log_init(ngx_conf_t *cf); @@ -242,9 +243,11 @@ ngx_http_log_handler(ngx_http_request_t continue; } + ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes); + len = 0; - op = log[l].ops->elts; - for (i = 0; i < log[l].ops->nelts; i++) { + op = log[l].format->ops->elts; + for (i = 0; i < log[l].format->ops->nelts; i++) { if (op[i].len == 0) { len += op[i].getlen(r, op[i].data); @@ -271,7 +274,7 @@ ngx_http_log_handler(ngx_http_request_t p = file->pos; - for (i = 0; i < log[l].ops->nelts; i++) { + for (i = 0; i < log[l].format->ops->nelts; i++) { p = op[i].run(r, p, &op[i]); } @@ -290,7 +293,7 @@ ngx_http_log_handler(ngx_http_request_t p = line; - for (i = 0; i < log[l].ops->nelts; i++) { + for (i = 0; i < log[l].format->ops->nelts; i++) { p = op[i].run(r, p, &op[i]); } @@ -726,6 +729,8 @@ ngx_http_log_create_main_conf(ngx_conf_t fmt->name.len = sizeof("combined") - 1; fmt->name.data = (u_char *) "combined"; + fmt->flushes = NULL; + fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t)); if (fmt->ops == NULL) { return NGX_CONF_ERROR; @@ -806,7 +811,7 @@ ngx_http_log_merge_loc_conf(ngx_conf_t * fmt = lmcf->formats.elts; /* the default "combined" format */ - log->ops = fmt[0].ops; + log->format = &fmt[0]; lmcf->combined_used = 1; return NGX_CONF_OK; @@ -900,7 +905,7 @@ ngx_http_log_set_log(ngx_conf_t *cf, ngx if (fmt[i].name.len == name.len && ngx_strcasecmp(fmt[i].name.data, name.data) == 0) { - log->ops = fmt[i].ops; + log->format = &fmt[i]; goto buffer; } } @@ -985,22 +990,28 @@ ngx_http_log_set_format(ngx_conf_t *cf, fmt->name = value[1]; + fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t)); + if (fmt->flushes == NULL) { + return NGX_CONF_ERROR; + } + fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t)); if (fmt->ops == NULL) { return NGX_CONF_ERROR; } - return ngx_http_log_compile_format(cf, fmt->ops, cf->args, 2); + return ngx_http_log_compile_format(cf, fmt->flushes, fmt->ops, cf->args, 2); } static char * -ngx_http_log_compile_format(ngx_conf_t *cf, ngx_array_t *ops, - ngx_array_t *args, ngx_uint_t s) +ngx_http_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes, + ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s) { u_char *data, *p, ch; size_t i, len; ngx_str_t *value, var; + ngx_int_t *flush; ngx_uint_t bracket; ngx_http_log_op_t *op; ngx_http_log_var_t *v; @@ -1114,6 +1125,16 @@ ngx_http_log_compile_format(ngx_conf_t * return NGX_CONF_ERROR; } + if (flushes) { + + flush = ngx_array_push(flushes); + if (flush == NULL) { + return NGX_CONF_ERROR; + } + + *flush = op->data; /* variable index */ + } + found: continue; @@ -1299,7 +1320,7 @@ ngx_http_log_init(ngx_conf_t *cf) *value = ngx_http_combined_fmt; fmt = lmcf->formats.elts; - if (ngx_http_log_compile_format(cf, fmt->ops, &a, 0) + if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0) != NGX_CONF_OK) { return NGX_ERROR; 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 @@ -147,7 +147,9 @@ static ngx_conf_bitmask_t ngx_http_prox { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT }, { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER }, { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 }, + { ngx_string("http_502"), NGX_HTTP_UPSTREAM_FT_HTTP_502 }, { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, + { ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 }, { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, { ngx_null_string, 0 } @@ -1214,7 +1216,7 @@ ngx_http_proxy_process_header(ngx_http_r for ( ;; ) { - rc = ngx_http_parse_header_line(r, &r->upstream->buffer); + rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1); if (rc == NGX_OK) { diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c --- a/src/http/modules/ngx_http_range_filter_module.c +++ b/src/http/modules/ngx_http_range_filter_module.c @@ -654,7 +654,13 @@ ngx_http_range_singlepart_body(ngx_http_ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http range body skip"); + if (buf->in_file) { + buf->file_pos = buf->file_last; + } + buf->pos = buf->last; + buf->sync = 1; + continue; } diff --git a/src/http/modules/ngx_http_realip_module.c b/src/http/modules/ngx_http_realip_module.c --- a/src/http/modules/ngx_http_realip_module.c +++ b/src/http/modules/ngx_http_realip_module.c @@ -9,6 +9,11 @@ #include +#define NGX_HTTP_REALIP_XREALIP 0 +#define NGX_HTTP_REALIP_XFWD 1 +#define NGX_HTTP_REALIP_HEADER 2 + + /* AF_INET only */ typedef struct { @@ -19,7 +24,9 @@ typedef struct { typedef struct { ngx_array_t *from; /* array of ngx_http_realip_from_t */ - ngx_uint_t xfwd; + ngx_uint_t type; + ngx_uint_t hash; + ngx_str_t header; } ngx_http_realip_loc_conf_t; @@ -34,19 +41,13 @@ static ngx_int_t ngx_http_realip_handler static void ngx_http_realip_cleanup(void *data); static char *ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void *ngx_http_realip_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_int_t ngx_http_realip_init(ngx_conf_t *cf); -static ngx_conf_enum_t ngx_http_realip_header[] = { - { ngx_string("X-Forwarded-For"), 1 }, - { ngx_string("X-Real-IP"), 0 }, - { ngx_null_string, 0 } -}; - - static ngx_command_t ngx_http_realip_commands[] = { { ngx_string("set_real_ip_from"), @@ -58,10 +59,10 @@ static ngx_command_t ngx_http_realip_co { ngx_string("real_ip_header"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_enum_slot, + ngx_http_realip, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_realip_loc_conf_t, xfwd), - &ngx_http_realip_header }, + 0, + NULL }, ngx_null_command }; @@ -105,7 +106,9 @@ ngx_http_realip_handler(ngx_http_request u_char *ip, *p; size_t len; in_addr_t addr; - ngx_uint_t i; + ngx_uint_t i, hash; + ngx_list_part_t *part; + ngx_table_elt_t *header; struct sockaddr_in *sin; ngx_connection_t *c; ngx_pool_cleanup_t *cln; @@ -130,7 +133,10 @@ ngx_http_realip_handler(ngx_http_request return NGX_DECLINED; } - if (rlcf->xfwd == 0) { + switch (rlcf->type) { + + case NGX_HTTP_REALIP_XREALIP: + if (r->headers_in.x_real_ip == NULL) { return NGX_DECLINED; } @@ -138,7 +144,10 @@ ngx_http_realip_handler(ngx_http_request len = r->headers_in.x_real_ip->value.len; ip = r->headers_in.x_real_ip->value.data; - } else { + break; + + case NGX_HTTP_REALIP_XFWD: + if (r->headers_in.x_forwarded_for == NULL) { return NGX_DECLINED; } @@ -154,8 +163,46 @@ ngx_http_realip_handler(ngx_http_request break; } } + + break; + + default: /* NGX_HTTP_REALIP_HEADER */ + + part = &r->headers_in.headers.part; + header = part->elts; + + hash = rlcf->hash; + len = rlcf->header.len; + p = rlcf->header.data; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (hash == header[i].hash + && len == header[i].key.len + && ngx_strncmp(p, header[i].lowcase_key, len) == 0) + { + len = header[i].value.len; + ip = header[i].value.data; + + goto found; + } + } + + return NGX_DECLINED; } +found: + c = r->connection; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "realip: \"%s\"", ip); @@ -231,10 +278,10 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx { ngx_http_realip_loc_conf_t *rlcf = conf; - ngx_int_t rc; - ngx_str_t *value; - ngx_inet_cidr_t in_cidr; - ngx_http_realip_from_t *from; + ngx_int_t rc; + ngx_str_t *value; + ngx_inet_cidr_t in_cidr; + ngx_http_realip_from_t *from; if (rlcf->from == NULL) { rlcf->from = ngx_array_create(cf->pool, 2, @@ -271,6 +318,33 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx } +static char * +ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_realip_loc_conf_t *rlcf = conf; + + ngx_str_t *value; + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "X-Real-IP") == 0) { + rlcf->type = NGX_HTTP_REALIP_XREALIP; + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[1].data, "X-Forwarded-For") == 0) { + rlcf->type = NGX_HTTP_REALIP_XFWD; + return NGX_CONF_OK; + } + + rlcf->type = NGX_HTTP_REALIP_HEADER; + rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len); + rlcf->header = value[1]; + + return NGX_CONF_OK; +} + + static void * ngx_http_realip_create_loc_conf(ngx_conf_t *cf) { @@ -285,9 +359,11 @@ ngx_http_realip_create_loc_conf(ngx_conf * set by ngx_pcalloc(): * * conf->from = NULL; + * conf->hash = 0; + * conf->header = { 0, NULL }; */ - conf->xfwd = NGX_CONF_UNSET_UINT; + conf->type = NGX_CONF_UNSET_UINT; return conf; } @@ -303,7 +379,12 @@ ngx_http_realip_merge_loc_conf(ngx_conf_ conf->from = prev->from; } - ngx_conf_merge_uint_value(conf->xfwd, prev->xfwd, 0); + ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP); + + if (conf->header.len == 0) { + conf->hash = prev->hash; + conf->header = prev->header; + } return NGX_CONF_OK; } diff --git a/src/http/modules/ngx_http_secure_link_module.c b/src/http/modules/ngx_http_secure_link_module.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_secure_link_module.c @@ -0,0 +1,193 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +typedef struct { + ngx_str_t secret; +} ngx_http_secure_link_conf_t; + + +static void *ngx_http_secure_link_create_conf(ngx_conf_t *cf); +static char *ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static ngx_int_t ngx_http_secure_link_add_variables(ngx_conf_t *cf); + + +static ngx_command_t ngx_http_secure_link_commands[] = { + + { ngx_string("secure_link_secret"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_secure_link_conf_t, secret), + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_secure_link_module_ctx = { + ngx_http_secure_link_add_variables, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_secure_link_create_conf, /* create location configuration */ + ngx_http_secure_link_merge_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_secure_link_module = { + NGX_MODULE_V1, + &ngx_http_secure_link_module_ctx, /* module context */ + ngx_http_secure_link_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_str_t ngx_http_secure_link = ngx_string("secure_link"); + + +static ngx_int_t +ngx_http_secure_link_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p, *start, *end, *last; + size_t len; + ngx_int_t n; + ngx_uint_t i; + ngx_md5_t md5; + ngx_http_secure_link_conf_t *conf; + u_char hash[16]; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_link_module); + + if (conf->secret.len == 0) { + goto not_found; + } + + p = &r->unparsed_uri.data[1]; + last = r->unparsed_uri.data + r->unparsed_uri.len; + + while (p < last) { + if (*p++ == '/') { + start = p; + goto md5_start; + } + } + + goto not_found; + +md5_start: + + while (p < last) { + if (*p++ == '/') { + end = p - 1; + goto url_start; + } + } + + goto not_found; + +url_start: + + len = last - p; + + if (end - start != 32 || len < 3) { + goto not_found; + } + + ngx_md5_init(&md5); + ngx_md5_update(&md5, p, len); + ngx_md5_update(&md5, conf->secret.data, conf->secret.len); + ngx_md5_final(hash, &md5); + + for (i = 0; i < 16; i++) { + n = ngx_hextoi(&start[2 * i], 2); + if (n == NGX_ERROR || n != hash[i]) { + goto not_found; + } + } + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; + +not_found: + + v->not_found = 1; + + return NGX_OK; +} + + +static void * +ngx_http_secure_link_create_conf(ngx_conf_t *cf) +{ + ngx_http_secure_link_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_secure_link_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + /* + * set by ngx_pcalloc(): + * + * conf->secret = { 0, NULL } + */ + + return conf; +} + + +static char * +ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_secure_link_conf_t *prev = parent; + ngx_http_secure_link_conf_t *conf = child; + + ngx_conf_merge_str_value(conf->secret, prev->secret, ""); + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_secure_link_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var; + + var = ngx_http_add_variable(cf, &ngx_http_secure_link, NGX_HTTP_VAR_NOHASH); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = ngx_http_secure_link_variable; + + return NGX_OK; +} 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.7.17'; +our $VERSION = '0.7.19'; require XSLoader; XSLoader::load('nginx', $VERSION); 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 @@ -72,7 +72,8 @@ ngx_int_t ngx_http_parse_complex_uri(ngx ngx_uint_t merge_slashes); ngx_int_t ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args, ngx_uint_t *flags); -ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b); +ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b, + ngx_uint_t allow_underscores); ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name, ngx_str_t *value); 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 @@ -231,6 +231,13 @@ static ngx_command_t ngx_http_core_comm offsetof(ngx_http_core_srv_conf_t, merge_slashes), NULL }, + { ngx_string("underscores_in_headers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_core_srv_conf_t, underscores_in_headers), + NULL }, + { ngx_string("location"), NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12, ngx_http_core_location, @@ -487,6 +494,13 @@ static ngx_command_t ngx_http_core_comm offsetof(ngx_http_core_loc_conf_t, log_not_found), NULL }, + { ngx_string("log_subrequest"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, log_subrequest), + NULL }, + { ngx_string("recursive_error_pages"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -2527,6 +2541,7 @@ ngx_http_core_create_srv_conf(ngx_conf_t cscf->client_header_buffer_size = NGX_CONF_UNSET_SIZE; cscf->ignore_invalid_headers = NGX_CONF_UNSET; cscf->merge_slashes = NGX_CONF_UNSET; + cscf->underscores_in_headers = NGX_CONF_UNSET; return cscf; } @@ -2605,6 +2620,9 @@ ngx_http_core_merge_srv_conf(ngx_conf_t ngx_conf_merge_value(conf->merge_slashes, prev->merge_slashes, 1); + ngx_conf_merge_value(conf->underscores_in_headers, + prev->underscores_in_headers, 0); + return NGX_CONF_OK; } @@ -2663,6 +2681,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t lcf->msie_padding = NGX_CONF_UNSET; lcf->msie_refresh = NGX_CONF_UNSET; lcf->log_not_found = NGX_CONF_UNSET; + lcf->log_subrequest = NGX_CONF_UNSET; lcf->recursive_error_pages = NGX_CONF_UNSET; lcf->server_tokens = NGX_CONF_UNSET; lcf->types_hash_max_size = NGX_CONF_UNSET_UINT; @@ -2887,6 +2906,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1); ngx_conf_merge_value(conf->msie_refresh, prev->msie_refresh, 0); ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1); + ngx_conf_merge_value(conf->log_subrequest, prev->log_subrequest, 0); ngx_conf_merge_value(conf->recursive_error_pages, prev->recursive_error_pages, 0); ngx_conf_merge_value(conf->server_tokens, prev->server_tokens, 1); @@ -3271,6 +3291,17 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c return NGX_CONF_ERROR; } + if (ngx_strstr(value[1].data, "$realpath_root") + || ngx_strstr(value[1].data, "${realpath_root}")) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the $realpath_root variable may not be used " + "in the \"%V\" directive", + &cmd->name); + + return NGX_CONF_ERROR; + } + lcf->alias = alias; lcf->root = value[1]; 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 @@ -155,6 +155,7 @@ typedef struct { ngx_flag_t ignore_invalid_headers; ngx_flag_t merge_slashes; + ngx_flag_t underscores_in_headers; ngx_http_core_loc_conf_t **named_locations; } ngx_http_core_srv_conf_t; @@ -310,6 +311,7 @@ struct ngx_http_core_loc_conf_s { ngx_flag_t msie_padding; /* msie_padding */ ngx_flag_t msie_refresh; /* msie_refresh */ ngx_flag_t log_not_found; /* log_not_found */ + ngx_flag_t log_subrequest; /* log_subrequest */ ngx_flag_t recursive_error_pages; /* recursive_error_pages */ ngx_flag_t server_tokens; /* server_tokens */ diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -700,7 +700,8 @@ done: ngx_int_t -ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b) +ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b, + ngx_uint_t allow_underscores) { u_char c, ch, *p; ngx_uint_t hash, i; @@ -720,7 +721,7 @@ ngx_http_parse_header_line(ngx_http_requ static u_char lowcase[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0" - "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0_" + "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" @@ -779,6 +780,19 @@ ngx_http_parse_header_line(ngx_http_requ break; } + if (ch == '_') { + if (allow_underscores) { + hash = ngx_hash(hash, ch); + r->lowcase_header[i++] = ch; + i &= (NGX_HTTP_LC_HEADER_LEN - 1); + + } else { + r->invalid_header = 1; + } + + break; + } + if (ch == ':') { r->header_name_end = p; state = sw_space_before_value; 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 @@ -47,6 +47,7 @@ static void ngx_http_lingering_close_han static ngx_int_t ngx_http_post_action(ngx_http_request_t *r); static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error); static void ngx_http_request_done(ngx_http_request_t *r, ngx_int_t error); +static void ngx_http_log_request(ngx_http_request_t *r); static void ngx_http_close_connection(ngx_connection_t *c); static u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len); @@ -902,7 +903,8 @@ ngx_http_process_request_headers(ngx_eve } } - rc = ngx_http_parse_header_line(r, r->header_in); + rc = ngx_http_parse_header_line(r, r->header_in, + cscf->underscores_in_headers); if (rc == NGX_OK) { @@ -1758,6 +1760,17 @@ ngx_http_finalize_request(ngx_http_reque return; } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r != r->main && !r->logged) { + + if (clcf->log_subrequest) { + ngx_http_log_request(r); + } + + r->logged = 1; + } + if (r != r->main || rc == NGX_AGAIN) { if (ngx_http_set_write_handler(r) != NGX_OK) { return; @@ -1856,8 +1869,6 @@ ngx_http_finalize_request(ngx_http_reque return; } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (!ngx_terminate && !ngx_exiting && r->keepalive @@ -2603,14 +2614,11 @@ ngx_http_close_request(ngx_http_request_ static void ngx_http_request_done(ngx_http_request_t *r, ngx_int_t error) { - ngx_log_t *log; - ngx_uint_t i, n; - struct linger linger; - ngx_http_cleanup_t *cln; - ngx_http_log_ctx_t *ctx; - ngx_http_handler_pt *log_handler; - ngx_http_core_loc_conf_t *clcf; - ngx_http_core_main_conf_t *cmcf; + ngx_log_t *log; + struct linger linger; + ngx_http_cleanup_t *cln; + ngx_http_log_ctx_t *ctx; + ngx_http_core_loc_conf_t *clcf; log = r->connection->log; @@ -2645,13 +2653,7 @@ ngx_http_request_done(ngx_http_request_t log->action = "logging request"; - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - - log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts; - n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts; - for (i = 0; i < n; i++) { - log_handler[i](r); - } + ngx_http_log_request(r); log->action = "closing request"; @@ -2684,6 +2686,24 @@ ngx_http_request_done(ngx_http_request_t static void +ngx_http_log_request(ngx_http_request_t *r) +{ + ngx_uint_t i, n; + ngx_http_handler_pt *log_handler; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts; + n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts; + + for (i = 0; i < n; i++) { + log_handler[i](r); + } +} + + +static void ngx_http_close_connection(ngx_connection_t *c) { ngx_pool_t *pool; 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 @@ -464,6 +464,7 @@ struct ngx_http_request_s { unsigned expect_tested:1; unsigned root_tested:1; unsigned done:1; + unsigned logged:1; unsigned utf8:1; unsigned buffered:4; 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 @@ -22,6 +22,10 @@ static void ngx_http_upstream_send_reque ngx_http_upstream_t *u); static void ngx_http_upstream_send_request_handler(ngx_event_t *wev); static void ngx_http_upstream_process_header(ngx_event_t *rev); +static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r, + ngx_http_upstream_t *u); static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c); static void ngx_http_upstream_process_body_in_memory(ngx_event_t *rev); static void ngx_http_upstream_send_response(ngx_http_request_t *r, @@ -282,6 +286,15 @@ static ngx_http_variable_t ngx_http_ups }; +static ngx_http_upstream_next_t ngx_http_upstream_next_errors[] = { + { 500, NGX_HTTP_UPSTREAM_FT_HTTP_500 }, + { 502, NGX_HTTP_UPSTREAM_FT_HTTP_502 }, + { 503, NGX_HTTP_UPSTREAM_FT_HTTP_503 }, + { 504, NGX_HTTP_UPSTREAM_FT_HTTP_504 }, + { 404, NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { 0, 0 } +}; + void ngx_http_upstream_init(ngx_http_request_t *r) { @@ -1047,8 +1060,6 @@ ngx_http_upstream_process_header(ngx_eve ngx_connection_t *c; ngx_http_request_t *r; ngx_http_upstream_t *u; - ngx_http_err_page_t *err_page; - ngx_http_core_loc_conf_t *clcf; ngx_http_upstream_header_t *hh; ngx_http_upstream_main_conf_t *umcf; @@ -1174,85 +1185,21 @@ ngx_http_upstream_process_header(ngx_eve /* rc == NGX_OK */ - if (u->headers_in.status_n >= NGX_HTTP_BAD_REQUEST - && r->subrequest_in_memory) - { - u->buffer.last = u->buffer.pos; - } - - if (u->headers_in.status_n == NGX_HTTP_INTERNAL_SERVER_ERROR) { - - if (u->peer.tries > 1 - && (u->conf->next_upstream & NGX_HTTP_UPSTREAM_FT_HTTP_500)) - { - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_HTTP_500); - return; + if (u->headers_in.status_n >= NGX_HTTP_BAD_REQUEST) { + + if (r->subrequest_in_memory) { + u->buffer.last = u->buffer.pos; } -#if (NGX_HTTP_CACHE) - - if (u->peer.tries == 0 - && u->stale - && (u->conf->use_stale & NGX_HTTP_UPSTREAM_FT_HTTP_500)) - { - ngx_http_upstream_finalize_request(r, u, - ngx_http_send_cached_response(r)); - return; - } - -#endif - } - - if (u->headers_in.status_n == NGX_HTTP_NOT_FOUND) { - - if (u->peer.tries > 1 - && u->conf->next_upstream & NGX_HTTP_UPSTREAM_FT_HTTP_404) - { - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_HTTP_404); + if (ngx_http_upstream_test_next(r, u) == NGX_OK) { return; } - if (u->conf->intercept_404) { - ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND); + if (ngx_http_upstream_intercept_errors(r, u) == NGX_OK) { return; } } - - if (u->headers_in.status_n >= NGX_HTTP_BAD_REQUEST - && u->conf->intercept_errors) - { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - if (clcf->error_pages) { - - err_page = clcf->error_pages->elts; - for (i = 0; i < clcf->error_pages->nelts; i++) { - if (err_page[i].status == (ngx_int_t) u->headers_in.status_n) { - - if (u->headers_in.status_n == NGX_HTTP_UNAUTHORIZED) { - - r->headers_out.www_authenticate = - ngx_list_push(&r->headers_out.headers); - - if (r->headers_out.www_authenticate == NULL) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - *r->headers_out.www_authenticate = - *u->headers_in.www_authenticate; - } - - ngx_http_upstream_finalize_request(r, u, - u->headers_in.status_n); - return; - } - } - } - } - umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); if (u->headers_in.x_accel_redirect) { @@ -1407,6 +1354,101 @@ ngx_http_upstream_process_header(ngx_eve static ngx_int_t +ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ngx_uint_t status; + ngx_http_upstream_next_t *un; + + if (!(u->conf->next_upstream & NGX_HTTP_UPSTREAM_FT_STATUS)) { + return NGX_DECLINED; + } + + status = u->headers_in.status_n; + + for (un = ngx_http_upstream_next_errors; un->status; un++) { + + if (status != un->status) { + continue; + } + + if (u->peer.tries > 1 && (u->conf->next_upstream & un->mask)) { + ngx_http_upstream_next(r, u, un->mask); + return NGX_OK; + } + + if (status == NGX_HTTP_NOT_FOUND && u->conf->intercept_404) { + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND); + return NGX_OK; + } + +#if (NGX_HTTP_CACHE) + + if (u->peer.tries == 0 && u->stale && (u->conf->use_stale & un->mask)) { + ngx_http_upstream_finalize_request(r, u, + ngx_http_send_cached_response(r)); + return NGX_OK; + } + +#endif + } + + return NGX_DECLINED; +} + + +static ngx_int_t +ngx_http_upstream_intercept_errors(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_int_t status; + ngx_uint_t i; + ngx_table_elt_t *h; + ngx_http_err_page_t *err_page; + ngx_http_core_loc_conf_t *clcf; + + if (!u->conf->intercept_errors) { + return NGX_DECLINED; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->error_pages == NULL) { + return NGX_DECLINED; + } + + status = u->headers_in.status_n; + + err_page = clcf->error_pages->elts; + for (i = 0; i < clcf->error_pages->nelts; i++) { + + if (err_page[i].status == status) { + + if (status == NGX_HTTP_UNAUTHORIZED) { + + h = ngx_list_push(&r->headers_out.headers); + + if (h == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + *h = *u->headers_in.www_authenticate; + + r->headers_out.www_authenticate = h; + } + + ngx_http_upstream_finalize_request(r, u, status); + + return NGX_OK; + } + } + + return NGX_DECLINED; +} + + +static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c) { int err; 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 @@ -20,13 +20,20 @@ #define NGX_HTTP_UPSTREAM_FT_TIMEOUT 0x00000004 #define NGX_HTTP_UPSTREAM_FT_INVALID_HEADER 0x00000008 #define NGX_HTTP_UPSTREAM_FT_HTTP_500 0x00000010 -#define NGX_HTTP_UPSTREAM_FT_HTTP_503 0x00000020 -#define NGX_HTTP_UPSTREAM_FT_HTTP_404 0x00000040 -#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00000080 -#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00000100 +#define NGX_HTTP_UPSTREAM_FT_HTTP_502 0x00000020 +#define NGX_HTTP_UPSTREAM_FT_HTTP_503 0x00000040 +#define NGX_HTTP_UPSTREAM_FT_HTTP_504 0x00000080 +#define NGX_HTTP_UPSTREAM_FT_HTTP_404 0x00000100 +#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00000200 +#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00000400 #define NGX_HTTP_UPSTREAM_FT_NOLIVE 0x40000000 #define NGX_HTTP_UPSTREAM_FT_OFF 0x80000000 +#define NGX_HTTP_UPSTREAM_FT_STATUS (NGX_HTTP_UPSTREAM_FT_HTTP_500 \ + |NGX_HTTP_UPSTREAM_FT_HTTP_502 \ + |NGX_HTTP_UPSTREAM_FT_HTTP_503 \ + |NGX_HTTP_UPSTREAM_FT_HTTP_504 \ + |NGX_HTTP_UPSTREAM_FT_HTTP_404) #define NGX_HTTP_UPSTREAM_INVALID_HEADER 40 @@ -267,6 +274,12 @@ struct ngx_http_upstream_s { }; +typedef struct { + ngx_uint_t status; + ngx_uint_t mask; +} ngx_http_upstream_next_t; + + ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -46,6 +46,8 @@ static ngx_int_t ngx_http_variable_is_ar ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_document_root(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_realpath_root(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_request_filename(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_name(ngx_http_request_t *r, @@ -162,6 +164,9 @@ static ngx_http_variable_t ngx_http_cor { ngx_string("document_root"), NULL, ngx_http_variable_document_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + { ngx_string("realpath_root"), NULL, + ngx_http_variable_realpath_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + { ngx_string("query_string"), NULL, ngx_http_variable_request, offsetof(ngx_http_request_t, args), NGX_HTTP_VAR_NOCACHEABLE, 0 }, @@ -1000,6 +1005,61 @@ ngx_http_variable_document_root(ngx_http static ngx_int_t +ngx_http_variable_realpath_root(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + size_t len; + ngx_str_t path; + ngx_http_core_loc_conf_t *clcf; + u_char real[NGX_MAX_PATH]; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->root_lengths == NULL) { + path = clcf->root; + + } else { + if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 1, + clcf->root_values->elts) + == NULL) + { + return NGX_ERROR; + } + + path.data[path.len - 1] = '\0'; + + if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0) + == NGX_ERROR) + { + return NGX_ERROR; + } + } + + if (ngx_realpath(path.data, real) == NULL) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_realpath_n " \"%s\" failed", path.data); + return NGX_ERROR; + } + + len = ngx_strlen(real); + + v->data = ngx_pnalloc(r->pool, len); + if (v->data == NULL) { + return NGX_ERROR; + } + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + ngx_memcpy(v->data, real, len); + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_request_filename(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { 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 @@ -145,6 +145,8 @@ ngx_int_t ngx_set_file_time(u_char *name #endif +#define ngx_realpath(p, r) realpath((char *) p, (char *) r) +#define ngx_realpath_n "realpath()" #define ngx_getcwd(buf, size) (getcwd(buf, size) != NULL) #define ngx_getcwd_n "getcwd()" #define NGX_MAX_PATH PATH_MAX