# HG changeset patch # User Igor Sysoev # Date 1238961600 -14400 # Node ID 09f0ef15d5446d925a7b556925235563ef7932f7 # Parent 0cc33540f2e0a6977ce84d59733dee94d51017e2 nginx 0.7.48 *) Feature: the "proxy_cache_key" directive. *) Bugfix: now nginx takes into account the "X-Accel-Expires", "Expires", and "Cache-Control" header lines in a backend response. *) Bugfix: now nginx caches responses for the GET requests only. *) Bugfix: the "fastcgi_cache_key" directive was not inherited. *) Bugfix: the $arg_... variables did not work with SSI subrequests. Thanks to Maxim Dounin. *) Bugfix: nginx could not be built with uclibc library. Thanks to Timothy Redaelli. *) Bugfix: nginx could not be built on OpenBSD; the bug had appeared in 0.7.46. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,25 @@ +Changes with nginx 0.7.48 06 Apr 2009 + + *) Feature: the "proxy_cache_key" directive. + + *) Bugfix: now nginx takes into account the "X-Accel-Expires", + "Expires", and "Cache-Control" header lines in a backend response. + + *) Bugfix: now nginx caches responses for the GET requests only. + + *) Bugfix: the "fastcgi_cache_key" directive was not inherited. + + *) Bugfix: the $arg_... variables did not work with SSI subrequests. + Thanks to Maxim Dounin. + + *) Bugfix: nginx could not be built with uclibc library. + Thanks to Timothy Redaelli. + + *) Bugfix: nginx could not be built on OpenBSD; the bug had + appeared in 0.7.46. + + Changes with nginx 0.7.47 01 Apr 2009 *) Bugfix: nginx could not be built on FreeBSD 6 and early versions; @@ -198,7 +219,7 @@ Changes with nginx 0.7.34 Thanks to Maxim Dounin. *) Bugfix: in a redirect rewrite directive original arguments were - concatenated with new arguments by an "?" rather than an "&"; + concatenated with new arguments by a "?" rather than an "&"; the bug had appeared in 0.1.18. Thanks to Maxim Dounin. @@ -3630,7 +3651,7 @@ Changes with nginx 0.1.18 inherited. *) Bugfix: in a redirect rewrite directive arguments were concatenated - with URI by an "&" rather than an "?". + with URI by an "&" rather than a "?". *) Bugfix: the lines without trailing ";" in the file being included by the ngx_http_geo_module were silently ignored. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,26 @@ +Изменения в nginx 0.7.48 06.04.2009 + + *) Добавление: директива proxy_cache_key. + + *) Исправление: теперь nginx учитывает при кэшировании строки + "X-Accel-Expires", "Expires" и "Cache-Control" в заголовке ответа + бэкенда. + + *) Исправление: теперь nginx кэширует только ответы на запросы GET. + + *) Исправление: директива fastcgi_cache_key не наследовалась. + + *) Исправление: переменные $arg_... не работали с SSI-подзапросами. + Спасибо Максиму Дунину. + + *) Исправление: nginx не собирался с библиотекой uclibc. + Спасибо Timothy Redaelli. + + *) Исправление: nginx не собирался на OpenBSD; ошибка появилась + в 0.7.46. + + Изменения в nginx 0.7.47 01.04.2009 *) Исправление: nginx не собирался на FreeBSD 6 и более ранних версиях; diff --git a/auto/os/linux b/auto/os/linux --- a/auto/os/linux +++ b/auto/os/linux @@ -124,6 +124,19 @@ ngx_feature_test="long mask = 0; . auto/feature +# crypt_r() + +ngx_feature="crypt_r()" +ngx_feature_name="NGX_HAVE_GNU_CRYPT_R" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs=-lcrypt +ngx_feature_test="struct crypt_data cd; + crypt_r(NULL, NULL, &cd);" +. auto/feature + + ngx_include="sys/vfs.h"; . auto/include diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,8 +8,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 007047 -#define NGINX_VERSION "0.7.47" +#define nginx_version 007048 +#define NGINX_VERSION "0.7.48" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" 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 @@ -692,6 +692,39 @@ ngx_strcasestrn(u_char *s1, char *s2, si } +/* + * ngx_strlcasestrn() is intended to search for static substring + * with known length in string until the argument last. The argument n + * must be length of the second substring - 1. + */ + +u_char * +ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n) +{ + ngx_uint_t c1, c2; + + c2 = (ngx_uint_t) *s2++; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + last -= n; + + do { + do { + if (s1 == last) { + return NULL; + } + + c1 = (ngx_uint_t) *s1++; + + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + + } while (c1 != c2); + + } while (ngx_strncasecmp(s1, s2, n) != 0); + + return --s1; +} + + ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n) { diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h --- a/src/core/ngx_string.h +++ b/src/core/ngx_string.h @@ -149,6 +149,7 @@ u_char *ngx_strnstr(u_char *s1, char *s2 u_char *ngx_strstrn(u_char *s1, char *s2, size_t n); u_char *ngx_strcasestrn(u_char *s1, char *s2, size_t n); +u_char *ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n); ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n); ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n); 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 @@ -2064,6 +2064,10 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf ngx_conf_merge_ptr_value(conf->upstream.cache_valid, prev->upstream.cache_valid, NULL); + if (conf->cache_key.value.data == NULL) { + conf->cache_key = prev->cache_key; + } + #endif ngx_conf_merge_value(conf->upstream.pass_request_headers, 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 @@ -64,6 +64,10 @@ typedef struct { ngx_str_t location; ngx_str_t url; +#if (NGX_HTTP_CACHE) + ngx_http_complex_value_t cache_key; +#endif + ngx_http_proxy_vars_t vars; ngx_flag_t redirect; @@ -132,6 +136,8 @@ static char *ngx_http_proxy_store(ngx_co #if (NGX_HTTP_CACHE) static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); #endif static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data); @@ -327,6 +333,13 @@ static ngx_command_t ngx_http_proxy_com 0, NULL }, + { ngx_string("proxy_cache_key"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_http_proxy_cache_key, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("proxy_cache_path"), NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE, ngx_http_file_cache_set_slot, @@ -715,6 +728,15 @@ ngx_http_proxy_create_key(ngx_http_reque return NGX_ERROR; } + if (plcf->cache_key.value.len) { + + if (ngx_http_complex_value(r, &plcf->cache_key, key) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; + } + *key = ctx->vars.key_start; key = ngx_array_push(&r->cache->keys); @@ -2068,6 +2090,10 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t ngx_conf_merge_ptr_value(conf->upstream.cache_valid, prev->upstream.cache_valid, NULL); + if (conf->cache_key.value.data == NULL) { + conf->cache_key = prev->cache_key; + } + #endif if (conf->method.len == 0) { @@ -2738,6 +2764,34 @@ ngx_http_proxy_cache(ngx_conf_t *cf, ngx return NGX_CONF_OK; } + +static char * +ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_proxy_loc_conf_t *plcf = conf; + + ngx_str_t *value; + ngx_http_compile_complex_value_t ccv; + + value = cf->args->elts; + + if (plcf->cache_key.value.len) { + return "is duplicate"; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &plcf->cache_key; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + #endif 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.47'; +our $VERSION = '0.7.48'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -1357,13 +1357,12 @@ ngx_http_file_cache_set_slot(ngx_conf_t if (ngx_strncmp(value[i].data, "levels=", 7) == 0) { - n = 0; p = value[i].data + 7; last = value[i].data + value[i].len; - while (p < last) { + for (n = 0; n < 3 && p < last; n++) { - if (*p > '0' && *p < '6') { + if (*p > '0' && *p < '3') { cache->path->level[n] = *p++ - '0'; cache->path->len += cache->path->level[n] + 1; @@ -1372,20 +1371,11 @@ ngx_http_file_cache_set_slot(ngx_conf_t break; } - if (*p++ == ':') { - - if (n > 2) { - goto invalid_levels; - } - - if (cache->path->level[n] == 0) { - goto invalid_levels; - } - - n++; - + if (*p++ == ':' && n < 2 && p != last) { continue; } + + goto invalid_levels; } goto invalid_levels; 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 @@ -1486,20 +1486,20 @@ ngx_http_parse_multi_header_lines(ngx_ar ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value) { - u_char *p; + u_char *p, *last; if (r->args.len == 0) { return NGX_DECLINED; } - for (p = r->args.data; *p && *p != ' '; p++) { + p = r->args.data; + last = p + r->args.len; - /* - * although r->args.data is not null-terminated by itself, - * however, there is null in the end of request line - */ + for ( /* void */ ; p < last; p++) { - p = ngx_strcasestrn(p, (char *) name, len - 1); + /* we need '=' after name, so drop one char from last */ + + p = ngx_strlcasestrn(p, last - 1, name, len - 1); if (p == NULL) { return NGX_DECLINED; @@ -1509,7 +1509,7 @@ ngx_http_arg(ngx_http_request_t *r, u_ch value->data = p + len + 1; - p = (u_char *) ngx_strchr(p, '&'); + p = ngx_strlchr(p, last, '&'); if (p == NULL) { p = r->args.data + r->args.len; 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 @@ -697,7 +697,6 @@ ngx_http_process_request_line(ngx_event_ r->request_line.len = r->request_end - r->request_start; r->request_line.data = r->request_start; - *r->request_end = '\0'; if (r->args_start) { 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 @@ -70,10 +70,14 @@ static void ngx_http_upstream_finalize_r static ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t - ngx_http_upstream_process_multi_header_lines(ngx_http_request_t *r, + ngx_http_upstream_process_cache_control(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_accel_expires(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r, @@ -184,14 +188,12 @@ ngx_http_upstream_header_t ngx_http_ups ngx_http_upstream_copy_header_line, 0, 1 }, { ngx_string("Cache-Control"), - ngx_http_upstream_process_multi_header_lines, - offsetof(ngx_http_upstream_headers_in_t, cache_control), + ngx_http_upstream_process_cache_control, 0, ngx_http_upstream_copy_multi_header_lines, offsetof(ngx_http_headers_out_t, cache_control), 1 }, { ngx_string("Expires"), - ngx_http_upstream_process_header_line, - offsetof(ngx_http_upstream_headers_in_t, expires), + ngx_http_upstream_process_expires, 0, ngx_http_upstream_copy_header_line, offsetof(ngx_http_headers_out_t, expires), 1 }, @@ -214,8 +216,7 @@ ngx_http_upstream_header_t ngx_http_ups ngx_http_upstream_copy_header_line, 0, 0 }, { ngx_string("X-Accel-Expires"), - ngx_http_upstream_process_header_line, - offsetof(ngx_http_upstream_headers_in_t, x_accel_expires), + ngx_http_upstream_process_accel_expires, 0, ngx_http_upstream_copy_header_line, 0, 0 }, { ngx_string("X-Accel-Redirect"), @@ -531,6 +532,10 @@ ngx_http_upstream_cache(ngx_http_request ngx_int_t rc; ngx_http_cache_t *c; + if (!(r->method & NGX_HTTP_GET)) { + return NGX_DECLINED; + } + c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t)); if (c == NULL) { return NGX_ERROR; @@ -576,6 +581,7 @@ ngx_http_upstream_cache(ngx_http_request } else if (rc == NGX_HTTP_CACHE_STALE) { + c->valid_sec = 0; u->stale_cache = 1; u->buffer.start = NULL; @@ -1910,14 +1916,19 @@ ngx_http_upstream_send_response(ngx_http if (u->cacheable) { time_t now, valid; - valid = ngx_http_file_cache_valid(u->conf->cache_valid, - u->headers_in.status_n); + now = ngx_time(); + + valid = r->cache->valid_sec; + + if (valid == 0) { + valid = ngx_http_file_cache_valid(u->conf->cache_valid, + u->headers_in.status_n); + if (valid) { + r->cache->valid_sec = now + valid; + } + } + if (valid) { - - now = ngx_time(); - - r->cache->valid_sec = now + valid; - r->cache->last_modified = r->headers_out.last_modified_time; r->cache->date = now; r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start); @@ -2793,13 +2804,23 @@ ngx_http_upstream_process_header_line(ng static ngx_int_t -ngx_http_upstream_process_multi_header_lines(ngx_http_request_t *r, +ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_cache_control(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { + u_char *p, *last; + ngx_int_t n; ngx_array_t *pa; ngx_table_elt_t **ph; - pa = (ngx_array_t *) ((char *) &r->upstream->headers_in + offset); + pa = &r->upstream->headers_in.cache_control; if (pa->elts == NULL) { if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK) @@ -2815,14 +2836,125 @@ ngx_http_upstream_process_multi_header_l *ph = h; + if (r->cache == NULL) { + return NGX_OK; + } + + if (r->cache->valid_sec != 0) { + return NGX_OK; + } + + last = h->value.data + h->value.len; + + if (ngx_strlcasestrn(h->value.data, last, (u_char *) "no-cache", 8 - 1) + != NULL) + { + r->upstream->cacheable = 0; + return NGX_OK; + } + + p = ngx_strlcasestrn(h->value.data, last, (u_char *) "max-age=", 8 - 1); + + if (p == NULL) { + return NGX_OK; + } + + n = 0; + + for (p += 8; p < last; p++) { + if (*p == ';' || *p == ' ') { + break; + } + + if (*p >= '0' && *p <= '9') { + n = n * 10 + *p - '0'; + continue; + } + + r->upstream->cacheable = 0; + return NGX_OK; + } + + if (n == 0) { + r->upstream->cacheable = 0; + return NGX_OK; + } + + r->cache->valid_sec = ngx_time() + n; return NGX_OK; } static ngx_int_t -ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, +ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { + time_t expires; + + r->upstream->headers_in.expires = h; + + if (r->cache == NULL) { + return NGX_OK; + } + + if (r->cache->valid_sec != 0) { + return NGX_OK; + } + + expires = ngx_http_parse_time(h->value.data, h->value.len); + + if (expires == NGX_ERROR || expires < ngx_time()) { + r->upstream->cacheable = 0; + return NGX_OK; + } + + r->cache->valid_sec = expires; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_accel_expires(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + u_char *p; + size_t len; + ngx_int_t n; + + r->upstream->headers_in.x_accel_expires = h; + + if (r->cache == NULL) { + return NGX_OK; + } + + len = h->value.len; + p = h->value.data; + + if (p[0] != '@') { + n = ngx_atoi(p, len); + + switch (n) { + case 0: + r->upstream->cacheable = 0; + case NGX_ERROR: + return NGX_OK; + + default: + r->cache->valid_sec = ngx_time() + n; + return NGX_OK; + } + } + + p++; + len--; + + n = ngx_atoi(p, len); + + if (n != NGX_ERROR) { + r->cache->valid_sec = n; + } + return NGX_OK; } diff --git a/src/os/unix/ngx_linux_config.h b/src/os/unix/ngx_linux_config.h --- a/src/os/unix/ngx_linux_config.h +++ b/src/os/unix/ngx_linux_config.h @@ -95,11 +95,6 @@ extern ssize_t sendfile(int s, int fd, i #endif -#ifndef NGX_HAVE_GNU_CRYPT_R -#define NGX_HAVE_GNU_CRYPT_R 1 -#endif - - #ifndef NGX_HAVE_INHERITED_NONBLOCK #define NGX_HAVE_INHERITED_NONBLOCK 0 #endif diff --git a/src/os/unix/ngx_posix_config.h b/src/os/unix/ngx_posix_config.h --- a/src/os/unix/ngx_posix_config.h +++ b/src/os/unix/ngx_posix_config.h @@ -44,6 +44,9 @@ #include #include #include +#if (NGX_HAVE_SYS_PARAM_H) +#include /* statfs() */ +#endif #if (NGX_HAVE_SYS_MOUNT_H) #include /* statfs() */ #endif