# HG changeset patch # User Igor Sysoev # Date 1236546000 -10800 # Node ID ca8f7f6cab16b29a7be5b2b789a743bad2d8f0a8 # Parent ead634c4b006bf5e709595bcc6d0c2ebd1121586 nginx 0.7.40 *) Feature: the "location" directive supports captures in regular expressions. *) Feature: an "alias" directive with capture references may be used inside a location given by a regular expression with captures. *) Feature: the "server_name" directive supports captures in regular expressions. *) Workaround: the ngx_http_autoindex_module did not show the trailing slash in directories on XFS filesystem; the issue had appeared in 0.7.15. Thanks to Dmitry Kuzmenko. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,21 @@ +Changes with nginx 0.7.40 09 Mar 2009 + + *) Feature: the "location" directive supports captures in regular + expressions. + + *) Feature: an "alias" directive with capture references may be used + inside a location given by a regular expression with captures. + + *) Feature: the "server_name" directive supports captures in regular + expressions. + + *) Workaround: the ngx_http_autoindex_module did not show the trailing + slash in directories on XFS filesystem; the issue had appeared in + 0.7.15. + Thanks to Dmitry Kuzmenko. + + Changes with nginx 0.7.39 02 Mar 2009 *) Bugfix: large response with SSI might hang, if gzipping was enabled; diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,22 @@ +Изменения в nginx 0.7.40 09.03.2009 + + *) Добавление: директива location поддерживает выделения в регулярных + выражениях. + + *) Добавление: директиву alias с ссылками на выделения в регулярных + выражениях можно использовать внутри location'а, заданного + регулярным выражением с выделениями. + + *) Добавление: директива server_name поддерживает выделения в + регулярных выражениях. + + *) Изменение: модуль ngx_http_autoindex_module не показывал последний + слэш для каталогов на файловой системе XFS; ошибка появилась в + 0.7.15. + Спасибо Дмитрию Кузьменко. + + Изменения в nginx 0.7.39 02.03.2009 *) Исправление: при включённом сжатии большие ответы с использованием 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.39" +#define NGINX_VERSION "0.7.40" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/http/modules/ngx_http_rewrite_module.c b/src/http/modules/ngx_http_rewrite_module.c --- a/src/http/modules/ngx_http_rewrite_module.c +++ b/src/http/modules/ngx_http_rewrite_module.c @@ -12,7 +12,6 @@ typedef struct { ngx_array_t *codes; /* uintptr_t */ - ngx_uint_t captures; ngx_uint_t stack_size; ngx_flag_t log; @@ -157,16 +156,6 @@ ngx_http_rewrite_handler(ngx_http_reques return NGX_HTTP_INTERNAL_SERVER_ERROR; } - if (rlcf->captures) { - e->captures = ngx_palloc(r->pool, rlcf->captures * sizeof(int)); - if (e->captures == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - } else { - e->captures = NULL; - } - e->ip = rlcf->codes->elts; e->request = r; e->quote = 1; @@ -436,10 +425,6 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com if (regex->ncaptures) { regex->ncaptures = (regex->ncaptures + 1) * 3; - - if (lcf->captures < regex->ncaptures) { - lcf->captures = regex->ncaptures; - } } regex_end = ngx_http_script_add_code(lcf->codes, @@ -618,11 +603,6 @@ ngx_http_rewrite_if(ngx_conf_t *cf, ngx_ } - if (lcf->captures < nlcf->captures) { - lcf->captures = nlcf->captures; - } - - if (elts != lcf->codes->elts) { if_code = (ngx_http_script_if_code_t *) ((u_char *) if_code + ((u_char *) lcf->codes->elts - elts)); @@ -777,10 +757,6 @@ ngx_http_rewrite_if_condition(ngx_conf_t if (n) { regex->ncaptures = (n + 1) * 3; - - if (lcf->captures < regex->ncaptures) { - lcf->captures = regex->ncaptures; - } } return NGX_CONF_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.39'; +our $VERSION = '0.7.40'; require XSLoader; XSLoader::load('nginx', $VERSION); 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 @@ -1337,7 +1337,7 @@ ngx_http_core_find_location(ngx_http_req ngx_int_t rc; ngx_http_core_loc_conf_t *pclcf; #if (NGX_PCRE) - ngx_int_t n; + ngx_int_t n, len; ngx_uint_t noregex; ngx_http_core_loc_conf_t *clcf, **clcfp; @@ -1371,12 +1371,24 @@ ngx_http_core_find_location(ngx_http_req if (noregex == 0 && pclcf->regex_locations) { + len = 0; + for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "test location: ~ \"%V\"", &(*clcfp)->name); - n = ngx_regex_exec((*clcfp)->regex, &r->uri, NULL, 0); + if ((*clcfp)->captures && r->captures == NULL) { + + len = (NGX_HTTP_MAX_CAPTURES + 1) * 3 * sizeof(int); + + r->captures = ngx_palloc(r->pool, len); + if (r->captures == NULL) { + return NGX_ERROR; + } + } + + n = ngx_regex_exec((*clcfp)->regex, &r->uri, r->captures, len); if (n == NGX_REGEX_NO_MATCHED) { continue; @@ -1394,6 +1406,9 @@ ngx_http_core_find_location(ngx_http_req r->loc_conf = (*clcfp)->loc_conf; + r->ncaptures = len; + r->captures_data = r->uri.data; + /* look up nested locations */ rc = ngx_http_core_find_location(r); @@ -1686,6 +1701,11 @@ ngx_http_map_uri_to_path(ngx_http_reques *root_length = path->len - reserved; last = path->data + *root_length; + + if (alias) { + *last = '\0'; + return last; + } } last = ngx_cpystrn(last, r->uri.data + alias, r->uri.len - alias + 1); @@ -2534,6 +2554,7 @@ ngx_http_core_regex_location(ngx_conf_t } clcf->name = *regex; + clcf->captures = (ngx_regex_capture_count(clcf->regex) > 0); return NGX_OK; @@ -2798,6 +2819,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t #if (NGX_PCRE) sn->regex = NULL; + sn->captures = 0; #endif sn->core_srv_conf = conf; sn->name.len = conf->server_name.len; @@ -3399,6 +3421,7 @@ ngx_http_core_server_name(ngx_conf_t *cf #if (NGX_PCRE) sn->regex = NULL; + sn->captures = 0; #endif sn->core_srv_conf = cscf; sn->name = value[i]; @@ -3425,6 +3448,7 @@ ngx_http_core_server_name(ngx_conf_t *cf return NGX_CONF_ERROR; } + sn->captures = (ngx_regex_capture_count(sn->regex) > 0); sn->name = value[i]; } #else @@ -3477,18 +3501,6 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c return NGX_CONF_ERROR; } -#if (NGX_PCRE) - - if (lcf->regex && alias) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the \"alias\" directive may not be used " - "inside location given by regular expression"); - - return NGX_CONF_ERROR; - } - -#endif - value = cf->args->elts; if (ngx_strstr(value[1].data, "$document_root") @@ -3528,24 +3540,36 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c n = ngx_http_script_variables_count(&lcf->root); - if (n == 0) { - return NGX_CONF_OK; + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + if (n) { + sc.cf = cf; + sc.source = &lcf->root; + sc.lengths = &lcf->root_lengths; + sc.values = &lcf->root_values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } } - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); - - sc.cf = cf; - sc.source = &lcf->root; - sc.lengths = &lcf->root_lengths; - sc.values = &lcf->root_values; - sc.variables = n; - sc.complete_lengths = 1; - sc.complete_values = 1; - - if (ngx_http_script_compile(&sc) != NGX_OK) { +#if (NGX_PCRE) + + if (alias && lcf->regex + && (ngx_regex_capture_count(lcf->regex) <= 0 || sc.ncaptures == 0)) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"alias\" directive must use captures " + "inside location given by regular expression"); + return NGX_CONF_ERROR; } +#endif + return NGX_CONF_OK; } 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 @@ -245,6 +245,7 @@ typedef struct { struct ngx_http_server_name_s { #if (NGX_PCRE) ngx_regex_t *regex; + ngx_uint_t captures; /* unsigned captures:1; */ #endif ngx_http_core_srv_conf_t *core_srv_conf; /* virtual name server conf */ ngx_str_t name; @@ -274,6 +275,8 @@ struct ngx_http_core_loc_conf_s { #if (NGX_PCRE) ngx_regex_t *regex; + + unsigned captures:1; #endif unsigned noname:1; /* "if () {}" block or limit_except */ @@ -289,7 +292,9 @@ struct ngx_http_core_loc_conf_s { #endif ngx_http_location_tree_node_t *static_locations; +#if (NGX_PCRE) ngx_http_core_loc_conf_t **regex_locations; +#endif /* pointer to the modules' loc_conf */ void **loc_conf; diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c --- a/src/http/ngx_http_header_filter_module.c +++ b/src/http/ngx_http_header_filter_module.c @@ -322,12 +322,12 @@ ngx_http_header_filter(ngx_http_request_ if (clcf->port_in_redirect) { #if (NGX_HTTP_SSL) - if (r->connection->ssl) - port = (port == 443) ? 0 : port; - else + if (r->connection->ssl) + port = (port == 443) ? 0 : port; + else #endif - port = (port == 80) ? 0 : port; - } + port = (port == 80) ? 0 : port; + } if (port) { len += sizeof(":65535") - 1; 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 @@ -1611,6 +1611,7 @@ static ngx_int_t ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len) { u_char *server; + size_t ncaptures; ngx_uint_t hash; ngx_http_virtual_names_t *vn; ngx_http_core_loc_conf_t *clcf; @@ -1653,11 +1654,33 @@ ngx_http_find_virtual_server(ngx_http_re name.len = len; name.data = server; + ncaptures = 0; + sn = vn->regex; for (i = 0; i < vn->nregex; i++) { - n = ngx_regex_exec(sn[i].regex, &name, NULL, 0); + if (sn[i].captures && r->captures == NULL) { + + ncaptures = (NGX_HTTP_MAX_CAPTURES + 1) * 3 * sizeof(int); + + r->captures = ngx_palloc(r->pool, ncaptures); + if (r->captures == NULL) { + return NGX_ERROR; + } + + if (server == buf) { + server = ngx_pnalloc(r->pool, len); + if (server == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(server, buf, len); + name.data = server; + } + } + + n = ngx_regex_exec(sn[i].regex, &name, r->captures, ncaptures); if (n == NGX_REGEX_NO_MATCHED) { continue; @@ -1675,6 +1698,9 @@ ngx_http_find_virtual_server(ngx_http_re cscf = sn[i].core_srv_conf; + r->ncaptures = ncaptures; + r->captures_data = server; + goto found; } } 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 @@ -10,6 +10,7 @@ #define NGX_HTTP_MAX_URI_CHANGES 10 #define NGX_HTTP_MAX_SUBREQUESTS 50 +#define NGX_HTTP_MAX_CAPTURES 9 /* must be 2^n */ #define NGX_HTTP_LC_HEADER_LEN 32 @@ -390,6 +391,12 @@ struct ngx_http_request_s { ngx_http_variable_value_t *variables; +#if (NGX_PCRE) + ngx_uint_t ncaptures; + int *captures; + u_char *captures_data; +#endif + size_t limit_rate; /* used to learn the Apache compatible response length without a header */ diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -40,7 +40,9 @@ ngx_http_script_compile(ngx_http_script_ ngx_uint_t i, n, bracket; ngx_http_script_var_code_t *var_code; ngx_http_script_copy_code_t *copy; +#if (NGX_PCRE) ngx_http_script_copy_capture_code_t *copy_capture; +#endif if (sc->flushes && *sc->flushes == NULL) { n = sc->variables ? sc->variables : 1; @@ -89,6 +91,10 @@ ngx_http_script_compile(ngx_http_script_ goto invalid_variable; } +#if (NGX_PCRE) + + /* NGX_HTTP_MAX_CAPTURES is 9 */ + if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') { n = sc->source->data[i] - '0'; @@ -130,6 +136,8 @@ ngx_http_script_compile(ngx_http_script_ continue; } +#endif + if (sc->source->data[i] == '{') { bracket = 1; @@ -519,65 +527,6 @@ ngx_http_script_copy_var_code(ngx_http_s size_t -ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e) -{ - ngx_http_script_copy_capture_code_t *code; - - code = (ngx_http_script_copy_capture_code_t *) e->ip; - - e->ip += sizeof(ngx_http_script_copy_capture_code_t); - - if (code->n < e->ncaptures) { - if ((e->is_args || e->quote) - && (e->request->quoted_uri || e->request->plus_in_uri)) - { - return e->captures[code->n + 1] - e->captures[code->n] - + 2 * ngx_escape_uri(NULL, - &e->line.data[e->captures[code->n]], - e->captures[code->n + 1] - e->captures[code->n], - NGX_ESCAPE_ARGS); - } else { - return e->captures[code->n + 1] - e->captures[code->n]; - } - } - - return 0; -} - - -void -ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e) -{ - u_char *p; - ngx_http_script_copy_capture_code_t *code; - - code = (ngx_http_script_copy_capture_code_t *) e->ip; - - e->ip += sizeof(ngx_http_script_copy_capture_code_t); - - p = e->pos; - - if (code->n < e->ncaptures) { - if ((e->is_args || e->quote) - && (e->request->quoted_uri || e->request->plus_in_uri)) - { - e->pos = (u_char *) ngx_escape_uri(p, - &e->line.data[e->captures[code->n]], - e->captures[code->n + 1] - e->captures[code->n], - NGX_ESCAPE_ARGS); - } else { - e->pos = ngx_copy(p, - &e->line.data[e->captures[code->n]], - e->captures[code->n + 1] - e->captures[code->n]); - } - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script capture: \"%*s\"", e->pos - p, p); -} - - -size_t ngx_http_script_mark_args_code(ngx_http_script_engine_t *e) { e->is_args = 1; @@ -599,7 +548,6 @@ ngx_http_script_start_args_code(ngx_http } - #if (NGX_PCRE) void @@ -628,7 +576,18 @@ ngx_http_script_regex_start_code(ngx_htt e->line.data = e->sp->data; } - rc = ngx_regex_exec(code->regex, &e->line, e->captures, code->ncaptures); + if (code->ncaptures && r->captures == NULL) { + + r->captures = ngx_palloc(r->pool, + (NGX_HTTP_MAX_CAPTURES + 1) * 3 * sizeof(int)); + if (r->captures == NULL) { + e->ip = ngx_http_script_exit; + e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; + return; + } + } + + rc = ngx_regex_exec(code->regex, &e->line, r->captures, code->ncaptures); if (rc == NGX_REGEX_NO_MATCHED) { if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { @@ -637,7 +596,7 @@ ngx_http_script_regex_start_code(ngx_htt &code->name, &e->line); } - e->ncaptures = 0; + r->ncaptures = 0; if (code->test) { if (code->negative_test) { @@ -674,7 +633,8 @@ ngx_http_script_regex_start_code(ngx_htt "\"%V\" matches \"%V\"", &code->name, &e->line); } - e->ncaptures = code->ncaptures; + r->ncaptures = code->ncaptures; + r->captures_data = e->line.data; if (code->test) { if (code->negative_test) { @@ -725,7 +685,7 @@ ngx_http_script_regex_start_code(ngx_htt } for (n = 1; n < (ngx_uint_t) rc; n++) { - e->buf.len += e->captures[2 * n + 1] - e->captures[2 * n]; + e->buf.len += r->captures[2 * n + 1] - r->captures[2 * n]; } } else { @@ -734,8 +694,6 @@ ngx_http_script_regex_start_code(ngx_htt le.ip = code->lengths->elts; le.line = e->line; le.request = r; - le.captures = e->captures; - le.ncaptures = e->ncaptures; le.quote = code->redirect; len = 0; @@ -874,6 +832,84 @@ ngx_http_script_regex_end_code(ngx_http_ e->ip += sizeof(ngx_http_script_regex_end_code_t); } + +size_t +ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e) +{ + int *cap; + u_char *p; + ngx_uint_t n; + ngx_http_request_t *r; + ngx_http_script_copy_capture_code_t *code; + + r = e->request; + + code = (ngx_http_script_copy_capture_code_t *) e->ip; + + e->ip += sizeof(ngx_http_script_copy_capture_code_t); + + n = code->n; + + if (n < r->ncaptures) { + + cap = r->captures; + + if ((e->is_args || e->quote) + && (e->request->quoted_uri || e->request->plus_in_uri)) + { + p = r->captures_data; + + return cap[n + 1] - cap[n] + + 2 * ngx_escape_uri(NULL, &p[cap[n]], cap[n + 1] - cap[n], + NGX_ESCAPE_ARGS); + } else { + return cap[n + 1] - cap[n]; + } + } + + return 0; +} + + +void +ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e) +{ + int *cap; + u_char *p, *pos; + ngx_uint_t n; + ngx_http_request_t *r; + ngx_http_script_copy_capture_code_t *code; + + r = e->request; + + code = (ngx_http_script_copy_capture_code_t *) e->ip; + + e->ip += sizeof(ngx_http_script_copy_capture_code_t); + + n = code->n; + + pos = e->pos; + + if (n < r->ncaptures) { + + cap = r->captures; + p = r->captures_data; + + if ((e->is_args || e->quote) + && (e->request->quoted_uri || e->request->plus_in_uri)) + { + e->pos = (u_char *) ngx_escape_uri(pos, &p[cap[n]], + cap[n + 1] - cap[n], + NGX_ESCAPE_ARGS); + } else { + e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]); + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + "http script capture: \"%*s\"", e->pos - pos, pos); +} + #endif @@ -1133,8 +1169,6 @@ ngx_http_script_complex_value_code(ngx_h le.ip = code->lengths->elts; le.line = e->line; le.request = e->request; - le.captures = e->captures; - le.ncaptures = e->ncaptures; le.quote = e->quote; for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) { diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h --- a/src/http/ngx_http_script.h +++ b/src/http/ngx_http_script.h @@ -30,9 +30,6 @@ typedef struct { unsigned is_args:1; unsigned log:1; - int *captures; - ngx_uint_t ncaptures; - ngx_int_t status; ngx_http_request_t *request; } ngx_http_script_engine_t; diff --git a/src/os/unix/ngx_files.c b/src/os/unix/ngx_files.c --- a/src/os/unix/ngx_files.c +++ b/src/os/unix/ngx_files.c @@ -249,11 +249,6 @@ ngx_open_dir(ngx_str_t *name, ngx_dir_t } dir->valid_info = 0; -#if (NGX_HAVE_D_TYPE) - dir->valid_type = 1; -#else - dir->valid_type = 0; -#endif return NGX_OK; } @@ -267,6 +262,9 @@ ngx_read_dir(ngx_dir_t *dir) if (dir->de) { #if (NGX_HAVE_D_TYPE) dir->type = dir->de->d_type; + dir->valid_type = dir->type ? 1 : 0; +#else + dir->valid_type = 0; #endif return NGX_OK; } 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 @@ -200,10 +200,25 @@ ngx_int_t ngx_read_dir(ngx_dir_t *dir); #if (NGX_HAVE_D_TYPE) +#if (NGX_LINUX) + +/* XFS on Linux does not set dirent.d_type */ + +#define ngx_de_is_dir(dir) \ + (((dir)->type) ? ((dir)->type == DT_DIR) : (S_ISDIR((dir)->info.st_mode))) +#define ngx_de_is_file(dir) \ + (((dir)->type) ? ((dir)->type == DT_REG) : (S_ISREG((dir)->info.st_mode))) +#define ngx_de_is_link(dir) \ + (((dir)->type) ? ((dir)->type == DT_LINK) : (S_ISLNK((dir)->info.st_mode))) + +#else + #define ngx_de_is_dir(dir) ((dir)->type == DT_DIR) #define ngx_de_is_file(dir) ((dir)->type == DT_REG) #define ngx_de_is_link(dir) ((dir)->type == DT_LINK) +#endif /* NGX_LINUX */ + #else #define ngx_de_is_dir(dir) (S_ISDIR((dir)->info.st_mode))