# HG changeset patch # User Igor Sysoev # Date 1252872000 -14400 # Node ID ed3d382670c78631f25ea55e6322fc77597ab7d1 # Parent f2c782e5161f897fc61a32309de9749943850d0c nginx 0.7.62 *) Security: a segmentation fault might occur in worker process while specially crafted request handling. Thanks to Chris Ries. *) Feature: the $upstream_cache_status variable. *) Bugfix: an expired cached response might stick in the "UPDATING" state. *) Bugfix: a segmentation fault might occur in worker process, if error_log was set to info or debug level. Thanks to Sergey Bochenkov. *) Bugfix: in handling FastCGI headers split in records. *) Bugfix: XSLT filter may fail with message "not well formed XML document" for valid XML document. Thanks to Kuramoto Eiji. *) Bugfix: now in MacOSX, Cygwin, and nginx/Windows locations given by a regular expression are always tested in case insensitive mode. *) Bugfix: now nginx/Windows ignores trailing dots in URI. Thanks to Hugo Leisink. *) Bugfix: name of file specified in --conf-path was not honored during installation; the bug had appeared in 0.6.6. Thanks to Maxim Dounin. *) Bugfix: a 500 error code was returned for invalid login/password while HTTP Basic authentication on Windows. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,8 +1,42 @@ +Changes with nginx 0.7.62 14 Sep 2009 + + *) Security: a segmentation fault might occur in worker process while + specially crafted request handling. + Thanks to Chris Ries. + + *) Feature: the $upstream_cache_status variable. + + *) Bugfix: an expired cached response might stick in the "UPDATING" + state. + + *) Bugfix: a segmentation fault might occur in worker process, if + error_log was set to info or debug level. + Thanks to Sergey Bochenkov. + + *) Bugfix: in handling FastCGI headers split in records. + + *) Bugfix: XSLT filter may fail with message "not well formed XML + document" for valid XML document. + Thanks to Kuramoto Eiji. + + *) Bugfix: now in MacOSX, Cygwin, and nginx/Windows locations given by + a regular expression are always tested in case insensitive mode. + + *) Bugfix: now nginx/Windows ignores trailing dots in URI. + Thanks to Hugo Leisink. + + *) Bugfix: name of file specified in --conf-path was not honored during + installation; the bug had appeared in 0.6.6. + Thanks to Maxim Dounin. + + *) Bugfix: a 500 error code was returned for invalid login/password + while HTTP Basic authentication on Windows. + + Changes with nginx 0.7.61 22 Jun 2009 - *) Bugfix: nginx could not be built on MacOSX 10.6. the bug had - appeared in 0.8.2. + *) Bugfix: nginx could not be built on MacOSX 10.6. *) Bugfix: nginx could not be built --without-http-cache; the bug had appeared in 0.7.60. @@ -23,7 +57,8 @@ Changes with nginx 0.7.60 *) Bugfix: in open_file_cache and proxy/fastcgi cache interaction on start up. - *) Bugfix: open_file_cache might cache open file descriptors too long. + *) Bugfix: open_file_cache might cache open file descriptors too long; + the bug had appeared in 0.7.4. *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request header lines were passed to backend while caching if no @@ -1828,8 +1863,8 @@ Changes with nginx 0.5.12 amd64, sparc, and ppc; the bug had appeared in 0.5.8. *) Bugfix: a segmentation fault might occur in worker process if the - temporarily files were used while working with FastCGI server; the - bug had appeared in 0.5.8. + temporary files were used while working with FastCGI server; the bug + had appeared in 0.5.8. *) Bugfix: a segmentation fault might occur in worker process if the $fastcgi_script_name variable was logged. @@ -2732,8 +2767,8 @@ Changes with nginx 0.3.31 in 0.3.18. *) Bugfix: if the HTTPS protocol was used in the "proxy_pass" directive - and the request body was in temporarily file then the request was - not transferred. + and the request body was in temporary file then the request was not + transferred. *) Bugfix: perl 5.8.8 compatibility. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,41 @@ +Изменения в nginx 0.7.62 14.09.2009 + + *) Безопасность: при обработке специально созданного запроса в рабочем + процессе мог произойти segmentation fault. + Спасибо Chris Ries. + + *) Добавление: переменная $upstream_cache_status. + + *) Исправление: устаревший закэшированный запрос мог залипнуть в + состоянии "UPDATING". + + *) Исправление: при использовании error_log на уровне info или debug в + рабочем процессе мог произойти segmentation fault. + Спасибо Сергею Боченкову. + + *) Исправление: в обработке заголовков ответа, разделённых в + FastCGI-записях. + + *) Исправление: XSLT-фильтр мог выдавать ошибку "not well formed XML + document" для правильного документа. + Спасибо Kuramoto Eiji. + + *) Исправление: в MacOSX, Cygwin и nginx/Windows при проверке + location'ов, заданных регулярным выражением, теперь всегда делается + сравнение без учёта регистра символов. + + *) Исправление: теперь nginx/Windows игнорирует точки в конце URI. + Спасибо Hugo Leisink. + + *) Исправление: имя файла указанного в --conf-path игнорировалось при + установке; ошибка появилась в 0.6.6. + Спасибо Максиму Дунину. + + *) Исправление: при использовании HTTP Basic-аутентификации на Windows + для неверных имени/пароля возвращалась 500-ая ошибка. + + Изменения в nginx 0.7.61 22.06.2009 *) Исправление: nginx не собирался на MacOSX 10.6. @@ -6,9 +43,9 @@ *) Исправление: nginx не собирался с параметром --without-http-cache; ошибка появилась в 0.7.60. - *) Исправление: если было использовался перехват 401 ошибки от бэкенда - и бэкенд не возвращал строку "WWW-Authenticate" в заголовке ответа, - то в рабочем процессе происходил segmentation fault. + *) Исправление: если использовался перехват 401 ошибки от бэкенда и + бэкенд не возвращал строку "WWW-Authenticate" в заголовке ответа, то + в рабочем процессе происходил segmentation fault. Спасибо Евгению Мычло. @@ -23,7 +60,7 @@ на старте. *) Исправление: open_file_cache мог кэшировать открытые файлы очень - долго. + долго; ошибка появилась в 0.7.4. *) Исправление: строки "If-Modified-Since", "If-Range" и им подобные в заголовке запроса клиента передавались бэкенду при кэшировании, если diff --git a/auto/install b/auto/install --- a/auto/install +++ b/auto/install @@ -102,7 +102,7 @@ install: $NGX_OBJS${ngx_dirsep}nginx${ng '\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi_params.default' test -f '\$(DESTDIR)$NGX_CONF_PATH' \ - || cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PREFIX' + || cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PATH' cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PREFIX/nginx.conf.default' test -d '\$(DESTDIR)`dirname "$NGX_PID_PATH"`' \ diff --git a/auto/os/linux b/auto/os/linux --- a/auto/os/linux +++ b/auto/os/linux @@ -18,7 +18,7 @@ CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURC # Linux kernel version version=$((`uname -r \ - | sed 's/^\([^.]*\)\.\([^.]*\)\.\([^.-]*\).*/\1*256*256+\2*256+\3/'`)) + | sed 's/^\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1*256*256+\2*256+\3/'`)) version=${version:-0} 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 7061 -#define NGINX_VERSION "0.7.61" +#define nginx_version 7062 +#define NGINX_VERSION "0.7.62" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_buf.h b/src/core/ngx_buf.h --- a/src/core/ngx_buf.h +++ b/src/core/ngx_buf.h @@ -75,13 +75,13 @@ typedef struct { ngx_chain_t *free; ngx_chain_t *busy; - unsigned sendfile; - unsigned directio; + unsigned sendfile:1; + unsigned directio:1; #if (NGX_HAVE_ALIGNED_DIRECTIO) - unsigned unaligned; + unsigned unaligned:1; #endif - unsigned need_in_memory; - unsigned need_in_temp; + unsigned need_in_memory:1; + unsigned need_in_temp:1; ngx_pool_t *pool; ngx_int_t allocated; 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 @@ -87,7 +87,7 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t * * reserved: * %t ptrdiff_t - * %S null-teminated wchar string + * %S null-terminated wchar string * %C wchar */ 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 @@ -1072,9 +1072,10 @@ ngx_http_fastcgi_reinit_request(ngx_http static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r) { - u_char *p, *start, *last, *part_start; + u_char *p, *msg, *start, *last, + *part_start, *part_end; size_t size; - ngx_str_t *status_line, line, *pattern; + ngx_str_t *status_line, *pattern; ngx_int_t rc, status; ngx_buf_t buf; ngx_uint_t i; @@ -1158,40 +1159,39 @@ ngx_http_fastcgi_process_header(ngx_http if (f->type == NGX_HTTP_FASTCGI_STDERR) { if (f->length) { - line.data = u->buffer.pos; + msg = u->buffer.pos; if (u->buffer.pos + f->length <= u->buffer.last) { - line.len = f->length; u->buffer.pos += f->length; f->length = 0; f->state = ngx_http_fastcgi_st_padding; } else { - line.len = u->buffer.last - u->buffer.pos; f->length -= u->buffer.last - u->buffer.pos; u->buffer.pos = u->buffer.last; } - while (line.data[line.len - 1] == LF - || line.data[line.len - 1] == CR - || line.data[line.len - 1] == '.' - || line.data[line.len - 1] == ' ') - { - line.len--; + for (p = u->buffer.pos - 1; msg < p; p--) { + if (*p != LF && *p != CR && *p != '.' && *p != ' ') { + break; + } } + p++; + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "FastCGI sent in stderr: \"%V\"", &line); + "FastCGI sent in stderr: \"%*s\"", p - msg, msg); flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); if (flcf->catch_stderr) { pattern = flcf->catch_stderr->elts; - line.data[line.len - 1] = '\0'; - for (i = 0; i < flcf->catch_stderr->nelts; i++) { - if (ngx_strstr(line.data, pattern[i].data)) { + if (ngx_strnstr(msg, (char *) pattern[i].data, + p - msg) + != NULL) + { return NGX_HTTP_UPSTREAM_INVALID_HEADER; } } @@ -1244,6 +1244,7 @@ ngx_http_fastcgi_process_header(ngx_http for ( ;; ) { part_start = u->buffer.pos; + part_end = u->buffer.last; rc = ngx_http_parse_header_line(r, &u->buffer, 1); @@ -1444,7 +1445,11 @@ ngx_http_fastcgi_process_header(ngx_http part = ngx_array_push(f->split_parts); part->start = part_start; - part->end = u->buffer.last; + part->end = part_end; + + if (u->buffer.pos < u->buffer.last) { + continue; + } return NGX_AGAIN; } @@ -1454,9 +1459,9 @@ ngx_http_fastcgi_process_header(ngx_http static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) { + u_char *m, *msg; ngx_int_t rc; ngx_buf_t *b, **prev; - ngx_str_t line; ngx_chain_t *cl; ngx_http_request_t *r; ngx_http_fastcgi_ctx_t *f; @@ -1540,30 +1545,27 @@ ngx_http_fastcgi_input_filter(ngx_event_ break; } - line.data = f->pos; + msg = f->pos; if (f->pos + f->length <= f->last) { - line.len = f->length; f->pos += f->length; f->length = 0; f->state = ngx_http_fastcgi_st_padding; } else { - line.len = f->last - f->pos; f->length -= f->last - f->pos; f->pos = f->last; } - while (line.data[line.len - 1] == LF - || line.data[line.len - 1] == CR - || line.data[line.len - 1] == '.' - || line.data[line.len - 1] == ' ') - { - line.len--; + for (m = f->pos - 1; msg < m; m--) { + if (*m != LF && *m != CR && *m != '.' && *m != ' ') { + break; + } } ngx_log_error(NGX_LOG_ERR, p->log, 0, - "FastCGI sent in stderr: \"%V\"", &line); + "FastCGI sent in stderr: \"%*s\"", + m + 1 - msg, msg); if (f->pos == f->last) { break; diff --git a/src/http/modules/ngx_http_xslt_filter_module.c b/src/http/modules/ngx_http_xslt_filter_module.c --- a/src/http/modules/ngx_http_xslt_filter_module.c +++ b/src/http/modules/ngx_http_xslt_filter_module.c @@ -194,7 +194,7 @@ ngx_module_t ngx_http_xslt_filter_modul NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ - ngx_http_xslt_filter_exit, /* exit process */ + ngx_http_xslt_filter_exit, /* exit process */ ngx_http_xslt_filter_exit, /* exit master */ NGX_MODULE_V1_PADDING }; @@ -247,6 +247,7 @@ ngx_http_xslt_header_filter(ngx_http_req static ngx_int_t ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { + int wellFormed; ngx_chain_t *cl; ngx_http_xslt_filter_ctx_t *ctx; @@ -288,9 +289,11 @@ ngx_http_xslt_body_filter(ngx_http_reque ctx->doc->extSubset = NULL; #endif + wellFormed = ctx->ctxt->wellFormed; + xmlFreeParserCtxt(ctx->ctxt); - if (ctx->ctxt->wellFormed) { + if (wellFormed) { return ngx_http_xslt_send(r, ctx, ngx_http_xslt_apply_stylesheet(r, ctx)); } @@ -717,7 +720,7 @@ ngx_http_xslt_sax_error(void *data, cons while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ } ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0, - "libxml2 error: \"%*s\"", n, buf); + "libxml2 error: \"%*s\"", n + 1, buf); } 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.61'; +our $VERSION = '0.7.62'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -13,8 +13,11 @@ #include -#define NGX_HTTP_CACHE_STALE 1 -#define NGX_HTTP_CACHE_UPDATING 2 +#define NGX_HTTP_CACHE_MISS 1 +#define NGX_HTTP_CACHE_EXPIRED 2 +#define NGX_HTTP_CACHE_STALE 3 +#define NGX_HTTP_CACHE_UPDATING 4 +#define NGX_HTTP_CACHE_HIT 5 #define NGX_HTTP_CACHE_KEY_LEN 16 @@ -124,11 +127,12 @@ ngx_int_t ngx_http_cache_send(ngx_http_r void ngx_http_file_cache_free(ngx_http_request_t *r, ngx_temp_file_t *tf); time_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status); - char *ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char *ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +extern ngx_str_t ngx_http_cache_status[]; + #endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */ 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 @@ -1220,10 +1220,7 @@ ngx_http_core_try_files_phase(ngx_http_r ngx_memcpy(p, name, path.len); } - if (ngx_http_set_exten(r) != NGX_OK) { - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_OK; - } + ngx_http_set_exten(r); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "try file uri: \"%V\"", &r->uri); @@ -1633,7 +1630,7 @@ ngx_http_set_content_type(ngx_http_reque } -ngx_int_t +void ngx_http_set_exten(ngx_http_request_t *r) { ngx_int_t i; @@ -1647,14 +1644,14 @@ ngx_http_set_exten(ngx_http_request_t *r r->exten.len = r->uri.len - i - 1; r->exten.data = &r->uri.data[i + 1]; - break; + return; } else if (r->uri.data[i] == '/') { - break; + return; } } - return NGX_OK; + return; } @@ -2079,9 +2076,7 @@ ngx_http_subrequest(ngx_http_request_t * sr->method_name = ngx_http_core_get_method; sr->http_protocol = r->http_protocol; - if (ngx_http_set_exten(sr) != NGX_OK) { - return NGX_ERROR; - } + ngx_http_set_exten(sr); sr->main = r->main; sr->parent = r; @@ -2160,10 +2155,7 @@ ngx_http_internal_redirect(ngx_http_requ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "internal redirect: \"%V?%V\"", uri, &r->args); - if (ngx_http_set_exten(r) != NGX_OK) { - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_DONE; - } + ngx_http_set_exten(r); /* clear the modules contexts */ ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module); @@ -2566,6 +2558,10 @@ ngx_http_core_regex_location(ngx_conf_t err.len = NGX_MAX_CONF_ERRSTR; err.data = errstr; +#if (NGX_HAVE_CASELESS_FILESYSTEM) + caseless = 1; +#endif + clcf->regex = ngx_regex_compile(regex, caseless ? NGX_REGEX_CASELESS: 0, cf->pool, &err); 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 @@ -437,7 +437,7 @@ ngx_int_t ngx_http_core_content_phase(ng void *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash); ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r); -ngx_int_t ngx_http_set_exten(ngx_http_request_t *r); +void ngx_http_set_exten(ngx_http_request_t *r); u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name, size_t *root_length, size_t reserved); ngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r); 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 @@ -35,6 +35,15 @@ static ngx_int_t ngx_http_file_cache_del ngx_str_t *path); +ngx_str_t ngx_http_cache_status[] = { + ngx_string("MISS"), + ngx_string("EXPIRED"), + ngx_string("STALE"), + ngx_string("UPDATING"), + ngx_string("HIT") +}; + + static u_char ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' }; 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 @@ -739,6 +739,7 @@ ngx_http_parse_header_line(ngx_http_requ /* first char */ case sw_start: + r->header_name_start = p; r->invalid_header = 0; switch (ch) { @@ -751,7 +752,6 @@ ngx_http_parse_header_line(ngx_http_requ goto header_done; default: state = sw_name; - r->header_name_start = p; c = lowcase[ch]; @@ -950,9 +950,6 @@ ngx_http_parse_complex_uri(ngx_http_requ sw_slash, sw_dot, sw_dot_dot, -#if (NGX_WIN32) - sw_dot_dot_dot, -#endif sw_quoted, sw_quoted_second } state, quoted_state; @@ -1137,65 +1134,15 @@ ngx_http_parse_complex_uri(ngx_http_requ #endif case '/': state = sw_slash; - u -= 4; - if (u < r->uri.data) { - return NGX_HTTP_PARSE_INVALID_REQUEST; - } - while (*(u - 1) != '/') { - u--; - } - break; - case '%': - quoted_state = state; - state = sw_quoted; - break; - case '?': - r->args_start = p; - goto args; - case '#': - goto done; -#if (NGX_WIN32) - case '.': - state = sw_dot_dot_dot; - *u++ = ch; - break; -#endif - case '+': - r->plus_in_uri = 1; - default: - state = sw_usual; - *u++ = ch; - break; - } - - ch = *p++; - break; - -#if (NGX_WIN32) - case sw_dot_dot_dot: - - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { - state = sw_usual; - *u++ = ch; - ch = *p++; - break; - } - - switch(ch) { - case '\\': - case '/': - state = sw_slash; u -= 5; - if (u < r->uri.data) { - return NGX_HTTP_PARSE_INVALID_REQUEST; - } - while (*u != '/') { - u--; - } - if (u < r->uri.data) { - return NGX_HTTP_PARSE_INVALID_REQUEST; - } - while (*(u - 1) != '/') { + for ( ;; ) { + if (u < r->uri.data) { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + if (*u == '/') { + u++; + break; + } u--; } break; @@ -1218,7 +1165,6 @@ ngx_http_parse_complex_uri(ngx_http_requ ch = *p++; break; -#endif case sw_quoted: r->quoted_uri = 1; @@ -1369,20 +1315,6 @@ ngx_http_parse_unsafe_uri(ngx_http_reque if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) { goto unsafe; } - -#if (NGX_WIN32) - - if (len > 3) { - - /* detect "/.../" */ - - if (p[0] == '.' && p[1] == '.' && p[2] == '.' - && ngx_path_separator(p[3])) - { - goto unsafe; - } - } -#endif } } 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 @@ -763,6 +763,22 @@ ngx_http_process_request_line(ngx_event_ r->args.data = r->args_start; } +#if (NGX_WIN32) + { + u_char *p; + + p = r->uri.data + r->uri.len - 1; + + if (*p == '.') { + + while (--p > r->uri.data && *p == '.') { /* void */ } + + r->uri.len = p + 1 - r->uri.data; + + ngx_http_set_exten(r); + } + } +#endif ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http request line: \"%V\"", &r->request_line); @@ -869,9 +885,10 @@ ngx_http_process_request_line(ngx_event_ static void ngx_http_process_request_headers(ngx_event_t *rev) { + u_char *p; + size_t len; ssize_t n; ngx_int_t rc, rv; - ngx_str_t header; ngx_table_elt_t *h; ngx_connection_t *c; ngx_http_header_t *hh; @@ -911,19 +928,17 @@ ngx_http_process_request_headers(ngx_eve } if (rv == NGX_DECLINED) { - header.len = r->header_in->end - r->header_name_start; - header.data = r->header_name_start; - - if (header.len > NGX_MAX_ERROR_STR - 300) { - header.len = NGX_MAX_ERROR_STR - 300; - header.data[header.len++] = '.'; - header.data[header.len++] = '.'; - header.data[header.len++] = '.'; + len = r->header_in->end - r->header_name_start; + p = r->header_name_start; + + if (len > NGX_MAX_ERROR_STR - 300) { + len = NGX_MAX_ERROR_STR - 300; + p[len++] = '.'; p[len++] = '.'; p[len++] = '.'; } ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent too long header line: \"%V\"", - &header); + "client sent too long header line: \"%*s\"", + len, r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } @@ -945,12 +960,10 @@ ngx_http_process_request_headers(ngx_eve /* there was error while a header line parsing */ - header.len = r->header_end - r->header_name_start; - header.data = r->header_name_start; - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid header line: \"%V\"", - &header); + "client sent invalid header line: \"%*s\"", + r->header_end - r->header_name_start, + r->header_name_start); continue; } @@ -1030,11 +1043,10 @@ ngx_http_process_request_headers(ngx_eve /* rc == NGX_HTTP_PARSE_INVALID_HEADER: "\r" is not followed by "\n" */ - header.len = r->header_end - r->header_name_start; - header.data = r->header_name_start; ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid header line: \"%V\\r...\"", - &header); + "client sent invalid header line: \"%*s\\r...\"", + r->header_end - r->header_name_start, + r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } 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 @@ -1074,11 +1074,7 @@ ngx_http_script_regex_end_code(ngx_http_ return; } - if (ngx_http_set_exten(r) != NGX_OK) { - e->ip = ngx_http_script_exit; - e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; - return; - } + ngx_http_set_exten(r); } e->ip += sizeof(ngx_http_script_regex_end_code_t); 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 @@ -14,6 +14,8 @@ static ngx_int_t ngx_http_upstream_cache ngx_http_upstream_t *u); static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_cache_status(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); #endif static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx); @@ -159,6 +161,12 @@ ngx_http_upstream_header_t ngx_http_ups offsetof(ngx_http_upstream_headers_in_t, last_modified), ngx_http_upstream_copy_last_modified, 0, 0 }, + { ngx_string("ETag"), + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, etag), + ngx_http_upstream_copy_header_line, + offsetof(ngx_http_headers_out_t, etag), 0 }, + { ngx_string("Server"), ngx_http_upstream_process_header_line, offsetof(ngx_http_upstream_headers_in_t, server), @@ -316,6 +324,14 @@ static ngx_http_variable_t ngx_http_ups ngx_http_upstream_response_length_variable, 0, NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, +#if (NGX_HTTP_CACHE) + + { ngx_string("upstream_cache_status"), NULL, + ngx_http_upstream_cache_status, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + +#endif + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -575,21 +591,34 @@ ngx_http_upstream_cache(ngx_http_request c->body_start = u->conf->buffer_size; c->file_cache = u->conf->cache->data; + u->cache_status = NGX_HTTP_CACHE_MISS; + rc = ngx_http_file_cache_open(r); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream cache: %i", rc); - if (rc == NGX_HTTP_CACHE_UPDATING) { + switch (rc) { + + case NGX_HTTP_CACHE_UPDATING: + if (u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) { + u->cache_status = rc; rc = NGX_OK; } else { rc = NGX_HTTP_CACHE_STALE; } + + break; + + case NGX_OK: + u->cache_status = NGX_HTTP_CACHE_HIT; } - if (rc == NGX_OK) { + switch (rc) { + + case NGX_OK: rc = ngx_http_upstream_cache_send(r, u); @@ -597,17 +626,21 @@ ngx_http_upstream_cache(ngx_http_request return rc; } - } else if (rc == NGX_ERROR) { + break; + + case NGX_ERROR: return NGX_ERROR; - } else if (rc == NGX_HTTP_CACHE_STALE) { + case NGX_HTTP_CACHE_STALE: c->valid_sec = 0; - u->stale_cache = 1; u->buffer.start = NULL; - - } else if (rc == NGX_DECLINED) { + u->cache_status = NGX_HTTP_CACHE_EXPIRED; + + break; + + case NGX_DECLINED: if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) { u->buffer.start = NULL; @@ -617,14 +650,20 @@ ngx_http_upstream_cache(ngx_http_request u->buffer.last = u->buffer.pos; } - } else if (rc == NGX_AGAIN) { + break; + + case NGX_AGAIN: u->cacheable = 0; - } else { + break; + + default: /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */ + u->cache_status = NGX_HTTP_CACHE_HIT; + return rc; } @@ -640,6 +679,7 @@ ngx_http_upstream_cache_send(ngx_http_re ngx_int_t rc; ngx_http_cache_t *c; + r->cached = 1; c = r->cache; /* TODO: cache stack */ @@ -1521,12 +1561,15 @@ ngx_http_upstream_test_next(ngx_http_req #if (NGX_HTTP_CACHE) - if (u->stale_cache && (u->conf->cache_use_stale & un->mask)) { + if (u->cache_status == NGX_HTTP_CACHE_EXPIRED + && (u->conf->cache_use_stale & un->mask)) + { ngx_int_t rc; rc = u->reinit_request(r); if (rc == NGX_OK) { + u->cache_status = NGX_HTTP_CACHE_STALE; rc = ngx_http_upstream_cache_send(r, u); } @@ -2007,6 +2050,10 @@ ngx_http_upstream_send_response(ngx_http ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http cacheable: %d", u->cacheable); + if (u->cacheable == 0 && r->cache) { + ngx_http_file_cache_free(r, u->pipe->temp_file); + } + #endif p = u->pipe; @@ -2681,12 +2728,15 @@ ngx_http_upstream_next(ngx_http_request_ #if (NGX_HTTP_CACHE) - if (u->stale_cache && (u->conf->cache_use_stale & ft_type)) { + if (u->cache_status == NGX_HTTP_CACHE_EXPIRED + && (u->conf->cache_use_stale & ft_type)) + { ngx_int_t rc; rc = u->reinit_request(r); if (rc == NGX_OK) { + u->cache_status = NGX_HTTP_CACHE_STALE; rc = ngx_http_upstream_cache_send(r, u); } @@ -3263,7 +3313,7 @@ ngx_http_upstream_copy_last_modified(ngx #if (NGX_HTTP_CACHE) - if (r->cached || r->upstream->cacheable) { + if (r->upstream->cacheable) { r->headers_out.last_modified = ho; r->headers_out.last_modified_time = ngx_http_parse_time(h->value.data, h->value.len); @@ -3714,6 +3764,33 @@ ngx_http_upstream_header_variable(ngx_ht } +#if (NGX_HTTP_CACHE) + +ngx_int_t +ngx_http_upstream_cache_status(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t n; + + if (r->upstream == NULL || r->upstream->cache_status == 0) { + v->not_found = 1; + return NGX_OK; + } + + n = r->upstream->cache_status - 1; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ngx_http_cache_status[n].len; + v->data = ngx_http_cache_status[n].data; + + return NGX_OK; +} + +#endif + + static char * ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { 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 @@ -298,7 +298,7 @@ struct ngx_http_upstream_s { unsigned accel:1; unsigned ssl:1; #if (NGX_HTTP_CACHE) - unsigned stale_cache:1; + unsigned cache_status:3; #endif unsigned buffering:1; diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c --- a/src/os/unix/ngx_process.c +++ b/src/os/unix/ngx_process.c @@ -359,6 +359,7 @@ ngx_signal_handler(int signo) break; case SIGALRM: + ngx_sigalrm = 1; break; case SIGIO: diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c --- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -34,6 +34,7 @@ ngx_uint_t ngx_threaded; sig_atomic_t ngx_reap; sig_atomic_t ngx_sigio; +sig_atomic_t ngx_sigalrm; sig_atomic_t ngx_terminate; sig_atomic_t ngx_quit; sig_atomic_t ngx_debug_quit; @@ -130,10 +131,13 @@ ngx_master_process_cycle(ngx_cycle_t *cy for ( ;; ) { if (delay) { - delay *= 2; + if (ngx_sigalrm) { + delay *= 2; + ngx_sigalrm = 0; + } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, - "temination cycle: %d", delay); + "termination cycle: %d", delay); itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; @@ -492,8 +496,7 @@ ngx_signal_worker_processes(ngx_cycle_t if (kill(ngx_processes[i].pid, signo) == -1) { err = ngx_errno; ngx_log_error(NGX_LOG_ALERT, cycle->log, err, - "kill(%P, %d) failed", - ngx_processes[i].pid, signo); + "kill(%P, %d) failed", ngx_processes[i].pid, signo); if (err == NGX_ESRCH) { ngx_processes[i].exited = 1; diff --git a/src/os/unix/ngx_process_cycle.h b/src/os/unix/ngx_process_cycle.h --- a/src/os/unix/ngx_process_cycle.h +++ b/src/os/unix/ngx_process_cycle.h @@ -39,6 +39,7 @@ extern ngx_uint_t ngx_exiting; extern sig_atomic_t ngx_reap; extern sig_atomic_t ngx_sigio; +extern sig_atomic_t ngx_sigalrm; extern sig_atomic_t ngx_quit; extern sig_atomic_t ngx_debug_quit; extern sig_atomic_t ngx_terminate;