# HG changeset patch # User Maxim Dounin # Date 1231683477 -10800 # Node ID d67e93e97b4a6710677c033292ddb386d55949e2 # Parent 4c92e29a737587845131035da40bd8324247bb87# Parent 5da91f7cde939290dccf2e5bc0078e7ee322642f Merge with nginx 0.7.30. diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -211,3 +211,14 @@ a2a3905c04abe7ff22d33176682cdba5eb6cf186 a8e3f1441eec1b608b77f39110e16f0b82b983d5 NGINX_0_7_17 b246022ef4543cf779e3a0e040922d069c109171 NGINX_0_7_18 4b19c15c95eb9f49f48295d139dcdfd77bb22f38 NGINX_0_7_19 +b4f69f2ef02c188a31125d0774191becd3c3a10d NGINX_0_7_20 +ff86d646f9df4cf9ec14662fe09269d57d9598ea NGINX_0_7_21 +ad0a34a8efa6b727cfb4bbc7d94cab0e882fd87b NGINX_0_7_22 +88d3e895bdf94af51c38b5da5a986ff942dd52c5 NGINX_0_7_23 +9da1d9d94d1860d5a209110197fe4826766faa60 NGINX_0_7_24 +e7dbea1ee115c746298309e05501fc13ce942547 NGINX_0_7_25 +21aff1b3da48c501503d5e47d373c6382413c5e4 NGINX_0_7_26 +dac47e9ef0d5fc6ec8cd1e0ff433c86e96d1a358 NGINX_0_7_27 +fd759445d8a890a9db4ab645581358fdce277908 NGINX_0_7_28 +49a0eb7ce20c1114dd25a73373b9ad5f77d02ed7 NGINX_0_7_29 +dc98ed169c03366ef89869d49da3b21b4b6663fe NGINX_0_7_30 diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,148 @@ +Changes with nginx 0.7.30 24 Dec 2008 + + *) Bugfix: a segmentation fault occurred in worker process, if + variables were used in the "fastcgi_pass" and "proxy_pass" + directives and host name must be resolved; the bug had appeared in + 0.7.29. + + +Changes with nginx 0.7.29 24 Dec 2008 + + *) Bugfix: the "fastcgi_pass" and "proxy_pass" directives did not + support variables if unix domain sockets were used. + + *) Bugfixes in subrequest processing; the bugs had appeared in 0.7.25. + + *) Bugfix: a "100 Continue" response was issued for HTTP/1.0 requests; + Thanks to Maxim Dounin. + + *) Bugfix: in memory allocation in the ngx_http_gzip_filter_module on + Cygwin. + + +Changes with nginx 0.7.28 22 Dec 2008 + + *) Change: in memory allocation in the ngx_http_gzip_filter_module. + + *) Change: the default "gzip_buffers" directive values have been + changed to 32 4k or 16 8k from 4 4k/8k. + + +Changes with nginx 0.7.27 15 Dec 2008 + + *) Feature: the "try_files" directive. + + *) Feature: variables support in the "fastcgi_pass" directive. + + *) Feature: now the $geo variable may get an address from a + variable. + Thanks to Andrei Nigmatulin. + + *) Feature: now a location's modifier may be used without space before + name. + + *) Feature: the $upstream_response_length variable. + + *) Bugfix: now a "add_header" directive does not add an empty value. + + *) Bugfix: if zero length static file was requested, then nginx just + closed connection; the bug had appeared in 0.7.25. + + *) Bugfix: a MOVE method could not move file in non-existent directory. + + *) Bugfix: a segmentation fault occurred in worker process, if no one + named location was defined in server, but some one was used in an + error_page directive. + Thanks to Sergey Bochenkov. + + +Changes with nginx 0.7.26 08 Dec 2008 + + *) Bugfix: in subrequest processing; the bug had appeared in 0.7.25. + + +Changes with nginx 0.7.25 08 Dec 2008 + + *) Change: in subrequest processing. + + *) Change: now POSTs without "Content-Length" header line are allowed. + + *) Bugfix: now the "limit_req" and "limit_conn" directives log a + prohibition reason. + + *) Bugfix: in the "delete" parameter of the "geo" directive. + + +Changes with nginx 0.7.24 01 Dec 2008 + + *) Feature: the "if_modified_since" directive. + + *) Bugfix: nginx did not process a FastCGI server response, if the + server send too many messages to stderr before response. + + *) Bugfix: the "$cookie_..." variables did not work in the SSI and the + perl module. + + +Changes with nginx 0.7.23 27 Nov 2008 + + *) Feature: the "delete" and "ranges" parameters in the "geo" directive. + + *) Feature: speeding up loading of geo base with large number of values. + + *) Feature: decrease of memory required for geo base load. + + +Changes with nginx 0.7.22 20 Nov 2008 + + *) Feature: the "none" parameter in the "smtp_auth" directive. + Thanks to Maxim Dounin. + + *) Feature: the "$cookie_..." variables. + + *) Bugfix: the "directio" directive did not work in XFS filesystem. + + *) Bugfix: the resolver did not understand big DNS responses. + Thanks to Zyb. + + +Changes with nginx 0.7.21 11 Nov 2008 + + *) Changes in the ngx_http_limit_req_module. + + *) Feature: the EXSLT support in the ngx_http_xslt_module. + Thanks to Denis F. Latypoff. + + *) Workaround: compatibility with glibc 2.3. + Thanks to Eric Benson and Maxim Dounin. + + *) Bugfix: nginx could not run on MacOSX 10.4 and earlier; the bug had + appeared in 0.7.6. + + +Changes with nginx 0.7.20 10 Nov 2008 + + *) Changes in the ngx_http_gzip_filter_module. + + *) Feature: the ngx_http_limit_req_module. + + *) Bugfix: worker processes might exit on a SIGBUS signal on sparc and + ppc platforms; the bug had appeared in 0.7.3. + Thanks to Maxim Dounin. + + *) Bugfix: the "proxy_pass http://host/some:uri" directives did not + work; the bug had appeared in 0.7.12. + + *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" + error. + + *) Bugfix: the ngx_http_secure_link_module did not work inside + locations, whose names are less than 3 characters. + + *) Bugfix: $server_addr variable might have no value. + + Changes with nginx 0.7.19 13 Oct 2008 *) Bugfix: version number update. @@ -1716,7 +1860,7 @@ Changes with nginx 0.3.55 *) Bugfix: if the request contained "//" or "/./" and escaped symbols after them, then the proxied request was sent unescaped. - *) Bugfix: the $r->headers_in("Cookie") of the ngx_http_perl_module now + *) Bugfix: the $r->header_in("Cookie") of the ngx_http_perl_module now returns all "Cookie" header lines. *) Bugfix: a segmentation fault occurred if diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,149 @@ +Изменения в nginx 0.7.30 24.12.2008 + + *) Исправление: в рабочем процессе происходил segmentation fault, если + в директивах fastcgi_pass и proxy_pass использовались переменные и + имя хоста должно было резолвиться; ошибка появилась в 0.7.29. + + +Изменения в nginx 0.7.29 24.12.2008 + + *) Исправление: директивы fastcgi_pass и proxy_pass не поддерживали + переменные при использовании unix domain сокетов. + + *) Исправления в обработке подзапросов; ошибки появились в 0.7.25. + + *) Исправление: ответ "100 Continue" выдавался для запросов версии + HTTP/1.0; Спасибо Максиму Дунину. + + *) Исправление: в выделении памяти в модуле ngx_http_gzip_filter_module + под Cygwin. + + +Изменения в nginx 0.7.28 22.12.2008 + + *) Изменение: в выделении памяти в модуле ngx_http_gzip_filter_module. + + *) Изменение: значения по умолчанию для директивы gzip_buffers изменены + с 4 4k/8k на 32 4k или 16 8k. + + +Изменения в nginx 0.7.27 15.12.2008 + + *) Добавление: директива try_files. + + *) Добавление: директива fastcgi_pass поддерживает переменные. + + *) Добавление: теперь директива geo может брать адрес из переменной. + Спасибо Андрею Нигматулину. + + *) Добавление: теперь модификатор location'а можно указывать без + пробела перед названием. + + *) Добавление: переменная $upstream_response_length. + + *) Исправление: теперь директива add_header не добавляет пустое + значение. + + *) Исправление: при запросе файла нулевой длины nginx закрывал + соединение, ничего не передав; ошибка появилась в 0.7.25. + + *) Исправление: метод MOVE не мог перемещать файл в несуществующий + каталог. + + *) Исправление: если в сервере не был описан ни один именованный + location, но такой location использовался в директиве error_page, то + в рабочем процессе происходил segmentation fault. + Спасибо Сергею Боченкову. + + +Изменения в nginx 0.7.26 08.12.2008 + + *) Исправление: в обработке подзапросов; ошибка появилась в 0.7.25. + + +Изменения в nginx 0.7.25 08.12.2008 + + *) Изменение: в обработке подзапросов. + + *) Изменение: теперь разрешаются POST'ы без строки "Content-Length" в + заголовке запроса. + + *) Исправление: теперь директивы limit_req и limit_conn указывают + причину запрета запроса. + + *) Исправление: в параметре delete директивы geo. + + +Изменения в nginx 0.7.24 01.12.2008 + + *) Добавление: директива if_modified_since. + + *) Исправление: nginx не обрабатывал ответ FastCGI-сервера, если перед + ответом сервер передавал много сообщений в stderr. + + *) Исправление: переменные "$cookie_..." не работали в SSI and в + перловом модуле. + + +Изменения в nginx 0.7.23 27.11.2008 + + *) Добавление: параметры delete и ranges в директиве geo. + + *) Добавление: ускорение загрузки geo-базы с большим числом значений. + + *) Добавление: уменьшение памяти, необходимой для загрузки geo-базы. + + +Изменения в nginx 0.7.22 20.11.2008 + + *) Добавление: параметр none в директиве smtp_auth. + Спасибо Максиму Дунину. + + *) Добавление: переменные "$cookie_...". + + *) Исправление: директива directio не работала с файловой системой XFS. + + *) Исправление: resolver не понимал большие DNS-ответы. + Спасибо Zyb. + + +Изменения в nginx 0.7.21 11.11.2008 + + *) Изменения в модуле ngx_http_limit_req_module. + + *) Добавление: поддержка EXSLT в модуле ngx_http_xslt_module. + Спасибо Денису Латыпову. + + *) Изменение: совместимость с glibc 2.3. + Спасибо Eric Benson и Максиму Дунину. + + *) Исправление: nginx не запускался на MacOSX 10.4 и более ранних; + ошибка появилась в 0.7.6. + + +Изменения в nginx 0.7.20 10.11.2008 + + *) Изменения в модуле ngx_http_gzip_filter_module. + + *) Добавление: модуль ngx_http_limit_req_module. + + *) Исправление: на платформах sparc и ppc рабочие процессы могли + выходить по сигналу SIGBUS; ошибка появилась в 0.7.3. + Спасибо Максиму Дунину. + + *) Исправление: директивы вида "proxy_pass http://host/some:uri" не + работали; ошибка появилась в 0.7.12. + + *) Исправление: при использовании HTTPS запросы могли завершаться с + ошибкой "bad write retry". + + *) Исправление: модуль ngx_http_secure_link_module не работал внутри + location'ов с именами меньше 3 символов. + + *) Исправление: переменная $server_addr могла не иметь значения. + + Изменения в nginx 0.7.19 13.10.2008 *) Исправление: обновление номера версии. @@ -1763,7 +1908,7 @@ закодированные символы в виде "%XX", то проксируемый запрос передавался незакодированным. - *) Исправление: метод $r->headers_in("Cookie") модуля + *) Исправление: метод $r->header_in("Cookie") модуля ngx_http_perl_module теперь возвращает все строки "Cookie" в заголовке запроса. diff --git a/auto/lib/libxslt/conf b/auto/lib/libxslt/conf --- a/auto/lib/libxslt/conf +++ b/auto/lib/libxslt/conf @@ -88,3 +88,68 @@ END exit 1 fi + + + ngx_feature="libexslt" + ngx_feature_name=NGX_HAVE_EXSLT + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path="/usr/include/libxml2" + ngx_feature_libs="-lexslt" + ngx_feature_test="exsltRegisterAll();" + . auto/feature + +if [ $ngx_found = no ]; then + + # FreeBSD port + + ngx_feature="libexslt in /usr/local/" + ngx_feature_path="/usr/local/include/libxml2 /usr/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lexslt" + else + ngx_feature_libs="-L/usr/local/lib -lexslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # NetBSD port + + ngx_feature="libexslt in /usr/pkg/" + ngx_feature_path="/usr/pkg/include/libxml2 /usr/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lexslt" + else + ngx_feature_libs="-L/usr/pkg/lib -lexslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # MacPorts + + ngx_feature="libexslt in /opt/local/" + ngx_feature_path="/opt/local/include/libxml2 /opt/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lexslt" + else + ngx_feature_libs="-L/opt/local/lib -lexslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS -lexslt" +fi diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -203,6 +203,11 @@ if [ $HTTP_LIMIT_ZONE = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_ZONE_SRCS" fi +if [ $HTTP_LIMIT_REQ = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_REQ_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_REQ_SRCS" +fi + if [ $HTTP_REALIP = YES ]; then have=NGX_HTTP_REALIP . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_REALIP_MODULE" diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -75,6 +75,7 @@ HTTP_FASTCGI=YES HTTP_PERL=NO HTTP_MEMCACHED=YES HTTP_LIMIT_ZONE=YES +HTTP_LIMIT_REQ=YES HTTP_EMPTY_GIF=YES HTTP_BROWSER=YES HTTP_SECURE_LINK=NO @@ -192,6 +193,7 @@ do --without-http_fastcgi_module) HTTP_FASTCGI=NO ;; --without-http_memcached_module) HTTP_MEMCACHED=NO ;; --without-http_limit_zone_module) HTTP_LIMIT_ZONE=NO ;; + --without-http_limit_req_module) HTTP_LIMIT_REQ=NO ;; --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO ;; --without-http_browser_module) HTTP_BROWSER=NO ;; --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;; @@ -310,6 +312,7 @@ cat << END --without-http_fastcgi_module disable ngx_http_fastcgi_module --without-http_memcached_module disable ngx_http_memcached_module --without-http_limit_zone_module disable ngx_http_limit_zone_module + --without-http_limit_req_module disable ngx_http_limit_req_module --without-http_empty_gif_module disable ngx_http_empty_gif_module --without-http_browser_module disable ngx_http_browser_module --without-http_upstream_ip_hash_module diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -417,6 +417,10 @@ HTTP_LIMIT_ZONE_MODULE=ngx_http_limit_zo HTTP_LIMIT_ZONE_SRCS=src/http/modules/ngx_http_limit_zone_module.c +HTTP_LIMIT_REQ_MODULE=ngx_http_limit_req_module +HTTP_LIMIT_REQ_SRCS=src/http/modules/ngx_http_limit_req_module.c + + HTTP_EMPTY_GIF_MODULE=ngx_http_empty_gif_module HTTP_EMPTY_GIF_SRCS=src/http/modules/ngx_http_empty_gif_module.c diff --git a/conf/nginx.conf b/conf/nginx.conf --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -18,7 +18,7 @@ http { include mime.types; default_type application/octet-stream; - #log_format main '$remote_addr - $remote_user [$time_local] $request ' + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '"$status" $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -191,6 +191,8 @@ static char **ngx_os_environ; int ngx_cdecl main(int argc, char *const *argv) { + char *p; + ssize_t n; ngx_int_t i; ngx_log_t *log; ngx_cycle_t *cycle, init_cycle; @@ -240,23 +242,30 @@ main(int argc, char *const *argv) } if (ngx_show_version) { - ngx_write_fd(ngx_stderr_fileno, "nginx version: " NGINX_VER CRLF, - sizeof("nginx version: " NGINX_VER CRLF) - 1); + + p = "nginx version: " NGINX_VER CRLF; + n = sizeof("nginx version: " NGINX_VER CRLF) - 1; + + if (ngx_write_fd(ngx_stderr_fileno, p, n) != n) { + return 1; + } if (ngx_show_configure) { #ifdef NGX_COMPILER - ngx_write_fd(ngx_stderr_fileno, "built by " NGX_COMPILER CRLF, - sizeof("built by " NGX_COMPILER CRLF) - 1); + p = "built by " NGX_COMPILER CRLF; + n = sizeof("built by " NGX_COMPILER CRLF) - 1; + + if (ngx_write_fd(ngx_stderr_fileno, p, n) != n) { + return 1; + } #endif -#ifndef __WATCOMC__ - - /* OpenWatcomC could not build the long NGX_CONFIGURE string */ + p = "configure arguments: " NGX_CONFIGURE CRLF; + n = sizeof("configure arguments :" NGX_CONFIGURE CRLF) - 1; - ngx_write_fd(ngx_stderr_fileno, - "configure arguments: " NGX_CONFIGURE CRLF, - sizeof("configure arguments :" NGX_CONFIGURE CRLF) - 1); -#endif + if (ngx_write_fd(ngx_stderr_fileno, p, n) != n) { + return 1; + } } if (!ngx_test_config) { 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.19" +#define NGINX_VERSION "0.7.30" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_buf.c b/src/core/ngx_buf.c --- a/src/core/ngx_buf.c +++ b/src/core/ngx_buf.c @@ -201,12 +201,6 @@ ngx_chain_update_chains(ngx_chain_t **fr break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if ((*busy)->buf->zerocopy_busy) { - break; - } -#endif - if ((*busy)->buf->tag != tag) { *busy = (*busy)->next; continue; 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 @@ -51,8 +51,6 @@ struct ngx_buf_s { unsigned last_shadow:1; unsigned temp_file:1; - unsigned zerocopy_busy:1; - /* STUB */ int num; }; diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c --- a/src/core/ngx_conf_file.c +++ b/src/core/ngx_conf_file.c @@ -98,8 +98,8 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t char *rv; ngx_fd_t fd; ngx_int_t rc; - ngx_buf_t *b; - ngx_conf_file_t *prev; + ngx_buf_t buf; + ngx_conf_file_t *prev, conf_file; enum { parse_file = 0, parse_block, @@ -125,32 +125,24 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t prev = cf->conf_file; - cf->conf_file = ngx_palloc(cf->pool, sizeof(ngx_conf_file_t)); - if (cf->conf_file == NULL) { - return NGX_CONF_ERROR; - } + cf->conf_file = &conf_file; if (ngx_fd_info(fd, &cf->conf_file->file.info) == -1) { ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, ngx_fd_info_n " \"%s\" failed", filename->data); } - b = ngx_calloc_buf(cf->pool); - if (b == NULL) { - return NGX_CONF_ERROR; + cf->conf_file->buffer = &buf; + + buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log); + if (buf.start == NULL) { + goto failed; } - cf->conf_file->buffer = b; - - b->start = ngx_alloc(NGX_CONF_BUFFER, cf->log); - if (b->start == NULL) { - return NGX_CONF_ERROR; - } - - b->pos = b->start; - b->last = b->start; - b->end = b->last + NGX_CONF_BUFFER; - b->temporary = 1; + buf.pos = buf.start; + buf.last = buf.start; + buf.end = buf.last + NGX_CONF_BUFFER; + buf.temporary = 1; cf->conf_file->file.fd = fd; cf->conf_file->file.name.len = filename->len; @@ -256,9 +248,9 @@ failed: done: if (filename) { - ngx_free(cf->conf_file->buffer->start); - - cf->conf_file = prev; + if (cf->conf_file->buffer->start) { + ngx_free(cf->conf_file->buffer->start); + } if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, @@ -266,6 +258,8 @@ done: cf->conf_file->file.name.data); return NGX_CONF_ERROR; } + + cf->conf_file = prev; } if (rc == NGX_ERROR) { @@ -834,7 +828,7 @@ ngx_conf_full_name(ngx_cycle_t *cycle, n name->len = len + old.len; name->data = ngx_pnalloc(cycle->pool, name->len + 1); if (name->data == NULL) { - return NGX_ERROR; + return NGX_ERROR; } p = ngx_cpymem(name->data, prefix, len); @@ -912,6 +906,7 @@ ngx_conf_open_file(ngx_cycle_t *cycle, n static void ngx_conf_flush_files(ngx_cycle_t *cycle) { + ssize_t n, len; ngx_uint_t i; ngx_list_part_t *part; ngx_open_file_t *file; @@ -932,11 +927,24 @@ ngx_conf_flush_files(ngx_cycle_t *cycle) i = 0; } - if (file[i].buffer == NULL || file[i].pos - file[i].buffer == 0) { + len = file[i].pos - file[i].buffer; + + if (file[i].buffer == NULL || len == 0) { continue; } - ngx_write_fd(file[i].fd, file[i].buffer, file[i].pos - file[i].buffer); + n = ngx_write_fd(file[i].fd, file[i].buffer, len); + + if (n == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_write_fd_n " to \"%s\" failed", + file[i].name.data); + + } else if (n != len) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + file[i].name.data, n, len); + } } } @@ -945,31 +953,47 @@ void ngx_cdecl ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err, char *fmt, ...) { - u_char errstr[NGX_MAX_CONF_ERRSTR], *buf, *last; + u_char errstr[NGX_MAX_CONF_ERRSTR], *p, *last; va_list args; last = errstr + NGX_MAX_CONF_ERRSTR; va_start(args, fmt); - buf = ngx_vsnprintf(errstr, last - errstr, fmt, args); + p = ngx_vsnprintf(errstr, last - errstr, fmt, args); va_end(args); - *buf = '\0'; + if (err) { + + if (p > last - 50) { + + /* leave a space for an error code */ - if (err) { - buf = ngx_snprintf(buf, last - buf - 1, " (%d: ", err); - buf = ngx_strerror_r(err, buf, last - buf - 1); - *buf++ = ')'; - *buf = '\0'; + p = last - 50; + *p++ = '.'; + *p++ = '.'; + *p++ = '.'; + } + +#if (NGX_WIN32) + p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000) + ? " (%d: " : " (%Xd: ", err); +#else + p = ngx_snprintf(p, last - p, " (%d: ", err); +#endif + + p = ngx_strerror_r(err, p, last - p); + + *p++ = ')'; } if (cf->conf_file == NULL) { - ngx_log_error(level, cf->log, 0, "%s", errstr); + ngx_log_error(level, cf->log, 0, "%*s", p - errstr, errstr); return; } - ngx_log_error(level, cf->log, 0, "%s in %s:%ui", - errstr, cf->conf_file->file.name.data, cf->conf_file->line); + ngx_log_error(level, cf->log, 0, "%*s in %s:%ui", + p - errstr, errstr, + cf->conf_file->file.name.data, cf->conf_file->line); } diff --git a/src/core/ngx_conf_file.h b/src/core/ngx_conf_file.h --- a/src/core/ngx_conf_file.h +++ b/src/core/ngx_conf_file.h @@ -71,7 +71,7 @@ #define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */ -#define NGX_MAX_CONF_ERRSTR 256 +#define NGX_MAX_CONF_ERRSTR 1024 struct ngx_command_s { diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -996,6 +996,7 @@ ngx_test_lockfile(u_char *file, ngx_log_ void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user) { + ssize_t n, len; ngx_fd_t fd; ngx_uint_t i; ngx_list_part_t *part; @@ -1019,9 +1020,23 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx continue; } - if (file[i].buffer && file[i].pos - file[i].buffer != 0) { - ngx_write_fd(file[i].fd, file[i].buffer, - file[i].pos - file[i].buffer); + len = file[i].pos - file[i].buffer; + + if (file[i].buffer && len != 0) { + + n = ngx_write_fd(file[i].fd, file[i].buffer, len); + + if (n == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_write_fd_n " to \"%s\" failed", + file[i].name.data); + + } else if (n != len) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + file[i].name.data, n, len); + } + file[i].pos = file[i].buffer; } diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -487,11 +487,13 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_ #if !(NGX_WIN32) - if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, - ngx_change_file_access_n " \"%s\" failed", src->data); - err = 0; - goto failed; + if (ext->access) { + if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_change_file_access_n " \"%s\" failed", src->data); + err = 0; + goto failed; + } } #endif @@ -517,7 +519,7 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_ goto failed; } - err = ngx_create_full_path(to->data, ngx_dir_access(ext->access)); + err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access)); if (err) { ngx_log_error(NGX_LOG_CRIT, ext->log, err, @@ -561,12 +563,14 @@ failed: } } - if (err) { + if (err && ext->log_rename_error) { ngx_log_error(NGX_LOG_CRIT, ext->log, err, ngx_rename_file_n " \"%s\" to \"%s\" failed", src->data, to->data); } + ext->rename_error = err; + return NGX_ERROR; } diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h --- a/src/core/ngx_file.h +++ b/src/core/ngx_file.h @@ -60,11 +60,14 @@ typedef struct { typedef struct { ngx_uint_t access; + ngx_uint_t path_access; time_t time; ngx_fd_t fd; + ngx_err_t rename_error; unsigned create_path:1; unsigned delete_file:1; + unsigned log_rename_error:1; ngx_log_t *log; } ngx_ext_rename_file_t; diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c --- a/src/core/ngx_inet.c +++ b/src/core/ngx_inet.c @@ -251,7 +251,7 @@ ngx_parse_unix_domain_url(ngx_pool_t *po static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u) { - u_char *p, *host, *port, *last, *uri; + u_char *p, *host, *port, *last, *uri, *args; size_t len; ngx_int_t n; struct hostent *h; @@ -264,7 +264,18 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx port = ngx_strlchr(host, last, ':'); - uri = ngx_strlchr(port ? port : host, last, '/'); + uri = ngx_strlchr(host, last, '/'); + + args = ngx_strlchr(host, last, '?'); + + if (args) { + if (uri == NULL) { + uri = args; + + } else if (args < uri) { + uri = args; + } + } if (uri) { if (u->listen || !u->uri_part) { @@ -276,6 +287,10 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx u->uri.data = uri; last = uri; + + if (uri < port) { + port = NULL; + } } if (port) { diff --git a/src/core/ngx_log.c b/src/core/ngx_log.c --- a/src/core/ngx_log.c +++ b/src/core/ngx_log.c @@ -127,18 +127,10 @@ ngx_log_error_core(ngx_uint_t level, ngx } #if (NGX_WIN32) - - if ((unsigned) err >= 0x80000000) { - p = ngx_snprintf(p, last - p, " (%Xd: ", err); - - } else { - p = ngx_snprintf(p, last - p, " (%d: ", err); - } - + p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000) + ? " (%d: " : " (%Xd: ", err); #else - p = ngx_snprintf(p, last - p, " (%d: ", err); - #endif p = ngx_strerror_r(err, p, last - p); @@ -158,7 +150,7 @@ ngx_log_error_core(ngx_uint_t level, ngx ngx_linefeed(p); - ngx_write_fd(log->file->fd, errstr, p - errstr); + (void) ngx_write_fd(log->file->fd, errstr, p - errstr); } diff --git a/src/core/ngx_open_file_cache.h b/src/core/ngx_open_file_cache.h --- a/src/core/ngx_open_file_cache.h +++ b/src/core/ngx_open_file_cache.h @@ -12,6 +12,9 @@ #define _NGX_OPEN_FILE_CACHE_H_INCLUDED_ +#define NGX_OPEN_FILE_DIRECTIO_OFF NGX_MAX_OFF_T_VALUE + + typedef struct { ngx_fd_t fd; ngx_file_uniq_t uniq; diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c --- a/src/core/ngx_output_chain.c +++ b/src/core/ngx_output_chain.c @@ -13,6 +13,17 @@ #define NGX_SENDFILE_LIMIT 4096 #endif +/* + * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly + * to an application memory from a device if parameters are aligned + * to device sector boundary(512 bytes). They fallback to usual read + * operation if the parameters are not aligned. + * Linux allows DIRECTIO only if the parameters are aligned to a filesystem + * sector boundary, otherwise it returns EINVAL. The sector size is + * usually 512 bytes, however, on XFS it may be 4096 bytes. + */ +#define NGX_DIRECTIO_BLOCK 4096 + #define NGX_NONE 1 @@ -327,7 +338,7 @@ ngx_output_chain_align_file_buf(ngx_outp ctx->directio = 1; - size = (size_t) (in->file_pos - (in->file_pos & ~511)); + size = (size_t) (in->file_pos - (in->file_pos & ~(NGX_DIRECTIO_BLOCK - 1))); if (size == 0) { @@ -338,7 +349,7 @@ ngx_output_chain_align_file_buf(ngx_outp size = (size_t) bsize; } else { - size = 512 - size; + size = NGX_DIRECTIO_BLOCK - size; if ((off_t) size > bsize) { size = (size_t) bsize; @@ -413,7 +424,7 @@ ngx_output_chain_get_buf(ngx_output_chai * userland buffer direct usage conjunctly with directio */ - b->start = ngx_pmemalign(ctx->pool, size, 512); + b->start = ngx_pmemalign(ctx->pool, size, NGX_DIRECTIO_BLOCK); if (b->start == NULL) { return NGX_ERROR; } diff --git a/src/core/ngx_palloc.c b/src/core/ngx_palloc.c --- a/src/core/ngx_palloc.c +++ b/src/core/ngx_palloc.c @@ -91,6 +91,26 @@ ngx_destroy_pool(ngx_pool_t *pool) } +void +ngx_reset_pool(ngx_pool_t *pool) +{ + ngx_pool_t *p; + ngx_pool_large_t *l; + + for (l = pool->large; l; l = l->next) { + if (l->alloc) { + ngx_free(l->alloc); + } + } + + pool->large = NULL; + + for (p = pool; p; p = p->d.next) { + p->d.last = (u_char *) p + sizeof(ngx_pool_t); + } +} + + void * ngx_palloc(ngx_pool_t *pool, size_t size) { @@ -171,6 +191,7 @@ ngx_palloc_block(ngx_pool_t *pool, size_ new->d.next = NULL; m += sizeof(ngx_pool_data_t); + m = ngx_align_ptr(m, NGX_ALIGNMENT); new->d.last = m + size; current = pool->current; diff --git a/src/core/ngx_palloc.h b/src/core/ngx_palloc.h --- a/src/core/ngx_palloc.h +++ b/src/core/ngx_palloc.h @@ -14,7 +14,6 @@ /* * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86. - * On FreeBSD 5.x it allows to use the zero copy sending. * On Windows NT it decreases a number of locked pages in a kernel. */ #define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) @@ -73,6 +72,7 @@ void *ngx_calloc(size_t size, ngx_log_t ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log); void ngx_destroy_pool(ngx_pool_t *pool); +void ngx_reset_pool(ngx_pool_t *pool); void *ngx_palloc(ngx_pool_t *pool, size_t size); void *ngx_pnalloc(ngx_pool_t *pool, size_t size); diff --git a/src/core/ngx_radix_tree.c b/src/core/ngx_radix_tree.c --- a/src/core/ngx_radix_tree.c +++ b/src/core/ngx_radix_tree.c @@ -42,13 +42,13 @@ ngx_radix_tree_create(ngx_pool_t *pool, } /* - * The preallocation the first nodes: 0, 1, 00, 01, 10, 11, 000, 001, etc. - * increases the TLB hits even if for the first lookup iterations. - * On the 32-bit platforms the 7 preallocated bits takes continuous 4K, - * 8 - 8K, 9 - 16K, etc. On the 64-bit platforms the 6 preallocated bits + * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc. + * increases TLB hits even if for first lookup iterations. + * On 32-bit platforms the 7 preallocated bits takes continuous 4K, + * 8 - 8K, 9 - 16K, etc. On 64-bit platforms the 6 preallocated bits * takes continuous 4K, 7 - 8K, 8 - 16K, etc. There is no sense to * to preallocate more than one page, because further preallocation - * distributes the only bit per page. Instead, the random insertion + * distributes the only bit per page. Instead, a random insertion * may distribute several bits per page. * * Thus, by default we preallocate maximum diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -956,11 +956,14 @@ ngx_resolver_process_response(ngx_resolv { char *err; size_t len; - ngx_uint_t i, ident, flags, code, nqs, nan, qtype, qclass; + ngx_uint_t i, times, ident, qident, flags, code, nqs, nan, + qtype, qclass; + ngx_queue_t *q; ngx_resolver_qs_t *qs; + ngx_resolver_node_t *rn; ngx_resolver_query_t *query; - if ((size_t) n < sizeof(ngx_resolver_query_t) + 1) { + if ((size_t) n < sizeof(ngx_resolver_query_t)) { goto short_response; } @@ -985,11 +988,31 @@ ngx_resolver_process_response(ngx_resolv code = flags & 0x7f; - if (code == NGX_RESOLVE_FORMERR || code > NGX_RESOLVE_REFUSED) { - ngx_log_error(r->log_level, r->log, 0, - "DNS error (%ui: %s), query id:%ui", - code, ngx_resolver_strerror(code), ident); - return; + if (code == NGX_RESOLVE_FORMERR) { + + times = 0; + + for (q = ngx_queue_head(&r->name_resend_queue); + q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100; + q = ngx_queue_next(q)) + { + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); + qident = (rn->query[0] << 8) + rn->query[1]; + + if (qident == ident) { + ngx_log_error(r->log_level, r->log, 0, + "DNS error (%ui: %s), query id:%ui, name:\"%*s\"", + code, ngx_resolver_strerror(code), ident, + rn->nlen, rn->name); + return; + } + } + + goto dns_error; + } + + if (code > NGX_RESOLVE_REFUSED) { + goto dns_error; } if (nqs != 1) { @@ -1069,6 +1092,13 @@ done: ngx_log_error(r->log_level, r->log, 0, err); return; + +dns_error: + + ngx_log_error(r->log_level, r->log, 0, + "DNS error (%ui: %s), query id:%ui", + code, ngx_resolver_strerror(code), ident); + return; } @@ -1836,7 +1866,7 @@ ngx_resolver_copy(ngx_resolver_t *r, ngx } if (n & 0xc0) { - n = (n & 0x3f << 8) + *p; + n = ((n & 0x3f) << 8) + *p; p = &buf[n]; } else { @@ -1886,7 +1916,7 @@ done: } } else { - n = (n & 0x3f << 8) + *src; + n = ((n & 0x3f) << 8) + *src; src = &buf[n]; n = *src++; 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 @@ -8,6 +8,10 @@ #include +static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, + u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width); + + void ngx_strlow(u_char *dst, u_char *src, size_t n) { @@ -67,6 +71,7 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t * %[0][width][u][x|X]D int32_t/uint32_t * %[0][width][u][x|X]L int64_t/uint64_t * %[0][width|m][u][x|X]A ngx_atomic_int_t/ngx_atomic_uint_t + * %[0][width][.width]f float * %P ngx_pid_t * %M ngx_msec_t * %r rlim_t @@ -118,22 +123,16 @@ ngx_snprintf(u_char *buf, size_t max, co u_char * ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args) { - u_char *p, zero, *last, temp[NGX_INT64_LEN + 1]; - /* - * really we need temp[NGX_INT64_LEN] only, - * but icc issues the warning - */ + u_char *p, zero, *last; int d; + float f, scale; size_t len, slen; - uint32_t ui32; int64_t i64; uint64_t ui64; ngx_msec_t ms; - ngx_uint_t width, sign, hexadecimal, max_width; + ngx_uint_t width, sign, hex, max_width, frac_width, i; ngx_str_t *v; ngx_variable_value_t *vv; - static u_char hex[] = "0123456789abcdef"; - static u_char HEX[] = "0123456789ABCDEF"; if (max == 0) { return buf; @@ -156,12 +155,11 @@ ngx_vsnprintf(u_char *buf, size_t max, c zero = (u_char) ((*++fmt == '0') ? '0' : ' '); width = 0; sign = 1; - hexadecimal = 0; + hex = 0; max_width = 0; + frac_width = 0; slen = (size_t) -1; - p = temp + NGX_INT64_LEN; - while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + *fmt++ - '0'; } @@ -181,17 +179,26 @@ ngx_vsnprintf(u_char *buf, size_t max, c continue; case 'X': - hexadecimal = 2; + hex = 2; sign = 0; fmt++; continue; case 'x': - hexadecimal = 1; + hex = 1; sign = 0; fmt++; continue; + case '.': + fmt++; + + while (*fmt >= '0' && *fmt <= '9') { + frac_width = frac_width * 10 + *fmt++ - '0'; + } + + break; + case '*': slen = va_arg(args, size_t); fmt++; @@ -339,6 +346,43 @@ ngx_vsnprintf(u_char *buf, size_t max, c break; + case 'f': + f = (float) va_arg(args, double); + + if (f < 0) { + *buf++ = '-'; + f = -f; + } + + ui64 = (int64_t) f; + + buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width); + + if (frac_width) { + + if (buf < last) { + *buf++ = '.'; + } + + scale = 1.0; + + for (i = 0; i < frac_width; i++) { + scale *= 10.0; + } + + /* + * (int64_t) cast is required for msvc6: + * it can not convert uint64_t to double + */ + ui64 = (uint64_t) ((f - (int64_t) ui64) * scale); + + buf = ngx_sprintf_num(buf, last, ui64, '0', 0, frac_width); + } + + fmt++; + + continue; + #if !(NGX_WIN32) case 'r': i64 = (int64_t) va_arg(args, rlim_t); @@ -348,7 +392,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c case 'p': ui64 = (uintptr_t) va_arg(args, void *); - hexadecimal = 2; + hex = 2; sign = 0; zero = '0'; width = NGX_PTR_SIZE * 2; @@ -398,63 +442,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c } } - if (hexadecimal == 1) { - do { - - /* the "(uint32_t)" cast disables the BCC's warning */ - *--p = hex[(uint32_t) (ui64 & 0xf)]; - - } while (ui64 >>= 4); - - } else if (hexadecimal == 2) { - do { - - /* the "(uint32_t)" cast disables the BCC's warning */ - *--p = HEX[(uint32_t) (ui64 & 0xf)]; - - } while (ui64 >>= 4); - - } else if (ui64 <= NGX_MAX_UINT32_VALUE) { - - /* - * To divide 64-bit number and to find the remainder - * on the x86 platform gcc and icc call the libc functions - * [u]divdi3() and [u]moddi3(), they call another function - * in its turn. On FreeBSD it is the qdivrem() function, - * its source code is about 170 lines of the code. - * The glibc counterpart is about 150 lines of the code. - * - * For 32-bit numbers and some divisors gcc and icc use - * the inlined multiplication and shifts. For example, - * unsigned "i32 / 10" is compiled to - * - * (i32 * 0xCCCCCCCD) >> 35 - */ - - ui32 = (uint32_t) ui64; - - do { - *--p = (u_char) (ui32 % 10 + '0'); - } while (ui32 /= 10); - - } else { - do { - *--p = (u_char) (ui64 % 10 + '0'); - } while (ui64 /= 10); - } - - len = (temp + NGX_INT64_LEN) - p; - - while (len++ < width && buf < last) { - *buf++ = zero; - } - - len = (temp + NGX_INT64_LEN) - p; - if (buf + len > last) { - len = last - buf; - } - - buf = ngx_cpymem(buf, p, len); + buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width); fmt++; @@ -467,6 +455,92 @@ ngx_vsnprintf(u_char *buf, size_t max, c } +static u_char * +ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero, + ngx_uint_t hexadecimal, ngx_uint_t width) +{ + u_char *p, temp[NGX_INT64_LEN + 1]; + /* + * we need temp[NGX_INT64_LEN] only, + * but icc issues the warning + */ + size_t len; + uint32_t ui32; + static u_char hex[] = "0123456789abcdef"; + static u_char HEX[] = "0123456789ABCDEF"; + + p = temp + NGX_INT64_LEN; + + if (hexadecimal == 0) { + + if (ui64 <= NGX_MAX_UINT32_VALUE) { + + /* + * To divide 64-bit numbers and to find remainders + * on the x86 platform gcc and icc call the libc functions + * [u]divdi3() and [u]moddi3(), they call another function + * in its turn. On FreeBSD it is the qdivrem() function, + * its source code is about 170 lines of the code. + * The glibc counterpart is about 150 lines of the code. + * + * For 32-bit numbers and some divisors gcc and icc use + * a inlined multiplication and shifts. For example, + * unsigned "i32 / 10" is compiled to + * + * (i32 * 0xCCCCCCCD) >> 35 + */ + + ui32 = (uint32_t) ui64; + + do { + *--p = (u_char) (ui32 % 10 + '0'); + } while (ui32 /= 10); + + } else { + do { + *--p = (u_char) (ui64 % 10 + '0'); + } while (ui64 /= 10); + } + + } else if (hexadecimal == 1) { + + do { + + /* the "(uint32_t)" cast disables the BCC's warning */ + *--p = hex[(uint32_t) (ui64 & 0xf)]; + + } while (ui64 >>= 4); + + } else { /* hexadecimal == 2 */ + + do { + + /* the "(uint32_t)" cast disables the BCC's warning */ + *--p = HEX[(uint32_t) (ui64 & 0xf)]; + + } while (ui64 >>= 4); + } + + /* zero or space padding */ + + len = (temp + NGX_INT64_LEN) - p; + + while (len++ < width && buf < last) { + *buf++ = zero; + } + + /* number safe copy */ + + len = (temp + NGX_INT64_LEN) - p; + + if (buf + len > last) { + len = last - buf; + } + + return ngx_cpymem(buf, p, len); +} + + /* * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only, * and implement our own ngx_strcasecmp()/ngx_strncasecmp() diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -188,13 +188,6 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_ SSL_CTX_set_options(ssl->ctx, ngx_ssl_protocols[protocols >> 1]); } - /* - * we need this option because in ngx_ssl_send_chain() - * we may switch to a buffered write and may copy leftover part of - * previously unbuffered data to our internal buffer - */ - SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_read_ahead(ssl->ctx, 1); return NGX_OK; @@ -505,11 +498,11 @@ ngx_ssl_handshake(ngx_connection_t *c) if (n == 1) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -576,7 +569,7 @@ ngx_ssl_handshake(ngx_connection_t *c) c->read->handler = ngx_ssl_handshake_handler; c->write->handler = ngx_ssl_handshake_handler; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -588,7 +581,7 @@ ngx_ssl_handshake(ngx_connection_t *c) c->read->handler = ngx_ssl_handshake_handler; c->write->handler = ngx_ssl_handshake_handler; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -774,7 +767,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c, c->ssl->saved_write_handler = NULL; c->write->ready = 1; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -802,7 +795,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c, c->write->ready = 0; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -860,14 +853,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, ssize_t send, size; ngx_buf_t *buf; - if (!c->ssl->buffer - || (in && in->next == NULL && !(c->buffered & NGX_SSL_BUFFERED))) - { - /* - * we avoid a buffer copy if - * we do not need to buffer the output - * or the incoming buf is a single and our buffer is empty - */ + if (!c->ssl->buffer) { while (in) { if (ngx_buf_special(in->buf)) { @@ -1033,7 +1019,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha c->ssl->saved_read_handler = NULL; c->read->ready = 1; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -1061,7 +1047,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha c->read->ready = 0; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -1161,11 +1147,11 @@ ngx_ssl_shutdown(ngx_connection_t *c) c->read->handler = ngx_ssl_shutdown_handler; c->write->handler = ngx_ssl_shutdown_handler; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -1254,23 +1240,37 @@ ngx_ssl_connection_error(ngx_connection_ n = ERR_GET_REASON(ERR_peek_error()); /* handshake failures */ - if (n == SSL_R_DIGEST_CHECK_FAILED - || n == SSL_R_NO_SHARED_CIPHER - || n == SSL_R_UNEXPECTED_MESSAGE - || n == SSL_R_WRONG_VERSION_NUMBER - || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC + if (n == SSL_R_DIGEST_CHECK_FAILED /* 149 */ + || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ + || n == SSL_R_NO_SHARED_CIPHER /* 193 */ + || n == SSL_R_UNEXPECTED_MESSAGE /* 244 */ + || n == SSL_R_UNEXPECTED_RECORD /* 245 */ + || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */ + || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */ || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */ - || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE - || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC - || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE - || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE - || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE - || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE - || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED - || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED - || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN - || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER - || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA) + || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE /* 1010 */ + || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC /* 1020 */ + || n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED /* 1021 */ + || n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW /* 1022 */ + || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE /* 1030 */ + || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE /* 1040 */ + || n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE /* 1041 */ + || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE /* 1042 */ + || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE /* 1043 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED /* 1044 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED /* 1045 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN /* 1046 */ + || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER /* 1047 */ + || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA /* 1048 */ + || n == SSL_R_TLSV1_ALERT_ACCESS_DENIED /* 1049 */ + || n == SSL_R_TLSV1_ALERT_DECODE_ERROR /* 1050 */ + || n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR /* 1051 */ + || n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION /* 1060 */ + || n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION /* 1070 */ + || n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY /* 1071 */ + || n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR /* 1080 */ + || n == SSL_R_TLSV1_ALERT_USER_CANCELLED /* 1090 */ + || n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION) /* 1100 */ { switch (c->log_error) { diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c --- a/src/event/ngx_event_pipe.c +++ b/src/event/ngx_event_pipe.c @@ -56,7 +56,7 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_ flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0; - if (ngx_handle_read_event(rev, flags) == NGX_ERROR) { + if (ngx_handle_read_event(rev, flags) != NGX_OK) { return NGX_ABORT; } @@ -70,7 +70,7 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_ if (p->downstream->fd != -1 && p->downstream->data == p->output_ctx) { wev = p->downstream->write; - if (ngx_handle_write_event(wev, p->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { return NGX_ABORT; } diff --git a/src/http/modules/ngx_http_addition_filter_module.c b/src/http/modules/ngx_http_addition_filter_module.c --- a/src/http/modules/ngx_http_addition_filter_module.c +++ b/src/http/modules/ngx_http_addition_filter_module.c @@ -151,10 +151,10 @@ ngx_http_addition_body_filter(ngx_http_r ctx->before_body_sent = 1; if (conf->before_body.len) { - rc = ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0); - - if (rc == NGX_ERROR || rc == NGX_DONE) { - return rc; + if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0) + != NGX_OK) + { + return NGX_ERROR; } } } @@ -180,10 +180,10 @@ ngx_http_addition_body_filter(ngx_http_r return rc; } - rc = ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0); - - if (rc == NGX_ERROR || rc == NGX_DONE) { - return rc; + if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0) + != NGX_OK) + { + return NGX_ERROR; } ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module); diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c --- a/src/http/modules/ngx_http_autoindex_module.c +++ b/src/http/modules/ngx_http_autoindex_module.c @@ -299,6 +299,11 @@ ngx_http_autoindex_handler(ngx_http_requ if (err != NGX_ENOENT) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, ngx_de_info_n " \"%s\" failed", filename); + + if (err == NGX_EACCES) { + continue; + } + return ngx_http_autoindex_error(r, &dir, &path); } diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -541,12 +541,6 @@ ngx_http_charset_body_filter(ngx_http_re break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if (b->zerocopy_busy) { - break; - } -#endif - ctx->busy = cl->next; if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) { diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c --- a/src/http/modules/ngx_http_dav_module.c +++ b/src/http/modules/ngx_http_dav_module.c @@ -246,9 +246,11 @@ ngx_http_dav_put_handler(ngx_http_reques dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); ext.access = dlcf->access; + ext.path_access = dlcf->access; ext.time = -1; ext.create_path = dlcf->create_full_put_path; ext.delete_file = 1; + ext.log_rename_error = 1; ext.log = r->connection->log; if (r->headers_in.date) { @@ -521,6 +523,7 @@ ngx_http_dav_copy_move_handler(ngx_http_ ngx_tree_ctx_t tree; ngx_file_info_t fi; ngx_table_elt_t *dest, *over; + ngx_ext_rename_file_t ext; ngx_http_dav_copy_ctx_t copy; ngx_http_dav_loc_conf_t *dlcf; @@ -781,9 +784,32 @@ overwrite_done: } else { if (r->method == NGX_HTTP_MOVE) { - if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) { + + dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); + + ext.access = 0; + ext.path_access = dlcf->access; + ext.time = -1; + ext.create_path = 1; + ext.delete_file = 0; + ext.log_rename_error = 0; + ext.log = r->connection->log; + + if (ngx_ext_rename_file(&path, ©.path, &ext) == NGX_OK) { return NGX_HTTP_NO_CONTENT; } + + if (ext.rename_error != NGX_EXDEV) { + + if (ext.rename_error) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, + ext.rename_error, + ngx_rename_file_n " \"%s\" to \"%s\" failed", + path.data, copy.path.data); + } + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } } dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); 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 @@ -19,6 +19,9 @@ typedef struct { ngx_array_t *params; ngx_array_t *params_source; ngx_array_t *catch_stderr; + + ngx_array_t *fastcgi_lengths; + ngx_array_t *fastcgi_values; } ngx_http_fastcgi_loc_conf_t; @@ -103,6 +106,8 @@ typedef struct { } ngx_http_fastcgi_request_start_t; +static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r, + ngx_http_fastcgi_loc_conf_t *flcf); static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r); static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r); @@ -414,14 +419,25 @@ ngx_http_fastcgi_handler(ngx_http_reques return NGX_HTTP_INTERNAL_SERVER_ERROR; } - flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + ngx_http_set_ctx(r, NULL, ngx_http_fastcgi_module); u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t)); if (u == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - u->schema = flcf->upstream.schema; + r->upstream = u; + + flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + + if (flcf->fastcgi_lengths) { + if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + u->schema.len = sizeof("fastcgi://") - 1; + u->schema.data = (u_char *) "fastcgi://"; u->peer.log = r->connection->log; u->peer.log_error = NGX_ERROR_ERR; @@ -449,8 +465,6 @@ ngx_http_fastcgi_handler(ngx_http_reques u->pipe->input_filter = ngx_http_fastcgi_input_filter; u->pipe->input_ctx = r; - r->upstream = u; - rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { @@ -462,6 +476,58 @@ ngx_http_fastcgi_handler(ngx_http_reques static ngx_int_t +ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf) +{ + ngx_url_t u; + + ngx_memzero(&u, sizeof(ngx_url_t)); + + if (ngx_http_script_run(r, &u.url, flcf->fastcgi_lengths->elts, 0, + flcf->fastcgi_values->elts) + == NULL) + { + return NGX_ERROR; + } + + u.no_resolve = 1; + + if (ngx_parse_url(r->pool, &u) != NGX_OK) { + if (u.err) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "%s in upstream \"%V\"", u.err, &u.url); + } + + return NGX_ERROR; + } + + if (u.no_port) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no port in upstream \"%V\"", &u.url); + return NGX_ERROR; + } + + r->upstream->resolved = ngx_pcalloc(r->pool, + sizeof(ngx_http_upstream_resolved_t)); + if (r->upstream->resolved == NULL) { + return NGX_ERROR; + } + + if (u.addrs && u.addrs[0].sockaddr) { + r->upstream->resolved->sockaddr = u.addrs[0].sockaddr; + r->upstream->resolved->socklen = u.addrs[0].socklen; + r->upstream->resolved->naddrs = 1; + r->upstream->resolved->host = u.addrs[0].name; + + } else { + r->upstream->resolved->host = u.host; + r->upstream->resolved->port = u.port; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r) { off_t file_pos; @@ -1631,7 +1697,6 @@ ngx_http_fastcgi_create_loc_conf(ngx_con * conf->upstream.next_upstream = 0; * conf->upstream.temp_path = NULL; * conf->upstream.hide_headers_hash = { NULL, 0 }; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; * conf->upstream.store_lengths = NULL; @@ -1861,7 +1926,11 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; - conf->upstream.schema = prev->upstream.schema; + } + + if (conf->fastcgi_lengths == NULL) { + conf->fastcgi_lengths = prev->fastcgi_lengths; + conf->fastcgi_values = prev->fastcgi_values; } if (conf->params_source == NULL) { @@ -2044,35 +2113,56 @@ ngx_http_fastcgi_script_name_variable(ng static char * ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_fastcgi_loc_conf_t *lcf = conf; - - ngx_url_t u; - ngx_str_t *value; - ngx_http_core_loc_conf_t *clcf; - - if (lcf->upstream.schema.len) { + ngx_http_fastcgi_loc_conf_t *flcf = conf; + + ngx_url_t u; + ngx_str_t *value, *url; + ngx_uint_t n; + ngx_http_core_loc_conf_t *clcf; + ngx_http_script_compile_t sc; + + if (flcf->upstream.upstream || flcf->fastcgi_lengths) { return "is duplicate"; } + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + clcf->handler = ngx_http_fastcgi_handler; + value = cf->args->elts; + url = &value[1]; + + n = ngx_http_script_variables_count(url); + + if (n) { + + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = url; + sc.lengths = &flcf->fastcgi_lengths; + sc.values = &flcf->fastcgi_values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + } + ngx_memzero(&u, sizeof(ngx_url_t)); u.url = value[1]; u.no_resolve = 1; - lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); - if (lcf->upstream.upstream == NULL) { + flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); + if (flcf->upstream.upstream == NULL) { return NGX_CONF_ERROR; } - lcf->upstream.schema.len = sizeof("fastcgi://") - 1; - lcf->upstream.schema.data = (u_char *) "fastcgi://"; - - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); - - clcf->handler = ngx_http_fastcgi_handler; - if (clcf->name.data[clcf->name.len - 1] == '/') { clcf->auto_redirect = 1; } diff --git a/src/http/modules/ngx_http_flv_module.c b/src/http/modules/ngx_http_flv_module.c --- a/src/http/modules/ngx_http_flv_module.c +++ b/src/http/modules/ngx_http_flv_module.c @@ -60,12 +60,12 @@ ngx_module_t ngx_http_flv_module = { static ngx_int_t ngx_http_flv_handler(ngx_http_request_t *r) { - u_char *p, *n, *last; + u_char *last; off_t start, len; size_t root; ngx_int_t rc; ngx_uint_t level, i; - ngx_str_t path; + ngx_str_t path, value; ngx_log_t *log; ngx_buf_t *b; ngx_chain_t out[2]; @@ -167,18 +167,10 @@ ngx_http_flv_handler(ngx_http_request_t i = 1; if (r->args.len) { - p = (u_char *) ngx_strnstr(r->args.data, "start=", r->args.len); - - if (p) { - p += 6; - for (n = p; n < r->args.data + r->args.len; n++) { - if (*n == '&') { - break; - } - } + if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) { - start = ngx_atoof(p, n - p); + start = ngx_atoof(value.data, value.len); if (start == NGX_ERROR || start >= len) { start = 0; diff --git a/src/http/modules/ngx_http_geo_module.c b/src/http/modules/ngx_http_geo_module.c --- a/src/http/modules/ngx_http_geo_module.c +++ b/src/http/modules/ngx_http_geo_module.c @@ -10,20 +10,66 @@ typedef struct { - ngx_radix_tree_t *tree; - ngx_pool_t *pool; - ngx_array_t values; + u_short start; + u_short end; + ngx_http_variable_value_t *value; +} ngx_http_geo_range_t; + + +typedef struct { + ngx_http_geo_range_t *ranges; + ngx_uint_t n; +} ngx_http_geo_low_ranges_t; + + +typedef struct { + ngx_http_geo_low_ranges_t low[0x10000]; + ngx_http_variable_value_t *default_value; +} ngx_http_geo_high_ranges_t; + + +typedef struct { + ngx_http_variable_value_t *value; + ngx_str_t *net; + ngx_http_geo_high_ranges_t *high; + ngx_radix_tree_t *tree; + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_pool_t *pool; + ngx_pool_t *temp_pool; } ngx_http_geo_conf_ctx_t; +typedef struct { + union { + ngx_radix_tree_t *tree; + ngx_http_geo_high_ranges_t *high; + } u; + + ngx_int_t index; +} ngx_http_geo_ctx_t; + + +static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, + ngx_http_geo_ctx_t *ctx); static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); +static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value); +static char *ngx_http_geo_add_range(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); +static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); +static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value); +static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value); static ngx_command_t ngx_http_geo_commands[] = { { ngx_string("geo"), - NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12, ngx_http_geo_block, NGX_HTTP_MAIN_CONF_OFFSET, 0, @@ -67,53 +113,126 @@ ngx_module_t ngx_http_geo_module = { /* AF_INET only */ static ngx_int_t -ngx_http_geo_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, +ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data; + ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; - struct sockaddr_in *sin; ngx_http_variable_value_t *vv; - sin = (struct sockaddr_in *) r->connection->sockaddr; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http geo started"); - vv = (ngx_http_variable_value_t *) - ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr)); + ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx)); *v = *vv; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http geo: %V %v", &r->connection->addr_text, v); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo: %v", v); return NGX_OK; } +static ngx_int_t +ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; + + in_addr_t addr; + ngx_uint_t i, n; + ngx_http_geo_range_t *range; + + *v = *ctx->u.high->default_value; + + addr = ngx_http_geo_addr(r, ctx); + + range = ctx->u.high->low[addr >> 16].ranges; + + n = addr & 0xffff; + + for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) { + if (n >= (ngx_uint_t) range[i].start + && n <= (ngx_uint_t) range[i].end) + { + *v = *range[i].value; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo: %v", v); + + return NGX_OK; +} + + +static in_addr_t +ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) +{ + struct sockaddr_in *sin; + ngx_http_variable_value_t *v; + + if (ctx->index == -1) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo started: %V", &r->connection->addr_text); + + sin = (struct sockaddr_in *) r->connection->sockaddr; + return ntohl(sin->sin_addr.s_addr); + } + + v = ngx_http_get_flushed_variable(r, ctx->index); + + if (v == NULL || v->not_found) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo not found"); + + return 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo started: %v", v); + + return ntohl(ngx_inet_addr(v->data, v->len)); +} + + static char * ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; + size_t len; ngx_str_t *value, name; + ngx_uint_t i; ngx_conf_t save; ngx_pool_t *pool; - ngx_radix_tree_t *tree; + ngx_array_t *a; ngx_http_variable_t *var; + ngx_http_geo_ctx_t *geo; ngx_http_geo_conf_ctx_t ctx; value = cf->args->elts; + geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t)); + if (geo == NULL) { + return NGX_CONF_ERROR; + } + name = value[1]; + name.len--; + name.data++; - if (name.data[0] != '$') { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "\"%V\" variable name should start with '$'", - &value[1]); - } else { + if (cf->args->nelts == 3) { + + geo->index = ngx_http_get_variable_index(cf, &name); + if (geo->index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + name = value[2]; name.len--; name.data++; + + } else { + geo->index = -1; } var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); @@ -121,29 +240,21 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c return NGX_CONF_ERROR; } - tree = ngx_radix_tree_create(cf->pool, -1); - - if (tree == NULL) { - return NGX_CONF_ERROR; - } - - var->get_handler = ngx_http_geo_variable; - var->data = (uintptr_t) tree; - pool = ngx_create_pool(16384, cf->log); if (pool == NULL) { return NGX_CONF_ERROR; } - if (ngx_array_init(&ctx.values, pool, 512, - sizeof(ngx_http_variable_value_t *)) - != NGX_OK) - { - ngx_destroy_pool(pool); + ctx.temp_pool = ngx_create_pool(16384, cf->log); + if (ctx.temp_pool == NULL) { return NGX_CONF_ERROR; } - ctx.tree = tree; + ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, + ngx_http_variable_value_rbtree_insert); + + ctx.high = NULL; + ctx.tree = NULL; ctx.pool = cf->pool; save = *cf; @@ -156,121 +267,475 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c *cf = save; - ngx_destroy_pool(pool); + if (ctx.high) { + + for (i = 0; i < 0x10000; i++) { + a = (ngx_array_t *) ctx.high->low[i].ranges; + + if (a == NULL || a->nelts == 0) { + continue; + } + + ctx.high->low[i].n = a->nelts; + + len = a->nelts * sizeof(ngx_http_geo_range_t); - if (ngx_radix32tree_find(tree, 0) != NGX_RADIX_NO_VALUE) { - return rv; - } + ctx.high->low[i].ranges = ngx_palloc(cf->pool, len); + if (ctx.high->low[i].ranges == NULL ){ + return NGX_CONF_ERROR; + } + + ngx_memcpy(ctx.high->low[i].ranges, a->elts, len); + } + + geo->u.high = ctx.high; + + var->get_handler = ngx_http_geo_range_variable; + var->data = (uintptr_t) geo; + + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); - if (ngx_radix32tree_insert(tree, 0, 0, - (uintptr_t) &ngx_http_variable_null_value) - == NGX_ERROR) - { - return NGX_CONF_ERROR; + if (ctx.high->default_value == NULL) { + ctx.high->default_value = &ngx_http_variable_null_value; + } + + } else { + if (ctx.tree == NULL) { + ctx.tree = ngx_radix_tree_create(cf->pool, -1); + if (ctx.tree == NULL) { + return NGX_CONF_ERROR; + } + } + + geo->u.tree = ctx.tree; + + var->get_handler = ngx_http_geo_cidr_variable; + var->data = (uintptr_t) geo; + + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); + + if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) { + return rv; + } + + if (ngx_radix32tree_insert(ctx.tree, 0, 0, + (uintptr_t) &ngx_http_variable_null_value) + == NGX_ERROR) + { + return NGX_CONF_ERROR; + } } return rv; } -/* AF_INET only */ - static char * ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { - ngx_int_t rc; - ngx_str_t *value, file; - ngx_uint_t i; - ngx_inet_cidr_t cidrin; - ngx_http_geo_conf_ctx_t *ctx; - ngx_http_variable_value_t *var, *old, **v; + char *rv; + ngx_str_t *value, file; + ngx_http_geo_conf_ctx_t *ctx; ctx = cf->ctx; + value = cf->args->elts; + + if (cf->args->nelts == 1) { + + if (ngx_strcmp(value[0].data, "ranges") == 0) { + + if (ctx->tree) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"ranges\" directive must be " + "the first directive inside \"geo\" block"); + goto failed; + } + + ctx->high = ngx_pcalloc(ctx->pool, + sizeof(ngx_http_geo_high_ranges_t)); + if (ctx->high == NULL) { + goto failed; + } + + rv = NGX_CONF_OK; + + goto done; + } + } + if (cf->args->nelts != 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of the geo parameters"); - return NGX_CONF_ERROR; + goto failed; } - value = cf->args->elts; + if (ngx_strcmp(value[0].data, "include") == 0) { + + file.len = value[1].len++; - if (ngx_strcmp(value[0].data, "include") == 0) { - file = value[1]; + file.data = ngx_pstrdup(ctx->temp_pool, &value[1]); + if (file.data == NULL) { + goto failed; + } - if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){ - return NGX_CONF_ERROR; + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){ + goto failed; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); - return ngx_conf_parse(cf, &file); + rv = ngx_conf_parse(cf, &file); + + goto done; + } + + if (ctx->high) { + rv = ngx_http_geo_range(cf, ctx, value); + + } else { + rv = ngx_http_geo_cidr(cf, ctx, value); + } + +done: + + ngx_reset_pool(cf->pool); + + return rv; + +failed: + + ngx_reset_pool(cf->pool); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + u_char *p, *last; + in_addr_t start, end; + ngx_str_t *net; + ngx_uint_t del; + ngx_http_variable_value_t *old; + + if (ngx_strcmp(value[0].data, "default") == 0) { + + old = ctx->high->default_value; + + ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]); + if (ctx->high->default_value == NULL) { + return NGX_CONF_ERROR; + } + + if (old) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", + &value[0], ctx->high->default_value, old); + } + + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[0].data, "delete") == 0) { + net = &value[1]; + del = 1; + + } else { + net = &value[0]; + del = 0; + } + + last = net->data + net->len; + + p = ngx_strlchr(net->data, last, '-'); + + if (p == NULL) { + goto invalid; + } + + start = ngx_inet_addr(net->data, p - net->data); + + if (start == INADDR_NONE) { + goto invalid; + } + + start = ntohl(start); + + p++; + + end = ngx_inet_addr(p, last - p); + + if (end == INADDR_NONE) { + goto invalid; + } + + end = ntohl(end); + + if (start > end) { + goto invalid; + } + + if (del) { + if (ngx_http_geo_delete_range(cf, ctx, start, end)) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "no address range \"%V\" to delete", net); + } + + return NGX_CONF_OK; + } + + ctx->value = ngx_http_geo_value(cf, ctx, &value[1]); + + if (ctx->value == NULL) { + return NGX_CONF_ERROR; + } + + ctx->net = net; + + return ngx_http_geo_add_range(cf, ctx, start, end); + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net); + + return NGX_CONF_ERROR; +} + + +/* the add procedure is optimized to add a growing up sequence */ + +static char * +ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + in_addr_t start, in_addr_t end) +{ + in_addr_t n; + ngx_uint_t h, i, s, e; + ngx_array_t *a; + ngx_http_geo_range_t *range; + + for (n = start; n < end; n += 0x10000) { + + h = n >> 16; + s = n & 0xffff; + + if ((n | 0xffff) > end) { + e = end & 0xffff; + + } else { + e = 0xffff; + } + + a = (ngx_array_t *) ctx->high->low[h].ranges; + + if (a == NULL) { + a = ngx_array_create(ctx->temp_pool, 64, + sizeof(ngx_http_geo_range_t)); + if (a == NULL) { + return NGX_CONF_ERROR; + } + + ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a; + } + + i = a->nelts; + range = a->elts; + + while (i) { + + i--; + + if (e < (ngx_uint_t) range[i].start) { + continue; + } + + if (s > (ngx_uint_t) range[i].end) { + + /* add after the range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memcpy(&range[i + 2], &range[i + 1], + (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); + + range = &range[i + 1]; + + goto next; + } + + if (s == (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", + ctx->net, ctx->value, range[i].value); + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "overlapped range \"%V\"", ctx->net); + + return NGX_CONF_ERROR; + } + + /* add the first range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + next: + + range->start = (u_short) s; + range->end = (u_short) e; + range->value = ctx->value; + } + + return NGX_CONF_OK; +} + + +static ngx_uint_t +ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + in_addr_t start, in_addr_t end) +{ + in_addr_t n; + ngx_uint_t h, i, s, e, warn; + ngx_array_t *a; + ngx_http_geo_range_t *range; + + warn = 0; + + for (n = start; n < end; n += 0x10000) { + + h = n >> 16; + s = n & 0xffff; + + if ((n | 0xffff) > end) { + e = end & 0xffff; + + } else { + e = 0xffff; + } + + a = (ngx_array_t *) ctx->high->low[h].ranges; + + if (a == NULL) { + warn = 1; + continue; + } + + range = a->elts; + for (i = 0; i < a->nelts; i++) { + + if (s == (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + ngx_memcpy(&range[i], &range[i + 1], + (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); + break; + } + + if (s != (ngx_uint_t) range[i].start + && e != (ngx_uint_t) range[i].end) + { + continue; + } + + warn = 1; + } + } + + return warn; +} + + +static char * +ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + ngx_int_t rc, del; + ngx_str_t *net; + ngx_uint_t i; + ngx_inet_cidr_t cidrin; + ngx_http_variable_value_t *val, *old; + + if (ctx->tree == NULL) { + ctx->tree = ngx_radix_tree_create(ctx->pool, -1); + if (ctx->tree == NULL) { + return NGX_CONF_ERROR; + } } if (ngx_strcmp(value[0].data, "default") == 0) { cidrin.addr = 0; cidrin.mask = 0; + net = &value[0]; } else { - rc = ngx_ptocidr(&value[0], &cidrin); + if (ngx_strcmp(value[0].data, "delete") == 0) { + net = &value[1]; + del = 1; - if (rc == NGX_ERROR) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid parameter \"%V\"", &value[0]); - return NGX_CONF_ERROR; - } - - if (rc == NGX_DONE) { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "low address bits of %V are meaningless", - &value[0]); + } else { + net = &value[0]; + del = 0; } - cidrin.addr = ntohl(cidrin.addr); - cidrin.mask = ntohl(cidrin.mask); - } + if (ngx_strcmp(net->data, "255.255.255.255") == 0) { + cidrin.addr = 0xffffffff; + cidrin.mask = 0xffffffff; + + } else { + rc = ngx_ptocidr(net, &cidrin); - var = NULL; - v = ctx->values.elts; + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid network \"%V\"", net); + return NGX_CONF_ERROR; + } - for (i = 0; i < ctx->values.nelts; i++) { - if ((size_t) v[i]->len != value[1].len) { - continue; + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", + net); + } + + cidrin.addr = ntohl(cidrin.addr); + cidrin.mask = ntohl(cidrin.mask); } - if (ngx_strncmp(value[1].data, v[i]->data, value[1].len) == 0) { - var = v[i]; - break; + if (del) { + if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask) + != NGX_OK) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "no network \"%V\" to delete", net); + } + + return NGX_CONF_OK; } } - if (var == NULL) { - var = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); - if (var == NULL) { - return NGX_CONF_ERROR; - } + val = ngx_http_geo_value(cf, ctx, &value[1]); - var->len = value[1].len; - var->data = ngx_pstrdup(ctx->pool, &value[1]); - if (var->data == NULL) { - return NGX_CONF_ERROR; - } - - var->valid = 1; - var->no_cacheable = 0; - var->not_found = 0; - - v = ngx_array_push(&ctx->values); - if (v == NULL) { - return NGX_CONF_ERROR; - } - - *v = var; + if (val == NULL) { + return NGX_CONF_ERROR; } for (i = 2; i; i--) { rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask, - (uintptr_t) var); + (uintptr_t) val); if (rc == NGX_OK) { return NGX_CONF_OK; } @@ -282,18 +747,65 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command /* rc == NGX_BUSY */ old = (ngx_http_variable_value_t *) - ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask); + ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask); ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "duplicate parameter \"%V\", value: \"%v\", old value: \"%v\"", - &value[0], var, old); + "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", + net, val, old); rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask); if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); return NGX_CONF_ERROR; } } return NGX_CONF_ERROR; } + + +static ngx_http_variable_value_t * +ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + uint32_t hash; + ngx_http_variable_value_t *val; + ngx_http_variable_value_node_t *vvn; + + hash = ngx_crc32_long(value->data, value->len); + + val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash); + + if (val) { + return val; + } + + val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); + if (val == NULL) { + return NULL; + } + + val->len = value->len; + val->data = ngx_pstrdup(ctx->pool, value); + if (val->data == NULL) { + return NULL; + } + + val->valid = 1; + val->no_cacheable = 0; + val->not_found = 0; + + vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t)); + if (vvn == NULL) { + return NULL; + } + + vvn->node.key = hash; + vvn->len = val->len; + vvn->value = val; + + ngx_rbtree_insert(&ctx->rbtree, &vvn->node); + + return val; +} diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c --- a/src/http/modules/ngx_http_gzip_filter_module.c +++ b/src/http/modules/ngx_http_gzip_filter_module.c @@ -34,19 +34,27 @@ typedef struct { ngx_chain_t *busy; ngx_chain_t *out; ngx_chain_t **last_out; + + ngx_chain_t *copied; + ngx_chain_t *copy_buf; + ngx_buf_t *in_buf; ngx_buf_t *out_buf; ngx_int_t bufs; - off_t length; - void *preallocated; char *free_mem; ngx_uint_t allocated; + int wbits; + int memlevel; + unsigned flush:4; unsigned redo:1; unsigned done:1; + unsigned nomem:1; + unsigned gzheader:1; + unsigned buffering:1; size_t zin; size_t zout; @@ -57,10 +65,45 @@ typedef struct { } ngx_http_gzip_ctx_t; +#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) + +struct gztrailer { + uint32_t crc32; + uint32_t zlen; +}; + +#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */ + +struct gztrailer { + u_char crc32[4]; + u_char zlen[4]; +}; + +#endif + + +static void ngx_http_gzip_filter_memory(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_copy_recycled(ngx_http_gzip_ctx_t *ctx, + ngx_chain_t *in); +static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); + static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size); static void ngx_http_gzip_filter_free(void *opaque, void *address); -static void ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx); +static void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf); static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r, @@ -176,25 +219,6 @@ ngx_module_t ngx_http_gzip_filter_modul }; -static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; - -#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) - -struct gztrailer { - uint32_t crc32; - uint32_t zlen; -}; - -#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */ - -struct gztrailer { - u_char crc32[4]; - u_char zlen[4]; -}; - -#endif - - static ngx_str_t ngx_http_gzip_ratio = ngx_string("gzip_ratio"); static ngx_http_output_header_filter_pt ngx_http_next_header_filter; @@ -233,6 +257,9 @@ ngx_http_gzip_header_filter(ngx_http_req ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module); ctx->request = r; + ctx->buffering = 1; + + ngx_http_gzip_filter_memory(r, ctx); h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { @@ -247,8 +274,6 @@ ngx_http_gzip_header_filter(ngx_http_req r->headers_out.content_encoding = h; - ctx->length = r->headers_out.content_length_n; - r->main_filter_need_in_memory = 1; ngx_http_clear_content_length(r); @@ -261,13 +286,9 @@ ngx_http_gzip_header_filter(ngx_http_req static ngx_int_t ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { - int rc, wbits, memlevel; - ngx_int_t last; - struct gztrailer *trailer; - ngx_buf_t *b; - ngx_chain_t *cl, out; - ngx_http_gzip_ctx_t *ctx; - ngx_http_gzip_conf_t *conf; + int rc; + ngx_chain_t *cl; + ngx_http_gzip_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); @@ -275,392 +296,635 @@ ngx_http_gzip_body_filter(ngx_http_reque return ngx_http_next_body_filter(r, in); } - conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http gzip filter"); + + if (ctx->buffering) { + + if (in) { + switch (ngx_http_gzip_filter_copy_recycled(ctx, in)) { + + case NGX_OK: + return NGX_OK; + + case NGX_DONE: + in = NULL; + break; + + default: /* NGX_ERROR */ + goto failed; + } + + } else { + ctx->buffering = 0; + } + } if (ctx->preallocated == NULL) { - wbits = conf->wbits; - memlevel = conf->memlevel; - - if (ctx->length > 0) { - - /* the actual zlib window size is smaller by 262 bytes */ - - while (ctx->length < ((1 << (wbits - 1)) - 262)) { - wbits--; - memlevel--; - } - } - - /* - * We preallocate a memory for zlib in one buffer (200K-400K), this - * decreases a number of malloc() and free() calls and also probably - * decreases a number of syscalls (sbrk() and so on). - * Besides we free this memory as soon as the gzipping will complete - * and do not wait while a whole response will be sent to a client. - * - * 8K is for zlib deflate_state, it takes - * *) 5816 bytes on i386 and sparc64 (32-bit mode) - * *) 5920 bytes on amd64 and sparc64 - */ - - ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); - - ctx->preallocated = ngx_palloc(r->pool, ctx->allocated); - if (ctx->preallocated == NULL) { - return NGX_ERROR; + if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) { + goto failed; } - - ctx->free_mem = ctx->preallocated; - - ctx->zstream.zalloc = ngx_http_gzip_filter_alloc; - ctx->zstream.zfree = ngx_http_gzip_filter_free; - ctx->zstream.opaque = ctx; - - rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED, - -wbits, memlevel, Z_DEFAULT_STRATEGY); - - if (rc != Z_OK) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflateInit2() failed: %d", rc); - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); - if (b == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - b->memory = 1; - b->pos = gzheader; - b->last = b->pos + 10; - - out.buf = b; - out.next = NULL; - - /* - * We pass the gzheader to the next filter now to avoid its linking - * to the ctx->busy chain. zlib does not usually output the compressed - * data in the initial iterations, so the gzheader that was linked - * to the ctx->busy chain would be flushed by ngx_http_write_filter(). - */ - - if (ngx_http_next_body_filter(r, &out) == NGX_ERROR) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; - - ctx->last_out = &ctx->out; - - ctx->crc32 = crc32(0L, Z_NULL, 0); - ctx->flush = Z_NO_FLUSH; } if (in) { if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; + goto failed; } } - last = NGX_NONE; + if (ctx->nomem) { + + /* flush busy buffers */ + + if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) { + goto failed; + } + + cl = NULL; + + ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl, + (ngx_buf_tag_t) &ngx_http_gzip_filter_module); + ctx->nomem = 0; + } for ( ;; ) { + /* cycle while we can write to a client */ + for ( ;; ) { - /* does zlib need a new data ? */ - - if (ctx->zstream.avail_in == 0 - && ctx->flush == Z_NO_FLUSH - && !ctx->redo) - { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "gzip in: %p", ctx->in); + /* cycle while there is data to feed zlib and ... */ - if (ctx->in == NULL) { - break; - } - - ctx->in_buf = ctx->in->buf; - ctx->in = ctx->in->next; - - ctx->zstream.next_in = ctx->in_buf->pos; - ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "gzip in_buf:%p ni:%p ai:%ud", - ctx->in_buf, - ctx->zstream.next_in, ctx->zstream.avail_in); + rc = ngx_http_gzip_filter_add_data(r, ctx); - /* STUB */ - if (ctx->in_buf->last < ctx->in_buf->pos) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "zstream.avail_in is huge"); - ctx->done = 1; - return NGX_ERROR; - } - /**/ - - if (ctx->in_buf->last_buf) { - ctx->flush = Z_FINISH; + if (rc == NGX_DECLINED) { + break; + } - } else if (ctx->in_buf->flush) { - ctx->flush = Z_SYNC_FLUSH; - } - - if (ctx->zstream.avail_in == 0) { - if (ctx->flush == Z_NO_FLUSH) { - continue; - } - - } else { - ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, - ctx->zstream.avail_in); - } + if (rc == NGX_AGAIN) { + continue; } - /* is there a space for the gzipped data ? */ - - if (ctx->zstream.avail_out == 0) { - - if (ctx->free) { - ctx->out_buf = ctx->free->buf; - ctx->free = ctx->free->next; - - } else if (ctx->bufs < conf->bufs.num) { - ctx->out_buf = ngx_create_temp_buf(r->pool, - conf->bufs.size); - if (ctx->out_buf == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - ctx->out_buf->tag = (ngx_buf_tag_t) - &ngx_http_gzip_filter_module; - ctx->out_buf->recycled = 1; - ctx->bufs++; + /* ... there are buffers to write zlib output */ - } else { - break; - } - - ctx->zstream.next_out = ctx->out_buf->pos; - ctx->zstream.avail_out = conf->bufs.size; - } + rc = ngx_http_gzip_filter_get_buf(r, ctx); - ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", - ctx->zstream.next_in, ctx->zstream.next_out, - ctx->zstream.avail_in, ctx->zstream.avail_out, - ctx->flush, ctx->redo); - - rc = deflate(&ctx->zstream, ctx->flush); - - if (rc != Z_OK && rc != Z_STREAM_END) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflate() failed: %d, %d", ctx->flush, rc); - ngx_http_gzip_error(ctx); - return NGX_ERROR; + if (rc == NGX_DECLINED) { + break; } - ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", - ctx->zstream.next_in, ctx->zstream.next_out, - ctx->zstream.avail_in, ctx->zstream.avail_out, - rc); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "gzip in_buf:%p pos:%p", - ctx->in_buf, ctx->in_buf->pos); - - - if (ctx->zstream.next_in) { - ctx->in_buf->pos = ctx->zstream.next_in; - - if (ctx->zstream.avail_in == 0) { - ctx->zstream.next_in = NULL; - } + if (rc == NGX_ERROR) { + goto failed; } - ctx->out_buf->last = ctx->zstream.next_out; - - if (ctx->zstream.avail_out == 0) { - - /* zlib wants to output some more gzipped data */ - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - - ctx->redo = 1; - continue; - } - - ctx->redo = 0; - - if (ctx->flush == Z_SYNC_FLUSH) { - - ctx->zstream.avail_out = 0; - ctx->out_buf->flush = 1; - ctx->flush = Z_NO_FLUSH; + rc = ngx_http_gzip_filter_deflate(r, ctx); - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - + if (rc == NGX_OK) { break; } - if (rc == Z_STREAM_END) { - - ctx->zin = ctx->zstream.total_in; - ctx->zout = 10 + ctx->zstream.total_out + 8; - - rc = deflateEnd(&ctx->zstream); - - if (rc != Z_OK) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflateEnd() failed: %d", rc); - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - ngx_pfree(r->pool, ctx->preallocated); - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - - if (ctx->zstream.avail_out >= 8) { - trailer = (struct gztrailer *) ctx->out_buf->last; - ctx->out_buf->last += 8; - ctx->out_buf->last_buf = 1; - - } else { - b = ngx_create_temp_buf(r->pool, 8); - if (b == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - b->last_buf = 1; - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = b; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - trailer = (struct gztrailer *) b->pos; - b->last += 8; - } - -#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) - - trailer->crc32 = ctx->crc32; - trailer->zlen = ctx->zin; - -#else - trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff); - trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff); - trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff); - trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff); - - trailer->zlen[0] = (u_char) (ctx->zin & 0xff); - trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff); - trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff); - trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff); -#endif - - ctx->zstream.avail_in = 0; - ctx->zstream.avail_out = 0; - - ctx->done = 1; - - r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; - - break; + if (rc == NGX_ERROR) { + goto failed; } - if (conf->no_buffer && ctx->in == NULL) { + /* rc == NGX_AGAIN */ + } + + if (ctx->out == NULL) { + ngx_http_gzip_filter_free_copy_buf(r, ctx); - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } + return ctx->busy ? NGX_AGAIN : NGX_OK; + } - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - - break; + if (!ctx->gzheader) { + if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) { + goto failed; } } - if (ctx->out == NULL) { + rc = ngx_http_next_body_filter(r, ctx->out); - if (last == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (ctx->busy == NULL) { - return NGX_OK; - } + if (rc == NGX_ERROR) { + goto failed; } - last = ngx_http_next_body_filter(r, ctx->out); - - /* - * we do not check NGX_AGAIN here because the downstream filters - * may free some buffers and zlib may compress some data into them - */ - - if (last == NGX_ERROR) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } + ngx_http_gzip_filter_free_copy_buf(r, ctx); ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, (ngx_buf_tag_t) &ngx_http_gzip_filter_module); ctx->last_out = &ctx->out; + ctx->nomem = 0; + if (ctx->done) { - return last; + return rc; + } + } + + /* unreachable */ + +failed: + + ctx->done = 1; + + if (ctx->preallocated) { + deflateEnd(&ctx->zstream); + + ngx_pfree(r->pool, ctx->preallocated); + } + + ngx_http_gzip_filter_free_copy_buf(r, ctx); + + return NGX_ERROR; +} + + +static void +ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + int wbits, memlevel; + ngx_http_gzip_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + wbits = conf->wbits; + memlevel = conf->memlevel; + + if (r->headers_out.content_length_n > 0) { + + /* the actual zlib window size is smaller by 262 bytes */ + + while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) { + wbits--; + memlevel--; } } + + ctx->wbits = wbits; + ctx->memlevel = memlevel; + + /* + * We preallocate a memory for zlib in one buffer (200K-400K), this + * decreases a number of malloc() and free() calls and also probably + * decreases a number of syscalls (sbrk()/mmap() and so on). + * Besides we free the memory as soon as a gzipping will complete + * and do not wait while a whole response will be sent to a client. + * + * 8K is for zlib deflate_state, it takes + * *) 5816 bytes on i386 and sparc64 (32-bit mode) + * *) 5920 bytes on amd64 and sparc64 + */ + + ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); +} + + +static ngx_int_t +ngx_http_gzip_filter_copy_recycled(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in) +{ + size_t size, buffered; + ngx_buf_t *b, *buf; + ngx_chain_t *cl, **ll; + ngx_http_request_t *r; + + r = ctx->request; + + r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; + + buffered = 0; + ll = &ctx->in; + + for (cl = ctx->in; cl; cl = cl->next) { + buffered += cl->buf->last - cl->buf->pos; + ll = &cl->next; + } + + while (in) { + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + b = in->buf; + + size = b->last - b->pos; + buffered += size; + + if (b->flush || b->last_buf) { + ctx->buffering = 0; + + } else if (buffered > ctx->allocated / 2) { + + /* + * With default memory settings zlib starts to output gzipped data + * only after it has got about 90K, so it makes sense to allocate + * zlib memory (200-400K) only after we have enough data + * to compress. Although we copy recycled buffers, nevertheless + * for responses up to 120K this allows to allocate zlib memory, + * to compress and to output the response in one step + * using hot CPU cache. + */ + + ctx->buffering = 0; + } + + if (ctx->buffering && size && b->recycled) { + + buf = ngx_create_temp_buf(r->pool, size); + if (buf == NULL) { + return NGX_ERROR; + } + + buf->last = ngx_cpymem(buf->pos, b->pos, size); + b->pos = b->last; + + buf->last_buf = b->last_buf; + buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module; + + cl->buf = buf; + + } else { + cl->buf = b; + } + + *ll = cl; + ll = &cl->next; + in = in->next; + } + + *ll = NULL; + + return ctx->buffering ? NGX_OK : NGX_DONE; +} + + +static ngx_int_t +ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx) +{ + int rc; + ngx_http_gzip_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + ctx->preallocated = ngx_palloc(r->pool, ctx->allocated); + if (ctx->preallocated == NULL) { + return NGX_ERROR; + } + + ctx->free_mem = ctx->preallocated; + + ctx->zstream.zalloc = ngx_http_gzip_filter_alloc; + ctx->zstream.zfree = ngx_http_gzip_filter_free; + ctx->zstream.opaque = ctx; + + rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED, + - ctx->wbits, ctx->memlevel, Z_DEFAULT_STRATEGY); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "deflateInit2() failed: %d", rc); + return NGX_ERROR; + } + + ctx->last_out = &ctx->out; + ctx->crc32 = crc32(0L, Z_NULL, 0); + ctx->flush = Z_NO_FLUSH; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + static u_char gzheader[10] = + { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + return NGX_ERROR; + } + + b->memory = 1; + b->pos = gzheader; + b->last = b->pos + 10; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = ctx->out; + ctx->out = cl; + + ctx->gzheader = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) { + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "gzip in: %p", ctx->in); + + if (ctx->in == NULL) { + return NGX_DECLINED; + } + + if (ctx->copy_buf) { + + /* + * to avoid CPU cache trashing we do not free() just quit buf, + * but postpone free()ing after zlib compressing and data output + */ + + ctx->copy_buf->next = ctx->copied; + ctx->copied = ctx->copy_buf; + ctx->copy_buf = NULL; + } + + ctx->in_buf = ctx->in->buf; + + if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) { + ctx->copy_buf = ctx->in; + } + + ctx->in = ctx->in->next; + + ctx->zstream.next_in = ctx->in_buf->pos; + ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "gzip in_buf:%p ni:%p ai:%ud", + ctx->in_buf, + ctx->zstream.next_in, ctx->zstream.avail_in); + + if (ctx->in_buf->last_buf) { + ctx->flush = Z_FINISH; + + } else if (ctx->in_buf->flush) { + ctx->flush = Z_SYNC_FLUSH; + } + + if (ctx->zstream.avail_in) { + + ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, + ctx->zstream.avail_in); + + } else if (ctx->flush == Z_NO_FLUSH) { + return NGX_AGAIN; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + ngx_http_gzip_conf_t *conf; + + if (ctx->zstream.avail_out) { + return NGX_OK; + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + if (ctx->free) { + ctx->out_buf = ctx->free->buf; + ctx->free = ctx->free->next; + + } else if (ctx->bufs < conf->bufs.num) { + + ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size); + if (ctx->out_buf == NULL) { + return NGX_ERROR; + } + + ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module; + ctx->out_buf->recycled = 1; + ctx->bufs++; + + } else { + ctx->nomem = 1; + return NGX_DECLINED; + } + + ctx->zstream.next_out = ctx->out_buf->pos; + ctx->zstream.avail_out = conf->bufs.size; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + int rc; + ngx_chain_t *cl; + ngx_http_gzip_conf_t *conf; + + ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", + ctx->zstream.next_in, ctx->zstream.next_out, + ctx->zstream.avail_in, ctx->zstream.avail_out, + ctx->flush, ctx->redo); + + rc = deflate(&ctx->zstream, ctx->flush); + + if (rc != Z_OK && rc != Z_STREAM_END) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "deflate() failed: %d, %d", ctx->flush, rc); + return NGX_ERROR; + } + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", + ctx->zstream.next_in, ctx->zstream.next_out, + ctx->zstream.avail_in, ctx->zstream.avail_out, + rc); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "gzip in_buf:%p pos:%p", + ctx->in_buf, ctx->in_buf->pos); + + if (ctx->zstream.next_in) { + ctx->in_buf->pos = ctx->zstream.next_in; + + if (ctx->zstream.avail_in == 0) { + ctx->zstream.next_in = NULL; + } + } + + ctx->out_buf->last = ctx->zstream.next_out; + + if (ctx->zstream.avail_out == 0) { + + /* zlib wants to output some more gzipped data */ + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + ctx->redo = 1; + + return NGX_AGAIN; + } + + ctx->redo = 0; + + if (ctx->flush == Z_SYNC_FLUSH) { + + ctx->zstream.avail_out = 0; + ctx->out_buf->flush = 1; + ctx->flush = Z_NO_FLUSH; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + return NGX_OK; + } + + if (rc == Z_STREAM_END) { + + if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + if (conf->no_buffer && ctx->in == NULL) { + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + return NGX_OK; + } + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx) +{ + int rc; + ngx_buf_t *b; + ngx_chain_t *cl; + struct gztrailer *trailer; + + ctx->zin = ctx->zstream.total_in; + ctx->zout = 10 + ctx->zstream.total_out + 8; + + rc = deflateEnd(&ctx->zstream); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "deflateEnd() failed: %d", rc); + return NGX_ERROR; + } + + ngx_pfree(r->pool, ctx->preallocated); + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + if (ctx->zstream.avail_out >= 8) { + trailer = (struct gztrailer *) ctx->out_buf->last; + ctx->out_buf->last += 8; + ctx->out_buf->last_buf = 1; + + } else { + b = ngx_create_temp_buf(r->pool, 8); + if (b == NULL) { + return NGX_ERROR; + } + + b->last_buf = 1; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + trailer = (struct gztrailer *) b->pos; + b->last += 8; + } + +#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) + + trailer->crc32 = ctx->crc32; + trailer->zlen = ctx->zin; + +#else + + trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff); + trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff); + trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff); + trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff); + + trailer->zlen[0] = (u_char) (ctx->zin & 0xff); + trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff); + trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff); + trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff); + +#endif + + ctx->zstream.avail_in = 0; + ctx->zstream.avail_out = 0; + + ctx->done = 1; + + r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; + + return NGX_OK; } @@ -674,14 +938,14 @@ ngx_http_gzip_filter_alloc(void *opaque, alloc = items * size; - if (alloc % 512 != 0) { + if (alloc % 512 != 0 && alloc < 8192) { /* * The zlib deflate_state allocation, it takes about 6K, * we allocate 8K. Other allocations are divisible by 512. */ - alloc = (alloc + ngx_pagesize - 1) & ~(ngx_pagesize - 1); + alloc = 8192; } if (alloc <= ctx->allocated) { @@ -719,20 +983,16 @@ ngx_http_gzip_filter_free(void *opaque, static void -ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx) +ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx) { - deflateEnd(&ctx->zstream); + ngx_chain_t *cl; - if (ctx->preallocated) { - ngx_pfree(ctx->request->pool, ctx->preallocated); + for (cl = ctx->copied; cl; cl = cl->next) { + ngx_pfree(r->pool, cl->buf->start); } - ctx->zstream.avail_in = 0; - ctx->zstream.avail_out = 0; - - ctx->done = 1; - - return; + ctx->copied = NULL; } @@ -834,7 +1094,8 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, ngx_conf_merge_value(conf->enable, prev->enable, 0); - ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize); + ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, + (128 * 1024) / ngx_pagesize, ngx_pagesize); ngx_conf_merge_value(conf->level, prev->level, 1); ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS); @@ -871,9 +1132,9 @@ ngx_http_gzip_filter_init(ngx_conf_t *cf static char * ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data) { - int *np = data; + size_t *np = data; - int wbits, wsize; + size_t wbits, wsize; wbits = 15; @@ -895,9 +1156,9 @@ ngx_http_gzip_window(ngx_conf_t *cf, voi static char * ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data) { - int *np = data; + size_t *np = data; - int memlevel, hsize; + size_t memlevel, hsize; memlevel = 9; diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -325,15 +325,17 @@ ngx_http_add_header(ngx_http_request_t * { ngx_table_elt_t *h; - h = ngx_list_push(&r->headers_out.headers); - if (h == NULL) { - return NGX_ERROR; + if (value->len) { + h = ngx_list_push(&r->headers_out.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = hv->value.hash; + h->key = hv->value.key; + h->value = *value; } - h->hash = hv->value.hash; - h->key = hv->value.key; - h->value = *value; - return NGX_OK; } diff --git a/src/http/modules/ngx_http_index_module.c b/src/http/modules/ngx_http_index_module.c --- a/src/http/modules/ngx_http_index_module.c +++ b/src/http/modules/ngx_http_index_module.c @@ -99,7 +99,6 @@ ngx_http_index_handler(ngx_http_request_ size_t len, nlen, root, allocated; ngx_int_t rc; ngx_str_t path, uri; - ngx_log_t *log; ngx_uint_t i, dir_tested; ngx_http_index_t *index; ngx_open_file_info_t of; @@ -122,8 +121,6 @@ ngx_http_index_handler(ngx_http_request_ return NGX_DECLINED; } - log = r->connection->log; - ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); @@ -206,7 +203,8 @@ ngx_http_index_handler(ngx_http_request_ *e.pos++ = '\0'; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "open index \"%V\"", &path); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "open index \"%V\"", &path); ngx_memzero(&of, sizeof(ngx_open_file_info_t)); @@ -219,7 +217,7 @@ ngx_http_index_handler(ngx_http_request_ if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, of.err, + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err, ngx_open_file_n " \"%s\" failed", path.data); if (of.err == 0) { @@ -244,7 +242,7 @@ ngx_http_index_handler(ngx_http_request_ continue; } - ngx_log_error(NGX_LOG_ERR, log, of.err, + ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err, ngx_open_file_n " \"%s\" failed", path.data); return NGX_HTTP_INTERNAL_SERVER_ERROR; diff --git a/src/http/modules/ngx_http_limit_req_module.c b/src/http/modules/ngx_http_limit_req_module.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_limit_req_module.c @@ -0,0 +1,767 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +typedef struct { + u_char color; + u_char dummy; + u_short len; + ngx_queue_t queue; + ngx_msec_t last; + ngx_uint_t excess; /* integer value, 1 corresponds to 0.001 r/s */ + u_char data[1]; +} ngx_http_limit_req_node_t; + + +typedef struct { + ngx_rbtree_t *rbtree; + ngx_queue_t *queue; + ngx_slab_pool_t *shpool; + ngx_uint_t rate; /* integer value, 1 corresponds to 0.001 r/s */ + ngx_int_t index; + ngx_str_t var; +} ngx_http_limit_req_ctx_t; + + +typedef struct { + ngx_shm_zone_t *shm_zone; + ngx_uint_t burst; /* integer value, 1 corresponds to 0.001 r/s */ + ngx_uint_t nodelay;/* unsigned nodelay:1 */ +} ngx_http_limit_req_conf_t; + + +static void ngx_http_limit_req_delay(ngx_http_request_t *r); +static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, + ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lrp); +static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, + ngx_uint_t n); + +static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf); +static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_http_limit_req_commands[] = { + + { ngx_string("limit_req_zone"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3, + ngx_http_limit_req_zone, + 0, + 0, + NULL }, + + { ngx_string("limit_req"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123, + ngx_http_limit_req, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_limit_req_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_limit_req_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_limit_req_create_conf, /* create location configration */ + ngx_http_limit_req_merge_conf /* merge location configration */ +}; + + +ngx_module_t ngx_http_limit_req_module = { + NGX_MODULE_V1, + &ngx_http_limit_req_module_ctx, /* module context */ + ngx_http_limit_req_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_int_t +ngx_http_limit_req_handler(ngx_http_request_t *r) +{ + size_t len, n; + uint32_t hash; + ngx_int_t rc; + ngx_uint_t excess; + ngx_time_t *tp; + ngx_rbtree_node_t *node; + ngx_http_variable_value_t *vv; + ngx_http_limit_req_ctx_t *ctx; + ngx_http_limit_req_node_t *lr; + ngx_http_limit_req_conf_t *lrcf; + + if (r->main->limit_req_set) { + return NGX_DECLINED; + } + + lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); + + if (lrcf->shm_zone == NULL) { + return NGX_DECLINED; + } + + ctx = lrcf->shm_zone->data; + + vv = ngx_http_get_indexed_variable(r, ctx->index); + + if (vv == NULL || vv->not_found) { + return NGX_DECLINED; + } + + len = vv->len; + + if (len == 0) { + return NGX_DECLINED; + } + + if (len > 65535) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "the value of the \"%V\" variable " + "is more than 65535 bytes: \"%v\"", + &ctx->var, vv); + return NGX_DECLINED; + } + + r->main->limit_req_set = 1; + + hash = ngx_crc32_short(vv->data, len); + + ngx_shmtx_lock(&ctx->shpool->mutex); + + ngx_http_limit_req_expire(ctx, 1); + + rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &lr); + + if (lr) { + ngx_queue_remove(&lr->queue); + + ngx_queue_insert_head(ctx->queue, &lr->queue); + + excess = lr->excess; + + } else { + excess = 0; + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000); + + if (rc == NGX_BUSY) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "limiting requests, excess: %ui.%03ui by zone \"%V\"", + excess / 1000, excess % 1000, &lrcf->shm_zone->name); + + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + + if (rc == NGX_AGAIN) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + if (lrcf->nodelay) { + return NGX_DECLINED; + } + + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "delaying request, excess: %ui.%03ui, by zone \"%V\"", + excess / 1000, excess % 1000, &lrcf->shm_zone->name); + + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->read_event_handler = ngx_http_test_reading; + r->write_event_handler = ngx_http_limit_req_delay; + ngx_add_timer(r->connection->write, (ngx_msec_t) excess); + + return NGX_AGAIN; + } + + if (rc == NGX_OK) { + goto done; + } + + /* rc == NGX_DECLINED */ + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_http_limit_req_node_t, data) + + len; + + node = ngx_slab_alloc_locked(ctx->shpool, n); + if (node == NULL) { + + ngx_http_limit_req_expire(ctx, 0); + + node = ngx_slab_alloc_locked(ctx->shpool, n); + if (node == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "could not allocate memory in zone \"%V\"", + &lrcf->shm_zone->name); + + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + } + + lr = (ngx_http_limit_req_node_t *) &node->color; + + node->key = hash; + lr->len = (u_char) len; + + tp = ngx_timeofday(); + lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + + lr->excess = 0; + ngx_memcpy(lr->data, vv->data, len); + + ngx_rbtree_insert(ctx->rbtree, node); + + ngx_queue_insert_head(ctx->queue, &lr->queue); + +done: + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_DECLINED; +} + + +static void +ngx_http_limit_req_delay(ngx_http_request_t *r) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "limit_req delay"); + + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + r->read_event_handler = ngx_http_block_reading; + r->write_event_handler = ngx_http_core_run_phases; + + ngx_http_core_run_phases(r); +} + + +static void +ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_http_limit_req_node_t *lrn, *lrnt; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + lrn = (ngx_http_limit_req_node_t *) &node->color; + lrnt = (ngx_http_limit_req_node_t *) &temp->color; + + p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0) + ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + + +static ngx_int_t +ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash, + u_char *data, size_t len, ngx_http_limit_req_node_t **lrp) +{ + ngx_int_t rc, excess; + ngx_time_t *tp; + ngx_msec_t now; + ngx_msec_int_t ms; + ngx_rbtree_node_t *node, *sentinel; + ngx_http_limit_req_ctx_t *ctx; + ngx_http_limit_req_node_t *lr; + + ctx = lrcf->shm_zone->data; + + node = ctx->rbtree->root; + sentinel = ctx->rbtree->sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + do { + lr = (ngx_http_limit_req_node_t *) &node->color; + + rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len); + + if (rc == 0) { + + tp = ngx_timeofday(); + + now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + ms = (ngx_msec_int_t) (now - lr->last); + + excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; + + if (excess < 0) { + excess = 0; + } + + lr->excess = excess; + lr->last = now; + + *lrp = lr; + + if ((ngx_uint_t) excess > lrcf->burst) { + return NGX_BUSY; + } + + if (excess) { + return NGX_AGAIN; + } + + return NGX_OK; + } + + node = (rc < 0) ? node->left : node->right; + + } while (node != sentinel && hash == node->key); + + break; + } + + *lrp = NULL; + + return NGX_DECLINED; +} + + +static void +ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) +{ + ngx_int_t excess; + ngx_time_t *tp; + ngx_msec_t now; + ngx_queue_t *q; + ngx_msec_int_t ms; + ngx_rbtree_node_t *node; + ngx_http_limit_req_node_t *lr; + + tp = ngx_timeofday(); + + now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + + /* + * n == 1 deletes one or two zero rate entries + * n == 0 deletes oldest entry by force + * and one or two zero rate entries + */ + + while (n < 3) { + + if (ngx_queue_empty(ctx->queue)) { + return; + } + + q = ngx_queue_last(ctx->queue); + + lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue); + + if (n++ != 0) { + + ms = (ngx_msec_int_t) (now - lr->last); + ms = ngx_abs(ms); + + if (ms < 60000) { + return; + } + + excess = lr->excess - ctx->rate * ms / 1000; + + if (excess > 0) { + return; + } + } + + ngx_queue_remove(q); + + node = (ngx_rbtree_node_t *) + ((u_char *) lr - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(ctx->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + } +} + + +static ngx_int_t +ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_http_limit_req_ctx_t *octx = data; + + ngx_rbtree_node_t *sentinel; + ngx_http_limit_req_ctx_t *ctx; + + ctx = shm_zone->data; + + if (octx) { + if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) { + ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, + "limit_req \"%V\" uses the \"%V\" variable " + "while previously it used the \"%V\" variable", + &shm_zone->name, &ctx->var, &octx->var); + return NGX_ERROR; + } + + ctx->rbtree = octx->rbtree; + ctx->queue = octx->queue; + ctx->shpool = octx->shpool; + + return NGX_OK; + } + + ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + ctx->rbtree = ngx_slab_alloc(ctx->shpool, sizeof(ngx_rbtree_t)); + if (ctx->rbtree == NULL) { + return NGX_ERROR; + } + + sentinel = ngx_slab_alloc(ctx->shpool, sizeof(ngx_rbtree_node_t)); + if (sentinel == NULL) { + return NGX_ERROR; + } + + ngx_rbtree_init(ctx->rbtree, sentinel, + ngx_http_limit_req_rbtree_insert_value); + + ctx->queue = ngx_slab_alloc(ctx->shpool, sizeof(ngx_queue_t)); + if (ctx->queue == NULL) { + return NGX_ERROR; + } + + ngx_queue_init(ctx->queue); + + return NGX_OK; +} + + +static void * +ngx_http_limit_req_create_conf(ngx_conf_t *cf) +{ + ngx_http_limit_req_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + /* + * set by ngx_pcalloc(): + * + * conf->shm_zone = NULL; + * conf->burst = 0; + * conf->nodelay = 0; + */ + + return conf; +} + + +static char * +ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_limit_req_conf_t *prev = parent; + ngx_http_limit_req_conf_t *conf = child; + + if (conf->shm_zone == NULL) { + *conf = *prev; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + u_char *p; + size_t size, len; + ngx_str_t *value, name, s; + ngx_int_t rate, scale; + ngx_uint_t i; + ngx_shm_zone_t *shm_zone; + ngx_http_limit_req_ctx_t *ctx; + + value = cf->args->elts; + + ctx = NULL; + size = 0; + rate = 1; + scale = 1; + name.len = 0; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { + + name.data = value[i].data + 5; + + p = (u_char *) ngx_strchr(name.data, ':'); + + if (p) { + name.len = p - name.data; + + p++; + + s.len = value[i].data + value[i].len - p; + s.data = p; + + size = ngx_parse_size(&s); + if (size > 8191) { + continue; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (ngx_strncmp(value[i].data, "rate=", 5) == 0) { + + len = value[i].len; + p = value[i].data + len - 3; + + if (ngx_strncmp(p, "r/s", 3) == 0) { + scale = 1; + len -= 3; + + } else if (ngx_strncmp(p, "r/m", 3) == 0) { + scale = 60; + len -= 3; + } + + rate = ngx_atoi(value[i].data + 5, len - 5); + if (rate <= NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid rate \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (value[i].data[0] == '$') { + + value[i].len--; + value[i].data++; + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + ctx->index = ngx_http_get_variable_index(cf, &value[i]); + if (ctx->index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + ctx->var = value[i]; + + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (name.len == 0 || size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"zone\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + if (ctx == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "no variable is defined for limit_req_zone \"%V\"", + &cmd->name); + return NGX_CONF_ERROR; + } + + ctx->rate = rate * 1000 / scale; + + shm_zone = ngx_shared_memory_add(cf, &name, size, + &ngx_http_limit_req_module); + if (shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + if (shm_zone->data) { + ctx = shm_zone->data; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "limit_req_zone \"%V\" is already bound to variable \"%V\"", + &value[1], &ctx->var); + return NGX_CONF_ERROR; + } + + shm_zone->init = ngx_http_limit_req_init_zone; + shm_zone->data = ctx; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_limit_req_conf_t *lrcf = conf; + + ngx_int_t burst; + ngx_str_t *value, s; + ngx_uint_t i; + + if (lrcf->shm_zone) { + return "is duplicate"; + } + + value = cf->args->elts; + + burst = 0; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { + + s.len = value[i].len - 5; + s.data = value[i].data + 5; + + lrcf->shm_zone = ngx_shared_memory_add(cf, &s, 0, + &ngx_http_limit_req_module); + if (lrcf->shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "burst=", 6) == 0) { + + burst = ngx_atoi(value[i].data + 6, value[i].len - 6); + if (burst <= 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid burst rate \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) { + lrcf->nodelay = 1; + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (lrcf->shm_zone == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"zone\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + if (lrcf->shm_zone->data == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown limit_req_zone \"%V\"", + &lrcf->shm_zone->name); + return NGX_CONF_ERROR; + } + + lrcf->burst = burst * 1000; + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_limit_req_init(ngx_conf_t *cf) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_limit_req_handler; + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_limit_zone_module.c b/src/http/modules/ngx_http_limit_zone_module.c --- a/src/http/modules/ngx_http_limit_zone_module.c +++ b/src/http/modules/ngx_http_limit_zone_module.c @@ -189,6 +189,10 @@ ngx_http_limit_zone_handler(ngx_http_req ngx_shmtx_unlock(&shpool->mutex); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "limiting connections by zone \"%V\"", + &lzcf->shm_zone->name); + return NGX_HTTP_SERVICE_UNAVAILABLE; } @@ -206,6 +210,11 @@ ngx_http_limit_zone_handler(ngx_http_req node = ngx_slab_alloc_locked(shpool, n); if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); + + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "could not allocate memory in zone \"%V\"", + &lzcf->shm_zone->name); + return NGX_HTTP_SERVICE_UNAVAILABLE; } @@ -462,6 +471,10 @@ ngx_http_limit_conn(ngx_conf_t *cf, ngx_ ngx_int_t n; ngx_str_t *value; + if (lzcf->shm_zone) { + return "is duplicate"; + } + value = cf->args->elts; lzcf->shm_zone = ngx_shared_memory_add(cf, &value[1], 0, 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 @@ -433,7 +433,7 @@ ngx_http_log_script_write(ngx_http_reque of.log = 1; of.valid = llcf->open_file_cache_valid; of.min_uses = llcf->open_file_cache_min_uses; - of.directio = NGX_MAX_OFF_T_VALUE; + of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool) != NGX_OK) diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c --- a/src/http/modules/ngx_http_memcached_module.c +++ b/src/http/modules/ngx_http_memcached_module.c @@ -183,7 +183,8 @@ ngx_http_memcached_handler(ngx_http_requ return NGX_HTTP_INTERNAL_SERVER_ERROR; } - u->schema = mlcf->upstream.schema; + u->schema.len = sizeof("memcached://") - 1; + u->schema.data = (u_char *) "memcached://"; u->peer.log = r->connection->log; u->peer.log_error = NGX_ERROR_ERR; @@ -521,7 +522,6 @@ ngx_http_memcached_create_loc_conf(ngx_c * conf->upstream.bufs.num = 0; * conf->upstream.next_upstream = 0; * conf->upstream.temp_path = NULL; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; */ @@ -584,7 +584,6 @@ ngx_http_memcached_merge_loc_conf(ngx_co if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; - conf->upstream.schema = prev->upstream.schema; } if (conf->index == NGX_CONF_UNSET) { @@ -598,13 +597,13 @@ ngx_http_memcached_merge_loc_conf(ngx_co static char * ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_memcached_loc_conf_t *lcf = conf; + ngx_http_memcached_loc_conf_t *mlcf = conf; ngx_str_t *value; ngx_url_t u; ngx_http_core_loc_conf_t *clcf; - if (lcf->upstream.schema.len) { + if (mlcf->upstream.upstream) { return "is duplicate"; } @@ -615,14 +614,11 @@ ngx_http_memcached_pass(ngx_conf_t *cf, u.url = value[1]; u.no_resolve = 1; - lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); - if (lcf->upstream.upstream == NULL) { + mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); + if (mlcf->upstream.upstream == NULL) { return NGX_CONF_ERROR; } - lcf->upstream.schema.len = sizeof("memcached://") - 1; - lcf->upstream.schema.data = (u_char *) "memcached://"; - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_memcached_handler; @@ -631,9 +627,9 @@ ngx_http_memcached_pass(ngx_conf_t *cf, clcf->auto_redirect = 1; } - lcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key); + mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key); - if (lcf->index == NGX_ERROR) { + if (mlcf->index == NGX_ERROR) { return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_not_modified_filter_module.c b/src/http/modules/ngx_http_not_modified_filter_module.c --- a/src/http/modules/ngx_http_not_modified_filter_module.c +++ b/src/http/modules/ngx_http_not_modified_filter_module.c @@ -50,7 +50,8 @@ static ngx_http_output_header_filter_pt static ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r) { - time_t ims; + time_t ims; + ngx_http_core_loc_conf_t *clcf; if (r->headers_out.status != NGX_HTTP_OK || r != r->main @@ -66,17 +67,22 @@ ngx_int_t ngx_http_not_modified_header_f ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http ims:%d lm:%d", ims, r->headers_out.last_modified_time); - /* - * I think that the equality of the dates is correcter - */ + if (ims != r->headers_out.last_modified_time) { + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (ims == r->headers_out.last_modified_time) { - r->headers_out.status = NGX_HTTP_NOT_MODIFIED; - r->headers_out.content_type.len = 0; - ngx_http_clear_content_length(r); - ngx_http_clear_accept_ranges(r); + if (clcf->if_modified_since == 0 + || ims < r->headers_out.last_modified_time) + { + return ngx_http_next_header_filter(r); + } } + r->headers_out.status = NGX_HTTP_NOT_MODIFIED; + r->headers_out.content_type.len = 0; + ngx_http_clear_content_length(r); + ngx_http_clear_accept_ranges(r); + return ngx_http_next_header_filter(r); } 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 @@ -32,6 +32,7 @@ struct ngx_http_proxy_redirect_s { typedef struct { + ngx_str_t schema; ngx_str_t host_header; ngx_str_t port; ngx_str_t uri; @@ -480,7 +481,7 @@ ngx_http_proxy_handler(ngx_http_request_ if (plcf->proxy_lengths == 0) { ctx->vars = plcf->vars; - u->schema = plcf->upstream.schema; + u->schema = plcf->vars.schema; #if (NGX_HTTP_SSL) u->ssl = (plcf->upstream.ssl != NULL); #endif @@ -536,10 +537,11 @@ static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf) { - size_t add; - u_short port; - ngx_str_t proxy; - ngx_url_t u; + u_char *p; + size_t add; + u_short port; + ngx_str_t proxy; + ngx_url_t u; if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0, plcf->proxy_values->elts) @@ -589,6 +591,19 @@ ngx_http_proxy_eval(ngx_http_request_t * return NGX_ERROR; } + if (u.uri.len && u.uri.data[0] == '?') { + p = ngx_pnalloc(r->pool, u.uri.len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + *p++ = '/'; + ngx_memcpy(p, u.uri.data, u.uri.len); + + u.uri.len++; + u.uri.data = p - 1; + } + if (ngx_http_proxy_set_vars(r->pool, &u, &ctx->vars) != NGX_OK) { return NGX_ERROR; } @@ -599,10 +614,18 @@ ngx_http_proxy_eval(ngx_http_request_t * return NGX_ERROR; } - r->upstream->resolved->host = u.host; - r->upstream->resolved->port = (in_port_t) (u.no_port ? u.default_port: - u.port); - r->upstream->resolved->default_port = u.default_port; + if (u.addrs && u.addrs[0].sockaddr) { + r->upstream->resolved->sockaddr = u.addrs[0].sockaddr; + r->upstream->resolved->socklen = u.addrs[0].socklen; + r->upstream->resolved->naddrs = 1; + r->upstream->resolved->host = u.addrs[0].name; + + } else { + r->upstream->resolved->host = u.host; + r->upstream->resolved->port = (in_port_t) (u.no_port ? u.default_port: + u.port); + r->upstream->resolved->no_port = u.no_port; + } return NGX_OK; } @@ -1617,7 +1640,6 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ * conf->upstream.next_upstream = 0; * conf->upstream.temp_path = NULL; * conf->upstream.hide_headers_hash = { NULL, 0 }; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; * conf->upstream.store_lengths = NULL; @@ -1917,9 +1939,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; - conf->vars = prev->vars; - conf->upstream.schema = prev->upstream.schema; } @@ -2202,7 +2222,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_ ngx_http_core_loc_conf_t *clcf; ngx_http_script_compile_t sc; - if (plcf->upstream.schema.len) { + if (plcf->upstream.upstream || plcf->proxy_lengths) { return "is duplicate"; } @@ -2282,8 +2302,8 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_ return NGX_CONF_ERROR; } - plcf->upstream.schema.len = add; - plcf->upstream.schema.data = url->data; + plcf->vars.schema.len = add; + plcf->vars.schema.data = url->data; plcf->location = clcf->name; clcf->handler = ngx_http_proxy_handler; diff --git a/src/http/modules/ngx_http_secure_link_module.c b/src/http/modules/ngx_http_secure_link_module.c --- a/src/http/modules/ngx_http_secure_link_module.c +++ b/src/http/modules/ngx_http_secure_link_module.c @@ -113,7 +113,7 @@ url_start: len = last - p; - if (end - start != 32 || len < 3) { + if (end - start != 32 || len == 0) { goto not_found; } diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c --- a/src/http/modules/ngx_http_ssi_filter_module.c +++ b/src/http/modules/ngx_http_ssi_filter_module.c @@ -375,7 +375,6 @@ ngx_http_ssi_body_filter(ngx_http_reques ngx_uint_t i, index; ngx_chain_t *cl, **ll; ngx_table_elt_t *param; - ngx_http_request_t *pr; ngx_http_ssi_ctx_t *ctx, *mctx; ngx_http_ssi_block_t *bl; ngx_http_ssi_param_t *prm; @@ -403,44 +402,37 @@ ngx_http_ssi_body_filter(ngx_http_reques } } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter \"%V?%V\"", &r->uri, &r->args); + if (ctx->wait) { - if (r->connection->data != r) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\" wait", &r->uri); + + if (r != r->connection->data) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter wait \"%V?%V\" non-active", + &ctx->wait->uri, &ctx->wait->args); + return NGX_AGAIN; } - for (pr = ctx->wait->parent; pr; pr = pr->parent) { - if (pr == r) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\" flush", &r->uri); - - rc = ngx_http_next_body_filter(r, NULL); - - if (ctx->wait->done) { - ctx->wait = NULL; - } - - if (rc == NGX_ERROR || rc == NGX_AGAIN) { - return rc; - } - - break; - } - } - - if (ctx->wait == r) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\" continue", &r->uri); + if (ctx->wait->done) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter wait \"%V?%V\" done", + &ctx->wait->uri, &ctx->wait->args); + ctx->wait = NULL; + + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter wait \"%V?%V\"", + &ctx->wait->uri, &ctx->wait->args); + + return ngx_http_next_body_filter(r, NULL); } } slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\"", &r->uri); - while (ctx->in || ctx->buf) { if (ctx->buf == NULL ){ @@ -788,16 +780,12 @@ ngx_http_ssi_body_filter(ngx_http_reques } } - if (cmd->flush) { - - if (ctx->out) { - rc = ngx_http_ssi_output(r, ctx); - - } else { - rc = ngx_http_next_body_filter(r, NULL); - } - - if (rc == NGX_ERROR) { + if (cmd->flush && ctx->out) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ssi flush"); + + if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) { return NGX_ERROR; } } @@ -947,12 +935,6 @@ ngx_http_ssi_output(ngx_http_request_t * break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if (b->zerocopy_busy) { - break; - } -#endif - if (b->shadow) { b->shadow->pos = b->shadow->last; } @@ -1899,6 +1881,7 @@ ngx_http_ssi_include(ngx_http_request_t if (uri == NULL) { uri = file; + wait = (ngx_str_t *) -1; } rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX); @@ -2001,6 +1984,10 @@ ngx_http_ssi_include(ngx_http_request_t } } + if (wait) { + flags |= NGX_HTTP_SUBREQUEST_WAITED; + } + if (set) { key = ngx_hash_strlow(set->data, set->data, set->len); @@ -2033,16 +2020,10 @@ ngx_http_ssi_include(ngx_http_request_t psr->data = &var->value; } - flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY; + flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED; } - rc = ngx_http_subrequest(r, uri, &args, &sr, psr, flags); - - if (rc == NGX_DONE) { - return NGX_DONE; - } - - if (rc == NGX_ERROR) { + if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) { return NGX_HTTP_SSI_ERROR; } @@ -2050,17 +2031,17 @@ ngx_http_ssi_include(ngx_http_request_t return NGX_OK; } - if (rc == NGX_AGAIN) { - if (ctx->wait == NULL) { - ctx->wait = sr; - - } else { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "only one subrequest may be waited at the same time"); - } + if (ctx->wait == NULL) { + ctx->wait = sr; + + return NGX_AGAIN; + + } else { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "only one subrequest may be waited at the same time"); } - return rc; + return NGX_OK; } diff --git a/src/http/modules/ngx_http_static_module.c b/src/http/modules/ngx_http_static_module.c diff --git a/src/http/modules/ngx_http_sub_filter_module.c b/src/http/modules/ngx_http_sub_filter_module.c --- a/src/http/modules/ngx_http_sub_filter_module.c +++ b/src/http/modules/ngx_http_sub_filter_module.c @@ -465,12 +465,6 @@ ngx_http_sub_output(ngx_http_request_t * break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if (b->zerocopy_busy) { - break; - } -#endif - if (b->shadow) { b->shadow->pos = b->shadow->last; } 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 @@ -15,6 +15,10 @@ #include #include +#if (NGX_HAVE_EXSLT) +#include +#endif + #ifndef NGX_HTTP_XSLT_REUSE_DTD #define NGX_HTTP_XSLT_REUSE_DTD 1 @@ -1269,6 +1273,10 @@ ngx_http_xslt_filter_init(ngx_conf_t *cf { xmlInitParser(); +#if (NGX_HAVE_EXSLT) + exsltRegisterAll(); +#endif + ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_xslt_header_filter; 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.19'; +our $VERSION = '0.7.30'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c --- a/src/http/modules/perl/ngx_http_perl_module.c +++ b/src/http/modules/perl/ngx_http_perl_module.c @@ -285,7 +285,7 @@ ngx_http_perl_sleep_handler(ngx_http_req return; } - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } } diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -489,7 +489,7 @@ ngx_http_init_phase_handlers(ngx_conf_t use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0; use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0; - n = use_rewrite + use_access + 1; /* find config phase */ + n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */; for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { n += cmcf->phases[i].handlers.nelts; @@ -558,6 +558,15 @@ ngx_http_init_phase_handlers(ngx_conf_t continue; + case NGX_HTTP_TRY_FILES_PHASE: + if (cmcf->try_files) { + ph->checker = ngx_http_core_try_files_phase; + n++; + ph++; + } + + continue; + case NGX_HTTP_CONTENT_PHASE: checker = ngx_http_core_content_phase; break; 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 @@ -76,15 +76,21 @@ ngx_int_t ngx_http_parse_header_line(ngx 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); +ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, + ngx_str_t *value); + ngx_int_t ngx_http_find_server_conf(ngx_http_request_t *r); void ngx_http_update_location_config(ngx_http_request_t *r); void ngx_http_handler(ngx_http_request_t *r); +void ngx_http_run_posted_requests(ngx_connection_t *c); +ngx_int_t ngx_http_post_request(ngx_http_request_t *r); void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_empty_handler(ngx_event_t *wev); void ngx_http_request_empty_handler(ngx_http_request_t *r); + #define NGX_HTTP_LAST 1 #define NGX_HTTP_FLUSH 2 @@ -106,6 +112,7 @@ size_t ngx_http_get_time(char *buf, time ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r); void ngx_http_block_reading(ngx_http_request_t *r); +void ngx_http_test_reading(ngx_http_request_t *r); char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); diff --git a/src/http/ngx_http_copy_filter_module.c b/src/http/ngx_http_copy_filter_module.c --- a/src/http/ngx_http_copy_filter_module.c +++ b/src/http/ngx_http_copy_filter_module.c @@ -117,10 +117,6 @@ ngx_http_copy_filter(ngx_http_request_t r->buffered |= NGX_HTTP_COPY_BUFFERED; } - if (r != r->main) { - r->out = ctx->in; - } - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args); } 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 @@ -39,6 +39,8 @@ static char *ngx_http_core_server(ngx_co void *dummy); static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); +static ngx_int_t ngx_http_core_regex_location(ngx_conf_t *cf, + ngx_http_core_loc_conf_t *clcf, ngx_str_t *regex, ngx_uint_t caseless); static char *ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -56,6 +58,8 @@ static char *ngx_http_core_directio(ngx_ void *conf); static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, @@ -108,6 +112,13 @@ static ngx_conf_enum_t ngx_http_core_sa }; +static ngx_conf_enum_t ngx_http_core_if_modified_since[] = { + { ngx_string("exact"), 0 }, + { ngx_string("before"), 1 }, + { ngx_null_string, 0 } +}; + + #if (NGX_HTTP_GZIP) static ngx_conf_enum_t ngx_http_gzip_http_version[] = { @@ -515,6 +526,13 @@ static ngx_command_t ngx_http_core_comm offsetof(ngx_http_core_loc_conf_t, server_tokens), NULL }, + { ngx_string("if_modified_since"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, if_modified_since), + &ngx_http_core_if_modified_since }, + { ngx_string("error_page"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_2MORE, @@ -523,6 +541,13 @@ static ngx_command_t ngx_http_core_comm 0, NULL }, + { ngx_string("try_files"), + NGX_HTTP_LOC_CONF|NGX_CONF_2MORE, + ngx_http_core_try_files, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("post_action"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, @@ -837,7 +862,7 @@ ngx_http_core_find_config_phase(ngx_http return NGX_OK; } - if (r->headers_in.expect) { + if (r->headers_in.expect && r->http_version > NGX_HTTP_VERSION_10) { expect = ngx_http_core_send_continue(r); if (expect != NGX_OK) { @@ -1016,6 +1041,177 @@ ngx_http_core_post_access_phase(ngx_http ngx_int_t +ngx_http_core_try_files_phase(ngx_http_request_t *r, + ngx_http_phase_handler_t *ph) +{ + size_t len, root, alias; + ssize_t reserve, allocated; + u_char *p, *name; + ngx_str_t path; + ngx_http_try_file_t *tf; + ngx_open_file_info_t of; + ngx_http_script_code_pt code; + ngx_http_script_engine_t e; + ngx_http_core_loc_conf_t *clcf; + ngx_http_script_len_code_pt lcode; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try files phase: %ui", r->phase_handler); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->try_files == NULL) { + r->phase_handler++; + return NGX_AGAIN; + } + + allocated = 0; + root = 0; + name = NULL; + path.len = 0; + path.data = NULL; + + tf = clcf->try_files; + + alias = clcf->alias ? clcf->name.len : 0; + + for ( ;; ) { + + if (tf->lengths) { + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + + e.ip = tf->lengths->elts; + e.request = r; + + /* 1 is for terminating '\0' as in static names */ + len = 1; + + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_http_script_len_code_pt *) e.ip; + len += lcode(&e); + } + + } else { + len = tf->name.len; + } + + reserve = len - r->uri.len; + + /* 16 bytes are preallocation */ + reserve = reserve < 16 ? 16 : reserve + 16; + + reserve += alias; + + if (reserve > allocated) { + + /* we just need to allocate path and to copy a root */ + + if (ngx_http_map_uri_to_path(r, &path, &root, reserve) == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + name = path.data + root; + allocated = path.len - root - (r->uri.len - alias); + } + + if (tf->values == NULL) { + + /* tf->name.len includes the terminating '\0' */ + + ngx_memcpy(name, tf->name.data, tf->name.len); + + path.len = (name + tf->name.len - 1) - path.data; + + } else { + e.ip = tf->values->elts; + e.pos = name; + e.flushed = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + + path.len = e.pos - path.data; + + *e.pos++ = '\0'; + + if (alias && ngx_strncmp(name, clcf->name.data, alias) == 0) { + ngx_memcpy(name, name + alias, len - alias); + path.len -= alias; + } + } + + tf++; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try to use file: \"%s\"", name); + + if (tf->lengths == NULL && tf->name.len == 0) { + + path.len -= root; + path.data += root; + + if (path.data[0] == '@') { + (void) ngx_http_named_location(r, &path); + + } else { + (void) ngx_http_internal_redirect(r, &path, NULL); + } + + return NGX_OK; + } + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + + of.directio = clcf->directio; + of.valid = clcf->open_file_cache_valid; + of.min_uses = clcf->open_file_cache_min_uses; + of.errors = clcf->open_file_cache_errors; + of.events = clcf->open_file_cache_events; + + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) + != NGX_OK) + { + if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, + ngx_open_file_n " \"%s\" failed", path.data); + } + + continue; + } + + path.len -= root; + path.data += root; + + if (!alias) { + r->uri = path; + + } else { + r->uri.len = alias + path.len; + r->uri.data = ngx_pnalloc(r->pool, r->uri.len); + if (r->uri.data == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + p = ngx_copy(r->uri.data, clcf->name.data, alias); + ngx_memcpy(p, name, path.len); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try file uri: \"%V\"", &r->uri); + + r->phase_handler++; + return NGX_AGAIN; + } + + /* not reached */ +} + + +ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { @@ -1631,6 +1827,10 @@ ngx_http_server_addr(ngx_http_request_t } r->in_addr = sin.sin_addr.s_addr; + + } else { + sin.sin_family = c->sockaddr->sa_family; + sin.sin_addr.s_addr = r->in_addr; } if (s == NULL) { @@ -1813,7 +2013,6 @@ ngx_http_subrequest(ngx_http_request_t * { ngx_connection_t *c; ngx_http_request_t *sr; - ngx_http_log_ctx_t *ctx; ngx_http_core_srv_conf_t *cscf; ngx_http_postponed_request_t *pr, *p; @@ -1878,6 +2077,7 @@ ngx_http_subrequest(ngx_http_request_t * sr->zero_in_uri = (flags & NGX_HTTP_ZERO_IN_URI) != 0; sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0; + sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0; sr->unparsed_uri = r->unparsed_uri; sr->method_name = ngx_http_core_get_method; @@ -1891,9 +2091,9 @@ ngx_http_subrequest(ngx_http_request_t * sr->parent = r; sr->post_subrequest = ps; sr->read_event_handler = ngx_http_request_empty_handler; - sr->write_event_handler = ngx_http_request_empty_handler; - - if (c->data == r) { + sr->write_event_handler = ngx_http_handler; + + if (c->data == r && r->postponed == NULL) { c->data = sr; } @@ -1922,39 +2122,18 @@ ngx_http_subrequest(ngx_http_request_t * r->postponed = pr; } - ctx = c->log->data; - ctx->current_request = sr; - sr->internal = 1; - sr->fast_subrequest = 1; sr->discard_body = r->discard_body; sr->main_filter_need_in_memory = r->main_filter_need_in_memory; sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; - ngx_http_handler(sr); - - if (!c->destroyed) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http subrequest done \"%V?%V\"", uri, &sr->args); - - r->main->subrequests++; - - *psr = sr; - - if (sr->fast_subrequest) { - sr->fast_subrequest = 0; - - if (sr->done) { - return NGX_OK; - } - } - - return NGX_AGAIN; - } - - return NGX_DONE; + r->main->subrequests++; + + *psr = sr; + + return ngx_http_post_request(sr); } @@ -2018,33 +2197,37 @@ ngx_http_named_location(ngx_http_request cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - for (clcfp = cscf->named_locations; *clcfp; clcfp++) { - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "test location: \"%V\"", &(*clcfp)->name); - - if (name->len != (*clcfp)->name.len - || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0) - { - continue; + if (cscf->named_locations) { + + for (clcfp = cscf->named_locations; *clcfp; clcfp++) { + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "test location: \"%V\"", &(*clcfp)->name); + + if (name->len != (*clcfp)->name.len + || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0) + { + continue; + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "using location: %V \"%V?%V\"", + name, &r->uri, &r->args); + + r->internal = 1; + r->content_handler = NULL; + r->loc_conf = (*clcfp)->loc_conf; + + ngx_http_update_location_config(r); + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + r->phase_handler = cmcf->phase_engine.location_rewrite_index; + + ngx_http_core_run_phases(r); + + return NGX_DONE; } - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "using location: %V \"%V?%V\"", name, &r->uri, &r->args); - - r->internal = 1; - r->content_handler = NULL; - r->loc_conf = (*clcfp)->loc_conf; - - ngx_http_update_location_config(r); - - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - - r->phase_handler = cmcf->phase_engine.location_rewrite_index; - - ngx_http_core_run_phases(r); - - return NGX_DONE; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, @@ -2185,8 +2368,10 @@ static char * ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { char *rv; + u_char *mod; + size_t len; + ngx_str_t *value, *name; ngx_uint_t i; - ngx_str_t *value; ngx_conf_t save; ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx, *pctx; @@ -2228,45 +2413,32 @@ ngx_http_core_location(ngx_conf_t *cf, n value = cf->args->elts; if (cf->args->nelts == 3) { - if (value[1].len == 1 && value[1].data[0] == '=') { - clcf->name = value[2]; + + len = value[1].len; + mod = value[1].data; + name = &value[2]; + + if (len == 1 && mod[0] == '=') { + + clcf->name = *name; clcf->exact_match = 1; - } else if (value[1].len == 2 - && value[1].data[0] == '^' - && value[1].data[1] == '~') - { - clcf->name = value[2]; + } else if (len == 2 && mod[0] == '^' && mod[1] == '~') { + + clcf->name = *name; clcf->noregex = 1; - } else if ((value[1].len == 1 && value[1].data[0] == '~') - || (value[1].len == 2 - && value[1].data[0] == '~' - && value[1].data[1] == '*')) - { -#if (NGX_PCRE) - ngx_str_t err; - u_char errstr[NGX_MAX_CONF_ERRSTR]; - - err.len = NGX_MAX_CONF_ERRSTR; - err.data = errstr; - - clcf->regex = ngx_regex_compile(&value[2], - value[1].len == 2 ? NGX_REGEX_CASELESS: 0, - cf->pool, &err); - - if (clcf->regex == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + } else if (len == 1 && mod[0] == '~') { + + if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { return NGX_CONF_ERROR; } - clcf->name = value[2]; -#else - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the using of the regex \"%V\" " - "requires PCRE library", &value[2]); - return NGX_CONF_ERROR; -#endif + } else if (len == 2 && mod[0] == '~' && mod[1] == '*') { + + if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -2276,10 +2448,47 @@ ngx_http_core_location(ngx_conf_t *cf, n } else { - clcf->name = value[1]; - - if (value[1].data[0] == '@') { - clcf->named = 1; + name = &value[1]; + + if (name->data[0] == '=') { + + clcf->name.len = name->len - 1; + clcf->name.data = name->data + 1; + clcf->exact_match = 1; + + } else if (name->data[0] == '^' && name->data[1] == '~') { + + clcf->name.len = name->len - 2; + clcf->name.data = name->data + 2; + clcf->noregex = 1; + + } else if (name->data[0] == '~') { + + name->len--; + name->data++; + + if (name->data[0] == '*') { + + name->len--; + name->data++; + + if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + } else { + if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + } else { + + clcf->name = *name; + + if (name->data[0] == '@') { + clcf->named = 1; + } } } @@ -2317,13 +2526,13 @@ ngx_http_core_location(ngx_conf_t *cf, n return NGX_CONF_ERROR; } + len = pclcf->name.len; + #if (NGX_PCRE) if (clcf->regex == NULL - && ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len) - != 0) + && ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0) #else - if (ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len) - != 0) + if (ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0) #endif { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -2349,6 +2558,40 @@ ngx_http_core_location(ngx_conf_t *cf, n } +static ngx_int_t +ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf, + ngx_str_t *regex, ngx_uint_t caseless) +{ +#if (NGX_PCRE) + ngx_str_t err; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + err.len = NGX_MAX_CONF_ERRSTR; + err.data = errstr; + + clcf->regex = ngx_regex_compile(regex, caseless ? NGX_REGEX_CASELESS: 0, + cf->pool, &err); + + if (clcf->regex == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + return NGX_ERROR; + } + + clcf->name = *regex; + + return NGX_OK; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the using of the regex \"%V\" requires PCRE library", + regex); + return NGX_ERROR; + +#endif +} + + static char * ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -2647,6 +2890,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t * lcf->default_type = { 0, NULL }; * lcf->err_log = NULL; * lcf->error_pages = NULL; + * lcf->try_files = NULL; * lcf->client_body_path = NULL; * lcf->regex = NULL; * lcf->exact_match = 0; @@ -2659,6 +2903,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t lcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE; lcf->client_body_timeout = NGX_CONF_UNSET_MSEC; lcf->satisfy = NGX_CONF_UNSET_UINT; + lcf->if_modified_since = NGX_CONF_UNSET_UINT; lcf->internal = NGX_CONF_UNSET; lcf->client_body_in_file_only = NGX_CONF_UNSET; lcf->sendfile = NGX_CONF_UNSET; @@ -2848,6 +3093,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy, NGX_HTTP_SATISFY_ALL); + ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since, + 0); ngx_conf_merge_value(conf->internal, prev->internal, 0); ngx_conf_merge_value(conf->client_body_in_file_only, prev->client_body_in_file_only, 0); @@ -3470,7 +3717,7 @@ ngx_http_core_directio(ngx_conf_t *cf, n value = cf->args->elts; if (ngx_strcmp(value[1].data, "off") == 0) { - clcf->directio = NGX_MAX_OFF_T_VALUE; + clcf->directio = NGX_OPEN_FILE_DIRECTIO_OFF; return NGX_CONF_OK; } @@ -3619,6 +3866,65 @@ ngx_http_core_error_page(ngx_conf_t *cf, static char * +ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *clcf = conf; + + ngx_str_t *value; + ngx_uint_t i, n; + ngx_http_try_file_t *tf; + ngx_http_script_compile_t sc; + ngx_http_core_main_conf_t *cmcf; + + if (clcf->try_files) { + return "is duplicate"; + } + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + cmcf->try_files = 1; + + tf = ngx_pcalloc(cf->pool, cf->args->nelts * sizeof(ngx_http_try_file_t)); + if (tf == NULL) { + return NGX_CONF_ERROR; + } + + clcf->try_files = tf; + + value = cf->args->elts; + + for (i = 0; i < cf->args->nelts - 1; i++) { + + tf[i].name = value[i + 1]; + + n = ngx_http_script_variables_count(&tf[i].name); + + if (n) { + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = &tf[i].name; + sc.lengths = &tf[i].lengths; + sc.values = &tf[i].values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + } else { + /* add trailing '\0' to length */ + tf[i].name.len++; + } + } + + return NGX_CONF_OK; +} + + +static char * ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *lcf = conf; 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 @@ -81,6 +81,7 @@ typedef enum { NGX_HTTP_ACCESS_PHASE, NGX_HTTP_POST_ACCESS_PHASE, + NGX_HTTP_TRY_FILES_PHASE, NGX_HTTP_CONTENT_PHASE, NGX_HTTP_LOG_PHASE @@ -129,6 +130,8 @@ typedef struct { ngx_hash_keys_arrays_t *variables_keys; + ngx_uint_t try_files; /* unsigned try_files:1 */ + ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1]; } ngx_http_core_main_conf_t; @@ -238,6 +241,13 @@ typedef struct { } ngx_http_err_page_t; +typedef struct { + ngx_array_t *lengths; + ngx_array_t *values; + ngx_str_t name; +} ngx_http_try_file_t; + + struct ngx_http_core_loc_conf_s { ngx_str_t name; /* location name */ @@ -299,6 +309,7 @@ struct ngx_http_core_loc_conf_s { time_t keepalive_header; /* keepalive_timeout */ ngx_uint_t satisfy; /* satisfy */ + ngx_uint_t if_modified_since; /* if_modified_since */ ngx_flag_t internal; /* internal */ ngx_flag_t client_body_in_file_only; /* client_body_in_file_only */ @@ -327,6 +338,7 @@ struct ngx_http_core_loc_conf_s { #endif ngx_array_t *error_pages; /* error_page */ + ngx_http_try_file_t *try_files; /* try_files */ ngx_path_t *client_body_temp_path; /* client_body_temp_path */ @@ -385,6 +397,8 @@ ngx_int_t ngx_http_core_access_phase(ngx ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); +ngx_int_t ngx_http_core_try_files_phase(ngx_http_request_t *r, + ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); 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 @@ -1481,3 +1481,45 @@ ngx_http_parse_multi_header_lines(ngx_ar return NGX_DECLINED; } + + +ngx_int_t +ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value) +{ + u_char *p; + + if (r->args.len == 0) { + return NGX_DECLINED; + } + + for (p = r->args.data; *p && *p != ' '; p++) { + + /* + * although r->args.data is not null-terminated by itself, + * however, there is null in the end of request line + */ + + p = ngx_strcasestrn(p, (char *) name, len - 1); + + if (p == NULL) { + return NGX_DECLINED; + } + + if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') { + + value->data = p + len + 1; + + p = (u_char *) ngx_strchr(p, '&'); + + if (p == NULL) { + p = r->args.data + r->args.len; + } + + value->len = p - value->data; + + return NGX_OK; + } + } + + return NGX_DECLINED; +} diff --git a/src/http/ngx_http_postpone_filter_module.c b/src/http/ngx_http_postpone_filter_module.c --- a/src/http/ngx_http_postpone_filter_module.c +++ b/src/http/ngx_http_postpone_filter_module.c @@ -9,8 +9,8 @@ #include -static ngx_int_t - ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r, + ngx_chain_t *in); static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf); @@ -51,181 +51,119 @@ static ngx_http_output_body_filter_pt static ngx_int_t ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in) { - ngx_int_t rc; - ngx_chain_t *out; - ngx_http_postponed_request_t *pr, **ppr; + ngx_connection_t *c; + ngx_http_postponed_request_t *pr; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + c = r->connection; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in); - if (r != r->connection->data || (r->postponed && in)) { - - if (r->postponed) { - for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ } - - ppr = pr->request ? &pr->next : NULL; - - } else { - ppr = &r->postponed; -#if (NGX_SUPPRESS_WARN) - pr = NULL; -#endif - } + if (r != c->data) { - if (ppr) { - pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t)); - if (pr == NULL) { - return NGX_ERROR; - } - - *ppr = pr; - - pr->request = NULL; - pr->out = NULL; - pr->next = NULL; - } - - if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_ERROR) { - return NGX_ERROR; + if (in) { + ngx_http_postpone_filter_add(r, in); + return NGX_OK; } -#if 1 - { - ngx_chain_t *cl; - ngx_buf_t *b = NULL; - for (cl = pr->out; cl; cl = cl->next) { - if (cl->buf == b) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "the same buf was used in postponed %p %p", - b, b->pos); - ngx_debug_point(); - return NGX_ERROR; - } - b = cl->buf; - } - } +#if 0 + /* TODO: SSI may pass NULL */ + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "http postpone filter NULL inactive request", + &r->uri, &r->args); #endif - if (r != r->connection->data || r->postponed->request) { - return NGX_AGAIN; - } - } - - if (r->postponed) { - out = r->postponed->out; - if (out) { - r->postponed = r->postponed->next; - } - - } else { - out = in; - } - - rc = NGX_OK; - - if (out - || (r->connection->buffered - & (NGX_HTTP_LOWLEVEL_BUFFERED|NGX_LOWLEVEL_BUFFERED))) - { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter out \"%V?%V\"", &r->uri, &r->args); - - if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) { - - rc = ngx_http_next_filter(r->main, out); - - if (rc == NGX_ERROR) { - /* NGX_ERROR may be returned by any filter */ - r->connection->error = 1; - } - } + return NGX_OK; } if (r->postponed == NULL) { - return rc; + + if (in || c->buffered) { + return ngx_http_next_filter(r->main, in); + } + + return NGX_OK; + } + + if (in) { + ngx_http_postpone_filter_add(r, in); } - rc = ngx_http_postpone_filter_output_postponed_request(r); + do { + pr = r->postponed; + + if (pr->request) { + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http postpone filter wake \"%V?%V\"", + &pr->request->uri, &pr->request->args); + + r->postponed = pr->next; + + c->data = pr->request; + + return ngx_http_post_request(pr->request); + } - if (rc == NGX_ERROR) { - /* NGX_ERROR may be returned by any filter */ - r->connection->error = 1; - } + if (pr->out == NULL) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "http postpone filter NULL output", + &r->uri, &r->args); + + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http postpone filter output \"%V?%V\"", + &r->uri, &r->args); - return rc; + if (ngx_http_next_filter(r->main, pr->out) == NGX_ERROR) { + return NGX_ERROR; + } + } + + r->postponed = pr->next; + + } while (r->postponed); + + return NGX_OK; } static ngx_int_t -ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r) +ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in) { - ngx_int_t rc; - ngx_chain_t *out; - ngx_http_log_ctx_t *ctx; - ngx_http_postponed_request_t *pr; + ngx_http_postponed_request_t *pr, **ppr; - for ( ;; ) { - pr = r->postponed; + if (r->postponed) { + for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ } - if (pr == NULL) { - break; + if (pr->request == NULL) { + goto found; } - if (pr->request) { - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter handle \"%V?%V\"", - &pr->request->uri, &pr->request->args); - - ctx = r->connection->log->data; - ctx->current_request = pr->request; - - if (!pr->request->done) { - r->connection->data = pr->request; - return NGX_AGAIN; - } - - rc = ngx_http_postpone_filter_output_postponed_request(pr->request); - - if (rc == NGX_AGAIN || rc == NGX_ERROR) { - return rc; - } + ppr = &pr->next; - r->postponed = r->postponed->next; - pr = r->postponed; - } - - if (pr == NULL) { - break; - } - - out = pr->out; + } else { + ppr = &r->postponed; + } - if (out) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter out postponed \"%V?%V\"", - &r->uri, &r->args); - - if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) { - if (ngx_http_next_filter(r->main, out) == NGX_ERROR) { - return NGX_ERROR; - } - } - } - - r->postponed = r->postponed->next; + pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t)); + if (pr == NULL) { + return NGX_ERROR; } - if (r->out) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter out again \"%V?%V\"", - &r->uri, &r->args); + *ppr = pr; - r->connection->data = r; - return NGX_AGAIN; + pr->request = NULL; + pr->out = NULL; + pr->next = NULL; + +found: + + if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) { + return NGX_OK; } - return NGX_OK; + return NGX_ERROR; } 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 @@ -38,8 +38,8 @@ static ngx_int_t ngx_http_find_virtual_s static void ngx_http_request_handler(ngx_event_t *ev); static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r); static void ngx_http_writer(ngx_http_request_t *r); - -static void ngx_http_test_reading(ngx_http_request_t *r); +static void ngx_http_request_finalizer(ngx_http_request_t *r); + static void ngx_http_set_keepalive(ngx_http_request_t *r); static void ngx_http_keepalive_handler(ngx_event_t *ev); static void ngx_http_set_lingering_close(ngx_http_request_t *r); @@ -215,7 +215,7 @@ ngx_http_init_connection(ngx_connection_ ngx_add_timer(rev, c->listening->post_accept_timeout); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { #if (NGX_STAT_STUB) ngx_atomic_fetch_add(ngx_stat_reading, -1); #endif @@ -504,7 +504,7 @@ ngx_http_ssl_handshake(ngx_event_t *rev) ngx_add_timer(rev, c->listening->post_accept_timeout); } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } @@ -1038,7 +1038,7 @@ ngx_http_read_request_header(ngx_http_re ngx_add_timer(rev, cscf->client_header_timeout); } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -1412,9 +1412,7 @@ ngx_http_process_request_header(ngx_http } } - if (r->method & (NGX_HTTP_POST|NGX_HTTP_PUT) - && r->headers_in.content_length_n == -1) - { + if (r->method & NGX_HTTP_PUT && r->headers_in.content_length_n == -1) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent %V method without \"Content-Length\" header", &r->method_name); @@ -1525,7 +1523,7 @@ ngx_http_process_request(ngx_http_reques ngx_http_handler(r); - return; + ngx_http_run_posted_requests(c); } @@ -1678,12 +1676,73 @@ ngx_http_request_handler(ngx_event_t *ev ctx = c->log->data; ctx->current_request = r; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http run request: \"%V?%V\"", &r->uri, &r->args); + if (ev->write) { r->write_event_handler(r); } else { r->read_event_handler(r); } + + ngx_http_run_posted_requests(c); +} + + +void +ngx_http_run_posted_requests(ngx_connection_t *c) +{ + ngx_http_request_t *r; + ngx_http_log_ctx_t *ctx; + ngx_http_posted_request_t *pr; + + for ( ;; ) { + + if (c->destroyed) { + return; + } + + r = c->data; + pr = r->main->posted_requests; + + if (pr == NULL) { + return; + } + + r->main->posted_requests = pr->next; + + r = pr->request; + + ctx = c->log->data; + ctx->current_request = r; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http posted request: \"%V?%V\"", &r->uri, &r->args); + + r->write_event_handler(r); + } +} + + +ngx_int_t +ngx_http_post_request(ngx_http_request_t *r) +{ + ngx_http_posted_request_t *pr, **p; + + pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t)); + if (pr == NULL) { + return NGX_ERROR; + } + + pr->request = r; + pr->next = NULL; + + for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ } + + *p = pr; + + return NGX_OK; } @@ -1692,7 +1751,6 @@ ngx_http_finalize_request(ngx_http_reque { ngx_connection_t *c; ngx_http_request_t *pr; - ngx_http_log_ctx_t *ctx; ngx_http_core_loc_conf_t *clcf; if (rc == NGX_DONE) { @@ -1702,9 +1760,9 @@ ngx_http_finalize_request(ngx_http_reque c = r->connection; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http finalize request: %d, \"%V?%V\"", - rc, &r->uri, &r->args); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http finalize request: %d, \"%V?%V\" %d", + rc, &r->uri, &r->args, r == c->data); if (rc == NGX_DECLINED) { r->content_handler = NULL; @@ -1760,88 +1818,91 @@ 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) { + if (r != r->main) { + + if (r->buffered || r->postponed) { + + if (ngx_http_set_write_handler(r) != NGX_OK) { + ngx_http_close_request(r->main, 0); + } + return; } - } - - r->done = 1; - - if (r != c->data) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http finalize non-active request: \"%V?%V\"", - &r->uri, &r->args); - return; - } - - if (r != r->main) { + +#if (NGX_DEBUG) + if (r != c->data) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http finalize non-active request: \"%V?%V\"", + &r->uri, &r->args); + } +#endif pr = r->parent; + if (r == c->data) { + + if (!r->logged) { + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->log_subrequest) { + ngx_http_log_request(r); + } + + r->logged = 1; + + } else { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "subrequest: \"%V?%V\" logged again", + &r->uri, &r->args); + } + + r->done = 1; + + if (pr->postponed && pr->postponed->request == r) { + pr->postponed = pr->postponed->next; + } + + c->data = pr; + + } else { + + r->write_event_handler = ngx_http_request_finalizer; + + if (r->waited) { + r->done = 1; + } + } + + if (ngx_http_post_request(pr) != NGX_OK) { + ngx_http_close_request(r->main, 0); + return; + } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http parent request: \"%V?%V\"", &pr->uri, &pr->args); - - if (rc != NGX_AGAIN) { - c->data = pr; - } - - ctx = c->log->data; - ctx->current_request = pr; - - if (pr->postponed) { - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http request: \"%V?%V\" has postponed", - &pr->uri, &pr->args); - - if (rc != NGX_AGAIN && pr->postponed->request == r) { - pr->postponed = pr->postponed->next; - } - - if (r->fast_subrequest) { - - if (rc == NGX_AGAIN) { - r->fast_subrequest = 0; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http fast subrequest: \"%V?%V\" done", - &r->uri, &r->args); - return; - } - - if (rc != NGX_AGAIN) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http wake parent request: \"%V?%V\"", - &pr->uri, &pr->args); - - pr->write_event_handler(pr); - } + "http wake parent request: \"%V?%V\"", + &pr->uri, &pr->args); + + return; + } + + if (r->buffered || c->buffered || r->postponed) { + + if (ngx_http_set_write_handler(r) != NGX_OK) { + ngx_http_close_request(r, 0); } return; } - if (rc == NGX_AGAIN) { + if (r != c->data) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "http finalize non-active request: \"%V?%V\"", + &r->uri, &r->args); return; } - if (c->buffered) { - (void) ngx_http_set_write_handler(r); - return; - } + r->done = 1; if (!r->post_action) { r->request_complete = 1; @@ -1869,6 +1930,8 @@ 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 @@ -1908,7 +1971,7 @@ ngx_http_set_write_handler(ngx_http_requ ngx_add_timer(wev, clcf->send_timeout); } - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); return NGX_ERROR; } @@ -1931,6 +1994,8 @@ ngx_http_writer(ngx_http_request_t *r) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http writer handler: \"%V?%V\"", &r->uri, &r->args); + clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); + if (wev->timedout) { if (!wev->delayed) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, @@ -1945,10 +2010,9 @@ ngx_http_writer(ngx_http_request_t *r) wev->delayed = 0; if (!wev->ready) { - clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); ngx_add_timer(wev, clcf->send_timeout); - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } @@ -1960,9 +2024,7 @@ ngx_http_writer(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http writer delayed"); - clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); - - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } @@ -1980,21 +2042,17 @@ ngx_http_writer(ngx_http_request_t *r) "http writer output filter: %d, \"%V?%V\"", rc, &r->uri, &r->args); - if (rc == NGX_AGAIN) { - clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); + if (r->buffered || r->postponed || (r == r->main && c->buffered)) { + if (!wev->ready && !wev->delayed) { ngx_add_timer(wev, clcf->send_timeout); } - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } - if (r == r->main || r->buffered) { - return; - } - - rc = NGX_OK; + return; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0, @@ -2004,6 +2062,16 @@ ngx_http_writer(ngx_http_request_t *r) } +static void +ngx_http_request_finalizer(ngx_http_request_t *r) +{ + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http finalizer done: \"%V?%V\"", &r->uri, &r->args); + + ngx_http_finalize_request(r, 0); +} + + void ngx_http_block_reading(ngx_http_request_t *r) { @@ -2015,16 +2083,14 @@ ngx_http_block_reading(ngx_http_request_ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && r->connection->read->active) { - if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) - == NGX_ERROR) - { + if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); } } } -static void +void ngx_http_test_reading(ngx_http_request_t *r) { int n; @@ -2079,7 +2145,7 @@ ngx_http_test_reading(ngx_http_request_t if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) { - if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { + if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); } } @@ -2173,7 +2239,7 @@ ngx_http_set_keepalive(ngx_http_request_ ngx_add_timer(rev, clcf->keepalive_timeout); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; } @@ -2260,7 +2326,7 @@ ngx_http_set_keepalive(ngx_http_request_ rev->handler = ngx_http_keepalive_handler; if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { - if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { ngx_http_close_connection(c); return; } @@ -2385,7 +2451,7 @@ ngx_http_keepalive_handler(ngx_event_t * c->log_error = NGX_ERROR_INFO; if (n == NGX_AGAIN) { - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); } @@ -2438,7 +2504,7 @@ ngx_http_set_lingering_close(ngx_http_re r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000); ngx_add_timer(rev, clcf->lingering_timeout); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } @@ -2447,7 +2513,7 @@ ngx_http_set_lingering_close(ngx_http_re wev->handler = ngx_http_empty_handler; if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { - if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } @@ -2506,7 +2572,7 @@ ngx_http_lingering_close_handler(ngx_eve } while (rev->ready); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } 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 @@ -58,6 +58,7 @@ #define NGX_HTTP_ZERO_IN_URI 1 #define NGX_HTTP_SUBREQUEST_IN_MEMORY 2 +#define NGX_HTTP_SUBREQUEST_WAITED 4 #define NGX_HTTP_OK 200 @@ -321,6 +322,14 @@ struct ngx_http_postponed_request_s { }; +typedef struct ngx_http_posted_request_s ngx_http_posted_request_t; + +struct ngx_http_posted_request_s { + ngx_http_request_t *request; + ngx_http_posted_request_t *next; +}; + + typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r); typedef void (*ngx_http_event_handler_pt)(ngx_http_request_t *r); @@ -373,6 +382,7 @@ struct ngx_http_request_s { ngx_http_request_t *parent; ngx_http_postponed_request_t *postponed; ngx_http_post_subrequest_t *post_subrequest; + ngx_http_posted_request_t *posted_requests; uint32_t in_addr; ngx_uint_t port; @@ -428,8 +438,8 @@ struct ngx_http_request_s { unsigned request_body_file_group_access:1; unsigned request_body_file_log_level:3; - unsigned fast_subrequest:1; unsigned subrequest_in_memory:1; + unsigned waited:1; unsigned gzip:2; @@ -438,10 +448,12 @@ struct ngx_http_request_s { unsigned no_cache:1; /* - * instead of using the request context data in ngx_http_limit_zone_module - * we use the single bit in the request structure + * instead of using the request context data in + * ngx_http_limit_zone_module and ngx_http_limit_req_module + * we use the single bits in the request structure */ unsigned limit_zone_set:1; + unsigned limit_req_set:1; #if 0 unsigned cacheable:1; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -327,7 +327,7 @@ ngx_http_do_read_client_request_body(ngx clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_add_timer(c->read, clcf->client_body_timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -462,7 +462,7 @@ ngx_http_discard_request_body(ngx_http_r r->read_event_handler = ngx_http_read_discarded_request_body_handler; - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -519,7 +519,7 @@ ngx_http_read_discarded_request_body_han /* rc == NGX_AGAIN */ - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { c->error = 1; ngx_http_finalize_request(r, rc); 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 @@ -430,20 +430,23 @@ ngx_http_script_copy_len_code(ngx_http_s void ngx_http_script_copy_code(ngx_http_script_engine_t *e) { + u_char *p; ngx_http_script_copy_code_t *code; code = (ngx_http_script_copy_code_t *) e->ip; + p = e->pos; + if (!e->skip) { - e->pos = ngx_copy(e->pos, e->ip + sizeof(ngx_http_script_copy_code_t), + e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_script_copy_code_t), code->len); } e->ip += sizeof(ngx_http_script_copy_code_t) + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script copy: \"%V\"", &e->buf); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + "http script copy: \"%*s\"", e->pos - p, p); } @@ -475,6 +478,7 @@ ngx_http_script_copy_var_len_code(ngx_ht void ngx_http_script_copy_var_code(ngx_http_script_engine_t *e) { + u_char *p; ngx_http_variable_value_t *value; ngx_http_script_var_code_t *code; @@ -492,11 +496,12 @@ ngx_http_script_copy_var_code(ngx_http_s } if (value && !value->not_found) { - e->pos = ngx_copy(e->pos, value->data, value->len); + p = e->pos; + e->pos = ngx_copy(p, value->data, value->len); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script var: \"%V\"", &e->buf); + "http script var: \"%*s\"", e->pos - p, p); } } } @@ -532,29 +537,32 @@ ngx_http_script_copy_capture_len_code(ng 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(e->pos, + 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(e->pos, + e->pos = ngx_copy(p, &e->line.data[e->captures[code->n]], e->captures[code->n + 1] - e->captures[code->n]); } } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script capture: \"%V\"", &e->buf); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + "http script capture: \"%*s\"", e->pos - p, p); } @@ -612,7 +620,7 @@ ngx_http_script_regex_start_code(ngx_htt rc = ngx_regex_exec(code->regex, &e->line, e->captures, code->ncaptures); if (rc == NGX_REGEX_NO_MATCHED) { - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "\"%V\" does not match \"%V\"", &code->name, &e->line); @@ -650,7 +658,7 @@ ngx_http_script_regex_start_code(ngx_htt return; } - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "\"%V\" matches \"%V\"", &code->name, &e->line); } @@ -786,7 +794,7 @@ ngx_http_script_regex_end_code(ngx_http_ e->buf.len = e->pos - e->buf.data; - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "rewritten redirect: \"%V\"", &e->buf); } @@ -828,7 +836,7 @@ ngx_http_script_regex_end_code(ngx_http_ } } - if (e->log) { + if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "rewritten data: \"%V\", args: \"%V\"", &e->buf, &r->args); @@ -928,8 +936,8 @@ ngx_http_script_equal_code(ngx_http_scri e->ip += sizeof(uintptr_t); - if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len) - == 0) + if (val->len == res->len + && ngx_strncmp(val->data, res->data, res->len) == 0) { *res = ngx_http_variable_true_value; return; @@ -956,8 +964,8 @@ ngx_http_script_not_equal_code(ngx_http_ e->ip += sizeof(uintptr_t); - if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len) - == 0) + if (val->len == res->len + && ngx_strncmp(val->data, res->data, res->len) == 0) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, "http script not equal: no"); @@ -1163,9 +1171,6 @@ ngx_http_script_set_var_code(ngx_http_sc ngx_http_request_t *r; ngx_http_script_var_code_t *code; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script set var"); - code = (ngx_http_script_var_code_t *) e->ip; e->ip += sizeof(ngx_http_script_var_code_t); @@ -1179,6 +1184,20 @@ ngx_http_script_set_var_code(ngx_http_sc r->variables[code->index].no_cacheable = 0; r->variables[code->index].not_found = 0; r->variables[code->index].data = e->sp->data; + +#if (NGX_DEBUG) + { + ngx_http_variable_t *v; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + v = cmcf->variables.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + "http script set $%V", &v[code->index].name); + } +#endif } 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 @@ -20,27 +20,38 @@ static ngx_int_t ngx_http_upstream_reini ngx_http_upstream_t *u); static void ngx_http_upstream_send_request(ngx_http_request_t *r, 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 void ngx_http_upstream_send_request_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_process_header(ngx_http_request_t *r, + ngx_http_upstream_t *u); 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_process_body_in_memory(ngx_http_request_t *r, + ngx_http_upstream_t *u); static void ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u); static void ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r); -static void ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev); +static void + ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void + ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, + ngx_uint_t do_write); static ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data); static ngx_int_t ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes); static void ngx_http_upstream_process_downstream(ngx_http_request_t *r); -static void ngx_http_upstream_process_body(ngx_event_t *ev); +static void ngx_http_upstream_process_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_process_request(ngx_http_request_t *r); static void ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u); -static void ngx_http_upstream_dummy_handler(ngx_event_t *wev); +static void ngx_http_upstream_dummy_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u); static void ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_uint_t ft_type); static void ngx_http_upstream_cleanup(void *data); @@ -85,6 +96,8 @@ static ngx_int_t ngx_http_upstream_statu ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_upstream_response_time_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_response_length_variable( + ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, @@ -274,13 +287,20 @@ ngx_module_t ngx_http_upstream_module = static ngx_http_variable_t ngx_http_upstream_vars[] = { { ngx_string("upstream_addr"), NULL, - ngx_http_upstream_addr_variable, 0, NGX_HTTP_VAR_NOHASH, 0 }, + ngx_http_upstream_addr_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("upstream_status"), NULL, - ngx_http_upstream_status_variable, 0, NGX_HTTP_VAR_NOHASH, 0 }, + ngx_http_upstream_status_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("upstream_response_time"), NULL, - ngx_http_upstream_response_time_variable, 0, NGX_HTTP_VAR_NOHASH, 0 }, + ngx_http_upstream_response_time_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_response_length"), NULL, + ngx_http_upstream_response_length_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -394,6 +414,20 @@ ngx_http_upstream_init(ngx_http_request_ } else { + if (u->resolved->sockaddr) { + + if (ngx_http_upstream_create_round_robin_peer(r, u->resolved) + != NGX_OK) + { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + ngx_http_upstream_connect(r, u); + + return; + } + host = &u->resolved->host; umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); @@ -405,7 +439,7 @@ ngx_http_upstream_init(ngx_http_request_ uscf = uscfp[i]; if (uscf->host.len == host->len - && ((uscf->port == 0 && u->resolved->default_port) + && ((uscf->port == 0 && u->resolved->no_port) || uscf->port == u->resolved->port) && ngx_memcmp(uscf->host.data, host->data, host->len) == 0) { @@ -511,6 +545,37 @@ ngx_http_upstream_resolve_handler(ngx_re static void +ngx_http_upstream_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_log_ctx_t *ctx; + ngx_http_upstream_t *u; + + c = ev->data; + r = c->data; + + u = r->upstream; + c = r->connection; + + ctx = c->log->data; + ctx->current_request = r; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream request: \"%V?%V\"", &r->uri, &r->args); + + if (ev->write) { + u->write_event_handler(r, u); + + } else { + u->read_event_handler(r, u); + } + + ngx_http_run_posted_requests(c); +} + + +static void ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r) { ngx_http_upstream_check_broken_connection(r, r->connection->read); @@ -712,8 +777,11 @@ ngx_http_upstream_connect(ngx_http_reque c->data = r; - c->write->handler = ngx_http_upstream_send_request_handler; - c->read->handler = ngx_http_upstream_process_header; + c->write->handler = ngx_http_upstream_handler; + c->read->handler = ngx_http_upstream_handler; + + u->write_event_handler = ngx_http_upstream_send_request_handler; + u->read_event_handler = ngx_http_upstream_process_header; c->sendfile &= r->connection->sendfile; u->output.sendfile = c->sendfile; @@ -838,8 +906,8 @@ ngx_http_upstream_ssl_handshake(ngx_conn u->peer.save_session(&u->peer, u->peer.data); } - c->write->handler = ngx_http_upstream_send_request_handler; - c->read->handler = ngx_http_upstream_process_header; + c->write->handler = ngx_http_upstream_handler; + c->read->handler = ngx_http_upstream_handler; ngx_http_upstream_send_request(r, u); @@ -955,8 +1023,7 @@ ngx_http_upstream_send_request(ngx_http_ if (rc == NGX_AGAIN) { ngx_add_timer(c->write, u->conf->send_timeout); - if (ngx_handle_write_event(c->write, u->conf->send_lowat) == NGX_ERROR) - { + if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -993,14 +1060,14 @@ ngx_http_upstream_send_request(ngx_http_ * it's better to do here because we postpone header buffer allocation */ - ngx_http_upstream_process_header(c->read); + ngx_http_upstream_process_header(r, u); return; } #endif - c->write->handler = ngx_http_upstream_dummy_handler; - - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + u->write_event_handler = ngx_http_upstream_dummy_handler; + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -1009,20 +1076,17 @@ ngx_http_upstream_send_request(ngx_http_ static void -ngx_http_upstream_send_request_handler(ngx_event_t *wev) +ngx_http_upstream_send_request_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u) { - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - - c = wev->data; - r = c->data; - u = r->upstream; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, + ngx_connection_t *c; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream send request handler"); - if (wev->timedout) { + if (c->write->timedout) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); return; } @@ -1037,9 +1101,9 @@ ngx_http_upstream_send_request_handler(n #endif if (u->header_sent) { - wev->handler = ngx_http_upstream_dummy_handler; - - (void) ngx_handle_write_event(wev, 0); + u->write_event_handler = ngx_http_upstream_dummy_handler; + + (void) ngx_handle_write_event(c->write, 0); return; } @@ -1049,7 +1113,7 @@ ngx_http_upstream_send_request_handler(n static void -ngx_http_upstream_process_header(ngx_event_t *rev) +ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u) { ssize_t n; ngx_int_t rc; @@ -1058,21 +1122,17 @@ ngx_http_upstream_process_header(ngx_eve ngx_list_part_t *part; ngx_table_elt_t *h; ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; ngx_http_upstream_header_t *hh; ngx_http_upstream_main_conf_t *umcf; - c = rev->data; - r = c->data; - u = r->upstream; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream process header"); c->log->action = "reading response header from upstream"; - if (rev->timedout) { + if (c->read->timedout) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); return; } @@ -1114,62 +1174,59 @@ ngx_http_upstream_process_header(ngx_eve #endif } - n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); - - if (n == NGX_AGAIN) { + for ( ;; ) { + + n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); + + if (n == NGX_AGAIN) { #if 0 - ngx_add_timer(rev, u->read_timeout); + ngx_add_timer(rev, u->read_timeout); #endif - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "upstream prematurely closed connection"); + } + + if (n == NGX_ERROR || n == 0) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); return; } - return; - } - - if (n == 0) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, - "upstream prematurely closed connection"); - } - - if (n == NGX_ERROR || n == 0) { - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); - return; - } - - u->buffer.last += n; + u->buffer.last += n; #if 0 - u->valid_header_in = 0; - - u->peer.cached = 0; -#endif - - rc = u->process_header(r); - - if (rc == NGX_AGAIN) { -#if 0 - ngx_add_timer(rev, u->read_timeout); + u->valid_header_in = 0; + + u->peer.cached = 0; #endif - if (u->buffer.pos == u->buffer.end) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, - "upstream sent too big header"); - - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); - return; + rc = u->process_header(r); + + if (rc == NGX_AGAIN) { + + if (u->buffer.pos == u->buffer.end) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "upstream sent too big header"); + + ngx_http_upstream_next(r, u, + NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); + return; + } + + continue; } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - return; + break; } if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { @@ -1337,19 +1394,27 @@ ngx_http_upstream_process_header(ngx_eve return; } - if (u->buffer.last - u->buffer.pos >= (ssize_t) u->length) { - if (u->input_filter(u->input_filter_ctx, 0) == NGX_ERROR) { + n = u->buffer.last - u->buffer.pos; + + if (n) { + u->buffer.last -= n; + + u->state->response_length += n; + + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; } - ngx_http_upstream_finalize_request(r, u, 0); - return; + if (u->length == 0) { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } } - rev->handler = ngx_http_upstream_process_body_in_memory; - - ngx_http_upstream_process_body_in_memory(rev); + u->read_event_handler = ngx_http_upstream_process_body_in_memory; + + ngx_http_upstream_process_body_in_memory(r, u); } @@ -1493,18 +1558,17 @@ ngx_http_upstream_test_connect(ngx_conne static void -ngx_http_upstream_process_body_in_memory(ngx_event_t *rev) +ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, + ngx_http_upstream_t *u) { - size_t size; - ssize_t n; - ngx_buf_t *b; - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - - c = rev->data; - r = c->data; - u = r->upstream; + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_event_t *rev; + ngx_connection_t *c; + + c = u->peer.connection; + rev = c->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream process body on memory"); @@ -1539,6 +1603,8 @@ ngx_http_upstream_process_body_in_memory return; } + u->state->response_length += n; + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; @@ -1549,7 +1615,7 @@ ngx_http_upstream_process_body_in_memory } } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; } @@ -1567,7 +1633,7 @@ static void ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) { int tcp_nodelay; - ssize_t size; + ssize_t n; ngx_int_t rc; ngx_event_pipe_t *p; ngx_connection_t *c; @@ -1611,8 +1677,7 @@ ngx_http_upstream_send_response(ngx_http u->input_filter_ctx = r; } - u->peer.connection->read->handler = - ngx_http_upstream_process_non_buffered_body; + u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream; r->write_event_handler = ngx_http_upstream_process_non_buffered_downstream; @@ -1640,17 +1705,19 @@ ngx_http_upstream_send_response(ngx_http c->tcp_nodelay = NGX_TCP_NODELAY_SET; } - size = u->buffer.last - u->buffer.pos; - - if (size) { + n = u->buffer.last - u->buffer.pos; + + if (n) { u->buffer.last = u->buffer.pos; - if (u->input_filter(u->input_filter_ctx, size) == NGX_ERROR) { + u->state->response_length += n; + + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, 0); return; } - ngx_http_upstream_process_non_buffered_body(c->write); + ngx_http_upstream_process_non_buffered_downstream(r); } else { u->buffer.pos = u->buffer.start; @@ -1662,8 +1729,7 @@ ngx_http_upstream_send_response(ngx_http } if (u->peer.connection->read->ready) { - ngx_http_upstream_process_non_buffered_body( - u->peer.connection->read); + ngx_http_upstream_process_non_buffered_upstream(r, u); } } @@ -1793,69 +1859,82 @@ ngx_http_upstream_send_response(ngx_http p->send_timeout = clcf->send_timeout; p->send_lowat = clcf->send_lowat; - u->peer.connection->read->handler = ngx_http_upstream_process_body; + u->read_event_handler = ngx_http_upstream_process_upstream; r->write_event_handler = ngx_http_upstream_process_downstream; - ngx_http_upstream_process_body(u->peer.connection->read); + ngx_http_upstream_process_upstream(r, u); } static void ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r) { - ngx_http_upstream_process_non_buffered_body(r->connection->write); + ngx_event_t *wev; + ngx_connection_t *c; + ngx_http_upstream_t *u; + + c = r->connection; + u = r->upstream; + wev = c->write; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process non buffered downstream"); + + c->log->action = "sending to client"; + + if (wev->timedout) { + c->timedout = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + + ngx_http_upstream_process_non_buffered_request(r, 1); } static void -ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev) +ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_connection_t *c; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process non buffered upstream"); + + c->log->action = "reading upstream"; + + if (c->read->timedout) { + ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + + ngx_http_upstream_process_non_buffered_request(r, 0); +} + + +static void +ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, + ngx_uint_t do_write) { size_t size; ssize_t n; ngx_buf_t *b; ngx_int_t rc; - ngx_uint_t do_write; - ngx_connection_t *c, *downstream, *upstream; - ngx_http_request_t *r; + ngx_connection_t *downstream, *upstream; ngx_http_upstream_t *u; ngx_http_core_loc_conf_t *clcf; - c = ev->data; - r = c->data; u = r->upstream; - - if (ev->write) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process non buffered downstream"); - c->log->action = "sending to client"; - - } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process non buffered upstream"); - c->log->action = "reading upstream"; - } - - if (ev->timedout) { - if (ev->write) { - c->timedout = 1; - ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); - - } else { - ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); - } - - ngx_http_upstream_finalize_request(r, u, 0); - return; - } - downstream = r->connection; upstream = u->peer.connection; b = &u->buffer; - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - do_write = ev->write || u->length == 0; + do_write = do_write || u->length == 0; for ( ;; ) { @@ -1907,6 +1986,8 @@ ngx_http_upstream_process_non_buffered_b } if (n > 0) { + u->state->response_length += n; + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, 0); return; @@ -1921,9 +2002,11 @@ ngx_http_upstream_process_non_buffered_b break; } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + if (downstream->data == r) { if (ngx_handle_write_event(downstream->write, clcf->send_lowat) - == NGX_ERROR) + != NGX_OK) { ngx_http_upstream_finalize_request(r, u, 0); return; @@ -1937,7 +2020,7 @@ ngx_http_upstream_process_non_buffered_b ngx_del_timer(downstream->write); } - if (ngx_handle_read_event(upstream->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, 0); return; } @@ -2003,96 +2086,71 @@ ngx_http_upstream_non_buffered_filter(vo static void ngx_http_upstream_process_downstream(ngx_http_request_t *r) { - ngx_http_upstream_process_body(r->connection->write); -} - - -static void -ngx_http_upstream_process_body(ngx_event_t *ev) -{ - ngx_temp_file_t *tf; + ngx_event_t *wev; + ngx_connection_t *c; ngx_event_pipe_t *p; - ngx_connection_t *c, *downstream; - ngx_http_log_ctx_t *ctx; - ngx_http_request_t *r; ngx_http_upstream_t *u; - c = ev->data; - r = c->data; + c = r->connection; u = r->upstream; - downstream = r->connection; - - if (ev->write) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process downstream"); - c->log->action = "sending to client"; - - } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process upstream"); - c->log->action = "reading upstream"; - - ctx = c->log->data; - ctx->current_request = r; - } - p = u->pipe; - - if (ev->timedout) { - if (ev->write) { - if (ev->delayed) { - - ev->timedout = 0; - ev->delayed = 0; - - if (!ev->ready) { - ngx_add_timer(ev, p->send_timeout); - - if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) - { - ngx_http_upstream_finalize_request(r, u, 0); - return; - } - + wev = c->write; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process downstream"); + + c->log->action = "sending to client"; + + if (wev->timedout) { + + if (wev->delayed) { + + wev->timedout = 0; + wev->delayed = 0; + + if (!wev->ready) { + ngx_add_timer(wev, p->send_timeout); + + if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, 0); + } + + return; + } + + if (ngx_event_pipe(p, wev->write) == NGX_ABORT) { + + if (c->destroyed) { return; } - if (ngx_event_pipe(p, ev->write) == NGX_ABORT) { - - if (downstream->destroyed) { - return; - } - - ngx_http_upstream_finalize_request(r, u, 0); - return; - } - - } else { - p->downstream_error = 1; - c->timedout = 1; - ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); + ngx_http_upstream_finalize_request(r, u, 0); + return; } } else { - p->upstream_error = 1; - ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + p->downstream_error = 1; + c->timedout = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); } } else { - if (ev->write && ev->delayed) { + + if (wev->delayed) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http downstream delayed"); - if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) { - return; + if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, 0); } return; } - if (ngx_event_pipe(p, ev->write) == NGX_ABORT) { - - if (downstream->destroyed) { + if (ngx_event_pipe(p, 1) == NGX_ABORT) { + + if (c->destroyed) { return; } @@ -2101,6 +2159,55 @@ ngx_http_upstream_process_body(ngx_event } } + ngx_http_upstream_process_request(r); +} + + +static void +ngx_http_upstream_process_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_connection_t *c; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process upstream"); + + c->log->action = "reading upstream"; + + if (c->read->timedout) { + u->pipe->upstream_error = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + + } else { + c = r->connection; + + if (ngx_event_pipe(u->pipe, 0) == NGX_ABORT) { + + if (c->destroyed) { + return; + } + + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + } + + ngx_http_upstream_process_request(r); +} + + +static void +ngx_http_upstream_process_request(ngx_http_request_t *r) +{ + ngx_temp_file_t *tf; + ngx_event_pipe_t *p; + ngx_http_upstream_t *u; + + u = r->upstream; + p = u->pipe; + if (u->peer.connection) { if (u->store) { @@ -2151,7 +2258,7 @@ ngx_http_upstream_process_body(ngx_event #endif if (p->upstream_done || p->upstream_eof || p->upstream_error) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream exit: %p", p->out); #if 0 ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock); @@ -2162,7 +2269,7 @@ ngx_http_upstream_process_body(ngx_event } if (p->downstream_error) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream downstream error"); if (!u->cacheable && u->peer.connection) { @@ -2209,9 +2316,11 @@ ngx_http_upstream_store(ngx_http_request } ext.access = u->conf->store_access; + ext.path_access = u->conf->store_access; ext.time = -1; ext.create_path = 1; ext.delete_file = 1; + ext.log_rename_error = 1; ext.log = r->connection->log; if (u->headers_in.last_modified) { @@ -2247,9 +2356,9 @@ ngx_http_upstream_store(ngx_http_request static void -ngx_http_upstream_dummy_handler(ngx_event_t *wev) +ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream dummy handler"); } @@ -2393,12 +2502,18 @@ ngx_http_upstream_finalize_request(ngx_h ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "finalize http upstream request: %i", rc); - *u->cleanup = NULL; + if (u->cleanup) { + *u->cleanup = NULL; + } if (u->state && u->state->response_sec) { tp = ngx_timeofday(); u->state->response_sec = tp->sec - u->state->response_sec; u->state->response_msec = tp->msec - u->state->response_msec; + + if (u->pipe) { + u->state->response_length = u->pipe->read_length; + } } u->finalize_request(r, rc); @@ -2461,17 +2576,8 @@ ngx_http_upstream_finalize_request(ngx_h r->connection->log->action = "sending to client"; - if (rc == 0) { - if (r == r->main) { - if (!r->post_action) { - rc = ngx_http_send_special(r, NGX_HTTP_LAST); - } - - } else { - if (r->out) { - rc = NGX_AGAIN; - } - } + if (rc == 0 && r == r->main && !r->post_action) { + rc = ngx_http_send_special(r, NGX_HTTP_LAST); } ngx_http_finalize_request(r, rc); @@ -3051,6 +3157,66 @@ ngx_http_upstream_response_time_variable } +static ngx_int_t +ngx_http_upstream_response_length_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_http_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = r->upstream_states->nelts * (NGX_OFF_T_LEN + 2); + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + state = r->upstream_states->elts; + + for ( ;; ) { + p = ngx_sprintf(p, "%O", state[i].response_length); + + if (++i == r->upstream_states->nelts) { + break; + } + + if (state[i].peer) { + *p++ = ','; + *p++ = ' '; + + } else { + *p++ = ' '; + *p++ = ':'; + *p++ = ' '; + + if (++i == r->upstream_states->nelts) { + break; + } + + continue; + } + } + + v->len = p - v->data; + + return NGX_OK; +} + + 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_upstream.h b/src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -45,6 +45,7 @@ typedef struct { ngx_uint_t status; time_t response_sec; ngx_uint_t response_msec; + off_t response_length; ngx_str_t *peer; } ngx_http_upstream_state_t; @@ -144,8 +145,6 @@ typedef struct { ngx_array_t *hide_headers; ngx_array_t *pass_headers; - ngx_str_t schema; - ngx_array_t *store_lengths; ngx_array_t *store_values; @@ -209,14 +208,26 @@ typedef struct { typedef struct { ngx_str_t host; in_port_t port; - ngx_uint_t default_port; /* unsigned default_port:1 */ + ngx_uint_t no_port; /* unsigned no_port:1 */ + ngx_uint_t naddrs; in_addr_t *addrs; + + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_resolver_ctx_t *ctx; } ngx_http_upstream_resolved_t; +typedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r, + ngx_http_upstream_t *u); + + struct ngx_http_upstream_s { + ngx_http_upstream_handler_pt read_event_handler; + ngx_http_upstream_handler_pt write_event_handler; + ngx_peer_connection_t peer; ngx_event_pipe_t *pipe; diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -279,35 +279,47 @@ ngx_http_upstream_create_round_robin_pee peers->number = ur->naddrs; peers->name = &ur->host; - for (i = 0; i < ur->naddrs; i++) { - - len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1; + if (ur->sockaddr) { + peers->peer[0].sockaddr = ur->sockaddr; + peers->peer[0].socklen = ur->socklen; + peers->peer[0].name = ur->host; + peers->peer[0].weight = 1; + peers->peer[0].current_weight = 1; + peers->peer[0].max_fails = 1; + peers->peer[0].fail_timeout = 10; - p = ngx_pnalloc(r->pool, len); - if (p == NULL) { - return NGX_ERROR; - } + } else { + + for (i = 0; i < ur->naddrs; i++) { - len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN); - len = ngx_sprintf(&p[len], ":%d", ur->port) - p; + len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1; + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } - sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in)); - if (sin == NULL) { - return NGX_ERROR; - } + len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN); + len = ngx_sprintf(&p[len], ":%d", ur->port) - p; - sin->sin_family = AF_INET; - sin->sin_port = htons(ur->port); - sin->sin_addr.s_addr = ur->addrs[i]; + sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in)); + if (sin == NULL) { + return NGX_ERROR; + } - peers->peer[i].sockaddr = (struct sockaddr *) sin; - peers->peer[i].socklen = sizeof(struct sockaddr_in); - peers->peer[i].name.len = len; - peers->peer[i].name.data = p; - peers->peer[i].weight = 1; - peers->peer[i].current_weight = 1; - peers->peer[i].max_fails = 1; - peers->peer[i].fail_timeout = 10; + sin->sin_family = AF_INET; + sin->sin_port = htons(ur->port); + sin->sin_addr.s_addr = ur->addrs[i]; + + peers->peer[i].sockaddr = (struct sockaddr *) sin; + peers->peer[i].socklen = sizeof(struct sockaddr_in); + peers->peer[i].name.len = len; + peers->peer[i].name.data = p; + peers->peer[i].weight = 1; + peers->peer[i].current_weight = 1; + peers->peer[i].max_fails = 1; + peers->peer[i].fail_timeout = 10; + } } rrp->peers = peers; 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 @@ -25,6 +25,8 @@ static ngx_int_t ngx_http_variable_unkno ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -488,6 +490,15 @@ ngx_http_get_variable(ngx_http_request_t return NULL; } + if (ngx_strncmp(name->data, "cookie_", 7) == 0) { + + if (ngx_http_variable_cookie(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } + if (ngx_strncmp(name->data, "arg_", 4) == 0) { if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) { @@ -728,56 +739,56 @@ ngx_http_variable_unknown_header(ngx_htt static ngx_int_t +ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_str_t cookie, s; + + s.len = name->len - (sizeof("cookie_") - 1); + s.data = name->data + sizeof("cookie_") - 1; + + if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &s, &cookie) + == NGX_DECLINED) + { + v->not_found = 1; + return NGX_OK; + } + + v->len = cookie.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = cookie.data; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_str_t *name = (ngx_str_t *) data; - u_char *p, *arg; - size_t len; + u_char *arg; + size_t len; + ngx_str_t value; - if (r->args.len == 0) { + len = name->len - (sizeof("arg_") - 1); + arg = name->data + sizeof("arg_") - 1; + + if (ngx_http_arg(r, arg, len, &value) != NGX_OK) { v->not_found = 1; return NGX_OK; } - len = name->len - 1 - (sizeof("arg_") - 1); - arg = name->data + sizeof("arg_") - 1; - - for (p = r->args.data; *p && *p != ' '; p++) { - - /* - * although r->args.data is not null-terminated by itself, - * however, there is null in the end of request line - */ - - p = ngx_strcasestrn(p, (char *) arg, len); - - if (p == NULL) { - v->not_found = 1; - return NGX_OK; - } - - if ((p == r->args.data || *(p - 1) == '&') && *(p + len + 1) == '=') { - - v->data = p + len + 2; - - p = (u_char *) ngx_strchr(p, '&'); - - if (p == NULL) { - p = r->args.data + r->args.len; - } - - v->len = p - v->data; - v->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; - - return NGX_OK; - } - } - - v->not_found = 1; + v->data = value.data; + v->len = value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; return NGX_OK; } @@ -1544,6 +1555,13 @@ ngx_http_variables_init_vars(ngx_conf_t continue; } + if (ngx_strncmp(v[i].name.data, "cookie_", 7) == 0) { + v[i].get_handler = ngx_http_variable_cookie; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } + if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) { v[i].get_handler = ngx_http_variable_argument; v[i].data = (uintptr_t) &v[i].name; @@ -1589,3 +1607,87 @@ ngx_http_variables_init_vars(ngx_conf_t return NGX_OK; } + + +void +ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_http_variable_value_node_t *vvn, *vvt; + + for ( ;; ) { + + vvn = (ngx_http_variable_value_node_t *) node; + vvt = (ngx_http_variable_value_node_t *) temp; + + if (node->key != temp->key) { + + p = (node->key < temp->key) ? &temp->left : &temp->right; + + } else if (vvn->len != vvt->len) { + + p = (vvn->len < vvt->len) ? &temp->left : &temp->right; + + } else { + p = (ngx_memcmp(vvn->value->data, vvt->value->data, vvn->len) < 0) + ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + + +ngx_http_variable_value_t * +ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val, + uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_http_variable_value_node_t *vvn; + + node = rbtree->root; + sentinel = rbtree->sentinel; + + while (node != sentinel) { + + vvn = (ngx_http_variable_value_node_t *) node; + + if (hash != node->key) { + node = (hash < node->key) ? node->left : node->right; + continue; + } + + if (val->len != vvn->len) { + node = (val->len < vvn->len) ? node->left : node->right; + continue; + } + + rc = ngx_memcmp(val->data, vvn->value->data, val->len); + + if (rc < 0) { + node = node->left; + continue; + } + + if (rc > 0) { + node = node->right; + continue; + } + + return vvn->value; + } + + return NULL; +} diff --git a/src/http/ngx_http_variables.h b/src/http/ngx_http_variables.h --- a/src/http/ngx_http_variables.h +++ b/src/http/ngx_http_variables.h @@ -63,6 +63,19 @@ ngx_int_t ngx_http_variables_add_core_va ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf); +typedef struct { + ngx_rbtree_node_t node; + size_t len; + ngx_http_variable_value_t *value; +} ngx_http_variable_value_node_t; + + +void ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +ngx_http_variable_value_t *ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree, + ngx_str_t *name, uint32_t hash); + + extern ngx_http_variable_value_t ngx_http_variable_null_value; extern ngx_http_variable_value_t ngx_http_variable_true_value; diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c --- a/src/mail/ngx_mail_auth_http_module.c +++ b/src/mail/ngx_mail_auth_http_module.c @@ -40,7 +40,6 @@ struct ngx_mail_auth_http_ctx_s { ngx_mail_auth_http_handler_pt handler; ngx_uint_t state; - ngx_uint_t hash; /* no needed ? */ u_char *header_name_start; u_char *header_name_end; @@ -269,7 +268,7 @@ ngx_mail_auth_http_write_handler(ngx_eve ngx_del_timer(wev); } - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); @@ -895,7 +894,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t return; } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -903,7 +902,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t } if (rev->active) { - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_mail_close_connection(c); } } @@ -915,7 +914,6 @@ ngx_mail_auth_http_parse_header_line(ngx ngx_mail_auth_http_ctx_t *ctx) { u_char c, ch, *p; - ngx_uint_t hash; enum { sw_start = 0, sw_name, @@ -927,7 +925,6 @@ ngx_mail_auth_http_parse_header_line(ngx } state; state = ctx->state; - hash = ctx->hash; for (p = ctx->response->pos; p < ctx->response->last; p++) { ch = *p; @@ -951,12 +948,10 @@ ngx_mail_auth_http_parse_header_line(ngx c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'z') { - hash = c; break; } if (ch >= '0' && ch <= '9') { - hash = ch; break; } @@ -968,7 +963,6 @@ ngx_mail_auth_http_parse_header_line(ngx case sw_name: c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'z') { - hash += c; break; } @@ -979,12 +973,10 @@ ngx_mail_auth_http_parse_header_line(ngx } if (ch == '-') { - hash += ch; break; } if (ch >= '0' && ch <= '9') { - hash += ch; break; } @@ -1081,7 +1073,6 @@ ngx_mail_auth_http_parse_header_line(ngx ctx->response->pos = p; ctx->state = state; - ctx->hash = hash; return NGX_AGAIN; @@ -1089,7 +1080,6 @@ done: ctx->response->pos = p + 1; ctx->state = sw_start; - ctx->hash = hash; return NGX_OK; @@ -1112,7 +1102,7 @@ ngx_mail_auth_http_block_read(ngx_event_ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth http block read"); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { c = rev->data; s = c->data; @@ -1166,6 +1156,7 @@ ngx_mail_auth_http_create_request(ngx_ma + sizeof(CRLF) - 1 + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len + sizeof(CRLF) - 1 + + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1 + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len @@ -1220,12 +1211,19 @@ ngx_mail_auth_http_create_request(ngx_ma b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1); b->last = ngx_copy(b->last, s->connection->addr_text.data, - s->connection->addr_text.len); + s->connection->addr_text.len); *b->last++ = CR; *b->last++ = LF; + if (s->host.len) { + b->last = ngx_cpymem(b->last, "Client-Host: ", + sizeof("Client-Host: ") - 1); + b->last = ngx_copy(b->last, s->host.data, s->host.len); + *b->last++ = CR; *b->last++ = LF; + } + if (s->auth_method == NGX_MAIL_AUTH_NONE) { - /* HELO / MAIL FROM / RCPT TO can't contain CRLF, no need to escape */ + /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */ b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ", sizeof("Auth-SMTP-Helo: ") - 1); diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -513,7 +513,7 @@ ngx_mail_send(ngx_event_t *wev) } if (s->out.len == 0) { - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -552,7 +552,7 @@ ngx_mail_send(ngx_event_t *wev) ngx_add_timer(c->write, cscf->timeout); - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_mail_close_connection(c); return; } @@ -579,7 +579,7 @@ ngx_mail_read_command(ngx_mail_session_t } if (n == NGX_AGAIN) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_session_internal_server_error(s); return NGX_ERROR; } diff --git a/src/mail/ngx_mail_imap_handler.c b/src/mail/ngx_mail_imap_handler.c --- a/src/mail/ngx_mail_imap_handler.c +++ b/src/mail/ngx_mail_imap_handler.c @@ -46,7 +46,7 @@ ngx_mail_imap_init_session(ngx_mail_sess ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } diff --git a/src/mail/ngx_mail_imap_module.c b/src/mail/ngx_mail_imap_module.c diff --git a/src/mail/ngx_mail_parse.c b/src/mail/ngx_mail_parse.c diff --git a/src/mail/ngx_mail_pop3_handler.c b/src/mail/ngx_mail_pop3_handler.c --- a/src/mail/ngx_mail_pop3_handler.c +++ b/src/mail/ngx_mail_pop3_handler.c @@ -67,7 +67,7 @@ ngx_mail_pop3_init_session(ngx_mail_sess ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c --- a/src/mail/ngx_mail_proxy_module.c +++ b/src/mail/ngx_mail_proxy_module.c @@ -104,7 +104,7 @@ ngx_module_t ngx_mail_proxy_module = { }; -static u_char smtp_ok[] = "235 2.0.0 OK" CRLF; +static u_char smtp_ok[] = "250 2.0.0 OK" CRLF; void @@ -201,7 +201,7 @@ ngx_mail_proxy_block_read(ngx_event_t *r ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read"); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { c = rev->data; s = c->data; @@ -465,6 +465,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t u_char *p; ngx_int_t rc; ngx_str_t line; + ngx_buf_t *b; ngx_connection_t *c; ngx_mail_session_t *s; ngx_mail_proxy_conf_t *pcf; @@ -520,9 +521,15 @@ ngx_mail_proxy_smtp_handler(ngx_event_t p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); *p++ = CR; *p = LF; - s->mail_state = pcf->xclient ? ngx_smtp_helo_xclient : - s->auth_method == NGX_MAIL_AUTH_NONE ? - ngx_smtp_helo_from : ngx_smtp_helo; + if (pcf->xclient) { + s->mail_state = ngx_smtp_helo_xclient; + + } else if (s->auth_method == NGX_MAIL_AUTH_NONE) { + s->mail_state = ngx_smtp_helo_from; + + } else { + s->mail_state = ngx_smtp_helo; + } break; @@ -544,16 +551,15 @@ ngx_mail_proxy_smtp_handler(ngx_event_t } line.len = ngx_sprintf(line.data, - "XCLIENT PROTO=%sSMTP%s%V ADDR=%V%s%V " - "NAME=%V" CRLF, - (s->esmtp ? "E" : ""), + "XCLIENT PROTO=%sSMTP%s%V ADDR=%V%s%V NAME=%V" CRLF, + (s->esmtp ? "E" : ""), (s->smtp_helo.len ? " HELO=" : ""), &s->smtp_helo, &s->connection->addr_text, (s->login.len ? " LOGIN=" : ""), &s->login, &s->host) - line.data; - s->mail_state = s->auth_method == NGX_MAIL_AUTH_NONE ? - ngx_smtp_xclient_from : ngx_smtp_xclient; + s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ? + ngx_smtp_xclient_from : ngx_smtp_xclient; break; @@ -602,15 +608,16 @@ ngx_mail_proxy_smtp_handler(ngx_event_t case ngx_smtp_xclient: case ngx_smtp_to: - if (s->auth_method != NGX_MAIL_AUTH_NONE) { - ngx_memcpy(s->proxy->buffer->start, smtp_ok, - sizeof(smtp_ok) - 1); - s->proxy->buffer->last = s->proxy->buffer->start - + sizeof(smtp_ok) - 1; - s->proxy->buffer->pos = s->proxy->buffer->start; + b = s->proxy->buffer; + + if (s->auth_method == NGX_MAIL_AUTH_NONE) { + b->pos = b->start; + + } else { + ngx_memcpy(b->start, smtp_ok, sizeof(smtp_ok) - 1); + b->last = b->start + sizeof(smtp_ok) - 1; } - s->connection->read->handler = ngx_mail_proxy_handler; s->connection->write->handler = ngx_mail_proxy_handler; rev->handler = ngx_mail_proxy_handler; @@ -662,7 +669,7 @@ ngx_mail_proxy_dummy_handler(ngx_event_t ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler"); - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { c = wev->data; s = c->data; @@ -753,20 +760,17 @@ ngx_mail_proxy_read_response(ngx_mail_se default: /* NGX_MAIL_SMTP_PROTOCOL */ switch (state) { - case ngx_smtp_to: - return NGX_OK; - - case ngx_smtp_helo: - case ngx_smtp_helo_from: - case ngx_smtp_helo_xclient: - case ngx_smtp_from: - if (p[0] == '2' && p[1] == '5' && p[2] == '0') { + case ngx_smtp_start: + if (p[0] == '2' && p[1] == '2' && p[2] == '0') { return NGX_OK; } break; - case ngx_smtp_start: - if (p[0] == '2' && p[1] == '2' && p[2] == '0') { + case ngx_smtp_helo: + case ngx_smtp_helo_xclient: + case ngx_smtp_helo_from: + case ngx_smtp_from: + if (p[0] == '2' && p[1] == '5' && p[2] == '0') { return NGX_OK; } break; @@ -777,6 +781,9 @@ ngx_mail_proxy_read_response(ngx_mail_se return NGX_OK; } break; + + case ngx_smtp_to: + return NGX_OK; } break; @@ -945,22 +952,22 @@ ngx_mail_proxy_handler(ngx_event_t *ev) return; } - if (ngx_handle_write_event(dst->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(dst->write, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } - if (ngx_handle_read_event(dst->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(dst->read, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } - if (ngx_handle_write_event(src->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(src->write, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } - if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(src->read, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c --- a/src/mail/ngx_mail_smtp_handler.c +++ b/src/mail/ngx_mail_smtp_handler.c @@ -228,7 +228,7 @@ ngx_mail_smtp_greeting(ngx_mail_session_ timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout; ngx_add_timer(c->read, timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -270,7 +270,7 @@ ngx_mail_smtp_invalid_pipelining(ngx_eve ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); return; } @@ -647,8 +647,7 @@ ngx_mail_smtp_mail(ngx_mail_session_t *s sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) { - ngx_mail_smtp_log_rejected_command(s, c, - "client was rejected: \"%V\""); + ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\""); s->out.len = sizeof(smtp_auth_required) - 1; s->out.data = smtp_auth_required; @@ -672,13 +671,16 @@ ngx_mail_smtp_mail(ngx_mail_session_t *s s->smtp_from.len = cmd.len; - s->smtp_from.data = ngx_palloc(c->pool, cmd.len); + s->smtp_from.data = ngx_pnalloc(c->pool, cmd.len); if (s->smtp_from.data == NULL) { return NGX_ERROR; } ngx_memcpy(s->smtp_from.data, cmd.data, cmd.len); + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "smtp mail from:\"%V\"", &s->smtp_from); + s->out.len = sizeof(smtp_ok) - 1; s->out.data = smtp_ok; @@ -705,13 +707,16 @@ ngx_mail_smtp_rcpt(ngx_mail_session_t *s s->smtp_to.len = cmd.len; - s->smtp_to.data = ngx_palloc(c->pool, cmd.len); + s->smtp_to.data = ngx_pnalloc(c->pool, cmd.len); if (s->smtp_to.data == NULL) { return NGX_ERROR; } ngx_memcpy(s->smtp_to.data, cmd.data, cmd.len); + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "smtp rcpt to:\"%V\"", &s->smtp_to); + s->auth_method = NGX_MAIL_AUTH_NONE; return NGX_DONE; @@ -784,7 +789,7 @@ ngx_mail_smtp_discard_command(ngx_mail_s } if (n == NGX_AGAIN) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_session_internal_server_error(s); return NGX_ERROR; } diff --git a/src/mail/ngx_mail_smtp_module.c b/src/mail/ngx_mail_smtp_module.c --- a/src/mail/ngx_mail_smtp_module.c +++ b/src/mail/ngx_mail_smtp_module.c @@ -141,7 +141,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t u_char *p, *auth, *last; size_t size; ngx_str_t *c; - ngx_uint_t i, m, smtp_auth_enabled; + ngx_uint_t i, m, auth_enabled; ngx_mail_core_srv_conf_t *cscf; ngx_conf_merge_size_value(conf->client_buffer_size, @@ -201,18 +201,19 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1; } - smtp_auth_enabled = 0; + auth_enabled = 0; + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; m <<= 1, i++) { if (m & conf->auth_methods) { size += 1 + ngx_mail_smtp_auth_methods_names[i].len; - smtp_auth_enabled = 1; + auth_enabled = 1; } } - if (smtp_auth_enabled) { + if (auth_enabled) { size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1; } @@ -225,6 +226,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t conf->capability.data = p; last = p; + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); *p++ = CR; *p++ = LF; @@ -238,7 +240,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t auth = p; - if (smtp_auth_enabled) { + if (auth_enabled) { last = p; *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; @@ -271,8 +273,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t conf->starttls_capability.len = size; conf->starttls_capability.data = p; - p = ngx_cpymem(p, conf->capability.data, - conf->capability.len); + p = ngx_cpymem(p, conf->capability.data, conf->capability.len); p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); *p++ = CR; *p = LF; @@ -292,8 +293,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t conf->starttls_only_capability.len = size; conf->starttls_only_capability.data = p; - p = ngx_cpymem(p, conf->capability.data, - auth - conf->capability.data); + p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data); ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); diff --git a/src/os/unix/ngx_darwin_init.c b/src/os/unix/ngx_darwin_init.c --- a/src/os/unix/ngx_darwin_init.c +++ b/src/os/unix/ngx_darwin_init.c @@ -64,32 +64,44 @@ ngx_os_specific_init(ngx_log_t *log) ngx_uint_t i; size = sizeof(ngx_darwin_kern_ostype); - if (sysctlbyname("kern.ostype", - ngx_darwin_kern_ostype, &size, NULL, 0) == -1) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - "sysctlbyname(kern.ostype) failed"); + if (sysctlbyname("kern.ostype", ngx_darwin_kern_ostype, &size, NULL, 0) + == -1) + { + err = ngx_errno; + + if (err != NGX_ENOENT) { - if (ngx_errno != NGX_ENOMEM) { - return NGX_ERROR; + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(kern.ostype) failed"); + + if (err != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_darwin_kern_ostype[size - 1] = '\0'; } - - ngx_darwin_kern_ostype[size - 1] = '\0'; } size = sizeof(ngx_darwin_kern_osrelease); - if (sysctlbyname("kern.osrelease", - ngx_darwin_kern_osrelease, &size, NULL, 0) == -1) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - "sysctlbyname(kern.osrelease) failed"); + if (sysctlbyname("kern.osrelease", ngx_darwin_kern_osrelease, &size, + NULL, 0) + == -1) + { + err = ngx_errno; + + if (err != NGX_ENOENT) { - if (ngx_errno != NGX_ENOMEM) { - return NGX_ERROR; - } + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(kern.osrelease) failed"); - ngx_darwin_kern_osrelease[size - 1] = '\0'; + if (err != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_darwin_kern_osrelease[size - 1] = '\0'; + } } - for (i = 0; sysctls[i].name; i++) { size = sysctls[i].size; @@ -136,8 +148,10 @@ ngx_os_specific_status(ngx_log_t *log) u_long value; ngx_uint_t i; - ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s", - ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease); + if (ngx_darwin_kern_ostype[0]) { + ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s", + ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease); + } for (i = 0; sysctls[i].name; i++) { if (sysctls[i].exists) { diff --git a/src/os/unix/ngx_errno.c b/src/os/unix/ngx_errno.c --- a/src/os/unix/ngx_errno.c +++ b/src/os/unix/ngx_errno.c @@ -10,10 +10,11 @@ #if (NGX_HAVE_STRERROR_R) -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0'; @@ -32,12 +33,13 @@ u_char *ngx_strerror_r(int err, u_char * /* Linux strerror_r() */ -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { char *str; if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0'; diff --git a/src/os/unix/ngx_errno.h b/src/os/unix/ngx_errno.h --- a/src/os/unix/ngx_errno.h +++ b/src/os/unix/ngx_errno.h @@ -23,6 +23,7 @@ typedef int ngx_err_t; #define NGX_EACCES EACCES #define NGX_EBUSY EBUSY #define NGX_EEXIST EEXIST +#define NGX_EXDEV EXDEV #define NGX_ENOTDIR ENOTDIR #define NGX_EISDIR EISDIR #define NGX_EINVAL EINVAL 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 @@ -99,7 +99,17 @@ ssize_t ngx_write_chain_to_file(ngx_file #define ngx_read_fd read #define ngx_read_fd_n "read()" -#define ngx_write_fd write +/* + * we use inlined function instead of simple #define + * because glibc 2.3 sets warn_unused_result attribute for write() + * and in this case gcc 4.3 ignores (void) cast + */ +static ngx_inline ssize_t +ngx_write_fd(ngx_fd_t fd, void *buf, size_t n) +{ + return write(fd, buf, n); +} + #define ngx_write_fd_n "write()" #define ngx_linefeed(p) *p++ = LF; diff --git a/src/os/unix/ngx_freebsd.h b/src/os/unix/ngx_freebsd.h --- a/src/os/unix/ngx_freebsd.h +++ b/src/os/unix/ngx_freebsd.h @@ -14,7 +14,6 @@ ngx_chain_t *ngx_freebsd_sendfile_chain( extern int ngx_freebsd_kern_osreldate; extern int ngx_freebsd_hw_ncpu; extern u_long ngx_freebsd_net_inet_tcp_sendspace; -extern int ngx_freebsd_kern_ipc_zero_copy_send; extern ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; extern ngx_uint_t ngx_freebsd_use_tcp_nopush; diff --git a/src/os/unix/ngx_freebsd_init.c b/src/os/unix/ngx_freebsd_init.c --- a/src/os/unix/ngx_freebsd_init.c +++ b/src/os/unix/ngx_freebsd_init.c @@ -19,9 +19,6 @@ u_long ngx_freebsd_net_inet_tcp_sendspa /* FreeBSD 4.9 */ int ngx_freebsd_machdep_hlt_logical_cpus; -/* FreeBSD 5.0 */ -int ngx_freebsd_kern_ipc_zero_copy_send; - ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; ngx_uint_t ngx_freebsd_use_tcp_nopush; @@ -68,10 +65,6 @@ sysctl_t sysctls[] = { &ngx_freebsd_kern_ipc_somaxconn, sizeof(ngx_freebsd_kern_ipc_somaxconn), 0 }, - { "kern.ipc.zero_copy.send", - &ngx_freebsd_kern_ipc_zero_copy_send, - sizeof(ngx_freebsd_kern_ipc_zero_copy_send), 0 }, - { NULL, NULL, 0, 0 } }; diff --git a/src/os/unix/ngx_os.h b/src/os/unix/ngx_os.h --- a/src/os/unix/ngx_os.h +++ b/src/os/unix/ngx_os.h @@ -13,7 +13,6 @@ #define NGX_IO_SENDFILE 1 -#define NGX_IO_ZEROCOPY 2 typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size);