diff options
author | Robert Nagy <robert@cvs.openbsd.org> | 2012-05-13 09:14:59 +0000 |
---|---|---|
committer | Robert Nagy <robert@cvs.openbsd.org> | 2012-05-13 09:14:59 +0000 |
commit | f939b758f1c469a3c6f7305fc788c5ffc29dbffe (patch) | |
tree | e3fd61d4112b4577e4afd9036b7de700fad9fb54 /usr.sbin | |
parent | 530053e003eb12ce7182895fcceef65346227fa5 (diff) |
update to nginx-1.2.0
Diffstat (limited to 'usr.sbin')
138 files changed, 8565 insertions, 1818 deletions
diff --git a/usr.sbin/nginx/CHANGES b/usr.sbin/nginx/CHANGES index e5ceff6d112..75026b6bc5d 100644 --- a/usr.sbin/nginx/CHANGES +++ b/usr.sbin/nginx/CHANGES @@ -1,22 +1,80 @@ -Changes with nginx 1.0.15 12 Apr 2012 +Changes with nginx 1.2.0 23 Apr 2012 + + *) Bugfix: a segmentation fault might occur in a worker process if the + "try_files" directive was used; the bug had appeared in 1.1.19. + + *) Bugfix: response might be truncated if there were more than IOV_MAX + buffers used. + + *) Bugfix: in the "crop" parameter of the "image_filter" directive. + Thanks to Maxim Bublis. + + +Changes with nginx 1.1.19 12 Apr 2012 *) Security: specially crafted mp4 file might allow to overwrite memory locations in a worker process if the ngx_http_mp4_module was used, potentially resulting in arbitrary code execution (CVE-2012-2089). Thanks to Matthew Daley. + *) Bugfix: nginx/Windows might be terminated abnormally. + Thanks to Vincent Lee. + + *) Bugfix: nginx hogged CPU if all servers in an upstream were marked as + "backup". + + *) Bugfix: the "allow" and "deny" directives might be inherited + incorrectly if they were used with IPv6 addresses. + + *) Bugfix: the "modern_browser" and "ancient_browser" directives might + be inherited incorrectly. + + *) Bugfix: timeouts might be handled incorrectly on Solaris/SPARC. + *) Bugfix: in the ngx_http_mp4_module. -Changes with nginx 1.0.14 15 Mar 2012 +Changes with nginx 1.1.18 28 Mar 2012 + + *) Change: keepalive connections are no longer disabled for Safari by + default. + + *) Feature: the $connection_requests variable. + + *) Feature: $tcpinfo_rtt, $tcpinfo_rttvar, $tcpinfo_snd_cwnd and + $tcpinfo_rcv_space variables. + + *) Feature: the "worker_cpu_affinity" directive now works on FreeBSD. + + *) Feature: the "xslt_param" and "xslt_string_param" directives. + Thanks to Samuel Behan. + + *) Bugfix: in configure tests. + Thanks to Piotr Sikora. + + *) Bugfix: in the ngx_http_xslt_filter_module. + + *) Bugfix: nginx could not be built on Debian GNU/Hurd. + + +Changes with nginx 1.1.17 15 Mar 2012 *) Security: content of previously freed memory might be sent to a client if backend returned specially crafted response. Thanks to Matthew Daley. + *) Bugfix: in the embedded perl module if used from SSI. + Thanks to Matthew Daley. + + *) Bugfix: in the ngx_http_uwsgi_module. + + +Changes with nginx 1.1.16 29 Feb 2012 + + *) Change: the simultaneous subrequest limit has been raised to 200. -Changes with nginx 1.0.13 05 Mar 2012 + *) Feature: the "from" parameter of the "disable_symlinks" directive. *) Feature: the "return" and "error_page" directives can now be used to return 307 redirections. @@ -26,74 +84,113 @@ Changes with nginx 1.0.13 05 Mar 2012 specified at global level. Thanks to Roman Arutyunyan. + *) Bugfix: a segmentation fault might occur in a worker process if the + "proxy_http_version 1.1" or "fastcgi_keep_conn on" directives were + used. + *) Bugfix: memory leaks. Thanks to Lanshun Zhou. + *) Bugfix: in the "disable_symlinks" directive. + + *) Bugfix: on ZFS filesystem disk cache size might be calculated + incorrectly; the bug had appeared in 1.0.1. + + *) Bugfix: nginx could not be built by the icc 12.1 compiler. + + *) Bugfix: nginx could not be built by gcc on Solaris; the bug had + appeared in 1.1.15. + + +Changes with nginx 1.1.15 15 Feb 2012 + + *) Feature: the "disable_symlinks" directive. + + *) Feature: the "proxy_cookie_domain" and "proxy_cookie_path" + directives. + *) Bugfix: nginx might log incorrect error "upstream prematurely closed connection" instead of correct "upstream sent too big header" one. Thanks to Feibo Li. - *) Bugfix: on ZFS filesystem disk cache size might be calculated - incorrectly; the bug had appeared in 1.0.1. + *) Bugfix: nginx could not be built with the ngx_http_perl_module if the + --with-openssl option was used. *) Bugfix: the number of internal redirects to named locations was not limited. + *) Bugfix: calling $r->flush() multiple times might cause errors in the + ngx_http_gzip_filter_module. + *) Bugfix: temporary files might be not removed if the "proxy_store" directive was used with SSI includes. *) Bugfix: in some cases non-cacheable variables (such as the $args variable) returned old empty cached value. + *) Bugfix: a segmentation fault might occur in a worker process if too + many SSI subrequests were issued simultaneously; the bug had appeared + in 0.7.25. + + +Changes with nginx 1.1.14 30 Jan 2012 + + *) Feature: multiple "limit_req" limits may be used simultaneously. + + *) Bugfix: in error handling while connecting to a backend. + Thanks to Piotr Sikora. + + *) Bugfix: in AIO error handling on FreeBSD. + + *) Bugfix: in the OpenSSL library initialization. + *) Bugfix: the "proxy_redirect" directives might be inherited incorrectly. - *) Bugfix: nginx could not be built with the ngx_http_perl_module if the - --with-openssl option was used. - - *) Bugfix: nginx could not be built by the icc 12.1 compiler. + *) Bugfix: memory leak during reconfiguration if the "pcre_jit" + directive was used. -Changes with nginx 1.0.12 06 Feb 2012 +Changes with nginx 1.1.13 16 Jan 2012 *) Feature: the "TLSv1.1" and "TLSv1.2" parameters of the "ssl_protocols" directive. - *) Feature: the "if" SSI command supports captures in regular - expressions. - - *) Bugfix: the "if" SSI command did not work inside the "block" command. + *) Bugfix: the "limit_req" directive parameters were not inherited + correctly; the bug had appeared in 1.1.12. - *) Bugfix: in AIO error handling on FreeBSD. + *) Bugfix: the "proxy_redirect" directive incorrectly processed + "Refresh" header if regular expression were used. - *) Bugfix: in the OpenSSL library initialization. + *) Bugfix: the "proxy_cache_use_stale" directive with "error" parameter + did not return answer from cache if there were no live upstreams. *) Bugfix: the "worker_cpu_affinity" directive might not work. - *) Bugfix: the "limit_conn_log_level" and "limit_req_log_level" - directives might not work. + *) Bugfix: nginx could not be built on Solaris; the bug had appeared in + 1.1.12. - *) Bugfix: the "read_ahead" directive might not work combined with - "try_files" and "open_file_cache". + *) Bugfix: in the ngx_http_mp4_module. - *) Bugfix: the "proxy_cache_use_stale" directive with "error" parameter - did not return answer from cache if there were no live upstreams. - *) Bugfix: a segmentation fault might occur in a worker process if small - time was used in the "inactive" parameter of the "proxy_cache_path" - directive. +Changes with nginx 1.1.12 26 Dec 2011 - *) Bugfix: responses from cache might hang. + *) Change: a "proxy_pass" directive without URI part now uses changed + URI after redirection with the "error_page" directive. + Thanks to Lanshun Zhou. - *) Bugfix: in error handling while connecting to a backend. - Thanks to Piotr Sikora. + *) Feature: the "proxy/fastcgi/scgi/uwsgi_cache_lock", + "proxy/fastcgi/scgi/uwsgi_cache_lock_timeout" directives. - *) Bugfix: in the "epoll" event method. - Thanks to Yichun Zhang. + *) Feature: the "pcre_jit" directive. - *) Bugfix: the $sent_http_cache_control variable might contain a wrong - value if the "expires" directive was used. - Thanks to Yichun Zhang. + *) Feature: the "if" SSI command supports captures in regular + expressions. + + *) Bugfix: the "if" SSI command did not work inside the "block" command. + + *) Bugfix: the "limit_conn_log_level" and "limit_req_log_level" + directives might not work. *) Bugfix: the "limit_rate" directive did not allow to use full throughput, even if limit value was very high. @@ -101,52 +198,114 @@ Changes with nginx 1.0.12 06 Feb 2012 *) Bugfix: the "sendfile_max_chunk" directive did not work, if the "limit_rate" directive was used. - *) Bugfix: nginx could not be built on Solaris; the bug had appeared in - 1.0.11. + *) Bugfix: a "proxy_pass" directive without URI part always used + original request URI if variables were used. + + *) Bugfix: a "proxy_pass" directive without URI part might use original + request after redirection with the "try_files" directive. + Thanks to Lanshun Zhou. *) Bugfix: in the ngx_http_scgi_module. *) Bugfix: in the ngx_http_mp4_module. + *) Bugfix: nginx could not be built on Solaris; the bug had appeared in + 1.1.9. + + +Changes with nginx 1.1.11 12 Dec 2011 + + *) Feature: the "so_keepalive" parameter of the "listen" directive. + Thanks to Vsevolod Stakhov. + + *) Feature: the "if_not_empty" parameter of the + "fastcgi/scgi/uwsgi_param" directives. + + *) Feature: the $https variable. + + *) Feature: the "proxy_redirect" directive supports variables in the + first parameter. + + *) Feature: the "proxy_redirect" directive supports regular expressions. + + *) Bugfix: the $sent_http_cache_control variable might contain a wrong + value if the "expires" directive was used. + Thanks to Yichun Zhang. + + *) Bugfix: the "read_ahead" directive might not work combined with + "try_files" and "open_file_cache". + + *) Bugfix: a segmentation fault might occur in a worker process if small + time was used in the "inactive" parameter of the "proxy_cache_path" + directive. + + *) Bugfix: responses from cache might hang. + + +Changes with nginx 1.1.10 30 Nov 2011 -Changes with nginx 1.0.11 15 Dec 2011 + *) Bugfix: a segmentation fault occured in a worker process if AIO was + used on Linux; the bug had appeared in 1.1.9. + + +Changes with nginx 1.1.9 28 Nov 2011 *) Change: now double quotes are encoded in an "echo" SSI-command output. Thanks to Zaur Abasmirzoev. - *) Feature: the "image_filter_sharpen" directive. + *) Feature: the "valid" parameter of the "resolver" directive. By + default TTL returned by a DNS server is used. + Thanks to Kirill A. Korinskiy. + + *) Bugfix: nginx might hang after a worker process abnormal termination. *) Bugfix: a segmentation fault might occur in a worker process if SNI - was used; the bug had appeared in 1.0.9. + was used; the bug had appeared in 1.1.2. + + *) Bugfix: in the "keepalive_disable" directive; the bug had appeared in + 1.1.8. + Thanks to Alexander Usov. *) Bugfix: SIGWINCH signal did not work after first binary upgrade; the - bug had appeared in 1.0.9. + bug had appeared in 1.1.1. - *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request - header lines might be passed to backend while caching; or not passed - without caching if caching was enabled in another part of the - configuration. + *) Bugfix: backend responses with length not matching "Content-Length" + header line are no longer cached. *) Bugfix: in the "scgi_param" directive, if complex parameters were used. - *) Bugfix: "add_header" and "expires" directives did not work if a - request was proxied and response status code was 206. - - *) Bugfix: in the "expires @time" directive. + *) Bugfix: in the "epoll" event method. + Thanks to Yichun Zhang. *) Bugfix: in the ngx_http_flv_module. Thanks to Piotr Sikora. *) Bugfix: in the ngx_http_mp4_module. + *) Bugfix: IPv6 addresses are now handled properly in a request line and + in a "Host" request header line. + + *) Bugfix: "add_header" and "expires" directives did not work if a + request was proxied and response status code was 206. + *) Bugfix: nginx could not be built on FreeBSD 10. *) Bugfix: nginx could not be built on AIX. -Changes with nginx 1.0.10 15 Nov 2011 +Changes with nginx 1.1.8 14 Nov 2011 + + *) Change: the ngx_http_limit_zone_module was renamed to the + ngx_http_limit_conn_module. + + *) Change: the "limit_zone" directive was superseded by the + "limit_conn_zone" directive with a new syntax. + + *) Feature: support for multiple "limit_conn" limits on the same level. + + *) Feature: the "image_filter_sharpen" directive. *) Bugfix: a segmentation fault might occur in a worker process if resolver got a big DNS response. @@ -155,34 +314,54 @@ Changes with nginx 1.0.10 15 Nov 2011 *) Bugfix: in cache key calculation if internal MD5 implementation was used; the bug had appeared in 1.0.4. + *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request + header lines might be passed to backend while caching; or not passed + without caching if caching was enabled in another part of the + configuration. + *) Bugfix: the module ngx_http_mp4_module sent incorrect "Content-Length" response header line if the "start" argument was used. Thanks to Piotr Sikora. -Changes with nginx 1.0.9 01 Nov 2011 +Changes with nginx 1.1.7 31 Oct 2011 + + *) Feature: support of several DNS servers in the "resolver" directive. + Thanks to Kirill A. Korinskiy. + + *) Bugfix: a segmentation fault occurred on start or during + reconfiguration if the "ssl" directive was used at http level and + there was no "ssl_certificate" defined. + + *) Bugfix: reduced memory consumption while proxying big files if they + were buffered to disk. + + *) Bugfix: a segmentation fault might occur in a worker process if + "proxy_http_version 1.1" directive was used. + + *) Bugfix: in the "expires @time" directive. + + +Changes with nginx 1.1.6 17 Oct 2011 + + *) Change in internal API: now module context data are cleared while + internal redirect to named location. + Requested by Yichun Zhang. + + *) Change: if a server in an upstream failed, only one request will be + sent to it after fail_timeout; the server will be considered alive if + it will successfully respond to the request. *) Change: now the 0x7F-0x1F characters are escaped as \xXX in an access_log. - *) Change: now SIGWINCH signal works only in daemon mode. - *) Feature: "proxy/fastcgi/scgi/uwsgi_ignore_headers" directives support the following additional values: X-Accel-Limit-Rate, X-Accel-Buffering, X-Accel-Charset. *) Feature: decrease of memory consumption if SSL is used. - *) Feature: accept filters are now supported on NetBSD. - - *) Feature: the "uwsgi_buffering" and "scgi_buffering" directives. - Thanks to Peter Smit. - - *) Bugfix: a segmentation fault occurred on start or while - reconfiguration if the "ssl" directive was used at http level and - there was no "ssl_certificate" defined. - *) Bugfix: some UTF-8 characters were processed incorrectly. Thanks to Alexey Kuts. @@ -199,10 +378,18 @@ Changes with nginx 1.0.9 01 Nov 2011 *) Bugfix: the module ngx_http_mp4_module did not support seeking on 32-bit platforms. + +Changes with nginx 1.1.5 05 Oct 2011 + + *) Feature: the "uwsgi_buffering" and "scgi_buffering" directives. + Thanks to Peter Smit. + *) Bugfix: non-cacheable responses might be cached if "proxy_cache_bypass" directive was used. Thanks to John Ferlito. + *) Bugfix: in HTTP/1.1 support in the ngx_http_proxy_module. + *) Bugfix: cached responses with an empty body were returned incorrectly; the bug had appeared in 0.8.31. @@ -211,26 +398,17 @@ Changes with nginx 1.0.9 01 Nov 2011 *) Bugfix: in the "return" directive. - *) Bugfix: the "ssl_verify_client", "ssl_verify_depth", and - "ssl_prefer_server_ciphers" directives might work incorrectly if SNI - was used. + *) Bugfix: the "ssl_session_cache builtin" directive caused segmentation + fault; the bug had appeared in 1.1.1. -Changes with nginx 1.0.8 01 Oct 2011 +Changes with nginx 1.1.4 20 Sep 2011 - *) Bugfix: nginx could not be built --with-http_mp4_module and without - --with-debug option. - - -Changes with nginx 1.0.7 30 Sep 2011 - - *) Change: now if total size of all ranges is greater than source - response size, then nginx disables ranges and returns just the source - response. + *) Feature: the ngx_http_upstream_keepalive module. - *) Feature: the "max_ranges" directive. + *) Feature: the "proxy_http_version" directive. - *) Feature: the module ngx_http_mp4_module. + *) Feature: the "fastcgi_keep_conn" directive. *) Feature: the "worker_aio_requests" directive. @@ -240,17 +418,57 @@ Changes with nginx 1.0.7 30 Sep 2011 *) Bugfix: in Linux AIO error processing. Thanks to Hagai Avrahami. + *) Bugfix: reduced memory consumption for long-lived requests. + + *) Bugfix: the module ngx_http_mp4_module did not support 64-bit MP4 + "co64" atom. + + +Changes with nginx 1.1.3 14 Sep 2011 + + *) Feature: the module ngx_http_mp4_module. + *) Bugfix: in Linux AIO combined with open_file_cache. *) Bugfix: open_file_cache did not update file info on retest if file was not atomically changed. - *) Bugfix: reduced memory consumption for long-lived requests. + *) Bugfix: nginx could not be built on MacOSX 10.7. + + +Changes with nginx 1.1.2 05 Sep 2011 + + *) Change: now if total size of all ranges is greater than source + response size, then nginx disables ranges and returns just the source + response. + + *) Feature: the "max_ranges" directive. + + *) Bugfix: the "ssl_verify_client", "ssl_verify_depth", and + "ssl_prefer_server_ciphers" directives might work incorrectly if SNI + was used. *) Bugfix: in the "proxy/fastcgi/scgi/uwsgi_ignore_client_abort" directives. - *) Bugfix: nginx could not be built on MacOSX 10.7. + +Changes with nginx 1.1.1 22 Aug 2011 + + *) Change: now cache loader processes either as many files as specified + by "loader_files" parameter or works no longer than time specified by + the "loader_threshold" parameter during each iteration. + + *) Change: now SIGWINCH signal works only in daemon mode. + + *) Feature: now shared zones and caches use POSIX semaphores on Solaris. + Thanks to Den Ivanov. + + *) Feature: accept filters are now supported on NetBSD. + + *) Bugfix: nginx could not be built on Linux 3.0. + + *) Bugfix: nginx did not use gzipping in some cases; the bug had + appeared in 1.1.0. *) Bugfix: request body might be processed incorrectly if client used pipelining. @@ -269,11 +487,18 @@ Changes with nginx 1.0.7 30 Sep 2011 *) Bugfix: a segmentation fault might occur in a worker process if many backup servers were used in an upstream. + *) Bugfix: a segmentation fault might occur in a worker process if + "fastcgi/scgi/uwsgi_param" directives were used with values starting + with "HTTP_"; the bug had appeared in 0.8.40. + -Changes with nginx 1.0.6 29 Aug 2011 +Changes with nginx 1.1.0 01 Aug 2011 *) Feature: cache loader run time decrease. + *) Feature: "loader_files", "loader_sleep", and "loader_threshold" + options of the "proxy/fastcgi/scgi/uwsgi_cache_path" directives. + *) Feature: loading time decrease of configuration with large number of HTTPS sites. @@ -281,31 +506,27 @@ Changes with nginx 1.0.6 29 Aug 2011 Thanks to Adrian Kotelba. *) Feature: the "lingering_close" directive. - - *) Feature: now shared zones and caches use POSIX semaphores on Solaris. - Thanks to Den Ivanov. - - *) Bugfix: nginx could not be built on Linux 3.0. - - *) Bugfix: a segmentation fault might occur in a worker process if - "fastcgi/scgi/uwsgi_param" directives were used with values starting - with "HTTP_"; the bug had appeared in 0.8.40. + Thanks to Maxim Dounin. *) Bugfix: in closing connection for pipelined requests. + Thanks to Maxim Dounin. *) Bugfix: nginx did not disable gzipping if client sent "gzip;q=0" in "Accept-Encoding" request header line. *) Bugfix: in timeout in unbuffered proxied mode. + Thanks to Maxim Dounin. *) Bugfix: memory leaks when a "proxy_pass" directive contains variables and proxies to an HTTPS backend. + Thanks to Maxim Dounin. *) Bugfix: in parameter validaiton of a "proxy_pass" directive with variables. Thanks to Lanshun Zhou. *) Bugfix: SSL did not work on QNX. + Thanks to Maxim Dounin. *) Bugfix: SSL modules could not be built by gcc 4.6 without --with-debug option. @@ -365,8 +586,8 @@ Changes with nginx 1.0.3 25 May 2011 testing IPv4 address mapped to IPv6 address, if access or deny rules were defined only for IPv6; the bug had appeared in 0.8.22. - *) Bugfix: a cached response may be broken if proxy/fastcgi/scgi/ - uwsgi_cache_bypass and proxy/fastcgi/scgi/uwsgi_no_cache directive + *) Bugfix: a cached response may be broken if "proxy/fastcgi/scgi/ + uwsgi_cache_bypass" and "proxy/fastcgi/scgi/uwsgi_no_cache" directive values were different; the bug had appeared in 0.8.46. @@ -2132,7 +2353,7 @@ Changes with nginx 0.7.15 08 Sep 2008 Changes with nginx 0.7.14 01 Sep 2008 *) Change: now the ssl_certificate and ssl_certificate_key directives - have not default values. + have no default values. *) Feature: the "listen" directive supports the "ssl" parameter. diff --git a/usr.sbin/nginx/CHANGES.ru b/usr.sbin/nginx/CHANGES.ru index 0bfe8f75ac8..9d62688079d 100644 --- a/usr.sbin/nginx/CHANGES.ru +++ b/usr.sbin/nginx/CHANGES.ru @@ -1,5 +1,17 @@ -Изменения в nginx 1.0.15 12.04.2012 +Изменения в nginx 1.2.0 23.04.2012 + + *) Исправление: в рабочем процессе мог произойти segmentation fault, + если использовалась директива try_files; ошибка появилась в 1.1.19. + + *) Исправление: ответ мог быть передан не полностью, если использовалось + больше IOV_MAX буферов. + + *) Исправление: в работе параметра crop директивы image_filter. + Спасибо Maxim Bublis. + + +Изменения в nginx 1.1.19 12.04.2012 *) Безопасность: при обработке специально созданного mp4 файла модулем ngx_http_mp4_module могли перезаписываться области памяти рабочего @@ -7,17 +19,64 @@ (CVE-2012-2089). Спасибо Matthew Daley. + *) Исправление: nginx/Windows мог завершаться аварийно. + Спасибо Vincent Lee. + + *) Исправление: nginx нагружал процессор, если все серверы в upstream'е + были помечены флагом backup. + + *) Исправление: директивы allow и deny могли наследоваться некорректно, + если в них использовались IPv6 адреса. + + *) Исправление: директивы modern_browser и ancient_browser могли + наследоваться некорректно. + + *) Исправление: таймауты могли работать некорректно на Solaris/SPARC. + *) Исправление: в модуле ngx_http_mp4_module. -Изменения в nginx 1.0.14 15.03.2012 +Изменения в nginx 1.1.18 28.03.2012 + + *) Изменение: теперь keepalive соединения не запрещены для Safari по + умолчанию. + + *) Добавление: переменная $connection_requests. + + *) Добавление: переменные $tcpinfo_rtt, $tcpinfo_rttvar, + $tcpinfo_snd_cwnd и $tcpinfo_rcv_space. + + *) Добавление: директива worker_cpu_affinity теперь работает на FreeBSD. + + *) Добавление: директивы xslt_param и xslt_string_param. + Спасибо Samuel Behan. + + *) Исправление: в configure. + Спасибо Piotr Sikora. + + *) Исправление: в модуле ngx_http_xslt_filter_module. + + *) Исправление: nginx не собирался на Debian GNU/Hurd. + + +Изменения в nginx 1.1.17 15.03.2012 *) Безопасность: содержимое ранее освобождённой памяти могло быть отправлено клиенту, если бэкенд возвращал специально созданный ответ. Спасибо Matthew Daley. + *) Исправление: при использовании встроенного перла из SSI. + Спасибо Matthew Daley. + + *) Исправление: в модуле ngx_http_uwsgi_module. + + +Изменения в nginx 1.1.16 29.02.2012 -Изменения в nginx 1.0.13 05.03.2012 + *) Изменение: ограничение на количество одновременных подзапросов + поднято до 200. + + *) Добавление: параметр from в директиве disable_symlinks. *) Добавление: директивы return и error_page теперь могут использоваться для возврата перенаправлений с кодом 307. @@ -27,74 +86,114 @@ задана директива error_log. Спасибо Роману Арутюняну. + *) Исправление: в рабочем процессе мог произойти segmentation fault, + если использовались директивы "proxy_http_version 1.1" или + "fastcgi_keep_conn on". + *) Исправление: утечек памяти. Спасибо Lanshun Zhou. + *) Исправление: в директиве disable_symlinks. + + *) Исправление: при использовании ZFS размер кэша на диске мог считаться + некорректно; ошибка появилась в 1.0.1. + + *) Исправление: nginx не собирался компилятором icc 12.1. + + *) Исправление: nginx не собирался gcc на Solaris; ошибка появилась в + 1.1.15. + + +Изменения в nginx 1.1.15 15.02.2012 + + *) Добавление: директива disable_symlinks. + + *) Добавление: директивы proxy_cookie_domain и proxy_cookie_path. + *) Исправление: nginx мог некорректно сообщать об ошибке "upstream prematurely closed connection" вместо "upstream sent too big header". Спасибо Feibo Li. - *) Исправление: при использовании ZFS размер кэша на диске мог считаться - некорректно; ошибка появилась в 1.0.1. + *) Исправление: nginx не собирался с модулем ngx_http_perl_module, если + использовался параметр --with-openssl. *) Исправление: количество внутренних перенаправлений в именованные location'ы не ограничивалось. + *) Исправление: вызов $r->flush() несколько раз подряд мог приводить к + ошибкам в модуле ngx_http_gzip_filter_module. + *) Исправление: при использовании директивы proxy_store с SSI-подзапросами временные файлы могли не удаляться. *) Исправление: в некоторых случаях некэшируемые переменные (такие, как $args) возвращали старое пустое закэшированное значение. - *) Исправление: директивы proxy_redirect могли наследоваться - некорректно. + *) Исправление: в рабочем процессе мог произойти segmentation fault, + если одновременно создавалось слишком много SSI-подзапросов; ошибка + появилась в 0.7.25. - *) Исправление: nginx не собирался с модулем ngx_http_perl_module, если - использовался параметр --with-openssl. - *) Исправление: nginx не собирался компилятором icc 12.1. +Изменения в nginx 1.1.14 30.01.2012 + *) Добавление: теперь можно указать несколько ограничений limit_req + одновременно. -Изменения в nginx 1.0.12 06.02.2012 + *) Исправление: в обработке ошибок при соединении с бэкендом. + Спасибо Piotr Sikora. - *) Добавление: параметры TLSv1.1 и TLSv1.2 в директиве ssl_protocols. + *) Исправление: в обработке ошибок при использовании AIO на FreeBSD. - *) Добавление: SSI команда if поддерживает выделения в регулярных - выражениях. + *) Исправление: в инициализации библиотеки OpenSSL. - *) Исправление: SSI команда if не работала внутри команды block. + *) Исправление: директивы proxy_redirect могли наследоваться + некорректно. - *) Исправление: в обработке ошибок при использовании AIO на FreeBSD. + *) Исправление: утечки памяти при переконфигурации, если использовалась + директива pcre_jit. - *) Исправление: в инициализации библиотеки OpenSSL. - *) Исправление: директива worker_cpu_affinity могла не работать. +Изменения в nginx 1.1.13 16.01.2012 - *) Исправление: директивы limit_conn_log_level и limit_req_log_level - могли не работать. + *) Добавление: параметры TLSv1.1 и TLSv1.2 в директиве ssl_protocols. - *) Исправление: директива read_ahead могла не работать при использовании - совместно с try_files и open_file_cache. + *) Исправление: параметры директивы limit_req наследовались некорректно; + ошибка появилась в 1.1.12. + + *) Исправление: директива proxy_redirect некорректно обрабатывала + заголовок Refresh при использовании регулярных выражений. *) Исправление: директива proxy_cache_use_stale с параметром error не возвращала ответ из кэша, если все бэкенды были признаны неработающими. - *) Исправление: если в параметре inactive директивы proxy_cache_path - было указано малое время, в рабочем процессе мог произойти - segmentation fault. + *) Исправление: директива worker_cpu_affinity могла не работать. - *) Исправление: ответы из кэша могли зависать. + *) Исправление: nginx не собирался на Solaris; ошибка появилась в + 1.1.12. - *) Исправление: в обработке ошибок при соединении с бэкендом. - Спасибо Piotr Sikora. + *) Исправление: в модуле ngx_http_mp4_module. - *) Исправление: в методе epoll. - Спасибо Yichun Zhang. - *) Исправление: переменная $sent_http_cache_control могла содержать - неверное значение при использовании директивы expires. - Спасибо Yichun Zhang. +Изменения в nginx 1.1.12 26.12.2011 + + *) Изменение: после перенаправления запроса с помощью директивы + error_page директива proxy_pass без URI теперь использует изменённый + URI. + Спасибо Lanshun Zhou. + + *) Добавление: директивы proxy/fastcgi/scgi/uwsgi_cache_lock, + proxy/fastcgi/scgi/uwsgi_cache_lock_timeout. + + *) Добавление: директива pcre_jit. + + *) Добавление: SSI команда if поддерживает выделения в регулярных + выражениях. + + *) Исправление: SSI команда if не работала внутри команды block. + + *) Исправление: директивы limit_conn_log_level и limit_req_log_level + могли не работать. *) Исправление: директива limit_rate не позволяла передавать на полной скорости, даже если был указан очень большой лимит. @@ -102,52 +201,116 @@ *) Исправление: директива sendfile_max_chunk не работала, если использовалась директива limit_rate. - *) Исправление: nginx не собирался на Solaris; ошибка появилась в - 1.0.11. + *) Исправление: если в директиве proxy_pass использовались переменные и + не был указан URI, всегда использовался URI исходного запроса. + + *) Исправление: после перенаправления запроса с помощью директивы + try_files директива proxy_pass без URI могла использовать URI + исходного запроса. + Спасибо Lanshun Zhou. *) Исправление: в модуле ngx_http_scgi_module. *) Исправление: в модуле ngx_http_mp4_module. + *) Исправление: nginx не собирался на Solaris; ошибка появилась в 1.1.9. + + +Изменения в nginx 1.1.11 12.12.2011 + + *) Добавление: параметр so_keepalive в директиве listen. + Спасибо Всеволоду Стахову. + + *) Добавление: параметр if_not_empty в директивах + fastcgi/scgi/uwsgi_param. + + *) Добавление: переменная $https. + + *) Добавление: директива proxy_redirect поддерживает переменные в первом + параметре. + + *) Добавление: директива proxy_redirect поддерживает регулярные + выражения. + + *) Исправление: переменная $sent_http_cache_control могла содержать + неверное значение при использовании директивы expires. + Спасибо Yichun Zhang. + + *) Исправление: директива read_ahead могла не работать при использовании + совместно с try_files и open_file_cache. + + *) Исправление: если в параметре inactive директивы proxy_cache_path + было указано малое время, в рабочем процессе мог произойти + segmentation fault. + + *) Исправление: ответы из кэша могли зависать. + + +Изменения в nginx 1.1.10 30.11.2011 + + *) Исправление: при использовании AIO на Linux в рабочем процессе + происходил segmentation fault; ошибка появилась в 1.1.9. + -Изменения в nginx 1.0.11 15.12.2011 +Изменения в nginx 1.1.9 28.11.2011 *) Изменение: теперь двойные кавычки экранируется при выводе SSI-командой echo. Спасибо Зауру Абасмирзоеву. - *) Добавление: директива image_filter_sharpen. + *) Добавление: параметр valid в директиве resolver. По умолчанию теперь + используется TTL, возвращённый DNS-сервером. + Спасибо Кириллу Коринскому. + + *) Исправление: nginx мог перестать отвечать, если рабочий процесс + завершался аварийно. *) Исправление: в рабочем процессе мог произойти segmentation fault, - если использовалось SNI; ошибка появилась в 1.0.9. + если использовалось SNI; ошибка появилась в 1.1.2. + + *) Исправление: в директиве keepalive_disable; ошибка появилась в 1.1.8. + Спасибо Александру Усову. *) Исправление: сигнал SIGWINCH переставал работать после первого - обновления исполняемого файла; ошибка появилась в 1.0.9. + обновления исполняемого файла; ошибка появилась в 1.1.1. - *) Исправление: строки "If-Modified-Since", "If-Range" и им подобные в - заголовке запроса клиента могли передаваться бэкенду при кэшировании; - или не передаваться при выключенном кэшировании, если кэширование - было включено в другой части конфигурации. + *) Исправление: теперь ответы бэкендов, длина которых не соответствует + заголовку Content-Length, не кэширутся. *) Исправление: в директиве scgi_param при использовании составных параметров. - *) Исправление: директивы add_header и expires не работали для ответов с - кодом 206, если запрос проксировался. - - *) Исправление: в директиве "expires @time". + *) Исправление: в методе epoll. + Спасибо Yichun Zhang. *) Исправление: в модуле ngx_http_flv_module. Спасибо Piotr Sikora. *) Исправление: в модуле ngx_http_mp4_module. + *) Исправление: теперь nginx понимает IPv6-адреса в строке запроса и в + заголовке Host. + + *) Исправление: директивы add_header и expires не работали для ответов с + кодом 206, если запрос проксировался. + *) Исправление: nginx не собирался на FreeBSD 10. *) Исправление: nginx не собирался на AIX. -Изменения в nginx 1.0.10 15.11.2011 +Изменения в nginx 1.1.8 14.11.2011 + + *) Изменение: модуль ngx_http_limit_zone_module переименован в + ngx_http_limit_conn_module. + + *) Изменение: директива limit_zone заменена директивой limit_conn_zone с + новым синтаксисом. + + *) Добавление: поддержка ограничения по нескольким limit_conn на одном + уровне. + + *) Добавление: директива image_filter_sharpen. *) Исправление: в рабочем процессе мог произойти segmentation fault, если resolver получил большой DNS-ответ. @@ -156,33 +319,54 @@ *) Исправление: в вычислении ключа для кэширования, если использовалась внутренняя реализация MD5; ошибка появилась в 1.0.4. + *) Исправление: строки "If-Modified-Since", "If-Range" и им подобные в + заголовке запроса клиента могли передаваться бэкенду при кэшировании; + или не передаваться при выключенном кэшировании, если кэширование + было включено в другой части конфигурации. + *) Исправление: модуль ngx_http_mp4_module выдавал неверную строку "Content-Length" в заголовке ответа, использовался аргумент start. Спасибо Piotr Sikora. -Изменения в nginx 1.0.9 01.11.2011 +Изменения в nginx 1.1.7 31.10.2011 + + *) Добавление: поддержка нескольких DNS серверов в директиве "resolver". + Спасибо Кириллу Коринскому. + + *) Исправление: на старте или во время переконфигурации происходил + segmentation fault, если директива ssl использовалась на уровне http + и не был указан ssl_certificate. + + *) Исправление: уменьшено потребление памяти при проксировании больших + файлов, если они буферизировались на диск. + + *) Исправление: в рабочем процессе мог произойти segmentation fault, + если использовалась директива "proxy_http_version 1.1". + + *) Исправление: в директиве "expires @time". + + +Изменения в nginx 1.1.6 17.10.2011 + + *) Изменение во внутреннем API: теперь при внутреннем редиректе в + именованный location контексты модулей очищаются. + По запросу Yichun Zhang. + + *) Изменение: теперь если сервер, описанный в блоке upstream, был + признан неработающим, то после истечения fail_timeout на него будет + отправлен только один запрос; сервер будет считаться работающим, если + успешно ответит на этот запрос. *) Изменение: теперь символы 0x7F-0xFF в access_log записываются в виде \xXX. - *) Изменение: SIGWINCH сигнал теперь работает только в режиме демона. - *) Добавление: директивы "proxy/fastcgi/scgi/uwsgi_ignore_headers" теперь поддерживают значения X-Accel-Limit-Rate, X-Accel-Buffering и X-Accel-Charset. *) Добавление: уменьшение потребления памяти при использовании SSL. - *) Добавление: теперь на NetBSD поддерживаются accept фильтры. - - *) Добавление: директивы uwsgi_buffering и scgi_buffering. - Спасибо Peter Smit. - - *) Исправление: на старте или во время переконфигурации происходил - segmentation fault, если директива ssl использовалась на уровне http - и не был указан ssl_certificate. - *) Исправление: некоторые UTF-8 символы обрабатывались неправильно. Спасибо Алексею Куцу. @@ -201,10 +385,19 @@ *) Исправление: модуль ngx_http_mp4_module не поддерживал перемотку на 32-битных платформах. + +Изменения в nginx 1.1.5 05.10.2011 + + *) Добавление: директивы uwsgi_buffering и scgi_buffering. + Спасибо Peter Smit. + *) Исправление: при использовании proxy_cache_bypass могли быть закэшированы некэшируемые ответы. Спасибо John Ferlito. + *) Исправление: в модуле ngx_http_proxy_module при работе с бэкендами по + HTTP/1.1. + *) Исправление: закэшированные ответы с пустым телом возвращались некорректно; ошибка появилась в 0.8.31. @@ -213,26 +406,17 @@ *) Исправление: в директиве return. - *) Исправление: директивы ssl_verify_client, ssl_verify_depth и - ssl_prefer_server_cipher могли работать некорректно, если - использовался SNI. - - -Изменения в nginx 1.0.8 01.10.2011 + *) Исправление: при использовании директивы "ssl_session_cache builtin" + происходил segmentation fault; ошибка появилась в 1.1.1. - *) Исправление: nginx не собирался с модулем ngx_http_mp4_module и без - параметра --with-debug. +Изменения в nginx 1.1.4 20.09.2011 -Изменения в nginx 1.0.7 30.09.2011 - - *) Изменение: теперь, если суммарный размер всех диапазонов больше - размера исходного ответа, то nginx возвращает только исходный ответ, - не обрабатывая диапазоны. + *) Добавление: модуль ngx_http_upstream_keepalive. - *) Добавление: директива max_ranges. + *) Добавление: директива proxy_http_version. - *) Добавление: модуль ngx_http_mp4_module. + *) Добавление: директива fastcgi_keep_conn. *) Добавление: директива worker_aio_requests. @@ -242,17 +426,58 @@ *) Исправление: в обработке ошибок при работе с Linux AIO. Спасибо Hagai Avrahami. + *) Исправление: уменьшено потребление памяти для долгоживущих запросов. + + *) Исправление: модуль ngx_http_mp4_module не поддерживал 64-битный + MP4-атом co64. + + +Изменения в nginx 1.1.3 14.09.2011 + + *) Добавление: модуль ngx_http_mp4_module. + *) Исправление: в Linux AIO, используемым совместно с open_file_cache. *) Исправление: open_file_cache не обновлял информацию о файле, если файл был изменён не атомарно. - *) Исправление: уменьшено потребление памяти для долгоживущих запросов. + *) Исправление: nginx не собирался на MacOSX 10.7. + + +Изменения в nginx 1.1.2 05.09.2011 + + *) Изменение: теперь, если суммарный размер всех диапазонов больше + размера исходного ответа, то nginx возвращает только исходный ответ, + не обрабатывая диапазоны. + + *) Добавление: директива max_ranges. + + *) Исправление: директивы ssl_verify_client, ssl_verify_depth и + ssl_prefer_server_cipher могли работать некорректно, если + использовался SNI. *) Исправление: в директивах proxy/fastcgi/scgi/ uwsgi_ignore_client_abort. - *) Исправление: nginx не собирался на MacOSX 10.7. + +Изменения в nginx 1.1.1 22.08.2011 + + *) Изменение: теперь загрузчик кэша за каждую итерацию либо обрабатывает + число файлов, указанное в параметре load_files, либо работает не + дольше времени, указанного в параметре loader_threshold. + + *) Изменение: SIGWINCH сигнал теперь работает только в режиме демона. + + *) Добавление: теперь разделяемые зоны и кэши используют семафоры POSIX + на Solaris. + Спасибо Денису Иванову. + + *) Добавление: теперь на NetBSD поддерживаются accept фильтры. + + *) Исправление: nginx не собирался на Linux 3.0. + + *) Исправление: в некоторых случаях nginx не использовал сжатие; ошибка + появилась в 1.1.0. *) Исправление: обработка тела запроса могла быть неверной, если клиент использовал pipelining. @@ -272,11 +497,18 @@ *) Исправление: при использовании большого количества backup-серверов в рабочем процессе мог произойти segmentation fault. + *) Исправление: при использовании директив fastcgi/scgi/uwsgi_param со + значениями, начинающимися со строки "HTTP_", в рабочем процессе мог + произойти segmentation fault; ошибка появилась в 0.8.40. + -Изменения в nginx 1.0.6 29.08.2011 +Изменения в nginx 1.1.0 01.08.2011 *) Добавление: уменьшение времени работы загрузчика кэша. + *) Добавление: параметры loader_files, loader_sleep и loader_threshold + директив proxy/fastcgi/scgi/uwsgi_cache_path. + *) Добавление: уменьшение времени загрузки конфигураций с большим количеством HTTPS серверов. @@ -284,32 +516,27 @@ Спасибо Adrian Kotelba. *) Добавление: директива lingering_close. - - *) Добавление: теперь разделяемые зоны и кэши используют семафоры POSIX - на Solaris. - Спасибо Денису Иванову. - - *) Исправление: nginx не собирался на Linux 3.0. - - *) Исправление: при использовании директив fastcgi/scgi/uwsgi_param со - значениями, начинающимися со строки "HTTP_", в рабочем процессе мог - произойти segmentation fault; ошибка появилась в 0.8.40. + Спасибо Максиму Дунину. *) Исправление: закрытия соединения для pipelined-запросов. + Спасибо Максиму Дунину. *) Исправление: nginx не запрещал сжатие при получении значения "gzip;q=0" в строке "Accept-Encoding" в заголовке запроса клиента. *) Исправление: таймаута при небуферизированном проксировании. + Спасибо Максиму Дунину. *) Исправление: утечки памяти при использовании переменных в директиве proxy_pass при работе с бэкендом по HTTPS. + Спасибо Максиму Дунину. *) Исправление: в проверке параметра директивы proxy_pass, заданного переменными. Спасибо Lanshun Zhou. *) Исправление: SSL не работал на QNX. + Спасибо Максиму Дунину. *) Исправление: SSL модули не собирались gcc 4.6 без параметра --with-debug. diff --git a/usr.sbin/nginx/auto/lib/pcre/conf b/usr.sbin/nginx/auto/lib/pcre/conf index d183079e93d..69dae0425e6 100644 --- a/usr.sbin/nginx/auto/lib/pcre/conf +++ b/usr.sbin/nginx/auto/lib/pcre/conf @@ -5,8 +5,6 @@ if [ $PCRE != NONE ]; then CORE_INCS="$CORE_INCS $PCRE" - CORE_DEPS="$CORE_DEPS $REGEX_DEPS" - CORE_SRCS="$CORE_SRCS $REGEX_SRCS" case "$NGX_CC_NAME" in @@ -82,6 +80,12 @@ if [ $PCRE != NONE ]; then esac + + if [ $PCRE_JIT = YES ]; then + have=NGX_HAVE_PCRE_JIT . auto/have + PCRE_CONF_OPT="$PCRE_CONF_OPT --enable-jit" + fi + else if [ "$NGX_PLATFORM" != win32 -a `uname -s` != OpenBSD ]; then @@ -94,7 +98,9 @@ else ngx_feature_incs="#include <pcre.h>" ngx_feature_path= ngx_feature_libs="-lpcre" - ngx_feature_test="pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL)" + ngx_feature_test="pcre *re; + re = pcre_compile(NULL, 0, NULL, 0, NULL); + if (re == NULL) return 1" . auto/feature if [ $ngx_found = no ]; then @@ -157,16 +163,27 @@ else fi if [ $ngx_found = yes ]; then - CORE_DEPS="$CORE_DEPS $REGEX_DEPS" - CORE_SRCS="$CORE_SRCS $REGEX_SRCS" CORE_INCS="$CORE_INCS $ngx_feature_path" CORE_LIBS="$CORE_LIBS $ngx_feature_libs" PCRE=YES fi + + if [ $PCRE = YES ]; then + ngx_feature="PCRE JIT support" + ngx_feature_name="NGX_HAVE_PCRE_JIT" + ngx_feature_test="int jit = 0; + pcre_config(PCRE_CONFIG_JIT, &jit); + if (jit != 1) return 1;" + . auto/feature + + if [ $ngx_found = yes ]; then + PCRE_JIT=YES + fi + fi fi have=NGX_PCRE . auto/have - CORE_SRCS="$CORE_SRCS $PCRE_SRCS $REGEX_SRCS" + CORE_SRCS="$CORE_SRCS $PCRE_SRCS" CORE_INCS="$CORE_INCS src/pcre" CFLAGS="$CFLAGS -DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10" PCRE=YES diff --git a/usr.sbin/nginx/auto/lib/pcre/make b/usr.sbin/nginx/auto/lib/pcre/make index 1695b934073..834779b9cce 100644 --- a/usr.sbin/nginx/auto/lib/pcre/make +++ b/usr.sbin/nginx/auto/lib/pcre/make @@ -51,7 +51,7 @@ $PCRE/Makefile: $NGX_MAKEFILE cd $PCRE \\ && if [ -f Makefile ]; then \$(MAKE) distclean; fi \\ && CC="\$(CC)" CFLAGS="$PCRE_OPT" \\ - ./configure --disable-shared + ./configure --disable-shared $PCRE_CONF_OPT $PCRE/.libs/libpcre.a: $PCRE/Makefile cd $PCRE \\ diff --git a/usr.sbin/nginx/auto/modules b/usr.sbin/nginx/auto/modules index f8d31a3833e..dab660f8733 100644 --- a/usr.sbin/nginx/auto/modules +++ b/usr.sbin/nginx/auto/modules @@ -211,9 +211,9 @@ if [ $HTTP_ACCESS = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_ACCESS_SRCS" fi -if [ $HTTP_LIMIT_ZONE = YES ]; then - HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_ZONE_MODULE" - HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_ZONE_SRCS" +if [ $HTTP_LIMIT_CONN = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_CONN_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_CONN_SRCS" fi if [ $HTTP_LIMIT_REQ = YES ]; then @@ -345,6 +345,11 @@ if [ $HTTP_UPSTREAM_IP_HASH = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_IP_HASH_SRCS" fi +if [ $HTTP_UPSTREAM_KEEPALIVE = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_UPSTREAM_KEEPALIVE_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_KEEPALIVE_SRCS" +fi + if [ $HTTP_STUB_STATUS = YES ]; then have=NGX_STAT_STUB . auto/have HTTP_MODULES="$HTTP_MODULES ngx_http_stub_status_module" @@ -392,6 +397,12 @@ if [ $USE_OPENSSL = YES ]; then CORE_SRCS="$CORE_SRCS $OPENSSL_SRCS" fi +if [ $USE_PCRE = YES ]; then + modules="$modules $REGEX_MODULE" + CORE_DEPS="$CORE_DEPS $REGEX_DEPS" + CORE_SRCS="$CORE_SRCS $REGEX_SRCS" +fi + if [ $HTTP = YES ]; then modules="$modules $HTTP_MODULES $HTTP_FILTER_MODULES \ $HTTP_HEADERS_FILTER_MODULE \ diff --git a/usr.sbin/nginx/auto/options b/usr.sbin/nginx/auto/options index 6eea8c4a8b8..393be406616 100644 --- a/usr.sbin/nginx/auto/options +++ b/usr.sbin/nginx/auto/options @@ -86,7 +86,7 @@ HTTP_UWSGI=YES HTTP_SCGI=YES HTTP_PERL=NO HTTP_MEMCACHED=YES -HTTP_LIMIT_ZONE=YES +HTTP_LIMIT_CONN=YES HTTP_LIMIT_REQ=YES HTTP_EMPTY_GIF=YES HTTP_BROWSER=YES @@ -96,6 +96,7 @@ HTTP_FLV=NO HTTP_MP4=NO HTTP_GZIP_STATIC=NO HTTP_UPSTREAM_IP_HASH=YES +HTTP_UPSTREAM_KEEPALIVE=YES # STUB HTTP_STUB_STATUS=NO @@ -111,6 +112,8 @@ NGX_ADDONS= USE_PCRE=NO PCRE=NONE PCRE_OPT= +PCRE_CONF_OPT= +PCRE_JIT=NO USE_OPENSSL=NO OPENSSL=NONE @@ -143,6 +146,8 @@ NGX_LIBATOMIC=NO NGX_CPU_CACHE_LINE= +NGX_POST_CONF_MSG= + opt= for option @@ -227,11 +232,18 @@ do --without-http_uwsgi_module) HTTP_UWSGI=NO ;; --without-http_scgi_module) HTTP_SCGI=NO ;; --without-http_memcached_module) HTTP_MEMCACHED=NO ;; - --without-http_limit_zone_module) HTTP_LIMIT_ZONE=NO ;; + --without-http_limit_zone_module) + HTTP_LIMIT_CONN=NO + NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG +$0: warning: the \"--without-http_limit_zone_module\" option is deprecated, \ +use the \"--without-http_limit_conn_module\" option instead" + ;; + --without-http_limit_conn_module) HTTP_LIMIT_CONN=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 ;; + --without-http_upstream_keepalive_module) HTTP_UPSTREAM_KEEPALIVE=NO ;; --with-http_perl_module) HTTP_PERL=YES ;; --with-perl_modules_path=*) NGX_PERL_MODULES="$value" ;; @@ -265,6 +277,7 @@ do --with-pcre) USE_PCRE=YES ;; --with-pcre=*) PCRE="$value" ;; --with-pcre-opt=*) PCRE_OPT="$value" ;; + --with-pcre-jit) PCRE_JIT=YES ;; --with-openssl=*) OPENSSL="$value" ;; --with-openssl-opt=*) OPENSSL_OPT="$value" ;; @@ -363,7 +376,7 @@ cat << END --without-http_uwsgi_module disable ngx_http_uwsgi_module --without-http_scgi_module disable ngx_http_scgi_module --without-http_memcached_module disable ngx_http_memcached_module - --without-http_limit_zone_module disable ngx_http_limit_zone_module + --without-http_limit_conn_module disable ngx_http_limit_conn_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 @@ -412,6 +425,7 @@ cat << END --with-pcre force PCRE library usage --with-pcre=DIR set path to PCRE library sources --with-pcre-opt=OPTIONS set additional build options for PCRE + --with-pcre-jit build PCRE with JIT compilation support --with-md5=DIR set path to md5 library sources --with-md5-opt=OPTIONS set additional build options for md5 diff --git a/usr.sbin/nginx/auto/os/freebsd b/usr.sbin/nginx/auto/os/freebsd index 88654ba60fb..6aa823f9250 100644 --- a/usr.sbin/nginx/auto/os/freebsd +++ b/usr.sbin/nginx/auto/os/freebsd @@ -134,3 +134,11 @@ END exit 1 fi fi + + +# cpuset_setaffinity() + +if [ $version -ge 701000 ]; then + echo " + cpuset_setaffinity() found" + have=NGX_HAVE_CPUSET_SETAFFINITY . auto/have +fi diff --git a/usr.sbin/nginx/auto/os/linux b/usr.sbin/nginx/auto/os/linux index 0b2173cae9c..c506d3dc3ad 100644 --- a/usr.sbin/nginx/auto/os/linux +++ b/usr.sbin/nginx/auto/os/linux @@ -52,7 +52,7 @@ ngx_feature_run=yes ngx_feature_incs="#include <sys/epoll.h>" ngx_feature_path= ngx_feature_libs= -ngx_feature_test="int efd = 0, fd = 1, n; +ngx_feature_test="int efd = 0; struct epoll_event ee; ee.events = EPOLLIN|EPOLLOUT|EPOLLET; ee.data.ptr = NULL; @@ -128,8 +128,9 @@ ngx_feature_run=no ngx_feature_incs="#include <sched.h>" ngx_feature_path= ngx_feature_libs= -ngx_feature_test="long mask = 0; - sched_setaffinity(0, 32, (cpu_set_t *) &mask)" +ngx_feature_test="cpu_set_t mask; + CPU_ZERO(&mask); + sched_setaffinity(0, sizeof(cpu_set_t), &mask)" . auto/feature @@ -142,7 +143,7 @@ ngx_feature_incs="#include <crypt.h>" ngx_feature_path= ngx_feature_libs=-lcrypt ngx_feature_test="struct crypt_data cd; - crypt_r(NULL, NULL, &cd);" + crypt_r(\"key\", \"salt\", &cd);" . auto/feature diff --git a/usr.sbin/nginx/auto/os/solaris b/usr.sbin/nginx/auto/os/solaris index 16da4b3fd31..d39df0bf713 100644 --- a/usr.sbin/nginx/auto/os/solaris +++ b/usr.sbin/nginx/auto/os/solaris @@ -35,7 +35,8 @@ ngx_feature_path= ngx_feature_libs="-lsendfile" ngx_feature_test="int fd = 1; sendfilevec_t vec[1]; size_t sent; ssize_t n; - n = sendfilev(fd, vec, 1, &sent)" + n = sendfilev(fd, vec, 1, &sent); + if (n == -1) return 1" . auto/feature diff --git a/usr.sbin/nginx/auto/sources b/usr.sbin/nginx/auto/sources index 2036776cf3e..d95f3badffe 100644 --- a/usr.sbin/nginx/auto/sources +++ b/usr.sbin/nginx/auto/sources @@ -70,6 +70,7 @@ CORE_SRCS="src/core/nginx.c \ src/core/ngx_crypt.c" +REGEX_MODULE=ngx_regex_module REGEX_DEPS=src/core/ngx_regex.h REGEX_SRCS=src/core/ngx_regex.c @@ -80,6 +81,7 @@ PCRE_SRCS="src/pcre/pcre_chartables.c \ src/pcre/pcre_globals.c \ src/pcre/pcre_newline.c \ src/pcre/pcre_ord2utf8.c \ + src/pcre/pcre_study.c \ src/pcre/pcre_tables.c \ src/pcre/pcre_ucd.c \ src/pcre/pcre_valid_utf8.c \ @@ -155,6 +157,7 @@ UNIX_DEPS="$CORE_DEPS $EVENT_DEPS \ src/os/unix/ngx_channel.h \ src/os/unix/ngx_shmem.h \ src/os/unix/ngx_process.h \ + src/os/unix/ngx_setaffinity.h \ src/os/unix/ngx_setproctitle.h \ src/os/unix/ngx_atomic.h \ src/os/unix/ngx_gcc_atomic_x86.h \ @@ -189,6 +192,7 @@ UNIX_SRCS="$CORE_SRCS $EVENT_SRCS \ src/os/unix/ngx_shmem.c \ src/os/unix/ngx_process.c \ src/os/unix/ngx_daemon.c \ + src/os/unix/ngx_setaffinity.c \ src/os/unix/ngx_setproctitle.c \ src/os/unix/ngx_posix_init.c \ src/os/unix/ngx_user.c \ @@ -447,8 +451,8 @@ HTTP_MEMCACHED_MODULE=ngx_http_memcached_module HTTP_MEMCACHED_SRCS=src/http/modules/ngx_http_memcached_module.c -HTTP_LIMIT_ZONE_MODULE=ngx_http_limit_zone_module -HTTP_LIMIT_ZONE_SRCS=src/http/modules/ngx_http_limit_zone_module.c +HTTP_LIMIT_CONN_MODULE=ngx_http_limit_conn_module +HTTP_LIMIT_CONN_SRCS=src/http/modules/ngx_http_limit_conn_module.c HTTP_LIMIT_REQ_MODULE=ngx_http_limit_req_module @@ -487,6 +491,11 @@ HTTP_UPSTREAM_IP_HASH_MODULE=ngx_http_upstream_ip_hash_module HTTP_UPSTREAM_IP_HASH_SRCS=src/http/modules/ngx_http_upstream_ip_hash_module.c +HTTP_UPSTREAM_KEEPALIVE_MODULE=ngx_http_upstream_keepalive_module +HTTP_UPSTREAM_KEEPALIVE_SRCS=" \ + src/http/modules/ngx_http_upstream_keepalive_module.c" + + MAIL_INCS="src/mail" MAIL_DEPS="src/mail/ngx_mail.h" diff --git a/usr.sbin/nginx/auto/summary b/usr.sbin/nginx/auto/summary index f7f32a442a4..dcebec9f0fe 100644 --- a/usr.sbin/nginx/auto/summary +++ b/usr.sbin/nginx/auto/summary @@ -112,3 +112,5 @@ fi if [ $HTTP_SCGI = YES ]; then echo " nginx http scgi temporary files: \"$NGX_HTTP_SCGI_TEMP_PATH\"" fi + +echo "$NGX_POST_CONF_MSG" diff --git a/usr.sbin/nginx/auto/types/sizeof b/usr.sbin/nginx/auto/types/sizeof index e84a0905ef9..e1d405c6500 100644 --- a/usr.sbin/nginx/auto/types/sizeof +++ b/usr.sbin/nginx/auto/types/sizeof @@ -20,12 +20,13 @@ cat << END > $NGX_AUTOTEST.c #include <sys/time.h> $NGX_INCLUDE_UNISTD_H #include <signal.h> +#include <stdio.h> #include <sys/resource.h> $NGX_INCLUDE_INTTYPES_H $NGX_INCLUDE_AUTO_CONFIG_H int main() { - printf("%d", sizeof($ngx_type)); + printf("%d", (int) sizeof($ngx_type)); return 0; } diff --git a/usr.sbin/nginx/auto/types/typedef b/usr.sbin/nginx/auto/types/typedef index f121e8fc2ea..d81229331fd 100644 --- a/usr.sbin/nginx/auto/types/typedef +++ b/usr.sbin/nginx/auto/types/typedef @@ -29,7 +29,7 @@ $NGX_INCLUDE_INTTYPES_H int main() { $ngx_try i = 0; - return 0; + return (int) i; } END diff --git a/usr.sbin/nginx/auto/types/uintptr_t b/usr.sbin/nginx/auto/types/uintptr_t index 07f7e7d7dba..2f19080352b 100644 --- a/usr.sbin/nginx/auto/types/uintptr_t +++ b/usr.sbin/nginx/auto/types/uintptr_t @@ -4,8 +4,8 @@ echo $ngx_n "checking for uintptr_t ...$ngx_c" -echo >> $NGX_ERR -echo "checking for uintptr_t" >> $NGX_ERR +echo >> $NGX_AUTOCONF_ERR +echo "checking for uintptr_t" >> $NGX_AUTOCONF_ERR found=no @@ -16,12 +16,15 @@ $NGX_INTTYPES_H int main() { uintptr_t i = 0; - return 0; + return (int) i; } END -eval "$CC -o $NGX_AUTOTEST $NGX_AUTOTEST.c >> $NGX_ERR 2>&1" +ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \ + -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT" + +eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1" if [ -x $NGX_AUTOTEST ]; then echo " uintptr_t found" diff --git a/usr.sbin/nginx/auto/unix b/usr.sbin/nginx/auto/unix index 78f82593d2f..783219e2db7 100755 --- a/usr.sbin/nginx/auto/unix +++ b/usr.sbin/nginx/auto/unix @@ -33,12 +33,12 @@ ngx_feature_run=no ngx_feature_incs="#include <poll.h>" ngx_feature_path= ngx_feature_libs= -ngx_feature_test="int n, dp; struct pollfd pl; - dp = 0; +ngx_feature_test="int n; struct pollfd pl; pl.fd = 0; pl.events = 0; pl.revents = 0; - n = poll(&pl, 1, 0)" + n = poll(&pl, 1, 0); + if (n == -1) return 1" . auto/feature if [ $ngx_found = no ]; then @@ -57,7 +57,8 @@ ngx_feature_test="int n, dp; struct dvpoll dvp; dvp.dp_fds = NULL; dvp.dp_nfds = 0; dvp.dp_timeout = 0; - n = ioctl(dp, DP_POLL, &dvp)" + n = ioctl(dp, DP_POLL, &dvp); + if (n == -1) return 1" . auto/feature if [ $ngx_found = yes ]; then @@ -237,7 +238,7 @@ ngx_feature_incs="$NGX_INCLUDE_SYS_PARAM_H ngx_feature_path= ngx_feature_libs= ngx_feature_test="struct statfs fs; - statfs(NULL, &fs);" + statfs(\".\", &fs);" . auto/feature @@ -249,7 +250,7 @@ ngx_feature_incs="#include <sys/types.h> ngx_feature_path= ngx_feature_libs= ngx_feature_test="struct statvfs fs; - statvfs(NULL, &fs);" + statvfs(\".\", &fs);" . auto/feature @@ -329,6 +330,38 @@ ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_DEFER_ACCEPT, NULL, 0)" . auto/feature +ngx_feature="TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT" +ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE" +ngx_feature_run=no +ngx_feature_incs="#include <sys/socket.h> + #include <netinet/in.h> + #include <netinet/tcp.h>" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPIDLE, NULL, 0); + setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0); + setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)" +. auto/feature + + +ngx_feature="TCP_INFO" +ngx_feature_name="NGX_HAVE_TCP_INFO" +ngx_feature_run=no +ngx_feature_incs="#include <sys/socket.h> + #include <netinet/in.h> + #include <netinet/tcp.h>" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="socklen_t optlen = sizeof(struct tcp_info); + struct tcp_info ti; + ti.tcpi_rtt = 0; + ti.tcpi_rttvar = 0; + ti.tcpi_snd_cwnd = 0; + ti.tcpi_rcv_space = 0; + getsockopt(0, IPPROTO_TCP, TCP_INFO, &ti, &optlen)" +. auto/feature + + ngx_feature="accept4()" ngx_feature_name="NGX_HAVE_ACCEPT4" ngx_feature_run=no @@ -467,7 +500,7 @@ fi ngx_feature="setproctitle()" ngx_feature_name="NGX_HAVE_SETPROCTITLE" ngx_feature_run=no -ngx_feature_incs= +ngx_feature_incs="#include <stdlib.h>" ngx_feature_path= ngx_feature_libs=$NGX_SETPROCTITLE_LIB ngx_feature_test="setproctitle(\"test\");" @@ -480,7 +513,8 @@ ngx_feature_run=no ngx_feature_incs= ngx_feature_path= ngx_feature_libs= -ngx_feature_test="char buf[1]; ssize_t n; n = pread(0, buf, 1, 0)" +ngx_feature_test="char buf[1]; ssize_t n; n = pread(0, buf, 1, 0); + if (n == -1) return 1" . auto/feature @@ -490,7 +524,8 @@ ngx_feature_run=no ngx_feature_incs= ngx_feature_path= ngx_feature_libs= -ngx_feature_test="char buf[1]; ssize_t n; n = pwrite(1, buf, 1, 0)" +ngx_feature_test="char buf[1]; ssize_t n; n = pwrite(1, buf, 1, 0); + if (n == -1) return 1" . auto/feature @@ -564,17 +599,20 @@ ngx_feature_run=no ngx_feature_incs="#include <stdlib.h>" ngx_feature_path= ngx_feature_libs= -ngx_feature_test="void *p; int n; n = posix_memalign(&p, 4096, 4096)" +ngx_feature_test="void *p; int n; n = posix_memalign(&p, 4096, 4096); + if (n != 0) return 1" . auto/feature ngx_feature="memalign()" ngx_feature_name="NGX_HAVE_MEMALIGN" ngx_feature_run=no -ngx_feature_incs="#include <stdlib.h>" +ngx_feature_incs="#include <stdlib.h> + #include <malloc.h>" ngx_feature_path= ngx_feature_libs= -ngx_feature_test="void *p; p = memalign(4096, 4096)" +ngx_feature_test="void *p; p = memalign(4096, 4096); + if (p == NULL) return 1" . auto/feature @@ -661,10 +699,12 @@ fi ngx_feature="struct msghdr.msg_control" ngx_feature_name="NGX_HAVE_MSGHDR_MSG_CONTROL" ngx_feature_run=no -ngx_feature_incs="#include <sys/socket.h>" +ngx_feature_incs="#include <sys/socket.h> + #include <stdio.h>" ngx_feature_path= ngx_feature_libs= -ngx_feature_test="struct msghdr msg; msg.msg_control = NULL" +ngx_feature_test="struct msghdr msg; + printf(\"%d\", (int) sizeof(msg.msg_control))" . auto/feature @@ -672,40 +712,47 @@ ngx_feature="ioctl(FIONBIO)" ngx_feature_name="NGX_HAVE_FIONBIO" ngx_feature_run=no ngx_feature_incs="#include <sys/ioctl.h> + #include <stdio.h> $NGX_INCLUDE_SYS_FILIO_H" ngx_feature_path= ngx_feature_libs= -ngx_feature_test="int i; i = FIONBIO" +ngx_feature_test="int i = FIONBIO; printf(\"%d\", i)" . auto/feature ngx_feature="struct tm.tm_gmtoff" ngx_feature_name="NGX_HAVE_GMTOFF" ngx_feature_run=no -ngx_feature_incs="#include <time.h>" +ngx_feature_incs="#include <time.h> + #include <stdio.h>" ngx_feature_path= ngx_feature_libs= -ngx_feature_test="struct tm tm; tm.tm_gmtoff = 0" +ngx_feature_test="struct tm tm; tm.tm_gmtoff = 0; + printf(\"%d\", (int) tm.tm_gmtoff)" . auto/feature ngx_feature="struct dirent.d_namlen" ngx_feature_name="NGX_HAVE_D_NAMLEN" ngx_feature_run=no -ngx_feature_incs="#include <dirent.h>" +ngx_feature_incs="#include <dirent.h> + #include <stdio.h>" ngx_feature_path= ngx_feature_libs= -ngx_feature_test="struct dirent dir; dir.d_namlen = 0" +ngx_feature_test="struct dirent dir; dir.d_namlen = 0; + printf(\"%d\", (int) dir.d_namlen)" . auto/feature ngx_feature="struct dirent.d_type" ngx_feature_name="NGX_HAVE_D_TYPE" ngx_feature_run=no -ngx_feature_incs="#include <dirent.h>" +ngx_feature_incs="#include <dirent.h> + #include <stdio.h>" ngx_feature_path= ngx_feature_libs= -ngx_feature_test="struct dirent dir; dir.d_type = DT_REG" +ngx_feature_test="struct dirent dir; dir.d_type = DT_REG; + printf(\"%d\", (int) dir.d_type)" . auto/feature @@ -717,3 +764,17 @@ ngx_feature_path= ngx_feature_libs= ngx_feature_test="sysconf(_SC_NPROCESSORS_ONLN)" . auto/feature + + +ngx_feature="openat(), fstatat()" +ngx_feature_name="NGX_HAVE_OPENAT" +ngx_feature_run=no +ngx_feature_incs="#include <sys/types.h> + #include <sys/stat.h> + #include <fcntl.h>" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="struct stat sb; + openat(AT_FDCWD, \".\", O_RDONLY|O_NOFOLLOW); + fstatat(AT_FDCWD, \".\", &sb, AT_SYMLINK_NOFOLLOW);" +. auto/feature diff --git a/usr.sbin/nginx/conf/fastcgi.conf b/usr.sbin/nginx/conf/fastcgi.conf index fab09eb2455..ac9ff92049f 100644 --- a/usr.sbin/nginx/conf/fastcgi.conf +++ b/usr.sbin/nginx/conf/fastcgi.conf @@ -10,6 +10,7 @@ fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param HTTPS $https if_not_empty; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; diff --git a/usr.sbin/nginx/conf/fastcgi_params b/usr.sbin/nginx/conf/fastcgi_params index 201c686e251..71e2c2e3bed 100644 --- a/usr.sbin/nginx/conf/fastcgi_params +++ b/usr.sbin/nginx/conf/fastcgi_params @@ -9,6 +9,7 @@ fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param HTTPS $https if_not_empty; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; diff --git a/usr.sbin/nginx/conf/scgi_params b/usr.sbin/nginx/conf/scgi_params index 257167eb7b3..47348ca381c 100644 --- a/usr.sbin/nginx/conf/scgi_params +++ b/usr.sbin/nginx/conf/scgi_params @@ -8,6 +8,7 @@ scgi_param DOCUMENT_URI $document_uri; scgi_param DOCUMENT_ROOT $document_root; scgi_param SCGI 1; scgi_param SERVER_PROTOCOL $server_protocol; +scgi_param HTTPS $https if_not_empty; scgi_param REMOTE_ADDR $remote_addr; scgi_param REMOTE_PORT $remote_port; diff --git a/usr.sbin/nginx/conf/uwsgi_params b/usr.sbin/nginx/conf/uwsgi_params index b57e8e78ea2..f539451b6f5 100644 --- a/usr.sbin/nginx/conf/uwsgi_params +++ b/usr.sbin/nginx/conf/uwsgi_params @@ -8,6 +8,7 @@ uwsgi_param REQUEST_URI $request_uri; uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; +uwsgi_param HTTPS $https if_not_empty; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; diff --git a/usr.sbin/nginx/man/nginx.8 b/usr.sbin/nginx/man/nginx.8 index 9d7fc1e9254..229dca91613 100644 --- a/usr.sbin/nginx/man/nginx.8 +++ b/usr.sbin/nginx/man/nginx.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: nginx.8,v 1.5 2012/04/05 10:28:03 sthen Exp $ +.\" $OpenBSD: nginx.8,v 1.6 2012/05/13 09:14:58 robert Exp $ .\" .\" Copyright (c) 2010 Sergey A. Osokin .\" Copyright (c) 2011,2012 Nginx, Inc. @@ -26,7 +26,7 @@ .\" SUCH DAMAGE. .\" .\" -.Dd $Mdocdate: April 5 2012 $ +.Dd $Mdocdate: May 13 2012 $ .Dt NGINX 8 .Os .Sh NAME @@ -195,9 +195,7 @@ nginx -t -c ~/mynginx.conf \e .\"Xr nginx.conf 5 .\"Pp Documentation at -.Pa http://nginx.org/ -and -.Pa http://sysoev.ru/nginx/ . +.Pa http://nginx.org/en/docs/ . .Pp For questions and technical support, please refer to .Pa http://nginx.org/en/support.html . diff --git a/usr.sbin/nginx/src/core/nginx.c b/usr.sbin/nginx/src/core/nginx.c index bb539e55a13..a59de2dbedf 100644 --- a/usr.sbin/nginx/src/core/nginx.c +++ b/usr.sbin/nginx/src/core/nginx.c @@ -204,9 +204,7 @@ main(int argc, char *const *argv) ngx_cycle_t *cycle, init_cycle; ngx_core_conf_t *ccf; -#if (NGX_FREEBSD) ngx_debug_init(); -#endif if (ngx_strerror_init() != NGX_OK) { return 1; @@ -653,7 +651,7 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv) if (ngx_rename_file(ccf->oldpid.data, ccf->pid.data) != NGX_OK) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, ngx_rename_file_n " %s back to %s failed after " - "the try to execute the new binary process \"%s\"", + "an attempt to execute new binary process \"%s\"", ccf->oldpid.data, ccf->pid.data, argv[0]); } } @@ -991,15 +989,15 @@ ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf) ngx_conf_init_value(ccf->worker_processes, 1); ngx_conf_init_value(ccf->debug_points, 0); -#if (NGX_HAVE_SCHED_SETAFFINITY) +#if (NGX_HAVE_CPU_AFFINITY) if (ccf->cpu_affinity_n && ccf->cpu_affinity_n != 1 && ccf->cpu_affinity_n != (ngx_uint_t) ccf->worker_processes) { ngx_log_error(NGX_LOG_WARN, cycle->log, 0, - "number of the \"worker_processes\" is not equal to " - "the number of the \"worker_cpu_affinity\" mask, " + "the number of \"worker_processes\" is not equal to " + "the number of \"worker_cpu_affinity\" masks, " "using last mask for remaining worker processes"); } @@ -1250,11 +1248,11 @@ ngx_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) static char * ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { -#if (NGX_HAVE_SCHED_SETAFFINITY) +#if (NGX_HAVE_CPU_AFFINITY) ngx_core_conf_t *ccf = conf; u_char ch; - u_long *mask; + uint64_t *mask; ngx_str_t *value; ngx_uint_t i, n; @@ -1262,7 +1260,7 @@ ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return "is duplicate"; } - mask = ngx_palloc(cf->pool, (cf->args->nelts - 1) * sizeof(long)); + mask = ngx_palloc(cf->pool, (cf->args->nelts - 1) * sizeof(uint64_t)); if (mask == NULL) { return NGX_CONF_ERROR; } @@ -1274,9 +1272,9 @@ ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) for (n = 1; n < cf->args->nelts; n++) { - if (value[n].len > 32) { + if (value[n].len > 64) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"worker_cpu_affinity\" supports up to 32 CPU only"); + "\"worker_cpu_affinity\" supports up to 64 CPUs only"); return NGX_CONF_ERROR; } @@ -1319,7 +1317,7 @@ ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } -u_long +uint64_t ngx_get_cpu_affinity(ngx_uint_t n) { ngx_core_conf_t *ccf; diff --git a/usr.sbin/nginx/src/core/nginx.h b/usr.sbin/nginx/src/core/nginx.h index fdc2c925cd8..ca777f6d5ac 100644 --- a/usr.sbin/nginx/src/core/nginx.h +++ b/usr.sbin/nginx/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1000015 -#define NGINX_VERSION "1.0.15" +#define nginx_version 1002000 +#define NGINX_VERSION "1.2.0" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/usr.sbin/nginx/src/core/ngx_buf.c b/usr.sbin/nginx/src/core/ngx_buf.c index 817a96cda7c..835c76ced6b 100644 --- a/usr.sbin/nginx/src/core/ngx_buf.c +++ b/usr.sbin/nginx/src/core/ngx_buf.c @@ -181,7 +181,7 @@ ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free) void -ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy, +ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, ngx_chain_t **busy, ngx_chain_t **out, ngx_buf_tag_t tag) { ngx_chain_t *cl; @@ -198,19 +198,21 @@ ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy, *out = NULL; while (*busy) { - if (ngx_buf_size((*busy)->buf) != 0) { + cl = *busy; + + if (ngx_buf_size(cl->buf) != 0) { break; } - if ((*busy)->buf->tag != tag) { - *busy = (*busy)->next; + if (cl->buf->tag != tag) { + *busy = cl->next; + ngx_free_chain(p, cl); continue; } - (*busy)->buf->pos = (*busy)->buf->start; - (*busy)->buf->last = (*busy)->buf->start; + cl->buf->pos = cl->buf->start; + cl->buf->last = cl->buf->start; - cl = *busy; *busy = cl->next; cl->next = *free; *free = cl; diff --git a/usr.sbin/nginx/src/core/ngx_buf.h b/usr.sbin/nginx/src/core/ngx_buf.h index 39f31b34f2c..ffc53109433 100644 --- a/usr.sbin/nginx/src/core/ngx_buf.h +++ b/usr.sbin/nginx/src/core/ngx_buf.h @@ -155,8 +155,8 @@ ngx_int_t ngx_chain_writer(void *ctx, ngx_chain_t *in); ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in); ngx_chain_t *ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free); -void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy, - ngx_chain_t **out, ngx_buf_tag_t tag); +void ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, + ngx_chain_t **busy, ngx_chain_t **out, ngx_buf_tag_t tag); #endif /* _NGX_BUF_H_INCLUDED_ */ diff --git a/usr.sbin/nginx/src/core/ngx_conf_file.c b/usr.sbin/nginx/src/core/ngx_conf_file.c index 83265014127..892fa4719a8 100644 --- a/usr.sbin/nginx/src/core/ngx_conf_file.c +++ b/usr.sbin/nginx/src/core/ngx_conf_file.c @@ -465,7 +465,7 @@ ngx_conf_read_token(ngx_conf_t *cf) if (cf->conf_file->file.offset >= file_size) { - if (cf->args->nelts > 0) { + if (cf->args->nelts > 0 || !last_space) { if (cf->conf_file->file.fd == NGX_INVALID_FILE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -1295,10 +1295,6 @@ ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return "invalid value"; } - if (*msp == (ngx_msec_t) NGX_PARSE_LARGE_TIME) { - return "value must be less than 597 hours"; - } - if (cmd->post) { post = cmd->post; return post->post_handler(cf, post, msp); @@ -1326,14 +1322,10 @@ ngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) value = cf->args->elts; *sp = ngx_parse_time(&value[1], 1); - if (*sp == NGX_ERROR) { + if (*sp == (time_t) NGX_ERROR) { return "invalid value"; } - if (*sp == NGX_PARSE_LARGE_TIME) { - return "value must be less than 68 years"; - } - if (cmd->post) { post = cmd->post; return post->post_handler(cf, post, sp); @@ -1489,7 +1481,8 @@ ngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data) } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "value must be equal or more than %i", bounds->low); + "value must be equal to or greater than %i", + bounds->low); return NGX_CONF_ERROR; } diff --git a/usr.sbin/nginx/src/core/ngx_conf_file.h b/usr.sbin/nginx/src/core/ngx_conf_file.h index e92668cd465..4b87f1aad48 100644 --- a/usr.sbin/nginx/src/core/ngx_conf_file.h +++ b/usr.sbin/nginx/src/core/ngx_conf_file.h @@ -14,7 +14,7 @@ /* - * AAAA number of agruments + * AAAA number of arguments * FF command flags * TT command type, i.e. HTTP "location" or "server" command */ diff --git a/usr.sbin/nginx/src/core/ngx_connection.c b/usr.sbin/nginx/src/core/ngx_connection.c index b16c5572086..ba1b3f9e2bc 100644 --- a/usr.sbin/nginx/src/core/ngx_connection.c +++ b/usr.sbin/nginx/src/core/ngx_connection.c @@ -463,6 +463,7 @@ ngx_open_listening_sockets(ngx_cycle_t *cycle) void ngx_configure_listening_sockets(ngx_cycle_t *cycle) { + int keepalive; ngx_uint_t i; ngx_listening_t *ls; @@ -500,6 +501,56 @@ ngx_configure_listening_sockets(ngx_cycle_t *cycle) } } + if (ls[i].keepalive) { + keepalive = (ls[i].keepalive == 1) ? 1 : 0; + + if (setsockopt(ls[i].fd, SOL_SOCKET, SO_KEEPALIVE, + (const void *) &keepalive, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(SO_KEEPALIVE, %d) %V failed, ignored", + keepalive, &ls[i].addr_text); + } + } + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + + if (ls[i].keepidle) { + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPIDLE, + (const void *) &ls[i].keepidle, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_KEEPIDLE, %d) %V failed, ignored", + ls[i].keepidle, &ls[i].addr_text); + } + } + + if (ls[i].keepintvl) { + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPINTVL, + (const void *) &ls[i].keepintvl, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_KEEPINTVL, %d) %V failed, ignored", + ls[i].keepintvl, &ls[i].addr_text); + } + } + + if (ls[i].keepcnt) { + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPCNT, + (const void *) &ls[i].keepcnt, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_KEEPCNT, %d) %V failed, ignored", + ls[i].keepcnt, &ls[i].addr_text); + } + } + +#endif + #if (NGX_HAVE_SETFIB) if (ls[i].setfib != -1) { if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB, @@ -657,7 +708,7 @@ ngx_close_listening_sockets(ngx_cycle_t *cycle) /* * it seems that Linux-2.6.x OpenVZ sends events * for closed shared listening sockets unless - * the events was explicity deleted + * the events was explicitly deleted */ ngx_del_event(c->read, NGX_READ_EVENT, 0); diff --git a/usr.sbin/nginx/src/core/ngx_connection.h b/usr.sbin/nginx/src/core/ngx_connection.h index 928d661c0a5..34af12e4f34 100644 --- a/usr.sbin/nginx/src/core/ngx_connection.h +++ b/usr.sbin/nginx/src/core/ngx_connection.h @@ -28,6 +28,11 @@ struct ngx_listening_s { int backlog; int rcvbuf; int sndbuf; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + int keepidle; + int keepintvl; + int keepcnt; +#endif /* handler of accepted connection */ ngx_connection_handler_pt handler; @@ -61,6 +66,7 @@ struct ngx_listening_s { #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:2; #endif + unsigned keepalive:2; #if (NGX_HAVE_DEFERRED_ACCEPT) unsigned deferred_accept:1; diff --git a/usr.sbin/nginx/src/core/ngx_core.h b/usr.sbin/nginx/src/core/ngx_core.h index 1ae55163d53..435ce64e7bb 100644 --- a/usr.sbin/nginx/src/core/ngx_core.h +++ b/usr.sbin/nginx/src/core/ngx_core.h @@ -91,5 +91,10 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c); void ngx_cpuinfo(void); +#if (NGX_HAVE_OPENAT) +#define NGX_DISABLE_SYMLINKS_OFF 0 +#define NGX_DISABLE_SYMLINKS_ON 1 +#define NGX_DISABLE_SYMLINKS_NOTOWNER 2 +#endif #endif /* _NGX_CORE_H_INCLUDED_ */ diff --git a/usr.sbin/nginx/src/core/ngx_cycle.c b/usr.sbin/nginx/src/core/ngx_cycle.c index a706138203e..4b04d320c1c 100644 --- a/usr.sbin/nginx/src/core/ngx_cycle.c +++ b/usr.sbin/nginx/src/core/ngx_cycle.c @@ -953,7 +953,7 @@ ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn) #endif - if (ngx_shmtx_create(&sp->mutex, (void *) &sp->lock, file) != NGX_OK) { + if (ngx_shmtx_create(&sp->mutex, &sp->lock, file) != NGX_OK) { return NGX_ERROR; } diff --git a/usr.sbin/nginx/src/core/ngx_cycle.h b/usr.sbin/nginx/src/core/ngx_cycle.h index dbc6e195e93..551b6288ec0 100644 --- a/usr.sbin/nginx/src/core/ngx_cycle.h +++ b/usr.sbin/nginx/src/core/ngx_cycle.h @@ -86,7 +86,7 @@ typedef struct { int priority; ngx_uint_t cpu_affinity_n; - u_long *cpu_affinity; + uint64_t *cpu_affinity; char *username; ngx_uid_t user; @@ -124,7 +124,7 @@ ngx_int_t ngx_signal_process(ngx_cycle_t *cycle, char *sig); void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user); char **ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last); ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv); -u_long ngx_get_cpu_affinity(ngx_uint_t n); +uint64_t ngx_get_cpu_affinity(ngx_uint_t n); ngx_shm_zone_t *ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag); diff --git a/usr.sbin/nginx/src/core/ngx_file.c b/usr.sbin/nginx/src/core/ngx_file.c index 56d5a2aa75c..e37f5f57f1b 100644 --- a/usr.sbin/nginx/src/core/ngx_file.c +++ b/usr.sbin/nginx/src/core/ngx_file.c @@ -820,7 +820,7 @@ failed: * reallocated if ctx->alloc is nonzero * * ctx->alloc - a size of data structure that is allocated at every level - * and is initilialized by ctx->init_handler() + * and is initialized by ctx->init_handler() * * ctx->log - a log * diff --git a/usr.sbin/nginx/src/core/ngx_inet.c b/usr.sbin/nginx/src/core/ngx_inet.c index 2785c8c8cf6..d2bbbfb58bb 100644 --- a/usr.sbin/nginx/src/core/ngx_inet.c +++ b/usr.sbin/nginx/src/core/ngx_inet.c @@ -44,11 +44,7 @@ ngx_inet_addr(u_char *text, size_t len) return INADDR_NONE; } - if (n != 3) { - return INADDR_NONE; - } - - if (octet < 256) { + if (n == 3 && octet < 256) { addr = (addr << 8) + octet; return htonl(addr); } @@ -407,6 +403,10 @@ ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr) #if (NGX_HAVE_INET6) case AF_INET6: + if (shift > 128) { + return NGX_ERROR; + } + addr = cidr->u.in6.addr.s6_addr; mask = cidr->u.in6.mask.s6_addr; rc = NGX_OK; @@ -416,7 +416,7 @@ ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr) s = (shift > 8) ? 8 : shift; shift -= s; - mask[i] = (u_char) (0 - (1 << (8 - s))); + mask[i] = (u_char) (0xffu << (8 - s)); if (addr[i] != (addr[i] & mask[i])) { rc = NGX_DONE; @@ -428,9 +428,12 @@ ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr) #endif default: /* AF_INET */ + if (shift > 32) { + return NGX_ERROR; + } if (shift) { - cidr->u.in.mask = htonl((ngx_uint_t) (0 - (1 << (32 - shift)))); + cidr->u.in.mask = htonl((uint32_t) (0xffffffffu << (32 - shift))); } else { /* x86 compilers use a shl instruction that shifts by modulo 32 */ @@ -459,7 +462,7 @@ ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, size_t len) struct sockaddr_in6 *sin6; /* - * prevent MSVC8 waring: + * prevent MSVC8 warning: * potentially uninitialized local variable 'inaddr6' used */ ngx_memzero(inaddr6.s6_addr, sizeof(struct in6_addr)); diff --git a/usr.sbin/nginx/src/core/ngx_murmurhash.h b/usr.sbin/nginx/src/core/ngx_murmurhash.h index 2b2b70d6ccf..54e867d303a 100644 --- a/usr.sbin/nginx/src/core/ngx_murmurhash.h +++ b/usr.sbin/nginx/src/core/ngx_murmurhash.h @@ -16,4 +16,4 @@ uint32_t ngx_murmur_hash2(u_char *data, size_t len); -#endif /* _NGX_CRC_H_INCLUDED_ */ +#endif /* _NGX_MURMURHASH_H_INCLUDED_ */ diff --git a/usr.sbin/nginx/src/core/ngx_open_file_cache.c b/usr.sbin/nginx/src/core/ngx_open_file_cache.c index 9a98fccf233..c44ac96b8b7 100644 --- a/usr.sbin/nginx/src/core/ngx_open_file_cache.c +++ b/usr.sbin/nginx/src/core/ngx_open_file_cache.c @@ -22,8 +22,17 @@ static void ngx_open_file_cache_cleanup(void *data); -static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, - ngx_log_t *log); +#if (NGX_HAVE_OPENAT) +static ngx_fd_t ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name, + ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log); +#endif +static ngx_fd_t ngx_open_file_wrapper(ngx_str_t *name, + ngx_open_file_info_t *of, ngx_int_t mode, ngx_int_t create, + ngx_int_t access, ngx_log_t *log); +static ngx_int_t ngx_file_info_wrapper(ngx_str_t *name, + ngx_open_file_info_t *of, ngx_file_info_t *fi, ngx_log_t *log); +static ngx_int_t ngx_open_and_stat_file(ngx_str_t *name, + ngx_open_file_info_t *of, ngx_log_t *log); static void ngx_open_file_add_event(ngx_open_file_cache_t *cache, ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log); static void ngx_open_file_cleanup(void *data); @@ -147,9 +156,9 @@ ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, if (of->test_only) { - if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { - of->err = ngx_errno; - of->failed = ngx_file_info_n; + if (ngx_file_info_wrapper(name, of, &fi, pool->log) + == NGX_FILE_ERROR) + { return NGX_ERROR; } @@ -170,7 +179,7 @@ ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, return NGX_ERROR; } - rc = ngx_open_and_stat_file(name->data, of, pool->log); + rc = ngx_open_and_stat_file(name, of, pool->log); if (rc == NGX_OK && !of->is_dir) { cln->handler = ngx_pool_cleanup_file; @@ -205,7 +214,7 @@ ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, /* file was not used often enough to keep open */ - rc = ngx_open_and_stat_file(name->data, of, pool->log); + rc = ngx_open_and_stat_file(name, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; @@ -217,7 +226,12 @@ ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, if (file->use_event || (file->event == NULL && (of->uniq == 0 || of->uniq == file->uniq) - && now - file->created < of->valid)) + && now - file->created < of->valid +#if (NGX_HAVE_OPENAT) + && of->disable_symlinks == file->disable_symlinks + && of->disable_symlinks_from == file->disable_symlinks_from +#endif + )) { if (file->err == 0) { @@ -239,7 +253,12 @@ ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, } else { of->err = file->err; +#if (NGX_HAVE_OPENAT) + of->failed = file->disable_symlinks ? ngx_openat_file_n + : ngx_open_file_n; +#else of->failed = ngx_open_file_n; +#endif } goto found; @@ -263,7 +282,7 @@ ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, of->fd = file->fd; of->uniq = file->uniq; - rc = ngx_open_and_stat_file(name->data, of, pool->log); + rc = ngx_open_and_stat_file(name, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; @@ -311,8 +330,7 @@ ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", - name->data); + ngx_close_file_n " \"%V\" failed", name); } goto add_event; @@ -329,7 +347,7 @@ ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, /* not found */ - rc = ngx_open_and_stat_file(name->data, of, pool->log); + rc = ngx_open_and_stat_file(name, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; @@ -376,6 +394,10 @@ update: file->fd = of->fd; file->err = of->err; +#if (NGX_HAVE_OPENAT) + file->disable_symlinks = of->disable_symlinks; + file->disable_symlinks_from = of->disable_symlinks_from; +#endif if (of->err == 0) { file->uniq = of->uniq; @@ -452,7 +474,7 @@ failed: if (of->fd != NGX_INVALID_FILE) { if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", name->data); + ngx_close_file_n " \"%V\" failed", name); } } @@ -460,17 +482,306 @@ failed: } +#if (NGX_HAVE_OPENAT) + +static ngx_fd_t +ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name, + ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log) +{ + ngx_fd_t fd; + ngx_err_t err; + ngx_file_info_t fi, atfi; + + /* + * To allow symlinks with the same owner, use openat() (followed + * by fstat()) and fstatat(AT_SYMLINK_NOFOLLOW), and then compare + * uids between fstat() and fstatat(). + * + * As there is a race between openat() and fstatat() we don't + * know if openat() in fact opened symlink or not. Therefore, + * we have to compare uids even if fstatat() reports the opened + * component isn't a symlink (as we don't know whether it was + * symlink during openat() or not). + */ + + fd = ngx_openat_file(at_fd, name, mode, create, access); + + if (fd == NGX_INVALID_FILE) { + return NGX_INVALID_FILE; + } + + if (ngx_file_at_info(at_fd, name, &atfi, AT_SYMLINK_NOFOLLOW) + == NGX_FILE_ERROR) + { + err = ngx_errno; + goto failed; + } + + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { + err = ngx_errno; + goto failed; + } + + if (fi.st_uid != atfi.st_uid) { + err = NGX_ELOOP; + goto failed; + } + + return fd; + +failed: + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%V\" failed", name); + } + + ngx_set_errno(err); + + return NGX_INVALID_FILE; +} + +#endif + + +static ngx_fd_t +ngx_open_file_wrapper(ngx_str_t *name, ngx_open_file_info_t *of, + ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log) +{ + ngx_fd_t fd; + +#if !(NGX_HAVE_OPENAT) + + fd = ngx_open_file(name->data, mode, create, access); + + if (fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_open_file_n; + return NGX_INVALID_FILE; + } + + return fd; + +#else + + u_char *p, *cp, *end; + ngx_fd_t at_fd; + ngx_str_t at_name; + + if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) { + fd = ngx_open_file(name->data, mode, create, access); + + if (fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_open_file_n; + return NGX_INVALID_FILE; + } + + return fd; + } + + p = name->data; + end = p + name->len; + + at_name = *name; + + if (of->disable_symlinks_from) { + + cp = p + of->disable_symlinks_from; + + *cp = '\0'; + + at_fd = ngx_open_file(p, NGX_FILE_SEARCH|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0); + + *cp = '/'; + + if (at_fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_open_file_n; + return NGX_INVALID_FILE; + } + + at_name.len = of->disable_symlinks_from; + p = cp + 1; + + } else if (*p == '/') { + + at_fd = ngx_open_file("/", + NGX_FILE_SEARCH|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0); + + if (at_fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_openat_file_n; + return NGX_INVALID_FILE; + } + + at_name.len = 1; + p++; + + } else { + at_fd = NGX_AT_FDCWD; + } + + for ( ;; ) { + cp = ngx_strlchr(p, end, '/'); + if (cp == NULL) { + break; + } + + if (cp == p) { + p++; + continue; + } + + *cp = '\0'; + + if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) { + fd = ngx_openat_file_owner(at_fd, p, + NGX_FILE_SEARCH|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0, log); + + } else { + fd = ngx_openat_file(at_fd, p, + NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW, + NGX_FILE_OPEN, 0); + } + + *cp = '/'; + + if (fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_openat_file_n; + goto failed; + } + + if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%V\" failed", &at_name); + } + + p = cp + 1; + at_fd = fd; + at_name.len = cp - at_name.data; + } + + if (p == end) { + + /* + * If pathname ends with a trailing slash, assume the last path + * component is a directory and reopen it with requested flags; + * if not, fail with ENOTDIR as per POSIX. + * + * We cannot rely on O_DIRECTORY in the loop above to check + * that the last path component is a directory because + * O_DIRECTORY doesn't work on FreeBSD 8. Fortunately, by + * reopening a directory, we don't depend on it at all. + */ + + fd = ngx_openat_file(at_fd, ".", mode, create, access); + goto done; + } + + if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER + && !(create & (NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE))) + { + fd = ngx_openat_file_owner(at_fd, p, mode, create, access, log); + + } else { + fd = ngx_openat_file(at_fd, p, mode|NGX_FILE_NOFOLLOW, create, access); + } + +done: + + if (fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_openat_file_n; + } + +failed: + + if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%V\" failed", &at_name); + } + + return fd; +#endif +} + + static ngx_int_t -ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log) +ngx_file_info_wrapper(ngx_str_t *name, ngx_open_file_info_t *of, + ngx_file_info_t *fi, ngx_log_t *log) +{ + ngx_int_t rc; + +#if !(NGX_HAVE_OPENAT) + + rc = ngx_file_info(name->data, fi); + + if (rc == NGX_FILE_ERROR) { + of->err = ngx_errno; + of->failed = ngx_file_info_n; + return NGX_FILE_ERROR; + } + + return rc; + +#else + + ngx_fd_t fd; + + if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) { + + rc = ngx_file_info(name->data, fi); + + if (rc == NGX_FILE_ERROR) { + of->err = ngx_errno; + of->failed = ngx_file_info_n; + return NGX_FILE_ERROR; + } + + return rc; + } + + fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0, log); + + if (fd == NGX_INVALID_FILE) { + return NGX_FILE_ERROR; + } + + rc = ngx_fd_info(fd, fi); + + if (rc == NGX_FILE_ERROR) { + of->err = ngx_errno; + of->failed = ngx_fd_info_n; + } + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%V\" failed", name); + } + + return rc; +#endif +} + + +static ngx_int_t +ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of, + ngx_log_t *log) { ngx_fd_t fd; ngx_file_info_t fi; if (of->fd != NGX_INVALID_FILE) { - if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { - of->failed = ngx_file_info_n; - goto failed; + if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) { + of->fd = NGX_INVALID_FILE; + return NGX_ERROR; } if (of->uniq == ngx_file_uniq(&fi)) { @@ -479,9 +790,9 @@ ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log) } else if (of->test_dir) { - if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { - of->failed = ngx_file_info_n; - goto failed; + if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) { + of->fd = NGX_INVALID_FILE; + return NGX_ERROR; } if (ngx_is_dir(&fi)) { @@ -496,26 +807,27 @@ ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log) * This flag has no effect on a regular files. */ - fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, - NGX_FILE_OPEN, 0); + fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0, log); } else { - fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, - NGX_FILE_DEFAULT_ACCESS); + fd = ngx_open_file_wrapper(name, of, NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS, log); } if (fd == NGX_INVALID_FILE) { - of->failed = ngx_open_file_n; - goto failed; + of->fd = NGX_INVALID_FILE; + return NGX_ERROR; } if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, - ngx_fd_info_n " \"%s\" failed", name); + ngx_fd_info_n " \"%V\" failed", name); if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_close_file_n " \"%s\" failed", name); + ngx_close_file_n " \"%V\" failed", name); } of->fd = NGX_INVALID_FILE; @@ -526,7 +838,7 @@ ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log) if (ngx_is_dir(&fi)) { if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_close_file_n " \"%s\" failed", name); + ngx_close_file_n " \"%V\" failed", name); } of->fd = NGX_INVALID_FILE; @@ -537,14 +849,14 @@ ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log) if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) { if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_read_ahead_n " \"%s\" failed", name); + ngx_read_ahead_n " \"%V\" failed", name); } } if (of->directio <= ngx_file_size(&fi)) { if (ngx_directio_on(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_directio_on_n " \"%s\" failed", name); + ngx_directio_on_n " \"%V\" failed", name); } else { of->is_directio = 1; @@ -564,13 +876,6 @@ done: of->is_exec = ngx_is_exec(&fi); return NGX_OK; - -failed: - - of->fd = NGX_INVALID_FILE; - of->err = ngx_errno; - - return NGX_ERROR; } diff --git a/usr.sbin/nginx/src/core/ngx_open_file_cache.h b/usr.sbin/nginx/src/core/ngx_open_file_cache.h index 436de30609d..d119c1296f4 100644 --- a/usr.sbin/nginx/src/core/ngx_open_file_cache.h +++ b/usr.sbin/nginx/src/core/ngx_open_file_cache.h @@ -32,6 +32,11 @@ typedef struct { ngx_uint_t min_uses; +#if (NGX_HAVE_OPENAT) + size_t disable_symlinks_from; + unsigned disable_symlinks:2; +#endif + unsigned test_dir:1; unsigned test_only:1; unsigned log:1; @@ -64,6 +69,11 @@ struct ngx_cached_open_file_s { uint32_t uses; +#if (NGX_HAVE_OPENAT) + size_t disable_symlinks_from; + unsigned disable_symlinks:2; +#endif + unsigned count:24; unsigned close:1; unsigned use_event:1; diff --git a/usr.sbin/nginx/src/core/ngx_output_chain.c b/usr.sbin/nginx/src/core/ngx_output_chain.c index 64500d52886..3cb60ea73dd 100644 --- a/usr.sbin/nginx/src/core/ngx_output_chain.c +++ b/usr.sbin/nginx/src/core/ngx_output_chain.c @@ -209,7 +209,8 @@ ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in) return last; } - ngx_chain_update_chains(&ctx->free, &ctx->busy, &out, ctx->tag); + ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out, + ctx->tag); last_out = &out; } } diff --git a/usr.sbin/nginx/src/core/ngx_parse.h b/usr.sbin/nginx/src/core/ngx_parse.h index 0c8114fb693..ec093b5a2be 100644 --- a/usr.sbin/nginx/src/core/ngx_parse.h +++ b/usr.sbin/nginx/src/core/ngx_parse.h @@ -13,9 +13,6 @@ #include <ngx_core.h> -#define NGX_PARSE_LARGE_TIME -2 - - ssize_t ngx_parse_size(ngx_str_t *line); off_t ngx_parse_offset(ngx_str_t *line); ngx_int_t ngx_parse_time(ngx_str_t *line, ngx_uint_t is_sec); diff --git a/usr.sbin/nginx/src/core/ngx_rbtree.c b/usr.sbin/nginx/src/core/ngx_rbtree.c index 6ae4d5c05ab..914ca7e884b 100644 --- a/usr.sbin/nginx/src/core/ngx_rbtree.c +++ b/usr.sbin/nginx/src/core/ngx_rbtree.c @@ -136,8 +136,7 @@ ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, /* node->key < temp->key */ - p = ((ngx_rbtree_key_int_t) node->key - (ngx_rbtree_key_int_t) temp->key - < 0) + p = ((ngx_rbtree_key_int_t) (node->key - temp->key) < 0) ? &temp->left : &temp->right; if (*p == sentinel) { diff --git a/usr.sbin/nginx/src/core/ngx_regex.c b/usr.sbin/nginx/src/core/ngx_regex.c index 979110e75cc..677f862bf17 100644 --- a/usr.sbin/nginx/src/core/ngx_regex.c +++ b/usr.sbin/nginx/src/core/ngx_regex.c @@ -9,11 +9,64 @@ #include <ngx_core.h> +typedef struct { + ngx_flag_t pcre_jit; +} ngx_regex_conf_t; + + static void * ngx_libc_cdecl ngx_regex_malloc(size_t size); static void ngx_libc_cdecl ngx_regex_free(void *p); +#if (NGX_HAVE_PCRE_JIT) +static void ngx_pcre_free_studies(void *data); +#endif + +static ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle); + +static void *ngx_regex_create_conf(ngx_cycle_t *cycle); +static char *ngx_regex_init_conf(ngx_cycle_t *cycle, void *conf); + +static char *ngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data); +static ngx_conf_post_t ngx_regex_pcre_jit_post = { ngx_regex_pcre_jit }; + + +static ngx_command_t ngx_regex_commands[] = { + + { ngx_string("pcre_jit"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + 0, + offsetof(ngx_regex_conf_t, pcre_jit), + &ngx_regex_pcre_jit_post }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_regex_module_ctx = { + ngx_string("regex"), + ngx_regex_create_conf, + ngx_regex_init_conf +}; + + +ngx_module_t ngx_regex_module = { + NGX_MODULE_V1, + &ngx_regex_module_ctx, /* module context */ + ngx_regex_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init master */ + ngx_regex_module_init, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; static ngx_pool_t *ngx_pcre_pool; +static ngx_list_t *ngx_pcre_studies; void @@ -63,10 +116,11 @@ ngx_regex_malloc_done(void) ngx_int_t ngx_regex_compile(ngx_regex_compile_t *rc) { - int n, erroff; - char *p; - const char *errstr; - ngx_regex_t *re; + int n, erroff; + char *p; + pcre *re; + const char *errstr; + ngx_regex_elt_t *elt; ngx_regex_malloc_init(rc->pool); @@ -93,7 +147,24 @@ ngx_regex_compile(ngx_regex_compile_t *rc) return NGX_ERROR; } - rc->regex = re; + rc->regex = ngx_pcalloc(rc->pool, sizeof(ngx_regex_t)); + if (rc->regex == NULL) { + return NGX_ERROR; + } + + rc->regex->pcre = re; + + /* do not study at runtime */ + + if (ngx_pcre_studies != NULL) { + elt = ngx_list_push(ngx_pcre_studies); + if (elt == NULL) { + return NGX_ERROR; + } + + elt->regex = rc->regex; + elt->name = rc->pattern.data; + } n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures); if (n < 0) { @@ -204,3 +275,190 @@ ngx_regex_free(void *p) { return; } + + +#if (NGX_HAVE_PCRE_JIT) + +static void +ngx_pcre_free_studies(void *data) +{ + ngx_list_t *studies = data; + + ngx_uint_t i; + ngx_list_part_t *part; + ngx_regex_elt_t *elts; + + part = &studies->part; + elts = part->elts; + + for (i = 0 ; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + elts = part->elts; + i = 0; + } + + if (elts[i].regex->extra != NULL) { + pcre_free_study(elts[i].regex->extra); + } + } +} + +#endif + + +static ngx_int_t +ngx_regex_module_init(ngx_cycle_t *cycle) +{ + int opt; + const char *errstr; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_regex_elt_t *elts; + + opt = 0; + +#if (NGX_HAVE_PCRE_JIT) + { + ngx_regex_conf_t *rcf; + ngx_pool_cleanup_t *cln; + + rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module); + + if (rcf->pcre_jit) { + opt = PCRE_STUDY_JIT_COMPILE; + + /* + * The PCRE JIT compiler uses mmap for its executable codes, so we + * have to explicitly call the pcre_free_study() function to free + * this memory. + */ + + cln = ngx_pool_cleanup_add(cycle->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_pcre_free_studies; + cln->data = ngx_pcre_studies; + } + } +#endif + + ngx_regex_malloc_init(cycle->pool); + + part = &ngx_pcre_studies->part; + elts = part->elts; + + for (i = 0 ; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + elts = part->elts; + i = 0; + } + + elts[i].regex->extra = pcre_study(elts[i].regex->pcre, opt, &errstr); + + if (errstr != NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "pcre_study() failed: %s in \"%s\"", + errstr, elts[i].name); + } + +#if (NGX_HAVE_PCRE_JIT) + if (opt & PCRE_STUDY_JIT_COMPILE) { + int jit, n; + + jit = 0; + n = pcre_fullinfo(elts[i].regex->pcre, elts[i].regex->extra, + PCRE_INFO_JIT, &jit); + + if (n != 0 || jit != 1) { + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, + "JIT compiler does not support pattern: \"%s\"", + elts[i].name); + } + } +#endif + } + + ngx_regex_malloc_done(); + + ngx_pcre_studies = NULL; + + return NGX_OK; +} + + +static void * +ngx_regex_create_conf(ngx_cycle_t *cycle) +{ + ngx_regex_conf_t *rcf; + + rcf = ngx_pcalloc(cycle->pool, sizeof(ngx_regex_conf_t)); + if (rcf == NULL) { + return NULL; + } + + rcf->pcre_jit = NGX_CONF_UNSET; + + ngx_pcre_studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t)); + if (ngx_pcre_studies == NULL) { + return NULL; + } + + return rcf; +} + + +static char * +ngx_regex_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_regex_conf_t *rcf = conf; + + ngx_conf_init_value(rcf->pcre_jit, 0); + + return NGX_CONF_OK; +} + + +static char * +ngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data) +{ + ngx_flag_t *fp = data; + + if (*fp == 0) { + return NGX_CONF_OK; + } + +#if (NGX_HAVE_PCRE_JIT) + { + int jit, r; + + jit = 0; + r = pcre_config(PCRE_CONFIG_JIT, &jit); + + if (r != 0 || jit != 1) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "PCRE library does not support JIT"); + *fp = 0; + } + } +#else + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "nginx was built without PCRE JIT support"); + *fp = 0; +#endif + + return NGX_CONF_OK; +} diff --git a/usr.sbin/nginx/src/core/ngx_regex.h b/usr.sbin/nginx/src/core/ngx_regex.h index 58b9eef7621..55bd331bb69 100644 --- a/usr.sbin/nginx/src/core/ngx_regex.h +++ b/usr.sbin/nginx/src/core/ngx_regex.h @@ -19,7 +19,11 @@ #define NGX_REGEX_CASELESS PCRE_CASELESS -typedef pcre ngx_regex_t; + +typedef struct { + pcre *pcre; + pcre_extra *extra; +} ngx_regex_t; typedef struct { @@ -46,7 +50,7 @@ void ngx_regex_init(void); ngx_int_t ngx_regex_compile(ngx_regex_compile_t *rc); #define ngx_regex_exec(re, s, captures, size) \ - pcre_exec(re, NULL, (const char *) (s)->data, (s)->len, 0, 0, \ + pcre_exec(re->pcre, re->extra, (const char *) (s)->data, (s)->len, 0, 0, \ captures, size) #define ngx_regex_exec_n "pcre_exec()" diff --git a/usr.sbin/nginx/src/core/ngx_resolver.c b/usr.sbin/nginx/src/core/ngx_resolver.c index 817e65df101..02c484da694 100644 --- a/usr.sbin/nginx/src/core/ngx_resolver.c +++ b/usr.sbin/nginx/src/core/ngx_resolver.c @@ -92,8 +92,11 @@ static u_char *ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len); ngx_resolver_t * -ngx_resolver_create(ngx_conf_t *cf, ngx_addr_t *addr) +ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, ngx_uint_t n) { + ngx_str_t s; + ngx_url_t u; + ngx_uint_t i; ngx_resolver_t *r; ngx_pool_cleanup_t *cln; ngx_udp_connection_t *uc; @@ -110,6 +113,15 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_addr_t *addr) return NULL; } + if (n) { + if (ngx_array_init(&r->udp_connections, cf->pool, n, + sizeof(ngx_udp_connection_t)) + != NGX_OK) + { + return NULL; + } + } + cln->data = r; r->event = ngx_calloc(sizeof(ngx_event_t), cf->log); @@ -136,22 +148,47 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_addr_t *addr) r->resend_timeout = 5; r->expire = 30; - r->valid = 300; + r->valid = 0; r->log = &cf->cycle->new_log; r->log_level = NGX_LOG_ERR; - if (addr) { - uc = ngx_calloc(sizeof(ngx_udp_connection_t), cf->log); + for (i = 0; i < n; i++) { + if (ngx_strncmp(names[i].data, "valid=", 6) == 0) { + s.len = names[i].len - 6; + s.data = names[i].data + 6; + + r->valid = ngx_parse_time(&s, 1); + + if (r->valid == (time_t) NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter: %V", &names[i]); + return NULL; + } + + continue; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.host = names[i]; + u.port = 53; + + if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V: %s", &u.host, u.err); + return NULL; + } + + uc = ngx_array_push(&r->udp_connections); if (uc == NULL) { return NULL; } - r->udp_connection = uc; + ngx_memzero(uc, sizeof(ngx_udp_connection_t)); - uc->sockaddr = addr->sockaddr; - uc->socklen = addr->socklen; - uc->server = addr->name; + uc->sockaddr = u.addrs->sockaddr; + uc->socklen = u.addrs->socklen; + uc->server = u.addrs->name; } return r; @@ -163,6 +200,9 @@ ngx_resolver_cleanup(void *data) { ngx_resolver_t *r = data; + ngx_uint_t i; + ngx_udp_connection_t *uc; + if (r) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "cleanup resolver"); @@ -175,12 +215,13 @@ ngx_resolver_cleanup(void *data) ngx_free(r->event); } - if (r->udp_connection) { - if (r->udp_connection->connection) { - ngx_close_connection(r->udp_connection->connection); - } - ngx_free(r->udp_connection); + uc = r->udp_connections.elts; + + for (i = 0; i < r->udp_connections.nelts; i++) { + if (uc[i].connection) { + ngx_close_connection(uc[i].connection); + } } ngx_free(r); @@ -238,7 +279,7 @@ ngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp) } } - if (r->udp_connection == NULL) { + if (r->udp_connections.nelts == 0) { return NGX_NO_RESOLVER; } @@ -822,7 +863,12 @@ ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn) ssize_t n; ngx_udp_connection_t *uc; - uc = r->udp_connection; + uc = r->udp_connections.elts; + + uc = &uc[r->last_connection++]; + if (r->last_connection == r->udp_connections.nelts) { + r->last_connection = 0; + } if (uc->connection == NULL) { @@ -1121,6 +1167,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, char *err; u_char *cname; size_t len; + int32_t ttl; uint32_t hash; in_addr_t addr, *addrs; ngx_str_t name; @@ -1191,6 +1238,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, addrs = NULL; cname = NULL; qtype = 0; + ttl = 0; for (a = 0; a < nan; a++) { @@ -1230,6 +1278,12 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, qtype = (an->type_hi << 8) + an->type_lo; len = (an->len_hi << 8) + an->len_lo; + ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16) + + (an->ttl[2] << 8) + (an->ttl[3]); + + if (ttl < 0) { + ttl = 0; + } if (qtype == NGX_RESOLVE_A) { @@ -1259,8 +1313,9 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, } } - ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, - "resolver naddrs:%ui cname:%p", naddrs, cname); + ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver naddrs:%ui cname:%p ttl:%d", + naddrs, cname, ttl); if (naddrs) { @@ -1329,7 +1384,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, ngx_queue_remove(&rn->queue); - rn->valid = ngx_time() + r->valid; + rn->valid = ngx_time() + (r->valid ? r->valid : ttl); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); @@ -1371,7 +1426,8 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, rn->cnlen = (u_short) name.len; rn->u.cname = name.data; - rn->valid = ngx_time() + r->valid; + + rn->valid = ngx_time() + (r->valid ? r->valid : ttl); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); @@ -1422,6 +1478,7 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n, char *err; size_t len; in_addr_t addr; + int32_t ttl; ngx_int_t digit; ngx_str_t name; ngx_uint_t i, mask, qident; @@ -1517,6 +1574,12 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n, an = (ngx_resolver_an_t *) &buf[i + 2]; len = (an->len_hi << 8) + an->len_lo; + ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16) + + (an->ttl[2] << 8) + (an->ttl[3]); + + if (ttl < 0) { + ttl = 0; + } ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qt:%ui cl:%ui len:%uz", @@ -1553,7 +1616,7 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n, ngx_queue_remove(&rn->queue); - rn->valid = ngx_time() + r->valid; + rn->valid = ngx_time() + (r->valid ? r->valid : ttl); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue); @@ -1777,7 +1840,7 @@ ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx) len++; } else { - if (len == 0) { + if (len == 0 || len > 255) { return NGX_DECLINED; } @@ -1788,6 +1851,10 @@ ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx) p--; } + if (len == 0 || len > 255) { + return NGX_DECLINED; + } + *p = (u_char) len; return NGX_OK; diff --git a/usr.sbin/nginx/src/core/ngx_resolver.h b/usr.sbin/nginx/src/core/ngx_resolver.h index 2301623df94..ae34ca56d3a 100644 --- a/usr.sbin/nginx/src/core/ngx_resolver.h +++ b/usr.sbin/nginx/src/core/ngx_resolver.h @@ -78,16 +78,16 @@ typedef struct { typedef struct { /* has to be pointer because of "incomplete type" */ ngx_event_t *event; - - /* TODO: DNS peers balancer */ - /* STUB */ - ngx_udp_connection_t *udp_connection; - + void *dummy; ngx_log_t *log; /* ident must be after 3 pointers */ ngx_int_t ident; + /* simple round robin DNS peers balancer */ + ngx_array_t udp_connections; + ngx_uint_t last_connection; + ngx_rbtree_t name_rbtree; ngx_rbtree_node_t name_sentinel; @@ -124,8 +124,6 @@ struct ngx_resolver_ctx_s { in_addr_t *addrs; in_addr_t addr; - /* TODO: DNS peers balancer ctx */ - ngx_resolver_handler_pt handler; void *data; ngx_msec_t timeout; @@ -136,7 +134,8 @@ struct ngx_resolver_ctx_s { }; -ngx_resolver_t *ngx_resolver_create(ngx_conf_t *cf, ngx_addr_t *addr); +ngx_resolver_t *ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, + ngx_uint_t n); ngx_resolver_ctx_t *ngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp); ngx_int_t ngx_resolve_name(ngx_resolver_ctx_t *ctx); diff --git a/usr.sbin/nginx/src/core/ngx_shmtx.c b/usr.sbin/nginx/src/core/ngx_shmtx.c index 368456e14f8..085294a914a 100644 --- a/usr.sbin/nginx/src/core/ngx_shmtx.c +++ b/usr.sbin/nginx/src/core/ngx_shmtx.c @@ -12,10 +12,13 @@ #if (NGX_HAVE_ATOMIC_OPS) +static void ngx_shmtx_wakeup(ngx_shmtx_t *mtx); + + ngx_int_t -ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name) +ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name) { - mtx->lock = addr; + mtx->lock = &addr->lock; if (mtx->spin == (ngx_uint_t) -1) { return NGX_OK; @@ -25,6 +28,8 @@ ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name) #if (NGX_HAVE_POSIX_SEM) + mtx->wait = &addr->wait; + if (sem_init(&mtx->sem, 1, 0) == -1) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, "sem_init() failed"); @@ -57,12 +62,7 @@ ngx_shmtx_destory(ngx_shmtx_t *mtx) ngx_uint_t ngx_shmtx_trylock(ngx_shmtx_t *mtx) { - ngx_atomic_uint_t val; - - val = *mtx->lock; - - return ((val & 0x80000000) == 0 - && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000)); + return (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)); } @@ -70,17 +70,12 @@ void ngx_shmtx_lock(ngx_shmtx_t *mtx) { ngx_uint_t i, n; - ngx_atomic_uint_t val; ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx lock"); for ( ;; ) { - val = *mtx->lock; - - if ((val & 0x80000000) == 0 - && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000)) - { + if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) { return; } @@ -92,10 +87,8 @@ ngx_shmtx_lock(ngx_shmtx_t *mtx) ngx_cpu_pause(); } - val = *mtx->lock; - - if ((val & 0x80000000) == 0 - && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000)) + if (*mtx->lock == 0 + && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) { return; } @@ -105,24 +98,24 @@ ngx_shmtx_lock(ngx_shmtx_t *mtx) #if (NGX_HAVE_POSIX_SEM) if (mtx->semaphore) { - val = *mtx->lock; + (void) ngx_atomic_fetch_add(mtx->wait, 1); - if ((val & 0x80000000) - && ngx_atomic_cmp_set(mtx->lock, val, val + 1)) - { - ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, - "shmtx wait %XA", val); + if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) { + return; + } - while (sem_wait(&mtx->sem) == -1) { - ngx_err_t err; + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "shmtx wait %uA", *mtx->wait); - err = ngx_errno; + while (sem_wait(&mtx->sem) == -1) { + ngx_err_t err; - if (err != NGX_EINTR) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, - "sem_wait() failed while waiting on shmtx"); - break; - } + err = ngx_errno; + + if (err != NGX_EINTR) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, + "sem_wait() failed while waiting on shmtx"); + break; } ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, @@ -142,31 +135,56 @@ ngx_shmtx_lock(ngx_shmtx_t *mtx) void ngx_shmtx_unlock(ngx_shmtx_t *mtx) { - ngx_atomic_uint_t val, old, wait; - if (mtx->spin != (ngx_uint_t) -1) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx unlock"); } - for ( ;; ) { + if (ngx_atomic_cmp_set(mtx->lock, ngx_pid, 0)) { + ngx_shmtx_wakeup(mtx); + } +} - old = *mtx->lock; - wait = old & 0x7fffffff; - val = wait ? wait - 1 : 0; - if (ngx_atomic_cmp_set(mtx->lock, old, val)) { - break; - } +ngx_uint_t +ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid) +{ + ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "shmtx forced unlock"); + + if (ngx_atomic_cmp_set(mtx->lock, pid, 0)) { + ngx_shmtx_wakeup(mtx); + return 1; } + return 0; +} + + +static void +ngx_shmtx_wakeup(ngx_shmtx_t *mtx) +{ #if (NGX_HAVE_POSIX_SEM) + ngx_atomic_uint_t wait; - if (wait == 0 || !mtx->semaphore) { + if (!mtx->semaphore) { return; } + for ( ;; ) { + + wait = *mtx->wait; + + if (wait == 0) { + return; + } + + if (ngx_atomic_cmp_set(mtx->wait, wait, wait - 1)) { + break; + } + } + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, - "shmtx wake %XA", old); + "shmtx wake %uA", wait); if (sem_post(&mtx->sem) == -1) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, @@ -181,7 +199,7 @@ ngx_shmtx_unlock(ngx_shmtx_t *mtx) ngx_int_t -ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name) +ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name) { if (mtx->name) { @@ -281,4 +299,11 @@ ngx_shmtx_unlock(ngx_shmtx_t *mtx) ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name); } + +ngx_uint_t +ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid) +{ + return 0; +} + #endif diff --git a/usr.sbin/nginx/src/core/ngx_shmtx.h b/usr.sbin/nginx/src/core/ngx_shmtx.h index 70a44cbc5fb..830b6f5b198 100644 --- a/usr.sbin/nginx/src/core/ngx_shmtx.h +++ b/usr.sbin/nginx/src/core/ngx_shmtx.h @@ -14,9 +14,18 @@ typedef struct { + ngx_atomic_t lock; +#if (NGX_HAVE_POSIX_SEM) + ngx_atomic_t wait; +#endif +} ngx_shmtx_sh_t; + + +typedef struct { #if (NGX_HAVE_ATOMIC_OPS) ngx_atomic_t *lock; #if (NGX_HAVE_POSIX_SEM) + ngx_atomic_t *wait; ngx_uint_t semaphore; sem_t sem; #endif @@ -28,11 +37,13 @@ typedef struct { } ngx_shmtx_t; -ngx_int_t ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name); +ngx_int_t ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, + u_char *name); void ngx_shmtx_destory(ngx_shmtx_t *mtx); ngx_uint_t ngx_shmtx_trylock(ngx_shmtx_t *mtx); void ngx_shmtx_lock(ngx_shmtx_t *mtx); void ngx_shmtx_unlock(ngx_shmtx_t *mtx); +ngx_uint_t ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid); #endif /* _NGX_SHMTX_H_INCLUDED_ */ diff --git a/usr.sbin/nginx/src/core/ngx_slab.c b/usr.sbin/nginx/src/core/ngx_slab.c index 74f23a35e19..3d566037708 100644 --- a/usr.sbin/nginx/src/core/ngx_slab.c +++ b/usr.sbin/nginx/src/core/ngx_slab.c @@ -43,14 +43,14 @@ #if (NGX_DEBUG_MALLOC) -#define ngx_slab_junk(p, size) ngx_memset(p, 0xD0, size) +#define ngx_slab_junk(p, size) ngx_memset(p, 0xA5, size) #else -#if (NGX_FREEBSD) +#if (NGX_HAVE_DEBUG_MALLOC) #define ngx_slab_junk(p, size) \ - if (ngx_freebsd_debug_malloc) ngx_memset(p, 0xD0, size) + if (ngx_debug_malloc) ngx_memset(p, 0xA5, size) #else diff --git a/usr.sbin/nginx/src/core/ngx_slab.h b/usr.sbin/nginx/src/core/ngx_slab.h index efb5d01f3b3..c5e420bfa8e 100644 --- a/usr.sbin/nginx/src/core/ngx_slab.h +++ b/usr.sbin/nginx/src/core/ngx_slab.h @@ -23,7 +23,7 @@ struct ngx_slab_page_s { typedef struct { - ngx_atomic_t lock; + ngx_shmtx_sh_t lock; size_t min_size; size_t min_shift; diff --git a/usr.sbin/nginx/src/core/ngx_string.c b/usr.sbin/nginx/src/core/ngx_string.c index 05184479c96..53cb946fee0 100644 --- a/usr.sbin/nginx/src/core/ngx_string.c +++ b/usr.sbin/nginx/src/core/ngx_string.c @@ -146,12 +146,12 @@ ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args) { u_char *p, zero; int d; - double f, scale; + double f; size_t len, slen; int64_t i64; - uint64_t ui64; + uint64_t ui64, frac; ngx_msec_t ms; - ngx_uint_t width, sign, hex, max_width, frac_width, n; + ngx_uint_t width, sign, hex, max_width, frac_width, scale, n; ngx_str_t *v; ngx_variable_value_t *vv; @@ -365,28 +365,31 @@ ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args) } ui64 = (int64_t) f; - - buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width); + frac = 0; if (frac_width) { - if (buf < last) { - *buf++ = '.'; + scale = 1; + for (n = frac_width; n; n--) { + scale *= 10; } - scale = 1.0; + frac = (uint64_t) ((f - (double) ui64) * scale + 0.5); - for (n = frac_width; n; n--) { - scale *= 10.0; + if (frac == scale) { + ui64++; + frac = 0; } + } - /* - * (int64_t) cast is required for msvc6: - * it cannot convert uint64_t to double - */ - ui64 = (uint64_t) ((f - (int64_t) ui64) * scale + 0.5); + buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width); + + if (frac_width) { + if (buf < last) { + *buf++ = '.'; + } - buf = ngx_sprintf_num(buf, last, ui64, '0', 0, frac_width); + buf = ngx_sprintf_num(buf, last, frac, '0', 0, frac_width); } fmt++; @@ -1841,9 +1844,9 @@ ngx_strip_chroot(ngx_str_t *path) { if (!ngx_strncmp(path->data, NGX_PREFIX, strlen(NGX_PREFIX))) { char *x, *buf = malloc(path->len); - x = ngx_cpystrn(buf, path->data + strlen(NGX_PREFIX) - 1, - path->len); - path->len = (x - buf); - path->data = buf; + x = ngx_cpystrn(buf, path->data + strlen(NGX_PREFIX) - 1, + path->len); + path->len = (x - buf); + path->data = buf; } } diff --git a/usr.sbin/nginx/src/core/ngx_times.c b/usr.sbin/nginx/src/core/ngx_times.c index 6a5808fe2ea..ed1bf64bc08 100644 --- a/usr.sbin/nginx/src/core/ngx_times.c +++ b/usr.sbin/nginx/src/core/ngx_times.c @@ -33,7 +33,7 @@ volatile ngx_str_t ngx_cached_http_log_iso8601; #if !(NGX_WIN32) /* - * locatime() and localtime_r() are not Async-Signal-Safe functions, therefore, + * localtime() and localtime_r() are not Async-Signal-Safe functions, therefore, * they must not be called by a signal handler, so we use the cached * GMT offset value. Fortunately the value is changed only two times a year. */ @@ -308,7 +308,7 @@ ngx_gmtime(time_t t, ngx_tm_t *tp) /* * The "days" should be adjusted to 1 only, however, some March 1st's go * to previous year, so we adjust them to 2. This causes also shift of the - * last Feburary days to next year, but we catch the case when "yday" + * last February days to next year, but we catch the case when "yday" * becomes negative. */ diff --git a/usr.sbin/nginx/src/event/modules/ngx_epoll_module.c b/usr.sbin/nginx/src/event/modules/ngx_epoll_module.c index ddf178927b4..a73394906d1 100644 --- a/usr.sbin/nginx/src/event/modules/ngx_epoll_module.c +++ b/usr.sbin/nginx/src/event/modules/ngx_epoll_module.c @@ -445,7 +445,7 @@ ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) /* * when the file descriptor is closed, the epoll automatically deletes - * it from its queue, so we do not need to delete explicity the event + * it from its queue, so we do not need to delete explicitly the event * before the closing the file descriptor */ @@ -524,7 +524,7 @@ ngx_epoll_del_connection(ngx_connection_t *c, ngx_uint_t flags) /* * when the file descriptor is closed the epoll automatically deletes - * it from its queue so we do not need to delete explicity the event + * it from its queue so we do not need to delete explicitly the event * before the closing the file descriptor */ diff --git a/usr.sbin/nginx/src/event/modules/ngx_eventport_module.c b/usr.sbin/nginx/src/event/modules/ngx_eventport_module.c index 0bfb47e5ca3..bbcd6dd6bae 100644 --- a/usr.sbin/nginx/src/event/modules/ngx_eventport_module.c +++ b/usr.sbin/nginx/src/event/modules/ngx_eventport_module.c @@ -322,7 +322,7 @@ ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) /* * when the file descriptor is closed, the event port automatically - * dissociates it from the port, so we do not need to dissociate explicity + * dissociates it from the port, so we do not need to dissociate explicitly * the event before the closing the file descriptor */ diff --git a/usr.sbin/nginx/src/event/modules/ngx_kqueue_module.c b/usr.sbin/nginx/src/event/modules/ngx_kqueue_module.c index c0f70752f01..30e456c4da0 100644 --- a/usr.sbin/nginx/src/event/modules/ngx_kqueue_module.c +++ b/usr.sbin/nginx/src/event/modules/ngx_kqueue_module.c @@ -377,7 +377,7 @@ ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) /* * when the file descriptor is closed the kqueue automatically deletes - * its filters so we do not need to delete explicity the event + * its filters so we do not need to delete explicitly the event * before the closing the file descriptor. */ diff --git a/usr.sbin/nginx/src/event/ngx_event.c b/usr.sbin/nginx/src/event/ngx_event.c index 88e151cc4b5..a0a9d743b5e 100644 --- a/usr.sbin/nginx/src/event/ngx_event.c +++ b/usr.sbin/nginx/src/event/ngx_event.c @@ -21,6 +21,7 @@ extern ngx_module_t ngx_rtsig_module; extern ngx_module_t ngx_select_module; +static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf); static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle); static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle); static char *ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -31,8 +32,8 @@ static char *ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static void *ngx_event_create_conf(ngx_cycle_t *cycle); -static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf); +static void *ngx_event_core_create_conf(ngx_cycle_t *cycle); +static char *ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf); static ngx_uint_t ngx_timer_resolution; @@ -93,7 +94,7 @@ static ngx_command_t ngx_events_commands[] = { static ngx_core_module_t ngx_events_module_ctx = { ngx_string("events"), NULL, - NULL + ngx_event_init_conf }; @@ -173,8 +174,8 @@ static ngx_command_t ngx_event_core_commands[] = { ngx_event_module_t ngx_event_core_module_ctx = { &event_core_name, - ngx_event_create_conf, /* create configuration */ - ngx_event_init_conf, /* init configuration */ + ngx_event_core_create_conf, /* create configuration */ + ngx_event_core_init_conf, /* init configuration */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -423,6 +424,19 @@ ngx_handle_write_event(ngx_event_t *wev, size_t lowat) } +static char * +ngx_event_init_conf(ngx_cycle_t *cycle, void *conf) +{ + if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "no \"events\" section in configuration"); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle) { @@ -435,13 +449,6 @@ ngx_event_module_init(ngx_cycle_t *cycle) ngx_event_conf_t *ecf; cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module); - - if (cf == NULL) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, - "no \"events\" section in configuration"); - return NGX_ERROR; - } - ecf = (*cf)[ngx_event_core_module.ctx_index]; if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) { @@ -471,7 +478,7 @@ ngx_event_module_init(ngx_cycle_t *cycle) (ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile; ngx_log_error(NGX_LOG_WARN, cycle->log, 0, - "%ui worker_connections are more than " + "%ui worker_connections exceed " "open file resource limit: %i", ecf->connections, limit); } @@ -489,7 +496,7 @@ ngx_event_module_init(ngx_cycle_t *cycle) } - /* cl should be equal or bigger than cache line size */ + /* cl should be equal to or greater than cache line size */ cl = 128; @@ -522,7 +529,8 @@ ngx_event_module_init(ngx_cycle_t *cycle) ngx_accept_mutex_ptr = (ngx_atomic_t *) shared; ngx_accept_mutex.spin = (ngx_uint_t) -1; - if (ngx_shmtx_create(&ngx_accept_mutex, shared, cycle->lock_file.data) + if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared, + cycle->lock_file.data) != NGX_OK) { return NGX_ERROR; @@ -1115,7 +1123,7 @@ ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) static void * -ngx_event_create_conf(ngx_cycle_t *cycle) +ngx_event_core_create_conf(ngx_cycle_t *cycle) { ngx_event_conf_t *ecf; @@ -1146,7 +1154,7 @@ ngx_event_create_conf(ngx_cycle_t *cycle) static char * -ngx_event_init_conf(ngx_cycle_t *cycle, void *conf) +ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf) { ngx_event_conf_t *ecf = conf; diff --git a/usr.sbin/nginx/src/event/ngx_event_openssl.c b/usr.sbin/nginx/src/event/ngx_event_openssl.c index 6b9dba16b71..21deefda926 100644 --- a/usr.sbin/nginx/src/event/ngx_event_openssl.c +++ b/usr.sbin/nginx/src/event/ngx_event_openssl.c @@ -478,6 +478,7 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) return NGX_OK; } + ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name) { @@ -488,7 +489,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name) /* * Elliptic-Curve Diffie-Hellman parameters are either "named curves" - * from RFC 4492 section 5.1.1, or explicitely described curves over + * from RFC 4492 section 5.1.1, or explicitly described curves over * binary fields. OpenSSL only supports the "named curves", which provide * maximum interoperability. */ @@ -518,6 +519,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name) return NGX_OK; } + ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags) { diff --git a/usr.sbin/nginx/src/event/ngx_event_pipe.c b/usr.sbin/nginx/src/event/ngx_event_pipe.c index 7af5a63fc24..c2c79837fcf 100644 --- a/usr.sbin/nginx/src/event/ngx_event_pipe.c +++ b/usr.sbin/nginx/src/event/ngx_event_pipe.c @@ -16,8 +16,6 @@ static ngx_int_t ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p); static ngx_int_t ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p); static ngx_inline void ngx_event_pipe_remove_shadow_links(ngx_buf_t *buf); -static ngx_inline void ngx_event_pipe_free_shadow_raw_buf(ngx_chain_t **free, - ngx_buf_t *buf); static ngx_int_t ngx_event_pipe_drain_chains(ngx_event_pipe_t *p); @@ -150,7 +148,7 @@ ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) && p->upstream->read->pending_eof) { p->upstream->read->ready = 0; - p->upstream->read->eof = 0; + p->upstream->read->eof = 1; p->upstream_eof = 1; p->read = 1; @@ -393,8 +391,33 @@ ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) cl->buf->file_last - cl->buf->file_pos); } + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe length: %O", p->length); + #endif + if (p->free_raw_bufs && p->length != -1) { + cl = p->free_raw_bufs; + + if (cl->buf->last - cl->buf->pos >= p->length) { + + p->free_raw_bufs = cl->next; + + /* STUB */ cl->buf->num = p->num++; + + if (p->input_filter(p, cl->buf) == NGX_ERROR) { + return NGX_ABORT; + } + + ngx_free_chain(p->pool, cl); + } + } + + if (p->length == 0) { + p->upstream_done = 1; + p->read = 1; + } + if ((p->upstream_eof || p->upstream_error) && p->free_raw_bufs) { /* STUB */ p->free_raw_bufs->buf->num = p->num++; @@ -553,17 +576,13 @@ ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p) if (p->out) { cl = p->out; - if (cl->buf->recycled - && bsize + cl->buf->last - cl->buf->pos > p->busy_size) - { - flush = 1; - break; + if (cl->buf->recycled) { + ngx_log_error(NGX_LOG_ALERT, p->log, 0, + "recycled buffer in pipe out chain"); } p->out = p->out->next; - ngx_event_pipe_free_shadow_raw_buf(&p->free_raw_bufs, cl->buf); - } else if (!p->cacheable && p->in) { cl = p->in; @@ -573,24 +592,13 @@ ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p) cl->buf->pos, cl->buf->last - cl->buf->pos); - if (cl->buf->recycled - && cl->buf->last_shadow - && bsize + cl->buf->last - cl->buf->pos > p->busy_size) - { - if (!prev_last_shadow) { - p->in = p->in->next; - - cl->next = NULL; - - if (out) { - *ll = cl; - } else { - out = cl; - } + if (cl->buf->recycled && prev_last_shadow) { + if (bsize + cl->buf->end - cl->buf->start > p->busy_size) { + flush = 1; + break; } - flush = 1; - break; + bsize += cl->buf->end - cl->buf->start; } prev_last_shadow = cl->buf->last_shadow; @@ -601,10 +609,6 @@ ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p) break; } - if (cl->buf->recycled) { - bsize += cl->buf->last - cl->buf->pos; - } - cl->next = NULL; if (out) { @@ -634,7 +638,7 @@ ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p) rc = p->output_filter(p->output_ctx, out); - ngx_chain_update_chains(&p->free, &p->busy, &out, p->tag); + ngx_chain_update_chains(p->pool, &p->free, &p->busy, &out, p->tag); if (rc == NGX_ERROR) { p->downstream_error = 1; @@ -678,9 +682,10 @@ ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p) static ngx_int_t ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p) { - ssize_t size, bsize; + ssize_t size, bsize, n; ngx_buf_t *b; - ngx_chain_t *cl, *tl, *next, *out, **ll, **last_free, fl; + ngx_uint_t prev_last_shadow; + ngx_chain_t *cl, *tl, *next, *out, **ll, **last_out, **last_free, fl; if (p->buf_to_file) { fl.buf = p->buf_to_file; @@ -696,6 +701,7 @@ ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p) size = 0; cl = out; ll = NULL; + prev_last_shadow = 1; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe offset: %O", p->temp_file->offset); @@ -703,16 +709,21 @@ ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p) do { bsize = cl->buf->last - cl->buf->pos; - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0, - "pipe buf %p, pos %p, size: %z", - cl->buf->start, cl->buf->pos, bsize); + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe buf ls:%d %p, pos %p, size: %z", + cl->buf->last_shadow, cl->buf->start, + cl->buf->pos, bsize); - if ((size + bsize > p->temp_file_write_size) - || (p->temp_file->offset + size + bsize > p->max_temp_file_size)) + if (prev_last_shadow + && ((size + bsize > p->temp_file_write_size) + || (p->temp_file->offset + size + bsize + > p->max_temp_file_size))) { break; } + prev_last_shadow = cl->buf->last_shadow; + size += bsize; ll = &cl->next; cl = cl->next; @@ -739,42 +750,77 @@ ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p) p->last_in = &p->in; } - if (ngx_write_chain_to_temp_file(p->temp_file, out) == NGX_ERROR) { - return NGX_ABORT; - } + n = ngx_write_chain_to_temp_file(p->temp_file, out); - for (last_free = &p->free_raw_bufs; - *last_free != NULL; - last_free = &(*last_free)->next) - { - /* void */ + if (n == NGX_ERROR) { + return NGX_ABORT; } if (p->buf_to_file) { p->temp_file->offset = p->buf_to_file->last - p->buf_to_file->pos; + n -= p->buf_to_file->last - p->buf_to_file->pos; p->buf_to_file = NULL; out = out->next; } - for (cl = out; cl; cl = next) { - next = cl->next; - cl->next = NULL; + if (n > 0) { + /* update previous buffer or add new buffer */ + + if (p->out) { + for (cl = p->out; cl->next; cl = cl->next) { /* void */ } + + b = cl->buf; + + if (b->file_last == p->temp_file->offset) { + p->temp_file->offset += n; + b->file_last = p->temp_file->offset; + goto free; + } + + last_out = &cl->next; + + } else { + last_out = &p->out; + } + + cl = ngx_chain_get_free_buf(p->pool, &p->free); + if (cl == NULL) { + return NGX_ABORT; + } b = cl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->tag = p->tag; + b->file = &p->temp_file->file; b->file_pos = p->temp_file->offset; - p->temp_file->offset += b->last - b->pos; + p->temp_file->offset += n; b->file_last = p->temp_file->offset; b->in_file = 1; b->temp_file = 1; - if (p->out) { - *p->last_out = cl; - } else { - p->out = cl; - } - p->last_out = &cl->next; + *last_out = cl; + } + +free: + + for (last_free = &p->free_raw_bufs; + *last_free != NULL; + last_free = &(*last_free)->next) + { + /* void */ + } + + for (cl = out; cl; cl = next) { + next = cl->next; + + cl->next = p->free; + p->free = cl; + + b = cl->buf; if (b->last_shadow) { @@ -849,6 +895,12 @@ ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) } p->last_in = &cl->next; + if (p->length == -1) { + return NGX_OK; + } + + p->length -= b->last - b->pos; + return NGX_OK; } @@ -884,35 +936,6 @@ ngx_event_pipe_remove_shadow_links(ngx_buf_t *buf) } -static ngx_inline void -ngx_event_pipe_free_shadow_raw_buf(ngx_chain_t **free, ngx_buf_t *buf) -{ - ngx_buf_t *s; - ngx_chain_t *cl, **ll; - - if (buf->shadow == NULL) { - return; - } - - for (s = buf->shadow; !s->last_shadow; s = s->shadow) { /* void */ } - - ll = free; - - for (cl = *free; cl; cl = cl->next) { - if (cl->buf == s) { - *ll = cl->next; - break; - } - - if (cl->buf->shadow) { - break; - } - - ll = &cl->next; - } -} - - ngx_int_t ngx_event_pipe_add_free_buf(ngx_event_pipe_t *p, ngx_buf_t *b) { diff --git a/usr.sbin/nginx/src/event/ngx_event_pipe.h b/usr.sbin/nginx/src/event/ngx_event_pipe.h index ff63cf64af3..f24e6d148f5 100644 --- a/usr.sbin/nginx/src/event/ngx_event_pipe.h +++ b/usr.sbin/nginx/src/event/ngx_event_pipe.h @@ -31,8 +31,6 @@ struct ngx_event_pipe_s { ngx_chain_t **last_in; ngx_chain_t *out; - ngx_chain_t **last_out; - ngx_chain_t *free; ngx_chain_t *busy; @@ -66,6 +64,7 @@ struct ngx_event_pipe_s { ssize_t busy_size; off_t read_length; + off_t length; off_t max_temp_file_size; ssize_t temp_file_write_size; diff --git a/usr.sbin/nginx/src/event/ngx_event_timer.c b/usr.sbin/nginx/src/event/ngx_event_timer.c index 7a810720930..177ac1cf113 100644 --- a/usr.sbin/nginx/src/event/ngx_event_timer.c +++ b/usr.sbin/nginx/src/event/ngx_event_timer.c @@ -67,7 +67,7 @@ ngx_event_find_timer(void) ngx_mutex_unlock(ngx_event_timer_mutex); - timer = (ngx_msec_int_t) node->key - (ngx_msec_int_t) ngx_current_msec; + timer = (ngx_msec_int_t) (node->key - ngx_current_msec); return (ngx_msec_t) (timer > 0 ? timer : 0); } @@ -95,8 +95,7 @@ ngx_event_expire_timers(void) /* node->key <= ngx_current_time */ - if ((ngx_msec_int_t) node->key - (ngx_msec_int_t) ngx_current_msec <= 0) - { + if ((ngx_msec_int_t) (node->key - ngx_current_msec) <= 0) { ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer)); #if (NGX_THREADS) diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_access_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_access_module.c index 84a75d777ab..70a4262fccb 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_access_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_access_module.c @@ -351,14 +351,19 @@ ngx_http_access_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_access_loc_conf_t *prev = parent; ngx_http_access_loc_conf_t *conf = child; - if (conf->rules == NULL) { +#if (NGX_HAVE_INET6) + + if (conf->rules == NULL && conf->rules6 == NULL) { conf->rules = prev->rules; + conf->rules6 = prev->rules6; } -#if (NGX_HAVE_INET6) - if (conf->rules6 == NULL) { - conf->rules6 = prev->rules6; +#else + + if (conf->rules == NULL) { + conf->rules = prev->rules; } + #endif return NGX_CONF_OK; diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_browser_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_browser_module.c index 13a7145385e..80da0d8fa86 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_browser_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_browser_module.c @@ -458,10 +458,11 @@ ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, void *child) * with a real skip value. The zero value means Opera. */ - if (conf->modern_browsers == NULL) { + if (conf->modern_browsers == NULL && conf->modern_unlisted_browsers == 0) { conf->modern_browsers = prev->modern_browsers; + conf->modern_unlisted_browsers = prev->modern_unlisted_browsers; - } else { + } else if (conf->modern_browsers != NULL) { browsers = conf->modern_browsers->elts; for (i = 0; i < conf->modern_browsers->nelts; i++) { @@ -501,8 +502,9 @@ found: } } - if (conf->ancient_browsers == NULL) { + if (conf->ancient_browsers == NULL && conf->netscape4 == 0) { conf->ancient_browsers = prev->ancient_browsers; + conf->netscape4 = prev->netscape4; } if (conf->modern_browser_value == NULL) { diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_chunked_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_chunked_filter_module.c index f07a8442e4d..94313a8f650 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_chunked_filter_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_chunked_filter_module.c @@ -222,7 +222,7 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) rc = ngx_http_next_body_filter(r, out); - ngx_chain_update_chains(&ctx->free, &ctx->busy, &out, + ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, (ngx_buf_tag_t) &ngx_http_chunked_filter_module); return rc; diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_degradation_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_degradation_module.c index 0b1368f3b5a..b9c65cdc9e0 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_degradation_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_degradation_module.c @@ -126,7 +126,7 @@ ngx_http_degraded(ngx_http_request_t *r) * ELF/i386 is loaded at 0x08000000, 128M * ELF/amd64 is loaded at 0x00400000, 4M * - * use a function address to substract the loading address + * use a function address to subtract the loading address */ sbrk_size = (size_t) sbrk(0) - ((uintptr_t) ngx_palloc & ~0x3FFFFF); diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_fastcgi_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_fastcgi_module.c index 23ef2957f6b..3bc994a8004 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_fastcgi_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_fastcgi_module.c @@ -27,6 +27,8 @@ typedef struct { ngx_hash_t headers_hash; ngx_uint_t header_params; + ngx_flag_t keep_conn; + #if (NGX_HTTP_CACHE) ngx_http_complex_value_t cache_key; #endif @@ -78,6 +80,8 @@ typedef struct { #define NGX_HTTP_FASTCGI_RESPONDER 1 +#define NGX_HTTP_FASTCGI_KEEP_CONN 1 + #define NGX_HTTP_FASTCGI_BEGIN_REQUEST 1 #define NGX_HTTP_FASTCGI_ABORT_REQUEST 2 #define NGX_HTTP_FASTCGI_END_REQUEST 3 @@ -131,6 +135,7 @@ static ngx_int_t ngx_http_fastcgi_create_key(ngx_http_request_t *r); 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); +static ngx_int_t ngx_http_fastcgi_input_filter_init(void *data); static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf); static ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r, @@ -376,6 +381,20 @@ static ngx_command_t ngx_http_fastcgi_commands[] = { offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_methods), &ngx_http_upstream_cache_method_mask }, + { ngx_string("fastcgi_cache_lock"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock), + NULL }, + + { ngx_string("fastcgi_cache_lock_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock_timeout), + NULL }, + #endif { ngx_string("fastcgi_temp_path"), @@ -407,8 +426,8 @@ static ngx_command_t ngx_http_fastcgi_commands[] = { &ngx_http_fastcgi_next_upstream_masks }, { ngx_string("fastcgi_param"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, - ngx_conf_set_keyval_slot, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23, + ngx_http_upstream_param_set_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_fastcgi_loc_conf_t, params_source), NULL }, @@ -441,6 +460,13 @@ static ngx_command_t ngx_http_fastcgi_commands[] = { offsetof(ngx_http_fastcgi_loc_conf_t, catch_stderr), NULL }, + { ngx_string("fastcgi_keep_conn"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fastcgi_loc_conf_t, keep_conn), + NULL }, + ngx_null_command }; @@ -604,6 +630,8 @@ ngx_http_fastcgi_handler(ngx_http_request_t *r) u->pipe->input_filter = ngx_http_fastcgi_input_filter; u->pipe->input_ctx = r; + u->input_filter_init = ngx_http_fastcgi_input_filter_init; + rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { @@ -695,7 +723,7 @@ ngx_http_fastcgi_create_request(ngx_http_request_t *r) u_char ch, *pos, *lowcase_key; size_t size, len, key_len, val_len, padding, allocated; - ngx_uint_t i, n, next, hash, header_params; + ngx_uint_t i, n, next, hash, skip_empty, header_params; ngx_buf_t *b; ngx_chain_t *cl, *body; ngx_list_part_t *part; @@ -726,11 +754,18 @@ ngx_http_fastcgi_create_request(ngx_http_request_t *r) lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = lcode(&le); + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = lcode(&le); + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; } le.ip += sizeof(uintptr_t); + if (skip_empty && val_len == 0) { + continue; + } + len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len; } } @@ -845,6 +880,9 @@ ngx_http_fastcgi_create_request(ngx_http_request_t *r) cl->buf = b; + ngx_http_fastcgi_request_start.br.flags = + flcf->keep_conn ? NGX_HTTP_FASTCGI_KEEP_CONN : 0; + ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start, sizeof(ngx_http_fastcgi_request_start_t)); @@ -877,11 +915,28 @@ ngx_http_fastcgi_create_request(ngx_http_request_t *r) lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = (u_char) lcode(&le); + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = lcode(&le); + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; } le.ip += sizeof(uintptr_t); + if (skip_empty && val_len == 0) { + e.skip = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + e.ip += sizeof(uintptr_t); + + e.skip = 0; + + continue; + } + *e.pos++ = (u_char) key_len; if (val_len > 127) { @@ -1199,7 +1254,7 @@ ngx_http_fastcgi_process_header(ngx_http_request_t *r) if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream closed prematurely FastCGI stdout"); + "upstream prematurely closed FastCGI stdout"); return NGX_HTTP_UPSTREAM_INVALID_HEADER; } @@ -1578,14 +1633,30 @@ ngx_http_fastcgi_process_header(ngx_http_request_t *r) static ngx_int_t +ngx_http_fastcgi_input_filter_init(void *data) +{ + ngx_http_request_t *r = data; + ngx_http_fastcgi_loc_conf_t *flcf; + + flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + + r->upstream->pipe->length = flcf->keep_conn ? + (off_t) sizeof(ngx_http_fastcgi_header_t) : -1; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) { - u_char *m, *msg; - ngx_int_t rc; - ngx_buf_t *b, **prev; - ngx_chain_t *cl; - ngx_http_request_t *r; - ngx_http_fastcgi_ctx_t *f; + u_char *m, *msg; + ngx_int_t rc; + ngx_buf_t *b, **prev; + ngx_chain_t *cl; + ngx_http_request_t *r; + ngx_http_fastcgi_ctx_t *f; + ngx_http_fastcgi_loc_conf_t *flcf; if (buf->pos == buf->last) { return NGX_OK; @@ -1593,6 +1664,7 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) r = p->input_ctx; f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); + flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); b = NULL; prev = &buf->shadow; @@ -1615,7 +1687,10 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) { f->state = ngx_http_fastcgi_st_version; - p->upstream_done = 1; + + if (!flcf->keep_conn) { + p->upstream_done = 1; + } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, "http fastcgi closed stdout"); @@ -1627,6 +1702,10 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) f->state = ngx_http_fastcgi_st_version; p->upstream_done = 1; + if (flcf->keep_conn) { + r->upstream->keepalive = 1; + } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, "http fastcgi sent end request"); @@ -1787,6 +1866,23 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) } + if (flcf->keep_conn) { + + /* set p->length, minimal amount of data we want to see */ + + if (f->state < ngx_http_fastcgi_st_data) { + p->length = 1; + + } else if (f->state == ngx_http_fastcgi_st_padding) { + p->length = f->padding; + + } else { + /* ngx_http_fastcgi_st_data */ + + p->length = f->length; + } + } + if (b) { b->shadow = buf; b->last_shadow = 1; @@ -2005,6 +2101,8 @@ ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf) conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; conf->upstream.no_cache = NGX_CONF_UNSET_PTR; conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; + conf->upstream.cache_lock = NGX_CONF_UNSET; + conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC; #endif conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; @@ -2017,6 +2115,8 @@ ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf) conf->catch_stderr = NGX_CONF_UNSET_PTR; + conf->keep_conn = NGX_CONF_UNSET; + ngx_str_set(&conf->upstream.module, "fastcgi"); return conf; @@ -2098,8 +2198,8 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->upstream.busy_buffers_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"fastcgi_busy_buffers_size\" must be equal or bigger than " - "maximum of the value of \"fastcgi_buffer_size\" and " + "\"fastcgi_busy_buffers_size\" must be equal to or greater than " + "the maximum of the value of \"fastcgi_buffer_size\" and " "one of the \"fastcgi_buffers\""); return NGX_CONF_ERROR; @@ -2129,8 +2229,8 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->upstream.temp_file_write_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"fastcgi_temp_file_write_size\" must be equal or bigger than " - "maximum of the value of \"fastcgi_buffer_size\" and " + "\"fastcgi_temp_file_write_size\" must be equal to or greater " + "than the maximum of the value of \"fastcgi_buffer_size\" and " "one of the \"fastcgi_buffers\""); return NGX_CONF_ERROR; @@ -2153,8 +2253,8 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"fastcgi_max_temp_file_size\" must be equal to zero to disable " - "the temporary files usage or must be equal or bigger than " - "maximum of the value of \"fastcgi_buffer_size\" and " + "temporary files usage or must be equal to or greater than " + "the maximum of the value of \"fastcgi_buffer_size\" and " "one of the \"fastcgi_buffers\""); return NGX_CONF_ERROR; @@ -2244,6 +2344,12 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) conf->cache_key = prev->cache_key; } + ngx_conf_merge_value(conf->upstream.cache_lock, + prev->upstream.cache_lock, 0); + + ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout, + prev->upstream.cache_lock_timeout, 5000); + #endif ngx_conf_merge_value(conf->upstream.pass_request_headers, @@ -2256,6 +2362,8 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL); + ngx_conf_merge_value(conf->keep_conn, prev->keep_conn, 0); + ngx_conf_merge_str_value(conf->index, prev->index, ""); @@ -2313,9 +2421,9 @@ ngx_http_fastcgi_merge_params(ngx_conf_t *cf, #if (NGX_HTTP_CACHE) ngx_array_t params_merged; #endif - ngx_keyval_t *src; ngx_hash_key_t *hk; ngx_hash_init_t hash; + ngx_http_upstream_param_t *src; ngx_http_script_compile_t sc; ngx_http_script_copy_code_t *copy; @@ -2324,7 +2432,8 @@ ngx_http_fastcgi_merge_params(ngx_conf_t *cf, if (prev->headers_hash.buckets #if (NGX_HTTP_CACHE) - && ((conf->upstream.cache == NULL) == (prev->upstream.cache == NULL)) + && ((conf->upstream.cache == NULL) + == (prev->upstream.cache == NULL)) #endif ) { @@ -2376,9 +2485,11 @@ ngx_http_fastcgi_merge_params(ngx_conf_t *cf, #if (NGX_HTTP_CACHE) if (conf->upstream.cache) { - ngx_keyval_t *h, *s; + ngx_keyval_t *h; + ngx_http_upstream_param_t *s; - if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t)) + if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, + sizeof(ngx_http_upstream_param_t)) != NGX_OK) { return NGX_ERROR; @@ -2412,7 +2523,9 @@ ngx_http_fastcgi_merge_params(ngx_conf_t *cf, return NGX_ERROR; } - *s = *h; + s->key = h->key; + s->value = h->value; + s->skip_empty = 0; next: @@ -2454,6 +2567,15 @@ ngx_http_fastcgi_merge_params(ngx_conf_t *cf, copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; copy->len = src[i].key.len; + copy = ngx_array_push_n(conf->params_len, + sizeof(ngx_http_script_copy_code_t)); + if (copy == NULL) { + return NGX_ERROR; + } + + copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; + copy->len = src[i].skip_empty; + size = (sizeof(ngx_http_script_copy_code_t) + src[i].key.len + sizeof(uintptr_t) - 1) diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_flv_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_flv_module.c index f6870235b34..719a0112499 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_flv_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_flv_module.c @@ -110,6 +110,10 @@ ngx_http_flv_handler(ngx_http_request_t *r) of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; + if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { @@ -127,6 +131,10 @@ ngx_http_flv_handler(ngx_http_request_t *r) break; case NGX_EACCES: +#if (NGX_HAVE_OPENAT) + case NGX_EMLINK: + case NGX_ELOOP: +#endif level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_geo_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_geo_module.c index 1d7599e5551..30b0e35a9aa 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_geo_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_geo_module.c @@ -566,7 +566,7 @@ ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, if (ctx->binary_include) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "binary geo range base \"%s\" may not be mixed with usual entries", + "binary geo range base \"%s\" cannot be mixed with usual entries", ctx->include_name.data); return NGX_CONF_ERROR; } @@ -1195,7 +1195,7 @@ ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, if (ctx->outside_entries) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "binary geo range base \"%s\" may not be mixed with usual entries", + "binary geo range base \"%s\" cannot be mixed with usual entries", name->data); rc = NGX_ERROR; goto done; @@ -1203,7 +1203,7 @@ ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, if (ctx->binary_include) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "second binary geo range base \"%s\" may not be mixed with \"%s\"", + "second binary geo range base \"%s\" cannot be mixed with \"%s\"", name->data, ctx->include_name.data); rc = NGX_ERROR; goto done; diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_gzip_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_gzip_filter_module.c index dccc554a312..128d3d9c89d 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_gzip_filter_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_gzip_filter_module.c @@ -379,7 +379,7 @@ ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) cl = NULL; - ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl, + ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl, (ngx_buf_tag_t) &ngx_http_gzip_filter_module); ctx->nomem = 0; } @@ -449,7 +449,7 @@ ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_http_gzip_filter_free_copy_buf(r, ctx); - ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, + ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out, (ngx_buf_tag_t) &ngx_http_gzip_filter_module); ctx->last_out = &ctx->out; @@ -759,6 +759,7 @@ static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) { int rc; + ngx_buf_t *b; ngx_chain_t *cl; ngx_http_gzip_conf_t *conf; @@ -770,7 +771,7 @@ ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) rc = deflate(&ctx->zstream, ctx->flush); - if (rc != Z_OK && rc != Z_STREAM_END) { + if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "deflate() failed: %d, %d", ctx->flush, rc); return NGX_ERROR; @@ -819,8 +820,6 @@ ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) 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); @@ -828,7 +827,22 @@ ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) return NGX_ERROR; } - cl->buf = ctx->out_buf; + b = ctx->out_buf; + + if (ngx_buf_size(b) == 0) { + + b = ngx_calloc_buf(ctx->request->pool); + if (b == NULL) { + return NGX_ERROR; + } + + } else { + ctx->zstream.avail_out = 0; + } + + b->flush = 1; + + cl->buf = b; cl->next = NULL; *ctx->last_out = cl; ctx->last_out = &cl->next; diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_gzip_static_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_gzip_static_module.c index 18c28d8f581..46ce8f3f327 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_gzip_static_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_gzip_static_module.c @@ -130,6 +130,10 @@ ngx_http_gzip_static_handler(ngx_http_request_t *r) of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; + if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { @@ -145,6 +149,10 @@ ngx_http_gzip_static_handler(ngx_http_request_t *r) return NGX_DECLINED; case NGX_EACCES: +#if (NGX_HAVE_OPENAT) + case NGX_EMLINK: + case NGX_ELOOP: +#endif level = NGX_LOG_ERR; break; diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_headers_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_headers_filter_module.c index a7065946305..93899343437 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_headers_filter_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_headers_filter_module.c @@ -25,23 +25,25 @@ typedef struct { struct ngx_http_header_val_s { ngx_http_complex_value_t value; - ngx_uint_t hash; ngx_str_t key; ngx_http_set_header_pt handler; ngx_uint_t offset; }; -#define NGX_HTTP_EXPIRES_OFF 0 -#define NGX_HTTP_EXPIRES_EPOCH 1 -#define NGX_HTTP_EXPIRES_MAX 2 -#define NGX_HTTP_EXPIRES_ACCESS 3 -#define NGX_HTTP_EXPIRES_MODIFIED 4 -#define NGX_HTTP_EXPIRES_DAILY 5 +typedef enum { + NGX_HTTP_EXPIRES_OFF, + NGX_HTTP_EXPIRES_EPOCH, + NGX_HTTP_EXPIRES_MAX, + NGX_HTTP_EXPIRES_ACCESS, + NGX_HTTP_EXPIRES_MODIFIED, + NGX_HTTP_EXPIRES_DAILY, + NGX_HTTP_EXPIRES_UNSET +} ngx_http_expires_t; typedef struct { - ngx_uint_t expires; + ngx_http_expires_t expires; time_t expires_time; ngx_array_t *headers; } ngx_http_headers_conf_t; @@ -51,6 +53,8 @@ static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf); static ngx_int_t ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv, ngx_str_t *value); +static ngx_int_t ngx_http_add_header(ngx_http_request_t *r, + ngx_http_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv, ngx_str_t *value); @@ -313,7 +317,7 @@ ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv, return NGX_ERROR; } - h->hash = hv->hash; + h->hash = 1; h->key = hv->key; h->value = *value; } @@ -366,16 +370,11 @@ ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv, { ngx_table_elt_t *h, **old; - if (hv->offset) { - old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset); - - } else { - old = NULL; - } + old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset); r->headers_out.last_modified_time = -1; - if (old == NULL || *old == NULL) { + if (*old == NULL) { if (value->len == 0) { return NGX_OK; @@ -386,6 +385,8 @@ ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv, return NGX_ERROR; } + *old = h; + } else { h = *old; @@ -395,7 +396,7 @@ ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv, } } - h->hash = hv->hash; + h->hash = 1; h->key = hv->key; h->value = *value; @@ -420,7 +421,7 @@ ngx_http_headers_create_conf(ngx_conf_t *cf) * conf->expires_time = 0; */ - conf->expires = NGX_CONF_UNSET_UINT; + conf->expires = NGX_HTTP_EXPIRES_UNSET; return conf; } @@ -432,11 +433,11 @@ ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_headers_conf_t *prev = parent; ngx_http_headers_conf_t *conf = child; - if (conf->expires == NGX_CONF_UNSET_UINT) { + if (conf->expires == NGX_HTTP_EXPIRES_UNSET) { conf->expires = prev->expires; conf->expires_time = prev->expires_time; - if (conf->expires == NGX_CONF_UNSET_UINT) { + if (conf->expires == NGX_HTTP_EXPIRES_UNSET) { conf->expires = NGX_HTTP_EXPIRES_OFF; } } @@ -467,7 +468,7 @@ ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_uint_t minus, n; ngx_str_t *value; - if (hcf->expires != NGX_CONF_UNSET_UINT) { + if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) { return "is duplicate"; } @@ -532,7 +533,7 @@ ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) hcf->expires_time = ngx_parse_time(&value[n], 1); - if (hcf->expires_time == NGX_ERROR) { + if (hcf->expires_time == (time_t) NGX_ERROR) { return "invalid value"; } @@ -542,10 +543,6 @@ ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return "daily time value must be less than 24 hours"; } - if (hcf->expires_time == NGX_PARSE_LARGE_TIME) { - return "value must be less than 68 years"; - } - if (minus) { hcf->expires_time = - hcf->expires_time; } @@ -580,7 +577,6 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - hv->hash = 1; hv->key = value[1]; hv->handler = ngx_http_add_header; hv->offset = 0; diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_image_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_image_filter_module.c index dd3c9cee5ae..c853c33d092 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_image_filter_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_image_filter_module.c @@ -817,9 +817,7 @@ transparent: resize = 0; - if ((ngx_uint_t) (dx * 100 / dy) - < ctx->max_width * 100 / ctx->max_height) - { + if ((double) dx / dy < (double) ctx->max_width / ctx->max_height) { if ((ngx_uint_t) dx > ctx->max_width) { dy = dy * ctx->max_width / dx; dy = dy ? dy : 1; diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_index_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_index_module.c index 5d91b011820..cfe4ba6cc63 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_index_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_index_module.c @@ -210,6 +210,10 @@ ngx_http_index_handler(ngx_http_request_t *r) of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; + if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { @@ -220,6 +224,14 @@ ngx_http_index_handler(ngx_http_request_t *r) return NGX_HTTP_INTERNAL_SERVER_ERROR; } +#if (NGX_HAVE_OPENAT) + if (of.err == NGX_EMLINK + || of.err == NGX_ELOOP) + { + return NGX_HTTP_FORBIDDEN; + } +#endif + if (of.err == NGX_ENOTDIR || of.err == NGX_ENAMETOOLONG || of.err == NGX_EACCES) @@ -297,11 +309,23 @@ ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf, of.valid = clcf->open_file_cache_valid; of.errors = clcf->open_file_cache_errors; + if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool) != NGX_OK) { if (of.err) { +#if (NGX_HAVE_OPENAT) + if (of.err == NGX_EMLINK + || of.err == NGX_ELOOP) + { + return NGX_HTTP_FORBIDDEN; + } +#endif + if (of.err == NGX_ENOENT) { *last = c; return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT); diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_limit_conn_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_limit_conn_module.c new file mode 100644 index 00000000000..c23c046ed4d --- /dev/null +++ b/usr.sbin/nginx/src/http/modules/ngx_http_limit_conn_module.c @@ -0,0 +1,747 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + +typedef struct { + u_char color; + u_char len; + u_short conn; + u_char data[1]; +} ngx_http_limit_conn_node_t; + + +typedef struct { + ngx_shm_zone_t *shm_zone; + ngx_rbtree_node_t *node; +} ngx_http_limit_conn_cleanup_t; + + +typedef struct { + ngx_rbtree_t *rbtree; + ngx_int_t index; + ngx_str_t var; +} ngx_http_limit_conn_ctx_t; + + +typedef struct { + ngx_shm_zone_t *shm_zone; + ngx_uint_t conn; +} ngx_http_limit_conn_limit_t; + + +typedef struct { + ngx_array_t limits; + ngx_uint_t log_level; +} ngx_http_limit_conn_conf_t; + + +static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, + ngx_http_variable_value_t *vv, uint32_t hash); +static void ngx_http_limit_conn_cleanup(void *data); +static ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool); + +static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf); +static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf); + + +static ngx_conf_deprecated_t ngx_conf_deprecated_limit_zone = { + ngx_conf_deprecated, "limit_zone", "limit_conn_zone" +}; + + +static ngx_conf_enum_t ngx_http_limit_conn_log_levels[] = { + { ngx_string("info"), NGX_LOG_INFO }, + { ngx_string("notice"), NGX_LOG_NOTICE }, + { ngx_string("warn"), NGX_LOG_WARN }, + { ngx_string("error"), NGX_LOG_ERR }, + { ngx_null_string, 0 } +}; + + +static ngx_command_t ngx_http_limit_conn_commands[] = { + + { ngx_string("limit_conn_zone"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2, + ngx_http_limit_conn_zone, + 0, + 0, + NULL }, + + { ngx_string("limit_zone"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3, + ngx_http_limit_zone, + 0, + 0, + NULL }, + + { ngx_string("limit_conn"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_http_limit_conn, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("limit_conn_log_level"), + 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_limit_conn_conf_t, log_level), + &ngx_http_limit_conn_log_levels }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_limit_conn_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_limit_conn_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_limit_conn_create_conf, /* create location configuration */ + ngx_http_limit_conn_merge_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_limit_conn_module = { + NGX_MODULE_V1, + &ngx_http_limit_conn_module_ctx, /* module context */ + ngx_http_limit_conn_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_conn_handler(ngx_http_request_t *r) +{ + size_t len, n; + uint32_t hash; + ngx_uint_t i; + ngx_slab_pool_t *shpool; + ngx_rbtree_node_t *node; + ngx_pool_cleanup_t *cln; + ngx_http_variable_value_t *vv; + ngx_http_limit_conn_ctx_t *ctx; + ngx_http_limit_conn_node_t *lc; + ngx_http_limit_conn_conf_t *lccf; + ngx_http_limit_conn_limit_t *limits; + ngx_http_limit_conn_cleanup_t *lccln; + + if (r->main->limit_conn_set) { + return NGX_DECLINED; + } + + lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module); + limits = lccf->limits.elts; + + for (i = 0; i < lccf->limits.nelts; i++) { + ctx = limits[i].shm_zone->data; + + vv = ngx_http_get_indexed_variable(r, ctx->index); + + if (vv == NULL || vv->not_found) { + continue; + } + + len = vv->len; + + if (len == 0) { + continue; + } + + if (len > 255) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "the value of the \"%V\" variable " + "is more than 255 bytes: \"%v\"", + &ctx->var, vv); + continue; + } + + r->main->limit_conn_set = 1; + + hash = ngx_crc32_short(vv->data, len); + + shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr; + + ngx_shmtx_lock(&shpool->mutex); + + node = ngx_http_limit_conn_lookup(ctx->rbtree, vv, hash); + + if (node == NULL) { + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_http_limit_conn_node_t, data) + + len; + + node = ngx_slab_alloc_locked(shpool, n); + + if (node == NULL) { + ngx_shmtx_unlock(&shpool->mutex); + ngx_http_limit_conn_cleanup_all(r->pool); + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + + lc = (ngx_http_limit_conn_node_t *) &node->color; + + node->key = hash; + lc->len = (u_char) len; + lc->conn = 1; + ngx_memcpy(lc->data, vv->data, len); + + ngx_rbtree_insert(ctx->rbtree, node); + + } else { + + lc = (ngx_http_limit_conn_node_t *) &node->color; + + if ((ngx_uint_t) lc->conn >= limits[i].conn) { + + ngx_shmtx_unlock(&shpool->mutex); + + ngx_log_error(lccf->log_level, r->connection->log, 0, + "limiting connections by zone \"%V\"", + &limits[i].shm_zone->shm.name); + + ngx_http_limit_conn_cleanup_all(r->pool); + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + + lc->conn++; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "limit zone: %08XD %d", node->key, lc->conn); + + ngx_shmtx_unlock(&shpool->mutex); + + cln = ngx_pool_cleanup_add(r->pool, + sizeof(ngx_http_limit_conn_cleanup_t)); + if (cln == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + cln->handler = ngx_http_limit_conn_cleanup; + lccln = cln->data; + + lccln->shm_zone = limits[i].shm_zone; + lccln->node = node; + } + + return NGX_DECLINED; +} + + +static void +ngx_http_limit_conn_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_conn_node_t *lcn, *lcnt; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + lcn = (ngx_http_limit_conn_node_t *) &node->color; + lcnt = (ngx_http_limit_conn_node_t *) &temp->color; + + p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->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_rbtree_node_t * +ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_http_variable_value_t *vv, + uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_http_limit_conn_node_t *lcn; + + node = rbtree->root; + sentinel = rbtree->sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + lcn = (ngx_http_limit_conn_node_t *) &node->color; + + rc = ngx_memn2cmp(vv->data, lcn->data, + (size_t) vv->len, (size_t) lcn->len); + if (rc == 0) { + return node; + } + + node = (rc < 0) ? node->left : node->right; + } + + return NULL; +} + + +static void +ngx_http_limit_conn_cleanup(void *data) +{ + ngx_http_limit_conn_cleanup_t *lccln = data; + + ngx_slab_pool_t *shpool; + ngx_rbtree_node_t *node; + ngx_http_limit_conn_ctx_t *ctx; + ngx_http_limit_conn_node_t *lc; + + ctx = lccln->shm_zone->data; + shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr; + node = lccln->node; + lc = (ngx_http_limit_conn_node_t *) &node->color; + + ngx_shmtx_lock(&shpool->mutex); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0, + "limit zone cleanup: %08XD %d", node->key, lc->conn); + + lc->conn--; + + if (lc->conn == 0) { + ngx_rbtree_delete(ctx->rbtree, node); + ngx_slab_free_locked(shpool, node); + } + + ngx_shmtx_unlock(&shpool->mutex); +} + + +static ngx_inline void +ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool) +{ + ngx_pool_cleanup_t *cln; + + cln = pool->cleanup; + + while (cln && cln->handler == ngx_http_limit_conn_cleanup) { + ngx_http_limit_conn_cleanup(cln->data); + cln = cln->next; + } + + pool->cleanup = cln; +} + + +static ngx_int_t +ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_http_limit_conn_ctx_t *octx = data; + + size_t len; + ngx_slab_pool_t *shpool; + ngx_rbtree_node_t *sentinel; + ngx_http_limit_conn_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_conn_zone \"%V\" uses the \"%V\" variable " + "while previously it used the \"%V\" variable", + &shm_zone->shm.name, &ctx->var, &octx->var); + return NGX_ERROR; + } + + ctx->rbtree = octx->rbtree; + + return NGX_OK; + } + + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + if (shm_zone->shm.exists) { + ctx->rbtree = shpool->data; + + return NGX_OK; + } + + ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); + if (ctx->rbtree == NULL) { + return NGX_ERROR; + } + + shpool->data = ctx->rbtree; + + sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); + if (sentinel == NULL) { + return NGX_ERROR; + } + + ngx_rbtree_init(ctx->rbtree, sentinel, + ngx_http_limit_conn_rbtree_insert_value); + + len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len; + + shpool->log_ctx = ngx_slab_alloc(shpool, len); + if (shpool->log_ctx == NULL) { + return NGX_ERROR; + } + + ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", + &shm_zone->shm.name); + + return NGX_OK; +} + + +static void * +ngx_http_limit_conn_create_conf(ngx_conf_t *cf) +{ + ngx_http_limit_conn_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->limits.elts = NULL; + */ + + conf->log_level = NGX_CONF_UNSET_UINT; + + return conf; +} + + +static char * +ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_limit_conn_conf_t *prev = parent; + ngx_http_limit_conn_conf_t *conf = child; + + if (conf->limits.elts == NULL) { + conf->limits = prev->limits; + } + + ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR); + + return NGX_CONF_OK; +} + + +static char * +ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + u_char *p; + ssize_t size; + ngx_str_t *value, name, s; + ngx_uint_t i; + ngx_shm_zone_t *shm_zone; + ngx_http_limit_conn_ctx_t *ctx; + + value = cf->args->elts; + + ctx = NULL; + size = 0; + 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 == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + name.len = p - name.data; + + s.data = p + 1; + s.len = value[i].data + value[i].len - s.data; + + size = ngx_parse_size(&s); + + if (size == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (size < (ssize_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "zone \"%V\" is too small", &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_conn_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) { + 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 %V \"%V\"", + &cmd->name, &name); + return NGX_CONF_ERROR; + } + + shm_zone = ngx_shared_memory_add(cf, &name, size, + &ngx_http_limit_conn_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, + "%V \"%V\" is already bound to variable \"%V\"", + &cmd->name, &name, &ctx->var); + return NGX_CONF_ERROR; + } + + shm_zone->init = ngx_http_limit_conn_init_zone; + shm_zone->data = ctx; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ssize_t n; + ngx_str_t *value; + ngx_shm_zone_t *shm_zone; + ngx_http_limit_conn_ctx_t *ctx; + + ngx_conf_deprecated(cf, &ngx_conf_deprecated_limit_zone, NULL); + + value = cf->args->elts; + + if (value[2].data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + + value[2].len--; + value[2].data++; + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + ctx->index = ngx_http_get_variable_index(cf, &value[2]); + if (ctx->index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + ctx->var = value[2]; + + n = ngx_parse_size(&value[3]); + + if (n == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid size of limit_zone \"%V\"", &value[3]); + return NGX_CONF_ERROR; + } + + if (n < (ngx_int_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "limit_zone \"%V\" is too small", &value[1]); + return NGX_CONF_ERROR; + } + + + shm_zone = ngx_shared_memory_add(cf, &value[1], n, + &ngx_http_limit_conn_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_zone \"%V\" is already bound to variable \"%V\"", + &value[1], &ctx->var); + return NGX_CONF_ERROR; + } + + shm_zone->init = ngx_http_limit_conn_init_zone; + shm_zone->data = ctx; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_shm_zone_t *shm_zone; + ngx_http_limit_conn_conf_t *lccf = conf; + ngx_http_limit_conn_limit_t *limit, *limits; + + ngx_str_t *value; + ngx_int_t n; + ngx_uint_t i; + + value = cf->args->elts; + + shm_zone = ngx_shared_memory_add(cf, &value[1], 0, + &ngx_http_limit_conn_module); + if (shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + limits = lccf->limits.elts; + + if (limits == NULL) { + if (ngx_array_init(&lccf->limits, cf->pool, 1, + sizeof(ngx_http_limit_conn_limit_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + for (i = 0; i < lccf->limits.nelts; i++) { + if (shm_zone == limits[i].shm_zone) { + return "is duplicate"; + } + } + + n = ngx_atoi(value[2].data, value[2].len); + if (n <= 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number of connections \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + + if (n > 65535) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "connection limit must be less 65536"); + return NGX_CONF_ERROR; + } + + limit = ngx_array_push(&lccf->limits); + limit->conn = n; + limit->shm_zone = shm_zone; + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_limit_conn_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_conn_handler; + + return NGX_OK; +} diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_limit_req_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_limit_req_module.c index 24707ee7f4f..18db7154958 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_limit_req_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_limit_req_module.c @@ -18,6 +18,7 @@ typedef struct { ngx_msec_t last; /* integer value, 1 corresponds to 0.001 r/s */ ngx_uint_t excess; + ngx_uint_t count; u_char data[1]; } ngx_http_limit_req_node_t; @@ -36,6 +37,7 @@ typedef struct { ngx_uint_t rate; ngx_int_t index; ngx_str_t var; + ngx_http_limit_req_node_t *node; } ngx_http_limit_req_ctx_t; @@ -43,16 +45,23 @@ typedef struct { ngx_shm_zone_t *shm_zone; /* integer value, 1 corresponds to 0.001 r/s */ ngx_uint_t burst; + ngx_uint_t nodelay; /* unsigned nodelay:1 */ +} ngx_http_limit_req_limit_t; + + +typedef struct { + ngx_array_t limits; ngx_uint_t limit_log_level; ngx_uint_t delay_log_level; - - 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_uint_t *ep); +static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, + ngx_uint_t hash, u_char *data, size_t len, ngx_uint_t *ep, + ngx_uint_t account); +static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, + ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit); static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n); @@ -136,123 +145,124 @@ ngx_module_t ngx_http_limit_req_module = { 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; + size_t len; + uint32_t hash; + ngx_int_t rc; + ngx_uint_t n, excess; + ngx_msec_t delay; + ngx_http_variable_value_t *vv; + ngx_http_limit_req_ctx_t *ctx; + ngx_http_limit_req_conf_t *lrcf; + ngx_http_limit_req_limit_t *limit, *limits; if (r->main->limit_req_set) { return NGX_DECLINED; } lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); + limits = lrcf->limits.elts; - if (lrcf->shm_zone == NULL) { - return NGX_DECLINED; - } + excess = 0; - ctx = lrcf->shm_zone->data; + rc = NGX_DECLINED; - vv = ngx_http_get_indexed_variable(r, ctx->index); +#if (NGX_SUPPRESS_WARN) + limit = NULL; +#endif - if (vv == NULL || vv->not_found) { - return NGX_DECLINED; - } + for (n = 0; n < lrcf->limits.nelts; n++) { - len = vv->len; + limit = &limits[n]; - if (len == 0) { - return NGX_DECLINED; - } + ctx = limit->shm_zone->data; - 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; - } + vv = ngx_http_get_indexed_variable(r, ctx->index); - r->main->limit_req_set = 1; - - hash = ngx_crc32_short(vv->data, len); + if (vv == NULL || vv->not_found) { + continue; + } - ngx_shmtx_lock(&ctx->shpool->mutex); + len = vv->len; - ngx_http_limit_req_expire(ctx, 1); + if (len == 0) { + continue; + } - rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &excess); + 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); + continue; + } - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000); + hash = ngx_crc32_short(vv->data, len); - if (rc == NGX_DECLINED) { + ngx_shmtx_lock(&ctx->shpool->mutex); - n = offsetof(ngx_rbtree_node_t, color) - + offsetof(ngx_http_limit_req_node_t, data) - + len; + rc = ngx_http_limit_req_lookup(limit, hash, vv->data, len, &excess, + (n == lrcf->limits.nelts - 1)); - node = ngx_slab_alloc_locked(ctx->shpool, n); - if (node == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); - ngx_http_limit_req_expire(ctx, 0); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "limit_req[%ui]: %i %ui.%03ui", + n, rc, excess / 1000, excess % 1000); - node = ngx_slab_alloc_locked(ctx->shpool, n); - if (node == NULL) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - return NGX_HTTP_SERVICE_UNAVAILABLE; - } + if (rc != NGX_AGAIN) { + break; } + } - lr = (ngx_http_limit_req_node_t *) &node->color; + if (rc == NGX_DECLINED) { + return NGX_DECLINED; + } - node->key = hash; - lr->len = (u_char) len; + r->main->limit_req_set = 1; - tp = ngx_timeofday(); - lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + if (rc == NGX_BUSY || rc == NGX_ERROR) { - lr->excess = 0; - ngx_memcpy(lr->data, vv->data, len); + if (rc == NGX_BUSY) { + ngx_log_error(lrcf->limit_log_level, r->connection->log, 0, + "limiting requests, excess: %ui.%03ui by zone \"%V\"", + excess / 1000, excess % 1000, + &limit->shm_zone->shm.name); + } - ngx_rbtree_insert(&ctx->sh->rbtree, node); + while (n--) { + ctx = limits[n].shm_zone->data; - ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); + if (ctx->node == NULL) { + continue; + } - ngx_shmtx_unlock(&ctx->shpool->mutex); + ngx_shmtx_lock(&ctx->shpool->mutex); - return NGX_DECLINED; - } + ctx->node->count--; - ngx_shmtx_unlock(&ctx->shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); - if (rc == NGX_OK) { - return NGX_DECLINED; + ctx->node = NULL; + } + + return NGX_HTTP_SERVICE_UNAVAILABLE; } - if (rc == NGX_BUSY) { - ngx_log_error(lrcf->limit_log_level, r->connection->log, 0, - "limiting requests, excess: %ui.%03ui by zone \"%V\"", - excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name); + /* rc == NGX_AGAIN || rc == NGX_OK */ - return NGX_HTTP_SERVICE_UNAVAILABLE; + if (rc == NGX_AGAIN) { + excess = 0; } - /* rc == NGX_AGAIN */ + delay = ngx_http_limit_req_account(limits, n, &excess, &limit); - if (lrcf->nodelay) { + if (!delay) { return NGX_DECLINED; } ngx_log_error(lrcf->delay_log_level, r->connection->log, 0, "delaying request, excess: %ui.%03ui, by zone \"%V\"", - excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name); + excess / 1000, excess % 1000, &limit->shm_zone->shm.name); if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -260,8 +270,7 @@ ngx_http_limit_req_handler(ngx_http_request_t *r) 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 * 1000 / ctx->rate); + ngx_add_timer(r->connection->write, delay); return NGX_AGAIN; } @@ -342,9 +351,10 @@ ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp, 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_uint_t *ep) +ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash, + u_char *data, size_t len, ngx_uint_t *ep, ngx_uint_t account) { + size_t size; ngx_int_t rc, excess; ngx_time_t *tp; ngx_msec_t now; @@ -353,7 +363,10 @@ ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash, ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; - ctx = lrcf->shm_zone->data; + tp = ngx_timeofday(); + now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + + ctx = limit->shm_zone->data; node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; @@ -380,9 +393,6 @@ ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash, ngx_queue_remove(&lr->queue); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); - 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; @@ -393,18 +403,21 @@ ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash, *ep = excess; - if ((ngx_uint_t) excess > lrcf->burst) { + if ((ngx_uint_t) excess > limit->burst) { return NGX_BUSY; } - lr->excess = excess; - lr->last = now; - - if (excess) { - return NGX_AGAIN; + if (account) { + lr->excess = excess; + lr->last = now; + return NGX_OK; } - return NGX_OK; + lr->count++; + + ctx->node = lr; + + return NGX_AGAIN; } node = (rc < 0) ? node->left : node->right; @@ -412,7 +425,115 @@ ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash, *ep = 0; - return NGX_DECLINED; + size = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_http_limit_req_node_t, data) + + len; + + ngx_http_limit_req_expire(ctx, 1); + + node = ngx_slab_alloc_locked(ctx->shpool, size); + + if (node == NULL) { + ngx_http_limit_req_expire(ctx, 0); + + node = ngx_slab_alloc_locked(ctx->shpool, size); + if (node == NULL) { + return NGX_ERROR; + } + } + + node->key = hash; + + ngx_rbtree_insert(&ctx->sh->rbtree, node); + + lr = (ngx_http_limit_req_node_t *) &node->color; + + ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); + + lr->len = (u_char) len; + lr->excess = 0; + + ngx_memcpy(lr->data, data, len); + + if (account) { + lr->last = now; + lr->count = 0; + return NGX_OK; + } + + lr->last = 0; + lr->count = 1; + + ctx->node = lr; + + return NGX_AGAIN; +} + + +static ngx_msec_t +ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n, + ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit) +{ + ngx_int_t excess; + ngx_time_t *tp; + ngx_msec_t now, delay, max_delay; + ngx_msec_int_t ms; + ngx_http_limit_req_ctx_t *ctx; + ngx_http_limit_req_node_t *lr; + + excess = *ep; + + if (excess == 0 || (*limit)->nodelay) { + max_delay = 0; + + } else { + ctx = (*limit)->shm_zone->data; + max_delay = excess * 1000 / ctx->rate; + } + + while (n--) { + ctx = limits[n].shm_zone->data; + lr = ctx->node; + + if (lr == NULL) { + continue; + } + + ngx_shmtx_lock(&ctx->shpool->mutex); + + 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->last = now; + lr->excess = excess; + lr->count--; + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + ctx->node = NULL; + + if (limits[n].nodelay) { + continue; + } + + delay = excess * 1000 / ctx->rate; + + if (delay > max_delay) { + max_delay = delay; + *ep = excess; + *limit = &limits[n]; + } + } + + return max_delay; } @@ -447,6 +568,16 @@ ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue); + if (lr->count) { + + /* + * There is not much sense in looking further, + * because we bump nodes on the lookup stage. + */ + + return; + } + if (n++ != 0) { ms = (ngx_msec_int_t) (now - lr->last); @@ -547,9 +678,7 @@ ngx_http_limit_req_create_conf(ngx_conf_t *cf) /* * set by ngx_pcalloc(): * - * conf->shm_zone = NULL; - * conf->burst = 0; - * conf->nodelay = 0; + * conf->limits.elts = NULL; */ conf->limit_log_level = NGX_CONF_UNSET_UINT; @@ -564,10 +693,8 @@ 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->shm_zone = prev->shm_zone; - conf->burst = prev->burst; - conf->nodelay = prev->nodelay; + if (conf->limits.elts == NULL) { + conf->limits = prev->limits; } ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level, @@ -584,7 +711,8 @@ static char * ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { u_char *p; - size_t size, len; + size_t len; + ssize_t size; ngx_str_t *value, name, s; ngx_int_t rate, scale; ngx_uint_t i; @@ -607,25 +735,32 @@ ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) p = (u_char *) ngx_strchr(name.data, ':'); - if (p) { - *p = '\0'; + if (p == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } - name.len = p - name.data; + name.len = p - name.data; - p++; + s.data = p + 1; + s.len = value[i].data + value[i].len - s.data; - s.len = value[i].data + value[i].len - p; - s.data = p; + size = ngx_parse_size(&s); - size = ngx_parse_size(&s); - if (size > 8191) { - continue; - } + if (size == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; } - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid zone size \"%V\"", &value[i]); - return NGX_CONF_ERROR; + if (size < (ssize_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "zone \"%V\" is too small", &value[i]); + return NGX_CONF_ERROR; + } + + continue; } if (ngx_strncmp(value[i].data, "rate=", 5) == 0) { @@ -677,7 +812,7 @@ ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - if (name.len == 0 || size == 0) { + if (name.len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%V\" must have \"zone\" parameter", &cmd->name); @@ -686,8 +821,8 @@ ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (ctx == NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "no variable is defined for limit_req_zone \"%V\"", - &cmd->name); + "no variable is defined for %V \"%V\"", + &cmd->name, &name); return NGX_CONF_ERROR; } @@ -703,8 +838,8 @@ ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 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); + "%V \"%V\" is already bound to variable \"%V\"", + &cmd->name, &name, &ctx->var); return NGX_CONF_ERROR; } @@ -720,17 +855,17 @@ 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"; - } + ngx_int_t burst; + ngx_str_t *value, s; + ngx_uint_t i, nodelay; + ngx_shm_zone_t *shm_zone; + ngx_http_limit_req_limit_t *limit, *limits; value = cf->args->elts; + shm_zone = NULL; burst = 0; + nodelay = 0; for (i = 1; i < cf->args->nelts; i++) { @@ -739,9 +874,9 @@ ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 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) { + shm_zone = ngx_shared_memory_add(cf, &s, 0, + &ngx_http_limit_req_module); + if (shm_zone == NULL) { return NGX_CONF_ERROR; } @@ -761,7 +896,7 @@ ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) { - lrcf->nodelay = 1; + nodelay = 1; continue; } @@ -770,21 +905,42 @@ ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - if (lrcf->shm_zone == NULL) { + if (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) { + if (shm_zone->data == NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unknown limit_req_zone \"%V\"", - &lrcf->shm_zone->shm.name); + &shm_zone->shm.name); return NGX_CONF_ERROR; } - lrcf->burst = burst * 1000; + limits = lrcf->limits.elts; + + if (limits == NULL) { + if (ngx_array_init(&lrcf->limits, cf->pool, 1, + sizeof(ngx_http_limit_req_limit_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + for (i = 0; i < lrcf->limits.nelts; i++) { + if (shm_zone == limits[i].shm_zone) { + return "is duplicate"; + } + } + + limit = ngx_array_push(&lrcf->limits); + + limit->shm_zone = shm_zone; + limit->burst = burst * 1000; + limit->nodelay = nodelay; return NGX_CONF_OK; } diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_limit_zone_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_limit_zone_module.c deleted file mode 100644 index 5ebedcd902b..00000000000 --- a/usr.sbin/nginx/src/http/modules/ngx_http_limit_zone_module.c +++ /dev/null @@ -1,553 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - * Copyright (C) Nginx, Inc. - */ - - -#include <ngx_config.h> -#include <ngx_core.h> -#include <ngx_http.h> - - -typedef struct { - u_char color; - u_char len; - u_short conn; - u_char data[1]; -} ngx_http_limit_zone_node_t; - - -typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_rbtree_node_t *node; -} ngx_http_limit_zone_cleanup_t; - - -typedef struct { - ngx_rbtree_t *rbtree; - ngx_int_t index; - ngx_str_t var; -} ngx_http_limit_zone_ctx_t; - - -typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_uint_t conn; - ngx_uint_t log_level; -} ngx_http_limit_zone_conf_t; - - -static void ngx_http_limit_zone_cleanup(void *data); - -static void *ngx_http_limit_zone_create_conf(ngx_conf_t *cf); -static char *ngx_http_limit_zone_merge_conf(ngx_conf_t *cf, void *parent, - void *child); -static char *ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); -static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); -static ngx_int_t ngx_http_limit_zone_init(ngx_conf_t *cf); - - -static ngx_conf_enum_t ngx_http_limit_conn_log_levels[] = { - { ngx_string("info"), NGX_LOG_INFO }, - { ngx_string("notice"), NGX_LOG_NOTICE }, - { ngx_string("warn"), NGX_LOG_WARN }, - { ngx_string("error"), NGX_LOG_ERR }, - { ngx_null_string, 0 } -}; - - -static ngx_command_t ngx_http_limit_zone_commands[] = { - - { ngx_string("limit_zone"), - NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3, - ngx_http_limit_zone, - 0, - 0, - NULL }, - - { ngx_string("limit_conn"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, - ngx_http_limit_conn, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("limit_conn_log_level"), - 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_limit_zone_conf_t, log_level), - &ngx_http_limit_conn_log_levels }, - - ngx_null_command -}; - - -static ngx_http_module_t ngx_http_limit_zone_module_ctx = { - NULL, /* preconfiguration */ - ngx_http_limit_zone_init, /* postconfiguration */ - - NULL, /* create main configuration */ - NULL, /* init main configuration */ - - NULL, /* create server configuration */ - NULL, /* merge server configuration */ - - ngx_http_limit_zone_create_conf, /* create location configuration */ - ngx_http_limit_zone_merge_conf /* merge location configuration */ -}; - - -ngx_module_t ngx_http_limit_zone_module = { - NGX_MODULE_V1, - &ngx_http_limit_zone_module_ctx, /* module context */ - ngx_http_limit_zone_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_zone_handler(ngx_http_request_t *r) -{ - size_t len, n; - uint32_t hash; - ngx_int_t rc; - ngx_slab_pool_t *shpool; - ngx_rbtree_node_t *node, *sentinel; - ngx_pool_cleanup_t *cln; - ngx_http_variable_value_t *vv; - ngx_http_limit_zone_ctx_t *ctx; - ngx_http_limit_zone_node_t *lz; - ngx_http_limit_zone_conf_t *lzcf; - ngx_http_limit_zone_cleanup_t *lzcln; - - if (r->main->limit_zone_set) { - return NGX_DECLINED; - } - - lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_zone_module); - - if (lzcf->shm_zone == NULL) { - return NGX_DECLINED; - } - - ctx = lzcf->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 > 255) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "the value of the \"%V\" variable " - "is more than 255 bytes: \"%v\"", - &ctx->var, vv); - return NGX_DECLINED; - } - - r->main->limit_zone_set = 1; - - hash = ngx_crc32_short(vv->data, len); - - cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_zone_cleanup_t)); - if (cln == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - shpool = (ngx_slab_pool_t *) lzcf->shm_zone->shm.addr; - - ngx_shmtx_lock(&shpool->mutex); - - 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 */ - - lz = (ngx_http_limit_zone_node_t *) &node->color; - - rc = ngx_memn2cmp(vv->data, lz->data, len, (size_t) lz->len); - - if (rc == 0) { - if ((ngx_uint_t) lz->conn < lzcf->conn) { - lz->conn++; - goto done; - } - - ngx_shmtx_unlock(&shpool->mutex); - - ngx_log_error(lzcf->log_level, r->connection->log, 0, - "limiting connections by zone \"%V\"", - &lzcf->shm_zone->shm.name); - - return NGX_HTTP_SERVICE_UNAVAILABLE; - } - - node = (rc < 0) ? node->left : node->right; - } - - n = offsetof(ngx_rbtree_node_t, color) - + offsetof(ngx_http_limit_zone_node_t, data) - + len; - - node = ngx_slab_alloc_locked(shpool, n); - if (node == NULL) { - ngx_shmtx_unlock(&shpool->mutex); - return NGX_HTTP_SERVICE_UNAVAILABLE; - } - - lz = (ngx_http_limit_zone_node_t *) &node->color; - - node->key = hash; - lz->len = (u_char) len; - lz->conn = 1; - ngx_memcpy(lz->data, vv->data, len); - - ngx_rbtree_insert(ctx->rbtree, node); - -done: - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "limit zone: %08XD %d", node->key, lz->conn); - - ngx_shmtx_unlock(&shpool->mutex); - - cln->handler = ngx_http_limit_zone_cleanup; - lzcln = cln->data; - - lzcln->shm_zone = lzcf->shm_zone; - lzcln->node = node; - - return NGX_DECLINED; -} - - -static void -ngx_http_limit_zone_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_zone_node_t *lzn, *lznt; - - for ( ;; ) { - - if (node->key < temp->key) { - - p = &temp->left; - - } else if (node->key > temp->key) { - - p = &temp->right; - - } else { /* node->key == temp->key */ - - lzn = (ngx_http_limit_zone_node_t *) &node->color; - lznt = (ngx_http_limit_zone_node_t *) &temp->color; - - p = (ngx_memn2cmp(lzn->data, lznt->data, lzn->len, lznt->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 void -ngx_http_limit_zone_cleanup(void *data) -{ - ngx_http_limit_zone_cleanup_t *lzcln = data; - - ngx_slab_pool_t *shpool; - ngx_rbtree_node_t *node; - ngx_http_limit_zone_ctx_t *ctx; - ngx_http_limit_zone_node_t *lz; - - ctx = lzcln->shm_zone->data; - shpool = (ngx_slab_pool_t *) lzcln->shm_zone->shm.addr; - node = lzcln->node; - lz = (ngx_http_limit_zone_node_t *) &node->color; - - ngx_shmtx_lock(&shpool->mutex); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lzcln->shm_zone->shm.log, 0, - "limit zone cleanup: %08XD %d", node->key, lz->conn); - - lz->conn--; - - if (lz->conn == 0) { - ngx_rbtree_delete(ctx->rbtree, node); - ngx_slab_free_locked(shpool, node); - } - - ngx_shmtx_unlock(&shpool->mutex); -} - - -static ngx_int_t -ngx_http_limit_zone_init_zone(ngx_shm_zone_t *shm_zone, void *data) -{ - ngx_http_limit_zone_ctx_t *octx = data; - - size_t len; - ngx_slab_pool_t *shpool; - ngx_rbtree_node_t *sentinel; - ngx_http_limit_zone_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_zone \"%V\" uses the \"%V\" variable " - "while previously it used the \"%V\" variable", - &shm_zone->shm.name, &ctx->var, &octx->var); - return NGX_ERROR; - } - - ctx->rbtree = octx->rbtree; - - return NGX_OK; - } - - shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; - - if (shm_zone->shm.exists) { - ctx->rbtree = shpool->data; - - return NGX_OK; - } - - ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); - if (ctx->rbtree == NULL) { - return NGX_ERROR; - } - - shpool->data = ctx->rbtree; - - sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); - if (sentinel == NULL) { - return NGX_ERROR; - } - - ngx_rbtree_init(ctx->rbtree, sentinel, - ngx_http_limit_zone_rbtree_insert_value); - - len = sizeof(" in limit_zone \"\"") + shm_zone->shm.name.len; - - shpool->log_ctx = ngx_slab_alloc(shpool, len); - if (shpool->log_ctx == NULL) { - return NGX_ERROR; - } - - ngx_sprintf(shpool->log_ctx, " in limit_zone \"%V\"%Z", - &shm_zone->shm.name); - - return NGX_OK; -} - - -static void * -ngx_http_limit_zone_create_conf(ngx_conf_t *cf) -{ - ngx_http_limit_zone_conf_t *conf; - - conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_zone_conf_t)); - if (conf == NULL) { - return NULL; - } - - /* - * set by ngx_pcalloc(): - * - * conf->shm_zone = NULL; - * conf->conn = 0; - */ - - conf->log_level = NGX_CONF_UNSET_UINT; - - return conf; -} - - -static char * -ngx_http_limit_zone_merge_conf(ngx_conf_t *cf, void *parent, void *child) -{ - ngx_http_limit_zone_conf_t *prev = parent; - ngx_http_limit_zone_conf_t *conf = child; - - if (conf->shm_zone == NULL) { - conf->shm_zone = prev->shm_zone; - conf->conn = prev->conn; - } - - ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR); - - return NGX_CONF_OK; -} - - -static char * -ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ssize_t n; - ngx_str_t *value; - ngx_shm_zone_t *shm_zone; - ngx_http_limit_zone_ctx_t *ctx; - - value = cf->args->elts; - - if (value[2].data[0] != '$') { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid variable name \"%V\"", &value[2]); - return NGX_CONF_ERROR; - } - - value[2].len--; - value[2].data++; - - ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_zone_ctx_t)); - if (ctx == NULL) { - return NGX_CONF_ERROR; - } - - ctx->index = ngx_http_get_variable_index(cf, &value[2]); - if (ctx->index == NGX_ERROR) { - return NGX_CONF_ERROR; - } - - ctx->var = value[2]; - - n = ngx_parse_size(&value[3]); - - if (n == NGX_ERROR) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid size of limit_zone \"%V\"", &value[3]); - return NGX_CONF_ERROR; - } - - if (n < (ngx_int_t) (8 * ngx_pagesize)) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "limit_zone \"%V\" is too small", &value[1]); - return NGX_CONF_ERROR; - } - - - shm_zone = ngx_shared_memory_add(cf, &value[1], n, - &ngx_http_limit_zone_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_zone \"%V\" is already bound to variable \"%V\"", - &value[1], &ctx->var); - return NGX_CONF_ERROR; - } - - shm_zone->init = ngx_http_limit_zone_init_zone; - shm_zone->data = ctx; - - return NGX_CONF_OK; -} - - -static char * -ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_limit_zone_conf_t *lzcf = conf; - - 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, - &ngx_http_limit_zone_module); - if (lzcf->shm_zone == NULL) { - return NGX_CONF_ERROR; - } - - n = ngx_atoi(value[2].data, value[2].len); - if (n <= 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid number of connections \"%V\"", &value[2]); - return NGX_CONF_ERROR; - } - - if (n > 65535) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "connection limit must be less 65536"); - return NGX_CONF_ERROR; - } - - lzcf->conn = n; - - return NGX_CONF_OK; -} - - -static ngx_int_t -ngx_http_limit_zone_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_zone_handler; - - return NGX_OK; -} diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_log_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_log_module.c index b40bdd4e59a..2d412853bd9 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_log_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_log_module.c @@ -80,6 +80,8 @@ static ssize_t ngx_http_log_script_write(ngx_http_request_t *r, static u_char *ngx_http_log_connection(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op); +static u_char *ngx_http_log_connection_requests(ngx_http_request_t *r, + u_char *buf, ngx_http_log_op_t *op); static u_char *ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op); static u_char *ngx_http_log_time(ngx_http_request_t *r, u_char *buf, @@ -193,6 +195,8 @@ static ngx_str_t ngx_http_combined_fmt = static ngx_http_log_var_t ngx_http_log_vars[] = { { ngx_string("connection"), NGX_ATOMIC_T_LEN, ngx_http_log_connection }, + { ngx_string("connection_requests"), NGX_INT_T_LEN, + ngx_http_log_connection_requests }, { ngx_string("pipe"), 1, ngx_http_log_pipe }, { ngx_string("time_local"), sizeof("28/Sep/1970:12:00:00 +0600") - 1, ngx_http_log_time }, @@ -373,6 +377,8 @@ ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script, ngx_http_log_loc_conf_t *llcf; ngx_http_core_loc_conf_t *clcf; + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + if (!r->root_tested) { /* test root directory existence */ @@ -384,8 +390,6 @@ ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script, path.data[root] = '\0'; - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - ngx_memzero(&of, sizeof(ngx_open_file_info_t)); of.valid = clcf->open_file_cache_valid; @@ -395,6 +399,11 @@ ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script, of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; + if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { + /* simulate successful logging */ + return len; + } + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { @@ -442,6 +451,11 @@ ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script, of.min_uses = llcf->open_file_cache_min_uses; of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; + if (ngx_http_set_disable_symlinks(r, clcf, &log, &of) != NGX_OK) { + /* simulate successful logging */ + return len; + } + if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool) != NGX_OK) { @@ -491,7 +505,15 @@ static u_char * ngx_http_log_connection(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op) { - return ngx_sprintf(buf, "%ui", r->connection->number); + return ngx_sprintf(buf, "%uA", r->connection->number); +} + + +static u_char * +ngx_http_log_connection_requests(ngx_http_request_t *r, u_char *buf, + ngx_http_log_op_t *op) +{ + return ngx_sprintf(buf, "%ui", r->connection->requests); } @@ -972,7 +994,7 @@ buffer: if (buf == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid parameter \"%V\"", &value[3]); + "invalid buffer value \"%V\"", &name); return NGX_CONF_ERROR; } @@ -1005,6 +1027,12 @@ ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_uint_t i; ngx_http_log_fmt_t *fmt; + if (cf->cmd_type != NGX_HTTP_MAIN_CONF) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "the \"log_format\" directive may be used " + "only on \"http\" level"); + } + value = cf->args->elts; fmt = lmcf->formats.elts; @@ -1243,7 +1271,7 @@ ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) s.data = value[i].data + 9; inactive = ngx_parse_time(&s, 1); - if (inactive < 0) { + if (inactive == (time_t) NGX_ERROR) { goto failed; } @@ -1266,7 +1294,7 @@ ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) s.data = value[i].data + 6; valid = ngx_parse_time(&s, 1); - if (valid < 0) { + if (valid == (time_t) NGX_ERROR) { goto failed; } diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_memcached_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_memcached_module.c index 604334be77e..5077ded9acb 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_memcached_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_memcached_module.c @@ -345,8 +345,8 @@ found: while (*p && *p++ != CR) { /* void */ } - r->headers_out.content_length_n = ngx_atoof(len, p - len - 1); - if (r->headers_out.content_length_n == -1) { + u->headers_in.content_length_n = ngx_atoof(len, p - len - 1); + if (u->headers_in.content_length_n == -1) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "memcached sent invalid length in response \"%V\" " "for key \"%V\"", @@ -367,6 +367,7 @@ found: u->headers_in.status_n = 404; u->state->status = 404; + u->keepalive = 1; return NGX_OK; } @@ -408,7 +409,7 @@ ngx_http_memcached_filter(void *data, ssize_t bytes) u = ctx->request->upstream; b = &u->buffer; - if (u->length == ctx->rest) { + if (u->length == (ssize_t) ctx->rest) { if (ngx_strncmp(b->last, ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest, @@ -427,6 +428,10 @@ ngx_http_memcached_filter(void *data, ssize_t bytes) u->length -= bytes; ctx->rest -= bytes; + if (u->length == 0) { + u->keepalive = 1; + } + return NGX_OK; } @@ -464,6 +469,13 @@ ngx_http_memcached_filter(void *data, ssize_t bytes) if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) { ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0, "memcached sent invalid trailer"); + + b->last = last; + cl->buf->last = last; + u->length = 0; + ctx->rest = 0; + + return NGX_OK; } ctx->rest -= b->last - last; @@ -471,6 +483,10 @@ ngx_http_memcached_filter(void *data, ssize_t bytes) cl->buf->last = last; u->length = ctx->rest; + if (u->length == 0) { + u->keepalive = 1; + } + return NGX_OK; } diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_mp4_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_mp4_module.c index 92dfbd4116a..433e118a227 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_mp4_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_mp4_module.c @@ -442,6 +442,10 @@ ngx_http_mp4_handler(ngx_http_request_t *r) of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; + if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { @@ -459,6 +463,10 @@ ngx_http_mp4_handler(ngx_http_request_t *r) break; case NGX_EACCES: +#if (NGX_HAVE_OPENAT) + case NGX_EMLINK: + case NGX_ELOOP: +#endif level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_proxy_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_proxy_module.c index 75249b74969..387f77f9b34 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_proxy_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_proxy_module.c @@ -10,25 +10,23 @@ #include <ngx_http.h> -typedef struct ngx_http_proxy_redirect_s ngx_http_proxy_redirect_t; +typedef struct ngx_http_proxy_rewrite_s ngx_http_proxy_rewrite_t; -typedef ngx_int_t (*ngx_http_proxy_redirect_pt)(ngx_http_request_t *r, - ngx_table_elt_t *h, size_t prefix, ngx_http_proxy_redirect_t *pr); +typedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r, + ngx_table_elt_t *h, size_t prefix, size_t len, + ngx_http_proxy_rewrite_t *pr); -struct ngx_http_proxy_redirect_s { - ngx_http_proxy_redirect_pt handler; - ngx_str_t redirect; +struct ngx_http_proxy_rewrite_s { + ngx_http_proxy_rewrite_pt handler; union { - ngx_str_t text; - - struct { - void *lengths; - void *values; - } vars; + ngx_http_complex_value_t complex; +#if (NGX_PCRE) + ngx_http_regex_t *regex; +#endif + } pattern; - void *regex; - } replacement; + ngx_http_complex_value_t replacement; }; @@ -57,6 +55,8 @@ typedef struct { ngx_array_t *proxy_values; ngx_array_t *redirects; + ngx_array_t *cookie_domains; + ngx_array_t *cookie_paths; ngx_str_t body_source; @@ -72,6 +72,8 @@ typedef struct { ngx_flag_t redirect; + ngx_uint_t http_version; + ngx_uint_t headers_hash_max_size; ngx_uint_t headers_hash_bucket_size; } ngx_http_proxy_loc_conf_t; @@ -81,6 +83,12 @@ typedef struct { ngx_http_status_t status; ngx_http_proxy_vars_t vars; size_t internal_body_length; + + ngx_uint_t state; + off_t size; + off_t length; + + ngx_uint_t head; /* unsigned head:1 */ } ngx_http_proxy_ctx_t; @@ -93,6 +101,15 @@ static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_proxy_input_filter_init(void *data); +static ngx_int_t ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, + ngx_buf_t *buf); +static ngx_int_t ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, + ngx_buf_t *buf); +static ngx_int_t ngx_http_proxy_non_buffered_copy_filter(void *data, + ssize_t bytes); +static ngx_int_t ngx_http_proxy_non_buffered_chunked_filter(void *data, + ssize_t bytes); static void ngx_http_proxy_abort_request(ngx_http_request_t *r); static void ngx_http_proxy_finalize_request(ngx_http_request_t *r, ngx_int_t rc); @@ -109,6 +126,12 @@ static ngx_int_t ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix); +static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, + ngx_table_elt_t *h); +static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, + ngx_table_elt_t *h, u_char *value, ngx_array_t *rewrites); +static ngx_int_t ngx_http_proxy_rewrite(ngx_http_request_t *r, + ngx_table_elt_t *h, size_t prefix, size_t len, ngx_str_t *replacement); static ngx_int_t ngx_http_proxy_add_variables(ngx_conf_t *cf); static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf); @@ -121,6 +144,10 @@ static char *ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); #if (NGX_HTTP_CACHE) @@ -132,6 +159,9 @@ static char *ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data); +static ngx_int_t ngx_http_proxy_rewrite_regex(ngx_conf_t *cf, + ngx_http_proxy_rewrite_t *pr, ngx_str_t *regex, ngx_uint_t caseless); + #if (NGX_HTTP_SSL) static ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf); @@ -158,6 +188,13 @@ static ngx_conf_bitmask_t ngx_http_proxy_next_upstream_masks[] = { }; +static ngx_conf_enum_t ngx_http_proxy_http_version[] = { + { ngx_string("1.0"), NGX_HTTP_VERSION_10 }, + { ngx_string("1.1"), NGX_HTTP_VERSION_11 }, + { ngx_null_string, 0 } +}; + + ngx_module_t ngx_http_proxy_module; @@ -177,6 +214,20 @@ static ngx_command_t ngx_http_proxy_commands[] = { 0, NULL }, + { ngx_string("proxy_cookie_domain"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_http_proxy_cookie_domain, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("proxy_cookie_path"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_http_proxy_cookie_path, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("proxy_store"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_proxy_store, @@ -382,6 +433,20 @@ static ngx_command_t ngx_http_proxy_commands[] = { offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_methods), &ngx_http_upstream_cache_method_mask }, + { ngx_string("proxy_cache_lock"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock), + NULL }, + + { ngx_string("proxy_cache_lock_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock_timeout), + NULL }, + #endif { ngx_string("proxy_temp_path"), @@ -433,6 +498,13 @@ static ngx_command_t ngx_http_proxy_commands[] = { offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_headers), &ngx_http_upstream_ignore_headers_masks }, + { ngx_string("proxy_http_version"), + 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_proxy_loc_conf_t, http_version), + &ngx_http_proxy_http_version }, + #if (NGX_HTTP_SSL) { ngx_string("proxy_ssl_session_reuse"), @@ -480,6 +552,7 @@ ngx_module_t ngx_http_proxy_module = { static char ngx_http_proxy_version[] = " HTTP/1.0" CRLF; +static char ngx_http_proxy_version_11[] = " HTTP/1.1" CRLF; static ngx_keyval_t ngx_http_proxy_headers[] = { @@ -487,6 +560,7 @@ static ngx_keyval_t ngx_http_proxy_headers[] = { { ngx_string("Connection"), ngx_string("close") }, { ngx_string("Keep-Alive"), ngx_string("") }, { ngx_string("Expect"), ngx_string("") }, + { ngx_string("Upgrade"), ngx_string("") }, { ngx_null_string, ngx_null_string } }; @@ -511,6 +585,7 @@ static ngx_keyval_t ngx_http_proxy_cache_headers[] = { { ngx_string("Connection"), ngx_string("close") }, { ngx_string("Keep-Alive"), ngx_string("") }, { ngx_string("Expect"), ngx_string("") }, + { ngx_string("Upgrade"), ngx_string("") }, { ngx_string("If-Modified-Since"), ngx_string("") }, { ngx_string("If-Unmodified-Since"), ngx_string("") }, { ngx_string("If-None-Match"), ngx_string("") }, @@ -604,6 +679,10 @@ ngx_http_proxy_handler(ngx_http_request_t *r) u->rewrite_redirect = ngx_http_proxy_rewrite_redirect; } + if (plcf->cookie_domains || plcf->cookie_paths) { + u->rewrite_cookie = ngx_http_proxy_rewrite_cookie; + } + u->buffering = plcf->upstream.buffering; u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t)); @@ -611,7 +690,12 @@ ngx_http_proxy_handler(ngx_http_request_t *r) return NGX_HTTP_INTERNAL_SERVER_ERROR; } - u->pipe->input_filter = ngx_event_pipe_copy_input_filter; + u->pipe->input_filter = ngx_http_proxy_copy_filter; + u->pipe->input_ctx = r; + + u->input_filter_init = ngx_http_proxy_input_filter_init; + u->input_filter = ngx_http_proxy_non_buffered_copy_filter; + u->input_filter_ctx = r; u->accel = 1; @@ -701,9 +785,6 @@ ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx, url.uri.len++; url.uri.data = p - 1; } - - } else { - url.uri = r->unparsed_uri; } ctx->vars.key_start = u->schema; @@ -771,7 +852,7 @@ ngx_http_proxy_create_key(ngx_http_request_t *r) return NGX_ERROR; } - if (plcf->proxy_lengths) { + if (plcf->proxy_lengths && ctx->vars.uri.len) { *key = ctx->vars.uri; u->uri = ctx->vars.uri; @@ -867,15 +948,21 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) method.len++; } + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + if (method.len == 5 + && ngx_strncasecmp(method.data, (u_char *) "HEAD ", 5) == 0) + { + ctx->head = 1; + } + len = method.len + sizeof(ngx_http_proxy_version) - 1 + sizeof(CRLF) - 1; escape = 0; loc_len = 0; unparsed_uri = 0; - ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); - - if (plcf->proxy_lengths) { + if (plcf->proxy_lengths && ctx->vars.uri.len) { uri_len = ctx->vars.uri.len; } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main) @@ -981,7 +1068,7 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) u->uri.data = b->last; - if (plcf->proxy_lengths) { + if (plcf->proxy_lengths && ctx->vars.uri.len) { b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len); } else if (unparsed_uri) { @@ -1010,8 +1097,14 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) u->uri.len = b->last - u->uri.data; - b->last = ngx_cpymem(b->last, ngx_http_proxy_version, - sizeof(ngx_http_proxy_version) - 1); + if (plcf->http_version == NGX_HTTP_VERSION_11) { + b->last = ngx_cpymem(b->last, ngx_http_proxy_version_11, + sizeof(ngx_http_proxy_version_11) - 1); + + } else { + b->last = ngx_cpymem(b->last, ngx_http_proxy_version, + sizeof(ngx_http_proxy_version) - 1); + } ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); @@ -1159,8 +1252,11 @@ ngx_http_proxy_reinit_request(ngx_http_request_t *r) ctx->status.count = 0; ctx->status.start = NULL; ctx->status.end = NULL; + ctx->state = 0; r->upstream->process_header = ngx_http_proxy_process_status_line; + r->upstream->pipe->input_filter = ngx_http_proxy_copy_filter; + r->upstream->input_filter = ngx_http_proxy_non_buffered_copy_filter; r->state = 0; return NGX_OK; @@ -1211,6 +1307,7 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r) r->http_version = NGX_HTTP_VERSION_9; u->state->status = NGX_HTTP_OK; + u->headers_in.connection_close = 1; return NGX_OK; } @@ -1235,6 +1332,10 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r) "http proxy status %ui \"%V\"", u->headers_in.status_n, &u->headers_in.status_line); + if (ctx->status.http_version < NGX_HTTP_VERSION_11) { + u->headers_in.connection_close = 1; + } + u->process_header = ngx_http_proxy_process_header; return ngx_http_proxy_process_header(r); @@ -1246,6 +1347,8 @@ ngx_http_proxy_process_header(ngx_http_request_t *r) { ngx_int_t rc; ngx_table_elt_t *h; + ngx_http_upstream_t *u; + ngx_http_proxy_ctx_t *ctx; ngx_http_upstream_header_t *hh; ngx_http_upstream_main_conf_t *umcf; @@ -1343,6 +1446,30 @@ ngx_http_proxy_process_header(ngx_http_request_t *r) h->lowcase_key = (u_char *) "date"; } + /* clear content length if response is chunked */ + + u = r->upstream; + + if (u->headers_in.chunked) { + u->headers_in.content_length_n = -1; + } + + /* + * set u->keepalive if response has no body; this allows to keep + * connections alive in case of r->header_only or X-Accel-Redirect + */ + + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT + || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED + || ctx->head + || (!u->headers_in.chunked + && u->headers_in.content_length_n == 0)) + { + u->keepalive = !u->headers_in.connection_close; + } + return NGX_OK; } @@ -1360,6 +1487,709 @@ ngx_http_proxy_process_header(ngx_http_request_t *r) } +static ngx_int_t +ngx_http_proxy_input_filter_init(void *data) +{ + ngx_http_request_t *r = data; + ngx_http_upstream_t *u; + ngx_http_proxy_ctx_t *ctx; + + u = r->upstream; + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy filter init s:%d h:%d c:%d l:%O", + u->headers_in.status_n, ctx->head, u->headers_in.chunked, + u->headers_in.content_length_n); + + /* as per RFC2616, 4.4 Message Length */ + + if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT + || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED + || ctx->head) + { + /* 1xx, 204, and 304 and replies to HEAD requests */ + /* no 1xx since we don't send Expect and Upgrade */ + + u->pipe->length = 0; + u->length = 0; + u->keepalive = !u->headers_in.connection_close; + + } else if (u->headers_in.chunked) { + /* chunked */ + + u->pipe->input_filter = ngx_http_proxy_chunked_filter; + u->pipe->length = 3; /* "0" LF LF */ + + u->input_filter = ngx_http_proxy_non_buffered_chunked_filter; + u->length = -1; + + } else if (u->headers_in.content_length_n == 0) { + /* empty body: special case as filter won't be called */ + + u->pipe->length = 0; + u->length = 0; + u->keepalive = !u->headers_in.connection_close; + + } else { + /* content length or connection close */ + + u->pipe->length = u->headers_in.content_length_n; + u->length = u->headers_in.content_length_n; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_http_request_t *r; + + if (buf->pos == buf->last) { + return NGX_OK; + } + + if (p->free) { + cl = p->free; + b = cl->buf; + p->free = cl->next; + ngx_free_chain(p->pool, cl); + + } else { + b = ngx_alloc_buf(p->pool); + if (b == NULL) { + return NGX_ERROR; + } + } + + ngx_memcpy(b, buf, sizeof(ngx_buf_t)); + b->shadow = buf; + b->tag = p->tag; + b->last_shadow = 1; + b->recycled = 1; + buf->shadow = b; + + cl = ngx_alloc_chain_link(p->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "input buf #%d", b->num); + + if (p->in) { + *p->last_in = cl; + } else { + p->in = cl; + } + p->last_in = &cl->next; + + if (p->length == -1) { + return NGX_OK; + } + + p->length -= b->last - b->pos; + + if (p->length == 0) { + r = p->input_ctx; + p->upstream_done = 1; + r->upstream->keepalive = !r->upstream->headers_in.connection_close; + + } else if (p->length < 0) { + r = p->input_ctx; + p->upstream_done = 1; + + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent too much data"); + } + + return NGX_OK; +} + + +static ngx_inline ngx_int_t +ngx_http_proxy_parse_chunked(ngx_http_request_t *r, ngx_buf_t *buf) +{ + u_char *pos, ch, c; + ngx_int_t rc; + ngx_http_proxy_ctx_t *ctx; + enum { + sw_chunk_start = 0, + sw_chunk_size, + sw_chunk_extension, + sw_chunk_extension_almost_done, + sw_chunk_data, + sw_after_data, + sw_after_data_almost_done, + sw_last_chunk_extension, + sw_last_chunk_extension_almost_done, + sw_trailer, + sw_trailer_almost_done, + sw_trailer_header, + sw_trailer_header_almost_done + } state; + + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + if (ctx == NULL) { + return NGX_ERROR; + } + + state = ctx->state; + + if (state == sw_chunk_data && ctx->size == 0) { + state = sw_after_data; + } + + rc = NGX_AGAIN; + + for (pos = buf->pos; pos < buf->last; pos++) { + + ch = *pos; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy chunked byte: %02Xd s:%d", ch, state); + + switch (state) { + + case sw_chunk_start: + if (ch >= '0' && ch <= '9') { + state = sw_chunk_size; + ctx->size = ch - '0'; + break; + } + + c = (u_char) (ch | 0x20); + + if (c >= 'a' && c <= 'f') { + state = sw_chunk_size; + ctx->size = c - 'a' + 10; + break; + } + + goto invalid; + + case sw_chunk_size: + if (ch >= '0' && ch <= '9') { + ctx->size = ctx->size * 16 + (ch - '0'); + break; + } + + c = (u_char) (ch | 0x20); + + if (c >= 'a' && c <= 'f') { + ctx->size = ctx->size * 16 + (c - 'a' + 10); + break; + } + + if (ctx->size == 0) { + + switch (ch) { + case CR: + state = sw_last_chunk_extension_almost_done; + break; + case LF: + state = sw_trailer; + break; + case ';': + case ' ': + case '\t': + state = sw_last_chunk_extension; + break; + default: + goto invalid; + } + + break; + } + + switch (ch) { + case CR: + state = sw_chunk_extension_almost_done; + break; + case LF: + state = sw_chunk_data; + break; + case ';': + case ' ': + case '\t': + state = sw_chunk_extension; + break; + default: + goto invalid; + } + + break; + + case sw_chunk_extension: + switch (ch) { + case CR: + state = sw_chunk_extension_almost_done; + break; + case LF: + state = sw_chunk_data; + } + break; + + case sw_chunk_extension_almost_done: + if (ch == LF) { + state = sw_chunk_data; + break; + } + goto invalid; + + case sw_chunk_data: + rc = NGX_OK; + goto data; + + case sw_after_data: + switch (ch) { + case CR: + state = sw_after_data_almost_done; + break; + case LF: + state = sw_chunk_start; + } + break; + + case sw_after_data_almost_done: + if (ch == LF) { + state = sw_chunk_start; + break; + } + goto invalid; + + case sw_last_chunk_extension: + switch (ch) { + case CR: + state = sw_last_chunk_extension_almost_done; + break; + case LF: + state = sw_trailer; + } + break; + + case sw_last_chunk_extension_almost_done: + if (ch == LF) { + state = sw_trailer; + break; + } + goto invalid; + + case sw_trailer: + switch (ch) { + case CR: + state = sw_trailer_almost_done; + break; + case LF: + goto done; + default: + state = sw_trailer_header; + } + break; + + case sw_trailer_almost_done: + if (ch == LF) { + goto done; + } + goto invalid; + + case sw_trailer_header: + switch (ch) { + case CR: + state = sw_trailer_header_almost_done; + break; + case LF: + state = sw_trailer; + } + break; + + case sw_trailer_header_almost_done: + if (ch == LF) { + state = sw_trailer; + break; + } + goto invalid; + + } + } + +data: + + ctx->state = state; + buf->pos = pos; + + switch (state) { + + case sw_chunk_start: + ctx->length = 3 /* "0" LF LF */; + break; + case sw_chunk_size: + ctx->length = 2 /* LF LF */ + + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */ : 0); + break; + case sw_chunk_extension: + case sw_chunk_extension_almost_done: + ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */; + break; + case sw_chunk_data: + ctx->length = ctx->size + 4 /* LF "0" LF LF */; + break; + case sw_after_data: + case sw_after_data_almost_done: + ctx->length = 4 /* LF "0" LF LF */; + break; + case sw_last_chunk_extension: + case sw_last_chunk_extension_almost_done: + ctx->length = 2 /* LF LF */; + break; + case sw_trailer: + case sw_trailer_almost_done: + ctx->length = 1 /* LF */; + break; + case sw_trailer_header: + case sw_trailer_header_almost_done: + ctx->length = 2 /* LF LF */; + break; + + } + + return rc; + +done: + + return NGX_DONE; + +invalid: + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) +{ + ngx_int_t rc; + ngx_buf_t *b, **prev; + ngx_chain_t *cl; + ngx_http_request_t *r; + ngx_http_proxy_ctx_t *ctx; + + if (buf->pos == buf->last) { + return NGX_OK; + } + + r = p->input_ctx; + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + if (ctx == NULL) { + return NGX_ERROR; + } + + b = NULL; + prev = &buf->shadow; + + for ( ;; ) { + + rc = ngx_http_proxy_parse_chunked(r, buf); + + if (rc == NGX_OK) { + + /* a chunk has been parsed successfully */ + + if (p->free) { + cl = p->free; + b = cl->buf; + p->free = cl->next; + ngx_free_chain(p->pool, cl); + + } else { + b = ngx_alloc_buf(p->pool); + if (b == NULL) { + return NGX_ERROR; + } + } + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->pos = buf->pos; + b->start = buf->start; + b->end = buf->end; + b->tag = p->tag; + b->temporary = 1; + b->recycled = 1; + + *prev = b; + prev = &b->shadow; + + cl = ngx_alloc_chain_link(p->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + if (p->in) { + *p->last_in = cl; + } else { + p->in = cl; + } + p->last_in = &cl->next; + + /* STUB */ b->num = buf->num; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, + "input buf #%d %p", b->num, b->pos); + + if (buf->last - buf->pos >= ctx->size) { + + buf->pos += ctx->size; + b->last = buf->pos; + ctx->size = 0; + + continue; + } + + ctx->size -= buf->last - buf->pos; + buf->pos = buf->last; + b->last = buf->last; + + continue; + } + + if (rc == NGX_DONE) { + + /* a whole response has been parsed successfully */ + + p->upstream_done = 1; + r->upstream->keepalive = !r->upstream->headers_in.connection_close; + + break; + } + + if (rc == NGX_AGAIN) { + + /* set p->length, minimal amount of data we want to see */ + + p->length = ctx->length; + + break; + } + + /* invalid response */ + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent invalid chunked response"); + + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy chunked state %d, length %d", + ctx->state, p->length); + + if (b) { + b->shadow = buf; + b->last_shadow = 1; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, + "input buf %p %z", b->pos, b->last - b->pos); + + return NGX_OK; + } + + /* there is no data record in the buf, add it to free chain */ + + if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_proxy_non_buffered_copy_filter(void *data, ssize_t bytes) +{ + ngx_http_request_t *r = data; + + ngx_buf_t *b; + ngx_chain_t *cl, **ll; + ngx_http_upstream_t *u; + + u = r->upstream; + + for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { + ll = &cl->next; + } + + cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); + if (cl == NULL) { + return NGX_ERROR; + } + + *ll = cl; + + cl->buf->flush = 1; + cl->buf->memory = 1; + + b = &u->buffer; + + cl->buf->pos = b->last; + b->last += bytes; + cl->buf->last = b->last; + cl->buf->tag = u->output.tag; + + if (u->length == -1) { + return NGX_OK; + } + + u->length -= bytes; + + if (u->length == 0) { + u->keepalive = !u->headers_in.connection_close; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes) +{ + ngx_http_request_t *r = data; + + ngx_int_t rc; + ngx_buf_t *b, *buf; + ngx_chain_t *cl, **ll; + ngx_http_upstream_t *u; + ngx_http_proxy_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + if (ctx == NULL) { + return NGX_ERROR; + } + + u = r->upstream; + buf = &u->buffer; + + buf->pos = buf->last; + buf->last += bytes; + + for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { + ll = &cl->next; + } + + for ( ;; ) { + + rc = ngx_http_proxy_parse_chunked(r, buf); + + if (rc == NGX_OK) { + + /* a chunk has been parsed successfully */ + + cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); + if (cl == NULL) { + return NGX_ERROR; + } + + *ll = cl; + ll = &cl->next; + + b = cl->buf; + + b->flush = 1; + b->memory = 1; + + b->pos = buf->pos; + b->tag = u->output.tag; + + if (buf->last - buf->pos >= ctx->size) { + buf->pos += ctx->size; + b->last = buf->pos; + ctx->size = 0; + + } else { + ctx->size -= buf->last - buf->pos; + buf->pos = buf->last; + b->last = buf->last; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy out buf %p %z", + b->pos, b->last - b->pos); + + continue; + } + + if (rc == NGX_DONE) { + + /* a whole response has been parsed successfully */ + + u->keepalive = !u->headers_in.connection_close; + u->length = 0; + + break; + } + + if (rc == NGX_AGAIN) { + break; + } + + /* invalid response */ + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent invalid chunked response"); + + return NGX_ERROR; + } + + /* provide continuous buffer for subrequests in memory */ + + if (r->subrequest_in_memory) { + + cl = u->out_bufs; + + if (cl) { + buf->pos = cl->buf->pos; + } + + buf->last = buf->pos; + + for (cl = u->out_bufs; cl; cl = cl->next) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy in memory %p-%p %uz", + cl->buf->pos, cl->buf->last, ngx_buf_size(cl->buf)); + + if (buf->last == cl->buf->pos) { + buf->last = cl->buf->last; + continue; + } + + buf->last = ngx_movemem(buf->last, cl->buf->pos, + cl->buf->last - cl->buf->pos); + + cl->buf->pos = buf->last - (cl->buf->last - cl->buf->pos); + cl->buf->last = buf->last; + } + } + + return NGX_OK; +} + + static void ngx_http_proxy_abort_request(ngx_http_request_t *r) { @@ -1496,10 +2326,11 @@ static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix) { + size_t len; ngx_int_t rc; ngx_uint_t i; + ngx_http_proxy_rewrite_t *pr; ngx_http_proxy_loc_conf_t *plcf; - ngx_http_proxy_redirect_t *pr; plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); @@ -1509,8 +2340,10 @@ ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h, return NGX_DECLINED; } + len = h->value.len - prefix; + for (i = 0; i < plcf->redirects->nelts; i++) { - rc = pr[i].handler(r, h, prefix, &pr[i]); + rc = pr[i].handler(r, h, prefix, len, &pr[i]); if (rc != NGX_DECLINED) { return rc; @@ -1522,91 +2355,209 @@ ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h, static ngx_int_t -ngx_http_proxy_rewrite_redirect_text(ngx_http_request_t *r, ngx_table_elt_t *h, - size_t prefix, ngx_http_proxy_redirect_t *pr) +ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h) { - size_t len; - u_char *data, *p; + size_t prefix; + u_char *p; + ngx_int_t rc, rv; + ngx_http_proxy_loc_conf_t *plcf; - if (pr->redirect.len > h->value.len - prefix - || ngx_rstrncmp(h->value.data + prefix, pr->redirect.data, - pr->redirect.len) != 0) - { + p = (u_char *) ngx_strchr(h->value.data, ';'); + if (p == NULL) { return NGX_DECLINED; } - len = pr->replacement.text.len + h->value.len - pr->redirect.len; + prefix = p + 1 - h->value.data; - data = ngx_pnalloc(r->pool, len); - if (data == NULL) { - return NGX_ERROR; + rv = NGX_DECLINED; + + plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + + if (plcf->cookie_domains) { + p = ngx_strcasestrn(h->value.data + prefix, "domain=", 7 - 1); + + if (p) { + rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 7, + plcf->cookie_domains); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc != NGX_DECLINED) { + rv = rc; + } + } } - p = ngx_copy(data, h->value.data, prefix); + if (plcf->cookie_paths) { + p = ngx_strcasestrn(h->value.data + prefix, "path=", 5 - 1); + + if (p) { + rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 5, + plcf->cookie_paths); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } - if (pr->replacement.text.len) { - p = ngx_copy(p, pr->replacement.text.data, pr->replacement.text.len); + if (rc != NGX_DECLINED) { + rv = rc; + } + } } - ngx_memcpy(p, h->value.data + prefix + pr->redirect.len, - h->value.len - pr->redirect.len - prefix); + return rv; +} - h->value.len = len; - h->value.data = data; - return NGX_OK; +static ngx_int_t +ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h, + u_char *value, ngx_array_t *rewrites) +{ + size_t len, prefix; + u_char *p; + ngx_int_t rc; + ngx_uint_t i; + ngx_http_proxy_rewrite_t *pr; + + prefix = value - h->value.data; + + p = (u_char *) ngx_strchr(value, ';'); + + len = p ? (size_t) (p - value) : (h->value.len - prefix); + + pr = rewrites->elts; + + for (i = 0; i < rewrites->nelts; i++) { + rc = pr[i].handler(r, h, prefix, len, &pr[i]); + + if (rc != NGX_DECLINED) { + return rc; + } + } + + return NGX_DECLINED; } static ngx_int_t -ngx_http_proxy_rewrite_redirect_vars(ngx_http_request_t *r, ngx_table_elt_t *h, - size_t prefix, ngx_http_proxy_redirect_t *pr) +ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r, + ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) { - size_t len; - u_char *data, *p; - ngx_http_script_code_pt code; - ngx_http_script_engine_t e; - ngx_http_script_len_code_pt lcode; + ngx_str_t pattern, replacement; - if (pr->redirect.len > h->value.len - prefix - || ngx_rstrncmp(h->value.data + prefix, pr->redirect.data, - pr->redirect.len) != 0) + if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) { + return NGX_ERROR; + } + + if (pattern.len > len + || ngx_rstrncmp(h->value.data + prefix, pattern.data, + pattern.len) != 0) { return NGX_DECLINED; } - ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) { + return NGX_ERROR; + } - e.ip = pr->replacement.vars.lengths; - e.request = r; + return ngx_http_proxy_rewrite(r, h, prefix, pattern.len, &replacement); +} + + +#if (NGX_PCRE) + +static ngx_int_t +ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_table_elt_t *h, + size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) +{ + ngx_str_t pattern, replacement; + + pattern.len = len; + pattern.data = h->value.data + prefix; - len = h->value.len - pr->redirect.len; + if (ngx_http_regex_exec(r, pr->pattern.regex, &pattern) != NGX_OK) { + return NGX_DECLINED; + } + + if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) { + return NGX_ERROR; + } - while (*(uintptr_t *) e.ip) { - lcode = *(ngx_http_script_len_code_pt *) e.ip; - len += lcode(&e); + if (prefix == 0 && h->value.len == len) { + h->value = replacement; + return NGX_OK; } - data = ngx_pnalloc(r->pool, len); - if (data == NULL) { + return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement); +} + +#endif + + +static ngx_int_t +ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r, + ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) +{ + u_char *p; + ngx_str_t pattern, replacement; + + if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) { return NGX_ERROR; } - p = ngx_copy(data, h->value.data, prefix); + p = h->value.data + prefix; + + if (p[0] == '.') { + p++; + prefix++; + len--; + } - e.ip = pr->replacement.vars.values; - e.pos = p; + if (pattern.len != len || ngx_rstrncasecmp(pattern.data, p, len) != 0) { + return NGX_DECLINED; + } - while (*(uintptr_t *) e.ip) { - code = *(ngx_http_script_code_pt *) e.ip; - code(&e); + if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) { + return NGX_ERROR; } - ngx_memcpy(e.pos, h->value.data + prefix + pr->redirect.len, - h->value.len - pr->redirect.len - prefix); + return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement); +} + + +static ngx_int_t +ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix, + size_t len, ngx_str_t *replacement) +{ + u_char *p, *data; + size_t new_len; + + new_len = replacement->len + h->value.len - len; + + if (replacement->len > len) { + + data = ngx_pnalloc(r->pool, new_len); + if (data == NULL) { + return NGX_ERROR; + } + + p = ngx_copy(data, h->value.data, prefix); + p = ngx_copy(p, replacement->data, replacement->len); + + ngx_memcpy(p, h->value.data + prefix + len, + h->value.len - len - prefix); + + h->value.data = data; + + } else { + p = ngx_copy(h->value.data + prefix, replacement->data, + replacement->len); + + ngx_memmove(p, h->value.data + prefix + len, + h->value.len - len - prefix); + } - h->value.len = len; - h->value.data = data; + h->value.len = new_len; return NGX_OK; } @@ -1692,6 +2643,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; conf->upstream.no_cache = NGX_CONF_UNSET_PTR; conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; + conf->upstream.cache_lock = NGX_CONF_UNSET; + conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC; #endif conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; @@ -1708,6 +2661,11 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) conf->redirect = NGX_CONF_UNSET; conf->upstream.change_buffering = 1; + conf->cookie_domains = NGX_CONF_UNSET_PTR; + conf->cookie_paths = NGX_CONF_UNSET_PTR; + + conf->http_version = NGX_CONF_UNSET_UINT; + conf->headers_hash_max_size = NGX_CONF_UNSET_UINT; conf->headers_hash_bucket_size = NGX_CONF_UNSET_UINT; @@ -1727,7 +2685,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) size_t size; ngx_hash_init_t hash; ngx_http_core_loc_conf_t *clcf; - ngx_http_proxy_redirect_t *pr; + ngx_http_proxy_rewrite_t *pr; ngx_http_script_compile_t sc; if (conf->upstream.store != 0) { @@ -1794,8 +2752,8 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->upstream.busy_buffers_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_busy_buffers_size\" must be equal or bigger than " - "maximum of the value of \"proxy_buffer_size\" and " + "\"proxy_busy_buffers_size\" must be equal to or greater than " + "the maximum of the value of \"proxy_buffer_size\" and " "one of the \"proxy_buffers\""); return NGX_CONF_ERROR; @@ -1825,8 +2783,8 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->upstream.temp_file_write_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_temp_file_write_size\" must be equal or bigger than " - "maximum of the value of \"proxy_buffer_size\" and " + "\"proxy_temp_file_write_size\" must be equal to or greater " + "than the maximum of the value of \"proxy_buffer_size\" and " "one of the \"proxy_buffers\""); return NGX_CONF_ERROR; @@ -1848,8 +2806,8 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"proxy_max_temp_file_size\" must be equal to zero to disable " - "the temporary files usage or must be equal or bigger than " - "maximum of the value of \"proxy_buffer_size\" and " + "temporary files usage or must be equal to or greater than " + "the maximum of the value of \"proxy_buffer_size\" and " "one of the \"proxy_buffers\""); return NGX_CONF_ERROR; @@ -1940,6 +2898,12 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) conf->cache_key = prev->cache_key; } + ngx_conf_merge_value(conf->upstream.cache_lock, + prev->upstream.cache_lock, 0); + + ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout, + prev->upstream.cache_lock_timeout, 5000); + #endif if (conf->method.len == 0) { @@ -1974,7 +2938,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->redirects == NULL && conf->url.data) { conf->redirects = ngx_array_create(cf->pool, 1, - sizeof(ngx_http_proxy_redirect_t)); + sizeof(ngx_http_proxy_rewrite_t)); if (conf->redirects == NULL) { return NGX_CONF_ERROR; } @@ -1984,36 +2948,49 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } - pr->handler = ngx_http_proxy_rewrite_redirect_text; + ngx_memzero(&pr->pattern.complex, + sizeof(ngx_http_complex_value_t)); + + ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t)); + + pr->handler = ngx_http_proxy_rewrite_complex_handler; if (conf->vars.uri.len) { - pr->redirect = conf->url; - pr->replacement.text = conf->location; + pr->pattern.complex.value = conf->url; + pr->replacement.value = conf->location; } else { - pr->redirect.len = conf->url.len + sizeof("/") - 1; + pr->pattern.complex.value.len = conf->url.len + + sizeof("/") - 1; - p = ngx_pnalloc(cf->pool, pr->redirect.len); + p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len); if (p == NULL) { return NGX_CONF_ERROR; } - pr->redirect.data = p; + pr->pattern.complex.value.data = p; p = ngx_cpymem(p, conf->url.data, conf->url.len); *p = '/'; - ngx_str_set(&pr->replacement.text, "/"); + ngx_str_set(&pr->replacement.value, "/"); } } } + ngx_conf_merge_ptr_value(conf->cookie_domains, prev->cookie_domains, NULL); + + ngx_conf_merge_ptr_value(conf->cookie_paths, prev->cookie_paths, NULL); + #if (NGX_HTTP_SSL) if (conf->upstream.ssl == NULL) { conf->upstream.ssl = prev->upstream.ssl; } #endif + ngx_conf_merge_uint_value(conf->http_version, prev->http_version, + NGX_HTTP_VERSION_10); + ngx_conf_merge_uint_value(conf->headers_hash_max_size, prev->headers_hash_max_size, 512); @@ -2466,11 +3443,11 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { if (plcf->vars.uri.len) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_pass\" may not have URI part in " + "\"proxy_pass\" cannot have URI part in " "location given by regular expression, " "or inside named location, " - "or inside the \"if\" statement, " - "or inside the \"limit_except\" block"); + "or inside \"if\" statement, " + "or inside \"limit_except\" block"); return NGX_CONF_ERROR; } @@ -2488,11 +3465,10 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_proxy_loc_conf_t *plcf = conf; - u_char *p; - ngx_str_t *value; - ngx_array_t *vars_lengths, *vars_values; - ngx_http_script_compile_t sc; - ngx_http_proxy_redirect_t *pr; + u_char *p; + ngx_str_t *value; + ngx_http_proxy_rewrite_t *pr; + ngx_http_compile_complex_value_t ccv; if (plcf->redirect == 0) { return NGX_CONF_OK; @@ -2526,7 +3502,7 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (plcf->redirects == NULL) { plcf->redirects = ngx_array_create(cf->pool, 1, - sizeof(ngx_http_proxy_redirect_t)); + sizeof(ngx_http_proxy_rewrite_t)); if (plcf->redirects == NULL) { return NGX_CONF_ERROR; } @@ -2540,76 +3516,307 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (ngx_strcmp(value[1].data, "default") == 0) { if (plcf->proxy_lengths) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_redirect default\" may not be used " + "\"proxy_redirect default\" cannot be used " "with \"proxy_pass\" directive with variables"); return NGX_CONF_ERROR; } if (plcf->url.data == NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_redirect default\" must go " + "\"proxy_redirect default\" should be placed " "after the \"proxy_pass\" directive"); return NGX_CONF_ERROR; } - pr->handler = ngx_http_proxy_rewrite_redirect_text; + pr->handler = ngx_http_proxy_rewrite_complex_handler; + + ngx_memzero(&pr->pattern.complex, sizeof(ngx_http_complex_value_t)); + + ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t)); if (plcf->vars.uri.len) { - pr->redirect = plcf->url; - pr->replacement.text = plcf->location; + pr->pattern.complex.value = plcf->url; + pr->replacement.value = plcf->location; } else { - pr->redirect.len = plcf->url.len + sizeof("/") - 1; + pr->pattern.complex.value.len = plcf->url.len + sizeof("/") - 1; - p = ngx_pnalloc(cf->pool, pr->redirect.len); + p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len); if (p == NULL) { return NGX_CONF_ERROR; } - pr->redirect.data = p; + pr->pattern.complex.value.data = p; p = ngx_cpymem(p, plcf->url.data, plcf->url.len); *p = '/'; - ngx_str_set(&pr->replacement.text, "/"); + ngx_str_set(&pr->replacement.value, "/"); + } + + return NGX_CONF_OK; + } + + + if (value[1].data[0] == '~') { + value[1].len--; + value[1].data++; + + if (value[1].data[0] == '*') { + value[1].len--; + value[1].data++; + + if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + } else { + if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } } + } else { + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &pr->pattern.complex; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + pr->handler = ngx_http_proxy_rewrite_complex_handler; + } + + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = &pr->replacement; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_proxy_loc_conf_t *plcf = conf; + + ngx_str_t *value; + ngx_http_proxy_rewrite_t *pr; + ngx_http_compile_complex_value_t ccv; + + if (plcf->cookie_domains == NULL) { return NGX_CONF_OK; } - if (ngx_http_script_variables_count(&value[2]) == 0) { - pr->handler = ngx_http_proxy_rewrite_redirect_text; - pr->redirect = value[1]; - pr->replacement.text = value[2]; + value = cf->args->elts; + if (cf->args->nelts == 2) { + + if (ngx_strcmp(value[1].data, "off") == 0) { + plcf->cookie_domains = NULL; + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + if (plcf->cookie_domains == NGX_CONF_UNSET_PTR) { + plcf->cookie_domains = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_proxy_rewrite_t)); + if (plcf->cookie_domains == NULL) { + return NGX_CONF_ERROR; + } + } + + pr = ngx_array_push(plcf->cookie_domains); + if (pr == NULL) { + return NGX_CONF_ERROR; + } + + if (value[1].data[0] == '~') { + value[1].len--; + value[1].data++; + + if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + } else { + + if (value[1].data[0] == '.') { + value[1].len--; + value[1].data++; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &pr->pattern.complex; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + pr->handler = ngx_http_proxy_rewrite_domain_handler; + + if (value[2].data[0] == '.') { + value[2].len--; + value[2].data++; + } + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = &pr->replacement; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_proxy_loc_conf_t *plcf = conf; + + ngx_str_t *value; + ngx_http_proxy_rewrite_t *pr; + ngx_http_compile_complex_value_t ccv; + + if (plcf->cookie_paths == NULL) { return NGX_CONF_OK; } - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + value = cf->args->elts; - vars_lengths = NULL; - vars_values = NULL; + if (cf->args->nelts == 2) { - sc.cf = cf; - sc.source = &value[2]; - sc.lengths = &vars_lengths; - sc.values = &vars_values; - sc.complete_lengths = 1; - sc.complete_values = 1; + if (ngx_strcmp(value[1].data, "off") == 0) { + plcf->cookie_paths = NULL; + return NGX_CONF_OK; + } - if (ngx_http_script_compile(&sc) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + if (plcf->cookie_paths == NGX_CONF_UNSET_PTR) { + plcf->cookie_paths = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_proxy_rewrite_t)); + if (plcf->cookie_paths == NULL) { + return NGX_CONF_ERROR; + } + } + + pr = ngx_array_push(plcf->cookie_paths); + if (pr == NULL) { return NGX_CONF_ERROR; } - pr->handler = ngx_http_proxy_rewrite_redirect_vars; - pr->redirect = value[1]; - pr->replacement.vars.lengths = vars_lengths->elts; - pr->replacement.vars.values = vars_values->elts; + if (value[1].data[0] == '~') { + value[1].len--; + value[1].data++; + + if (value[1].data[0] == '*') { + value[1].len--; + value[1].data++; + + if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + } else { + if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + } else { + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &pr->pattern.complex; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + pr->handler = ngx_http_proxy_rewrite_complex_handler; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = &pr->replacement; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } return NGX_CONF_OK; } +static ngx_int_t +ngx_http_proxy_rewrite_regex(ngx_conf_t *cf, ngx_http_proxy_rewrite_t *pr, + ngx_str_t *regex, ngx_uint_t caseless) +{ +#if (NGX_PCRE) + u_char errstr[NGX_MAX_CONF_ERRSTR]; + ngx_regex_compile_t rc; + + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); + + rc.pattern = *regex; + rc.err.len = NGX_MAX_CONF_ERRSTR; + rc.err.data = errstr; + + if (caseless) { + rc.options = NGX_REGEX_CASELESS; + } + + pr->pattern.regex = ngx_http_regex_compile(cf, &rc); + if (pr->pattern.regex == NULL) { + return NGX_ERROR; + } + + pr->handler = ngx_http_proxy_rewrite_regex_handler; + + return NGX_OK; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "using regex \"%V\" requires PCRE library", regex); + return NGX_ERROR; + +#endif +} + + static char * ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_realip_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_realip_module.c index bb606beb74c..c54c2ffa14c 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_realip_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_realip_module.c @@ -16,13 +16,7 @@ typedef struct { - in_addr_t mask; - in_addr_t addr; -} ngx_http_realip_from_t; - - -typedef struct { - ngx_array_t *from; /* array of ngx_http_realip_from_t */ + ngx_array_t *from; /* array of ngx_in_cidr_t */ ngx_uint_t type; ngx_uint_t hash; ngx_str_t header; @@ -114,9 +108,9 @@ ngx_http_realip_handler(ngx_http_request_t *r) ngx_list_part_t *part; ngx_table_elt_t *header; struct sockaddr_in *sin; + ngx_in_cidr_t *from; ngx_connection_t *c; ngx_http_realip_ctx_t *ctx; - ngx_http_realip_from_t *from; ngx_http_realip_loc_conf_t *rlcf; ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module); @@ -317,7 +311,7 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_int_t rc; ngx_str_t *value; ngx_cidr_t cidr; - ngx_http_realip_from_t *from; + ngx_in_cidr_t *from; value = cf->args->elts; @@ -332,7 +326,7 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (rlcf->from == NULL) { rlcf->from = ngx_array_create(cf->pool, 2, - sizeof(ngx_http_realip_from_t)); + sizeof(ngx_in_cidr_t)); if (rlcf->from == NULL) { return NGX_CONF_ERROR; } diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_scgi_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_scgi_module.c index 5fb2ac5ae68..239da6b941e 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_scgi_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_scgi_module.c @@ -247,6 +247,20 @@ static ngx_command_t ngx_http_scgi_commands[] = { offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_methods), &ngx_http_upstream_cache_method_mask }, + { ngx_string("scgi_cache_lock"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock), + NULL }, + + { ngx_string("scgi_cache_lock_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock_timeout), + NULL }, + #endif { ngx_string("scgi_temp_path"), @@ -278,8 +292,8 @@ static ngx_command_t ngx_http_scgi_commands[] = { &ngx_http_scgi_next_upstream_masks }, { ngx_string("scgi_param"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, - ngx_conf_set_keyval_slot, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23, + ngx_http_upstream_param_set_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_scgi_loc_conf_t, params_source), NULL }, @@ -519,10 +533,10 @@ static ngx_int_t ngx_http_scgi_create_request(ngx_http_request_t *r) { u_char ch, *key, *val, *lowcase_key; - size_t len, allocated; + size_t len, key_len, val_len, allocated; ngx_buf_t *b; ngx_str_t *content_length; - ngx_uint_t i, n, hash, header_params; + ngx_uint_t i, n, hash, skip_empty, header_params; ngx_chain_t *cl, *body; ngx_list_part_t *part; ngx_table_elt_t *header, **ignored; @@ -554,15 +568,21 @@ ngx_http_scgi_create_request(ngx_http_request_t *r) while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; - len += lcode(&le); + key_len = lcode(&le); - while (*(uintptr_t *) le.ip) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = lcode(&le); + + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; - len += lcode(&le); } - len++; - le.ip += sizeof(uintptr_t); + + if (skip_empty && val_len == 0) { + continue; + } + + len += key_len + val_len + 1; } } @@ -665,7 +685,34 @@ ngx_http_scgi_create_request(ngx_http_request_t *r) e.request = r; e.flushed = 1; - while (*(uintptr_t *) e.ip) { + le.ip = scf->params_len->elts; + + while (*(uintptr_t *) le.ip) { + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + lcode(&le); /* key length */ + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = lcode(&le); + + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + } + le.ip += sizeof(uintptr_t); + + if (skip_empty && val_len == 0) { + e.skip = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + e.ip += sizeof(uintptr_t); + + e.skip = 0; + + continue; + } #if (NGX_DEBUG) key = e.pos; @@ -1032,6 +1079,8 @@ ngx_http_scgi_create_loc_conf(ngx_conf_t *cf) conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; conf->upstream.no_cache = NGX_CONF_UNSET_PTR; conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; + conf->upstream.cache_lock = NGX_CONF_UNSET; + conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC; #endif conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; @@ -1124,8 +1173,8 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->upstream.busy_buffers_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"scgi_busy_buffers_size\" must be equal or bigger " - "than maximum of the value of \"scgi_buffer_size\" and " + "\"scgi_busy_buffers_size\" must be equal to or greater " + "than the maximum of the value of \"scgi_buffer_size\" and " "one of the \"scgi_buffers\""); return NGX_CONF_ERROR; @@ -1155,8 +1204,8 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->upstream.temp_file_write_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"scgi_temp_file_write_size\" must be equal or bigger than " - "maximum of the value of \"scgi_buffer_size\" and " + "\"scgi_temp_file_write_size\" must be equal to or greater than " + "the maximum of the value of \"scgi_buffer_size\" and " "one of the \"scgi_buffers\""); return NGX_CONF_ERROR; @@ -1178,8 +1227,8 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) && conf->upstream.max_temp_file_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"scgi_max_temp_file_size\" must be equal to zero to disable " - "the temporary files usage or must be equal or bigger than " - "maximum of the value of \"scgi_buffer_size\" and " + "temporary files usage or must be equal to or greater than " + "the maximum of the value of \"scgi_buffer_size\" and " "one of the \"scgi_buffers\""); return NGX_CONF_ERROR; @@ -1263,6 +1312,12 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) conf->cache_key = prev->cache_key; } + ngx_conf_merge_value(conf->upstream.cache_lock, + prev->upstream.cache_lock, 0); + + ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout, + prev->upstream.cache_lock_timeout, 5000); + #endif ngx_conf_merge_value(conf->upstream.pass_request_headers, @@ -1320,9 +1375,9 @@ ngx_http_scgi_merge_params(ngx_conf_t *cf, ngx_http_scgi_loc_conf_t *conf, #if (NGX_HTTP_CACHE) ngx_array_t params_merged; #endif - ngx_keyval_t *src; ngx_hash_key_t *hk; ngx_hash_init_t hash; + ngx_http_upstream_param_t *src; ngx_http_script_compile_t sc; ngx_http_script_copy_code_t *copy; @@ -1331,7 +1386,8 @@ ngx_http_scgi_merge_params(ngx_conf_t *cf, ngx_http_scgi_loc_conf_t *conf, if (prev->headers_hash.buckets #if (NGX_HTTP_CACHE) - && ((conf->upstream.cache == NULL) == (prev->upstream.cache == NULL)) + && ((conf->upstream.cache == NULL) + == (prev->upstream.cache == NULL)) #endif ) { @@ -1383,9 +1439,11 @@ ngx_http_scgi_merge_params(ngx_conf_t *cf, ngx_http_scgi_loc_conf_t *conf, #if (NGX_HTTP_CACHE) if (conf->upstream.cache) { - ngx_keyval_t *h, *s; + ngx_keyval_t *h; + ngx_http_upstream_param_t *s; - if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t)) + if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, + sizeof(ngx_http_upstream_param_t)) != NGX_OK) { return NGX_ERROR; @@ -1419,7 +1477,9 @@ ngx_http_scgi_merge_params(ngx_conf_t *cf, ngx_http_scgi_loc_conf_t *conf, return NGX_ERROR; } - *s = *h; + s->key = h->key; + s->value = h->value; + s->skip_empty = 0; next: @@ -1461,6 +1521,15 @@ ngx_http_scgi_merge_params(ngx_conf_t *cf, ngx_http_scgi_loc_conf_t *conf, copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; copy->len = src[i].key.len + 1; + copy = ngx_array_push_n(conf->params_len, + sizeof(ngx_http_script_copy_code_t)); + if (copy == NULL) { + return NGX_ERROR; + } + + copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; + copy->len = src[i].skip_empty; + size = (sizeof(ngx_http_script_copy_code_t) + src[i].key.len + 1 + sizeof(uintptr_t) - 1) diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_split_clients_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_split_clients_module.c index f2160b149de..726269c3c73 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_split_clients_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_split_clients_module.c @@ -97,7 +97,7 @@ ngx_http_split_clients_variable(ngx_http_request_t *r, ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http split: %uD %uD", hash, part[i].percent); - if (hash < part[i].percent) { + if (hash < part[i].percent || part[i].percent == 0) { *v = part[i].value; return NGX_OK; } @@ -111,8 +111,9 @@ static char * ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; + uint32_t sum, last; ngx_str_t *value, name; - ngx_uint_t i, sum, last; + ngx_uint_t i; ngx_conf_t save; ngx_http_variable_t *var; ngx_http_split_clients_ctx_t *ctx; @@ -175,19 +176,15 @@ ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) for (i = 0; i < ctx->parts.nelts; i++) { sum = part[i].percent ? sum + part[i].percent : 10000; if (sum > 10000) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "percent sum is more than 100%%"); - return NGX_CONF_ERROR; + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "percent total is greater than 100%%"); + return NGX_CONF_ERROR; } if (part[i].percent) { - part[i].percent = (uint32_t) - (last + 0xffffffff / 10000 * part[i].percent); - } else { - part[i].percent = 0xffffffff; + last += part[i].percent * (uint64_t) 0xffffffff / 10000; + part[i].percent = last; } - - last = part[i].percent; } return rv; diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_ssi_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_ssi_filter_module.c index b02300f39f7..219465ae909 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_ssi_filter_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_ssi_filter_module.c @@ -714,7 +714,7 @@ ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in) if (ctx->params.nelts > NGX_HTTP_SSI_MAX_PARAMS) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "too many SSI command paramters: \"%V\"", + "too many SSI command parameters: \"%V\"", &ctx->command); goto ssi_error; } @@ -1204,7 +1204,7 @@ ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx) if (ctx->value_buf == NULL) { ctx->param->value.data = ngx_pnalloc(r->pool, - ctx->value_len); + ctx->value_len + 1); if (ctx->param->value.data == NULL) { return NGX_ERROR; } @@ -1375,6 +1375,16 @@ ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx) case ssi_quoted_symbol_state: state = ctx->saved_state; + if (ctx->param->value.len == ctx->value_len) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "too long \"%V%c...\" value of \"%V\" " + "parameter in \"%V\" SSI command", + &ctx->param->value, ch, &ctx->param->key, + &ctx->command); + state = ssi_error_state; + break; + } + ctx->param->value.data[ctx->param->value.len++] = ch; break; @@ -1993,7 +2003,7 @@ ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, if (set && stub) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "\"set\" and \"stub\" may not be used together " + "\"set\" and \"stub\" cannot be used together " "in \"include\" SSI command"); return NGX_HTTP_SSI_ERROR; } @@ -2001,7 +2011,7 @@ ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, if (wait) { if (uri == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "\"wait\" may not be used with file=\"%V\"", file); + "\"wait\" cannot be used with file=\"%V\"", file); return NGX_HTTP_SSI_ERROR; } @@ -2178,7 +2188,7 @@ ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, } else { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "only one subrequest may be waited at the same time"); + "can only wait for one subrequest at a time"); } return NGX_OK; @@ -2886,7 +2896,7 @@ ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) prev->ignore_recycled_buffers, 0); ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024); - ngx_conf_merge_size_value(conf->value_len, prev->value_len, 256); + ngx_conf_merge_size_value(conf->value_len, prev->value_len, 255); if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, &prev->types_keys, &prev->types, diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_static_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_static_module.c index a0e302ad1d5..9d77e43b119 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_static_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_static_module.c @@ -95,6 +95,10 @@ ngx_http_static_handler(ngx_http_request_t *r) of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; + if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { @@ -112,6 +116,10 @@ ngx_http_static_handler(ngx_http_request_t *r) break; case NGX_EACCES: +#if (NGX_HAVE_OPENAT) + case NGX_EMLINK: + case NGX_ELOOP: +#endif level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c index fd9ecbe0bff..100ea34dd04 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c @@ -186,8 +186,8 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data) break; } - if (now - peer->accessed > peer->fail_timeout) { - peer->fails = 0; + if (now - peer->checked > peer->fail_timeout) { + peer->checked = now; break; } } diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_upstream_keepalive_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_upstream_keepalive_module.c new file mode 100644 index 00000000000..6fd9ac70a34 --- /dev/null +++ b/usr.sbin/nginx/src/http/modules/ngx_http_upstream_keepalive_module.c @@ -0,0 +1,570 @@ + +/* + * Copyright (C) Maxim Dounin + * Copyright (C) Nginx, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + +typedef struct { + ngx_uint_t max_cached; + ngx_uint_t single; /* unsigned:1 */ + + ngx_queue_t cache; + ngx_queue_t free; + + ngx_http_upstream_init_pt original_init_upstream; + ngx_http_upstream_init_peer_pt original_init_peer; + +} ngx_http_upstream_keepalive_srv_conf_t; + + +typedef struct { + ngx_http_upstream_keepalive_srv_conf_t *conf; + + ngx_http_upstream_t *upstream; + + void *data; + + ngx_event_get_peer_pt original_get_peer; + ngx_event_free_peer_pt original_free_peer; + +#if (NGX_HTTP_SSL) + ngx_event_set_peer_session_pt original_set_session; + ngx_event_save_peer_session_pt original_save_session; +#endif + + ngx_uint_t failed; /* unsigned:1 */ + +} ngx_http_upstream_keepalive_peer_data_t; + + +typedef struct { + ngx_http_upstream_keepalive_srv_conf_t *conf; + + ngx_queue_t queue; + ngx_connection_t *connection; + + socklen_t socklen; + u_char sockaddr[NGX_SOCKADDRLEN]; + +} ngx_http_upstream_keepalive_cache_t; + + +static ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r, + ngx_http_upstream_srv_conf_t *us); +static ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, + void *data); +static void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, + void *data, ngx_uint_t state); + +static void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev); +static void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev); +static void ngx_http_upstream_keepalive_close(ngx_connection_t *c); + + +#if (NGX_HTTP_SSL) +static ngx_int_t ngx_http_upstream_keepalive_set_session( + ngx_peer_connection_t *pc, void *data); +static void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, + void *data); +#endif + +static void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf); +static char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_http_upstream_keepalive_commands[] = { + + { ngx_string("keepalive"), + NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12, + ngx_http_upstream_keepalive, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_upstream_keepalive_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_http_upstream_keepalive_create_conf, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_upstream_keepalive_module = { + NGX_MODULE_V1, + &ngx_http_upstream_keepalive_module_ctx, /* module context */ + ngx_http_upstream_keepalive_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_upstream_init_keepalive(ngx_conf_t *cf, + ngx_http_upstream_srv_conf_t *us) +{ + ngx_uint_t i; + ngx_http_upstream_keepalive_srv_conf_t *kcf; + ngx_http_upstream_keepalive_cache_t *cached; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, + "init keepalive"); + + kcf = ngx_http_conf_upstream_srv_conf(us, + ngx_http_upstream_keepalive_module); + + if (kcf->original_init_upstream(cf, us) != NGX_OK) { + return NGX_ERROR; + } + + kcf->original_init_peer = us->peer.init; + + us->peer.init = ngx_http_upstream_init_keepalive_peer; + + /* allocate cache items and add to free queue */ + + cached = ngx_pcalloc(cf->pool, + sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached); + if (cached == NULL) { + return NGX_ERROR; + } + + ngx_queue_init(&kcf->cache); + ngx_queue_init(&kcf->free); + + for (i = 0; i < kcf->max_cached; i++) { + ngx_queue_insert_head(&kcf->free, &cached[i].queue); + cached[i].conf = kcf; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r, + ngx_http_upstream_srv_conf_t *us) +{ + ngx_http_upstream_keepalive_peer_data_t *kp; + ngx_http_upstream_keepalive_srv_conf_t *kcf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "init keepalive peer"); + + kcf = ngx_http_conf_upstream_srv_conf(us, + ngx_http_upstream_keepalive_module); + + kp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t)); + if (kp == NULL) { + return NGX_ERROR; + } + + if (kcf->original_init_peer(r, us) != NGX_OK) { + return NGX_ERROR; + } + + kp->conf = kcf; + kp->upstream = r->upstream; + kp->data = r->upstream->peer.data; + kp->original_get_peer = r->upstream->peer.get; + kp->original_free_peer = r->upstream->peer.free; + + r->upstream->peer.data = kp; + r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer; + r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer; + +#if (NGX_HTTP_SSL) + kp->original_set_session = r->upstream->peer.set_session; + kp->original_save_session = r->upstream->peer.save_session; + r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session; + r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session; +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data) +{ + ngx_http_upstream_keepalive_peer_data_t *kp = data; + ngx_http_upstream_keepalive_cache_t *item; + + ngx_int_t rc; + ngx_queue_t *q, *cache; + ngx_connection_t *c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "get keepalive peer"); + + kp->failed = 0; + + /* single pool of cached connections */ + + if (kp->conf->single && !ngx_queue_empty(&kp->conf->cache)) { + + q = ngx_queue_head(&kp->conf->cache); + + item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); + c = item->connection; + + ngx_queue_remove(q); + ngx_queue_insert_head(&kp->conf->free, q); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "get keepalive peer: using connection %p", c); + + c->idle = 0; + c->log = pc->log; + c->read->log = pc->log; + c->write->log = pc->log; + c->pool->log = pc->log; + + pc->connection = c; + pc->cached = 1; + + return NGX_DONE; + } + + rc = kp->original_get_peer(pc, kp->data); + + if (kp->conf->single || rc != NGX_OK) { + return rc; + } + + /* search cache for suitable connection */ + + cache = &kp->conf->cache; + + for (q = ngx_queue_head(cache); + q != ngx_queue_sentinel(cache); + q = ngx_queue_next(q)) + { + item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); + c = item->connection; + + if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr, + item->socklen, pc->socklen) + == 0) + { + ngx_queue_remove(q); + ngx_queue_insert_head(&kp->conf->free, q); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "get keepalive peer: using connection %p", c); + + c->idle = 0; + c->log = pc->log; + c->read->log = pc->log; + c->write->log = pc->log; + c->pool->log = pc->log; + + pc->connection = c; + pc->cached = 1; + + return NGX_DONE; + } + } + + return NGX_OK; +} + + +static void +ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data, + ngx_uint_t state) +{ + ngx_http_upstream_keepalive_peer_data_t *kp = data; + ngx_http_upstream_keepalive_cache_t *item; + + ngx_queue_t *q; + ngx_connection_t *c; + ngx_http_upstream_t *u; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "free keepalive peer"); + + /* remember failed state - peer.free() may be called more than once */ + + if (state & NGX_PEER_FAILED) { + kp->failed = 1; + } + + /* cache valid connections */ + + u = kp->upstream; + c = pc->connection; + + if (kp->failed + || c == NULL + || c->read->eof + || c->read->error + || c->read->timedout + || c->write->error + || c->write->timedout) + { + goto invalid; + } + + if (!u->keepalive) { + goto invalid; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + goto invalid; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "free keepalive peer: saving connection %p", c); + + if (ngx_queue_empty(&kp->conf->free)) { + + q = ngx_queue_last(&kp->conf->cache); + ngx_queue_remove(q); + + item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); + + ngx_http_upstream_keepalive_close(item->connection); + + } else { + q = ngx_queue_head(&kp->conf->free); + ngx_queue_remove(q); + + item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); + } + + item->connection = c; + ngx_queue_insert_head(&kp->conf->cache, q); + + pc->connection = NULL; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + c->write->handler = ngx_http_upstream_keepalive_dummy_handler; + c->read->handler = ngx_http_upstream_keepalive_close_handler; + + c->data = item; + c->idle = 1; + c->log = ngx_cycle->log; + c->read->log = ngx_cycle->log; + c->write->log = ngx_cycle->log; + c->pool->log = ngx_cycle->log; + + item->socklen = pc->socklen; + ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen); + + if (c->read->ready) { + ngx_http_upstream_keepalive_close_handler(c->read); + } + +invalid: + + kp->original_free_peer(pc, kp->data, state); +} + + +static void +ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "keepalive dummy handler"); +} + + +static void +ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev) +{ + ngx_http_upstream_keepalive_srv_conf_t *conf; + ngx_http_upstream_keepalive_cache_t *item; + + int n; + char buf[1]; + ngx_connection_t *c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "keepalive close handler"); + + c = ev->data; + + if (c->close) { + goto close; + } + + n = recv(c->fd, buf, 1, MSG_PEEK); + + if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { + /* stale event */ + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + goto close; + } + + return; + } + +close: + + item = c->data; + conf = item->conf; + + ngx_http_upstream_keepalive_close(c); + + ngx_queue_remove(&item->queue); + ngx_queue_insert_head(&conf->free, &item->queue); +} + + +static void +ngx_http_upstream_keepalive_close(ngx_connection_t *c) +{ + +#if (NGX_HTTP_SSL) + + if (c->ssl) { + c->ssl->no_wait_shutdown = 1; + c->ssl->no_send_shutdown = 1; + + if (ngx_ssl_shutdown(c) == NGX_AGAIN) { + c->ssl->handler = ngx_http_upstream_keepalive_close; + return; + } + } + +#endif + + ngx_destroy_pool(c->pool); + ngx_close_connection(c); +} + + +#if (NGX_HTTP_SSL) + +static ngx_int_t +ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data) +{ + ngx_http_upstream_keepalive_peer_data_t *kp = data; + + return kp->original_set_session(pc, kp->data); +} + + +static void +ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data) +{ + ngx_http_upstream_keepalive_peer_data_t *kp = data; + + kp->original_save_session(pc, kp->data); + return; +} + +#endif + + +static void * +ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf) +{ + ngx_http_upstream_keepalive_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, + sizeof(ngx_http_upstream_keepalive_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->original_init_upstream = NULL; + * conf->original_init_peer = NULL; + */ + + conf->max_cached = 1; + + return conf; +} + + +static char * +ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_upstream_srv_conf_t *uscf; + ngx_http_upstream_keepalive_srv_conf_t *kcf; + + ngx_int_t n; + ngx_str_t *value; + ngx_uint_t i; + + uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); + + kcf = ngx_http_conf_upstream_srv_conf(uscf, + ngx_http_upstream_keepalive_module); + + kcf->original_init_upstream = uscf->peer.init_upstream + ? uscf->peer.init_upstream + : ngx_http_upstream_init_round_robin; + + uscf->peer.init_upstream = ngx_http_upstream_init_keepalive; + + /* read options */ + + value = cf->args->elts; + + n = ngx_atoi(value[1].data, value[1].len); + + if (n == NGX_ERROR || n == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%V\" in \"%V\" directive", + &value[1], &cmd->name); + return NGX_CONF_ERROR; + } + + kcf->max_cached = n; + + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strcmp(value[i].data, "single") == 0) { + kcf->single = 1; + continue; + } + + goto invalid; + } + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + + return NGX_CONF_ERROR; +} diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_userid_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_userid_filter_module.c index 05343f2f1a4..1487c091eee 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_userid_filter_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_userid_filter_module.c @@ -774,14 +774,10 @@ ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } ucf->expires = ngx_parse_time(&value[1], 1); - if (ucf->expires == NGX_ERROR) { + if (ucf->expires == (time_t) NGX_ERROR) { return "invalid value"; } - if (ucf->expires == NGX_PARSE_LARGE_TIME) { - return "value must be less than 68 years"; - } - return NGX_CONF_OK; } diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_uwsgi_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_uwsgi_module.c index fb5ff0d7876..0c999ca6299 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_uwsgi_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_uwsgi_module.c @@ -43,7 +43,6 @@ static ngx_int_t ngx_http_uwsgi_create_request(ngx_http_request_t *r); static ngx_int_t ngx_http_uwsgi_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_uwsgi_process_status_line(ngx_http_request_t *r); static ngx_int_t ngx_http_uwsgi_process_header(ngx_http_request_t *r); -static ngx_int_t ngx_http_uwsgi_process_header(ngx_http_request_t *r); static void ngx_http_uwsgi_abort_request(ngx_http_request_t *r); static void ngx_http_uwsgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc); @@ -275,6 +274,20 @@ static ngx_command_t ngx_http_uwsgi_commands[] = { offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_methods), &ngx_http_upstream_cache_method_mask }, + { ngx_string("uwsgi_cache_lock"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_lock), + NULL }, + + { ngx_string("uwsgi_cache_lock_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_lock_timeout), + NULL }, + #endif { ngx_string("uwsgi_temp_path"), @@ -306,8 +319,8 @@ static ngx_command_t ngx_http_uwsgi_commands[] = { &ngx_http_uwsgi_next_upstream_masks }, { ngx_string("uwsgi_param"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, - ngx_conf_set_keyval_slot, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23, + ngx_http_upstream_param_set_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_uwsgi_loc_conf_t, params_source), NULL }, @@ -554,7 +567,7 @@ ngx_http_uwsgi_create_request(ngx_http_request_t *r) { u_char ch, *lowcase_key; size_t key_len, val_len, len, allocated; - ngx_uint_t i, n, hash, header_params; + ngx_uint_t i, n, hash, skip_empty, header_params; ngx_buf_t *b; ngx_chain_t *cl, *body; ngx_list_part_t *part; @@ -584,11 +597,18 @@ ngx_http_uwsgi_create_request(ngx_http_request_t *r) lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = lcode(&le); + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = lcode(&le); + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode (&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; } le.ip += sizeof(uintptr_t); + if (skip_empty && val_len == 0) { + continue; + } + len += 2 + key_len + 2 + val_len; } } @@ -707,11 +727,28 @@ ngx_http_uwsgi_create_request(ngx_http_request_t *r) lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = (u_char) lcode (&le); + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = lcode(&le); + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; } le.ip += sizeof(uintptr_t); + if (skip_empty && val_len == 0) { + e.skip = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + e.ip += sizeof(uintptr_t); + + e.skip = 0; + + continue; + } + *e.pos++ = (u_char) (key_len & 0xff); *e.pos++ = (u_char) ((key_len >> 8) & 0xff); @@ -874,10 +911,7 @@ ngx_http_uwsgi_process_status_line(ngx_http_request_t *r) } if (rc == NGX_ERROR) { - r->http_version = NGX_HTTP_VERSION_9; - u->process_header = ngx_http_uwsgi_process_header; - return ngx_http_uwsgi_process_header(r); } @@ -979,12 +1013,12 @@ ngx_http_uwsgi_process_header(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http uwsgi header done"); - if (r->http_version > NGX_HTTP_VERSION_9) { + u = r->upstream; + + if (u->headers_in.status_n) { return NGX_OK; } - u = r->upstream; - if (u->headers_in.status) { status_line = &u->headers_in.status->value; @@ -996,20 +1030,15 @@ ngx_http_uwsgi_process_header(ngx_http_request_t *r) return NGX_HTTP_UPSTREAM_INVALID_HEADER; } - r->http_version = NGX_HTTP_VERSION_10; u->headers_in.status_n = status; u->headers_in.status_line = *status_line; } else if (u->headers_in.location) { - r->http_version = NGX_HTTP_VERSION_10; u->headers_in.status_n = 302; ngx_str_set(&u->headers_in.status_line, "302 Moved Temporarily"); } else { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent neither valid HTTP/1.0 header " - "nor \"Status\" header line"); u->headers_in.status_n = 200; ngx_str_set(&u->headers_in.status_line, "200 OK"); } @@ -1093,6 +1122,8 @@ ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf) conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; conf->upstream.no_cache = NGX_CONF_UNSET_PTR; conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; + conf->upstream.cache_lock = NGX_CONF_UNSET; + conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC; #endif conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; @@ -1185,8 +1216,8 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->upstream.busy_buffers_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"uwsgi_busy_buffers_size\" must be equal or bigger " - "than maximum of the value of \"uwsgi_buffer_size\" and " + "\"uwsgi_busy_buffers_size\" must be equal to or greater " + "than the maximum of the value of \"uwsgi_buffer_size\" and " "one of the \"uwsgi_buffers\""); return NGX_CONF_ERROR; @@ -1216,8 +1247,8 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->upstream.temp_file_write_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"uwsgi_temp_file_write_size\" must be equal or bigger than " - "maximum of the value of \"uwsgi_buffer_size\" and " + "\"uwsgi_temp_file_write_size\" must be equal to or greater than " + "the maximum of the value of \"uwsgi_buffer_size\" and " "one of the \"uwsgi_buffers\""); return NGX_CONF_ERROR; @@ -1239,8 +1270,8 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) && conf->upstream.max_temp_file_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"uwsgi_max_temp_file_size\" must be equal to zero to disable " - "the temporary files usage or must be equal or bigger than " - "maximum of the value of \"uwsgi_buffer_size\" and " + "temporary files usage or must be equal to or greater than " + "the maximum of the value of \"uwsgi_buffer_size\" and " "one of the \"uwsgi_buffers\""); return NGX_CONF_ERROR; @@ -1324,6 +1355,12 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) conf->cache_key = prev->cache_key; } + ngx_conf_merge_value(conf->upstream.cache_lock, + prev->upstream.cache_lock, 0); + + ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout, + prev->upstream.cache_lock_timeout, 5000); + #endif ngx_conf_merge_value(conf->upstream.pass_request_headers, @@ -1386,9 +1423,9 @@ ngx_http_uwsgi_merge_params(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *conf, #if (NGX_HTTP_CACHE) ngx_array_t params_merged; #endif - ngx_keyval_t *src; ngx_hash_key_t *hk; ngx_hash_init_t hash; + ngx_http_upstream_param_t *src; ngx_http_script_compile_t sc; ngx_http_script_copy_code_t *copy; @@ -1397,7 +1434,8 @@ ngx_http_uwsgi_merge_params(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *conf, if (prev->headers_hash.buckets #if (NGX_HTTP_CACHE) - && ((conf->upstream.cache == NULL) == (prev->upstream.cache == NULL)) + && ((conf->upstream.cache == NULL) + == (prev->upstream.cache == NULL)) #endif ) { @@ -1449,9 +1487,11 @@ ngx_http_uwsgi_merge_params(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *conf, #if (NGX_HTTP_CACHE) if (conf->upstream.cache) { - ngx_keyval_t *h, *s; + ngx_keyval_t *h; + ngx_http_upstream_param_t *s; - if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t)) + if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, + sizeof(ngx_http_upstream_param_t)) != NGX_OK) { return NGX_ERROR; @@ -1485,7 +1525,9 @@ ngx_http_uwsgi_merge_params(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *conf, return NGX_ERROR; } - *s = *h; + s->key = h->key; + s->value = h->value; + s->skip_empty = 0; next: @@ -1527,6 +1569,15 @@ ngx_http_uwsgi_merge_params(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *conf, copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; copy->len = src[i].key.len; + copy = ngx_array_push_n(conf->params_len, + sizeof(ngx_http_script_copy_code_t)); + if (copy == NULL) { + return NGX_ERROR; + } + + copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; + copy->len = src[i].skip_empty; + size = (sizeof(ngx_http_script_copy_code_t) + src[i].key.len + sizeof(uintptr_t) - 1) diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_xslt_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_xslt_filter_module.c index f86c3038102..4309a448908 100644 --- a/usr.sbin/nginx/src/http/modules/ngx_http_xslt_filter_module.c +++ b/usr.sbin/nginx/src/http/modules/ngx_http_xslt_filter_module.c @@ -14,6 +14,7 @@ #include <libxslt/xslt.h> #include <libxslt/xsltInternals.h> #include <libxslt/transform.h> +#include <libxslt/variables.h> #include <libxslt/xsltutils.h> #if (NGX_HAVE_EXSLT) @@ -27,38 +28,47 @@ typedef struct { - u_char *name; - void *data; + u_char *name; + void *data; } ngx_http_xslt_file_t; typedef struct { - ngx_array_t dtd_files; /* ngx_http_xslt_file_t */ - ngx_array_t sheet_files; /* ngx_http_xslt_file_t */ + ngx_array_t dtd_files; /* ngx_http_xslt_file_t */ + ngx_array_t sheet_files; /* ngx_http_xslt_file_t */ } ngx_http_xslt_filter_main_conf_t; typedef struct { - xsltStylesheetPtr stylesheet; - ngx_array_t params; /* ngx_http_complex_value_t */ + u_char *name; + ngx_http_complex_value_t value; + ngx_uint_t quote; /* unsigned quote:1; */ +} ngx_http_xslt_param_t; + + +typedef struct { + xsltStylesheetPtr stylesheet; + ngx_array_t params; /* ngx_http_xslt_param_t */ } ngx_http_xslt_sheet_t; typedef struct { - xmlDtdPtr dtd; - ngx_array_t sheets; /* ngx_http_xslt_sheet_t */ - ngx_hash_t types; - ngx_array_t *types_keys; + xmlDtdPtr dtd; + ngx_array_t sheets; /* ngx_http_xslt_sheet_t */ + ngx_hash_t types; + ngx_array_t *types_keys; + ngx_array_t *params; /* ngx_http_xslt_param_t */ } ngx_http_xslt_filter_loc_conf_t; typedef struct { - xmlDocPtr doc; - xmlParserCtxtPtr ctxt; - ngx_http_request_t *request; - ngx_array_t params; + xmlDocPtr doc; + xmlParserCtxtPtr ctxt; + xsltTransformContextPtr transform; + ngx_http_request_t *request; + ngx_array_t params; - ngx_uint_t done; /* unsigned done:1; */ + ngx_uint_t done; /* unsigned done:1; */ } ngx_http_xslt_filter_ctx_t; @@ -76,7 +86,7 @@ static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...); static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx); static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r, - ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params); + ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params, ngx_uint_t final); static u_char *ngx_http_xslt_content_type(xsltStylesheetPtr s); static u_char *ngx_http_xslt_encoding(xsltStylesheetPtr s); static void ngx_http_xslt_cleanup(void *data); @@ -85,6 +95,8 @@ static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static void ngx_http_xslt_cleanup_dtd(void *data); static void ngx_http_xslt_cleanup_stylesheet(void *data); static void *ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf); @@ -117,6 +129,20 @@ static ngx_command_t ngx_http_xslt_filter_commands[] = { 0, NULL }, + { ngx_string("xslt_param"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_http_xslt_param, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("xslt_string_param"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_http_xslt_param, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + (void *) 1 }, + { ngx_string("xslt_types"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, ngx_http_types_slot, @@ -336,15 +362,14 @@ ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, "xmlCreatePushParserCtxt() failed"); return NGX_ERROR; } + xmlCtxtUseOptions(ctxt, XML_PARSE_NOENT|XML_PARSE_DTDLOAD + |XML_PARSE_NOWARNING); ctxt->sax->externalSubset = ngx_http_xslt_sax_external_subset; ctxt->sax->setDocumentLocator = NULL; - ctxt->sax->warning = NULL; ctxt->sax->error = ngx_http_xslt_sax_error; ctxt->sax->fatalError = ngx_http_xslt_sax_error; ctxt->sax->_private = ctx; - ctxt->replaceEntities = 1; - ctxt->loadsubset = 1; ctx->ctxt = ctxt; ctx->request = r; @@ -469,13 +494,31 @@ ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r, for (i = 0; i < conf->sheets.nelts; i++) { - if (ngx_http_xslt_params(r, ctx, &sheet[i].params) != NGX_OK) { + ctx->transform = xsltNewTransformContext(sheet[i].stylesheet, doc); + if (ctx->transform == NULL) { xmlFreeDoc(doc); return NULL; } - res = xsltApplyStylesheet(sheet[i].stylesheet, doc, ctx->params.elts); + if (conf->params + && ngx_http_xslt_params(r, ctx, conf->params, 0) != NGX_OK) + { + xsltFreeTransformContext(ctx->transform); + xmlFreeDoc(doc); + return NULL; + } + if (ngx_http_xslt_params(r, ctx, &sheet[i].params, 1) != NGX_OK) { + xsltFreeTransformContext(ctx->transform); + xmlFreeDoc(doc); + return NULL; + } + + res = xsltApplyStylesheetUser(sheet[i].stylesheet, doc, + ctx->params.elts, NULL, NULL, + ctx->transform); + + xsltFreeTransformContext(ctx->transform); xmlFreeDoc(doc); if (res == NULL) { @@ -565,27 +608,68 @@ ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r, static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, - ngx_array_t *params) + ngx_array_t *params, ngx_uint_t final) { - u_char *p, *last, *value, *dst, *src, **s; - size_t len; - ngx_uint_t i; - ngx_str_t string; - ngx_http_complex_value_t *param; + u_char *p, *last, *value, *dst, *src, **s; + size_t len; + ngx_uint_t i; + ngx_str_t string; + ngx_http_xslt_param_t *param; param = params->elts; for (i = 0; i < params->nelts; i++) { - if (ngx_http_complex_value(r, ¶m[i], &string) != NGX_OK) { + if (ngx_http_complex_value(r, ¶m[i].value, &string) != NGX_OK) { return NGX_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xslt filter param: \"%s\"", string.data); + if (param[i].name) { + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter param name: \"%s\"", param[i].name); + + if (param[i].quote) { + if (xsltQuoteOneUserParam(ctx->transform, param[i].name, + string.data) + != 0) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xsltQuoteOneUserParam(\"%s\", \"%s\") failed", + param[i].name, string.data); + return NGX_ERROR; + } + + continue; + } + + s = ngx_array_push(&ctx->params); + if (s == NULL) { + return NGX_ERROR; + } + + *s = param[i].name; + + s = ngx_array_push(&ctx->params); + if (s == NULL) { + return NGX_ERROR; + } + + *s = string.data; + + continue; + } + + /* + * parse param1=value1:param2=value2 syntax as used by parameters + * specified in xslt_stylesheet directives + */ + p = string.data; - last = string.data + string.len - 1; + last = string.data + string.len; while (p && *p) { @@ -641,12 +725,14 @@ ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, } } - s = ngx_array_push(&ctx->params); - if (s == NULL) { - return NGX_ERROR; - } + if (final) { + s = ngx_array_push(&ctx->params); + if (s == NULL) { + return NGX_ERROR; + } - *s = NULL; + *s = NULL; + } return NGX_OK; } @@ -768,7 +854,7 @@ ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_pool_cleanup_t *cln; ngx_http_xslt_file_t *file; ngx_http_xslt_sheet_t *sheet; - ngx_http_complex_value_t *param; + ngx_http_xslt_param_t *param; ngx_http_compile_complex_value_t ccv; ngx_http_xslt_filter_main_conf_t *xmcf; @@ -837,7 +923,7 @@ found: } if (ngx_array_init(&sheet->params, cf->pool, n - 2, - sizeof(ngx_http_complex_value_t)) + sizeof(ngx_http_xslt_param_t)) != NGX_OK) { return NGX_CONF_ERROR; @@ -850,11 +936,12 @@ found: return NGX_CONF_ERROR; } + ngx_memzero(param, sizeof(ngx_http_xslt_param_t)); ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[i]; - ccv.complex_value = param; + ccv.complex_value = ¶m->value; ccv.zero = 1; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { @@ -866,6 +953,48 @@ found: } +static char * +ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_xslt_filter_loc_conf_t *xlcf = conf; + + ngx_http_xslt_param_t *param; + ngx_http_compile_complex_value_t ccv; + ngx_str_t *value; + + value = cf->args->elts; + + if (xlcf->params == NULL) { + xlcf->params = ngx_array_create(cf->pool, 2, + sizeof(ngx_http_xslt_param_t)); + if (xlcf->params == NULL) { + return NGX_CONF_ERROR; + } + } + + param = ngx_array_push(xlcf->params); + if (param == NULL) { + return NGX_CONF_ERROR; + } + + param->name = value[1].data; + param->quote = (cmd->post == NULL) ? 0 : 1; + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = ¶m->value; + ccv.zero = 1; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + static void ngx_http_xslt_cleanup_dtd(void *data) { @@ -925,6 +1054,7 @@ ngx_http_xslt_filter_create_conf(ngx_conf_t *cf) * conf->sheets = { NULL }; * conf->types = { NULL }; * conf->types_keys = NULL; + * conf->params = NULL; */ return conf; @@ -945,6 +1075,10 @@ ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) conf->sheets = prev->sheets; } + if (conf->params == NULL) { + conf->params = prev->params; + } + if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, &prev->types_keys, &prev->types, ngx_http_xslt_default_types) diff --git a/usr.sbin/nginx/src/http/modules/perl/nginx.pm b/usr.sbin/nginx/src/http/modules/perl/nginx.pm index 76ec1d3915b..766b8101787 100644 --- a/usr.sbin/nginx/src/http/modules/perl/nginx.pm +++ b/usr.sbin/nginx/src/http/modules/perl/nginx.pm @@ -50,7 +50,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '1.0.15'; +our $VERSION = '1.2.0'; require XSLoader; XSLoader::load('nginx', $VERSION); @@ -132,6 +132,7 @@ Igor Sysoev =head1 COPYRIGHT AND LICENSE Copyright (C) Igor Sysoev +Copyright (C) Nginx, Inc. =cut diff --git a/usr.sbin/nginx/src/http/modules/perl/nginx.xs b/usr.sbin/nginx/src/http/modules/perl/nginx.xs index dc69d509ae3..ecd11ffbc07 100644 --- a/usr.sbin/nginx/src/http/modules/perl/nginx.xs +++ b/usr.sbin/nginx/src/http/modules/perl/nginx.xs @@ -663,6 +663,10 @@ sendfile(r, filename, offset = -1, bytes = 0) of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; + if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { + XSRETURN_EMPTY; + } + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { diff --git a/usr.sbin/nginx/src/http/ngx_http.c b/usr.sbin/nginx/src/http/ngx_http.c index 8f45b71c9ed..3e077fb95ab 100644 --- a/usr.sbin/nginx/src/http/ngx_http.c +++ b/usr.sbin/nginx/src/http/ngx_http.c @@ -1417,7 +1417,7 @@ ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, /* * check whether all name-based servers have the same - * configuraiton as a default server for given address:port + * configuration as a default server for given address:port */ addr = port[p].addrs.elts; @@ -1763,6 +1763,13 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) ls->rcvbuf = addr->opt.rcvbuf; ls->sndbuf = addr->opt.sndbuf; + ls->keepalive = addr->opt.so_keepalive; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + ls->keepidle = addr->opt.tcp_keepidle; + ls->keepintvl = addr->opt.tcp_keepintvl; + ls->keepcnt = addr->opt.tcp_keepcnt; +#endif + #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) ls->accept_filter = addr->opt.accept_filter; #endif diff --git a/usr.sbin/nginx/src/http/ngx_http.h b/usr.sbin/nginx/src/http/ngx_http.h index 930ac71ce06..f152006a005 100644 --- a/usr.sbin/nginx/src/http/ngx_http.h +++ b/usr.sbin/nginx/src/http/ngx_http.h @@ -53,6 +53,7 @@ struct ngx_http_log_ctx_s { typedef struct { + ngx_uint_t http_version; ngx_uint_t code; ngx_uint_t count; u_char *start; diff --git a/usr.sbin/nginx/src/http/ngx_http_busy_lock.c b/usr.sbin/nginx/src/http/ngx_http_busy_lock.c index 35a03acaac9..3b4b28c8b32 100644 --- a/usr.sbin/nginx/src/http/ngx_http_busy_lock.c +++ b/usr.sbin/nginx/src/http/ngx_http_busy_lock.c @@ -274,7 +274,7 @@ char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd, line.data = value[i].data + 2; bl->timeout = ngx_parse_time(&line, 1); - if (bl->timeout == NGX_ERROR) { + if (bl->timeout == (time_t) NGX_ERROR) { invalid = 1; break; } @@ -300,7 +300,7 @@ char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd, if (bl->timeout == 0 && bl->max_waiting) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "busy lock waiting is useless with zero timeout, ignoring"); + "busy lock waiting is useless with zero timeout, ignoring"); } return NGX_CONF_OK; diff --git a/usr.sbin/nginx/src/http/ngx_http_cache.h b/usr.sbin/nginx/src/http/ngx_http_cache.h index cb5d766882f..2a2d7229153 100644 --- a/usr.sbin/nginx/src/http/ngx_http_cache.h +++ b/usr.sbin/nginx/src/http/ngx_http_cache.h @@ -80,6 +80,14 @@ struct ngx_http_cache_s { ngx_http_file_cache_t *file_cache; ngx_http_file_cache_node_t *node; + ngx_msec_t lock_timeout; + ngx_msec_t wait_time; + + ngx_event_t wait_event; + + unsigned lock:1; + unsigned waiting:1; + unsigned updated:1; unsigned updating:1; unsigned exists:1; @@ -119,8 +127,11 @@ struct ngx_http_file_cache_s { time_t inactive; - ngx_msec_t last; ngx_uint_t files; + ngx_uint_t loader_files; + ngx_msec_t last; + ngx_msec_t loader_sleep; + ngx_msec_t loader_threshold; ngx_shm_zone_t *shm_zone; }; diff --git a/usr.sbin/nginx/src/http/ngx_http_copy_filter_module.c b/usr.sbin/nginx/src/http/ngx_http_copy_filter_module.c index d52fbebe869..95bc0b83564 100644 --- a/usr.sbin/nginx/src/http/ngx_http_copy_filter_module.c +++ b/usr.sbin/nginx/src/http/ngx_http_copy_filter_module.c @@ -74,7 +74,7 @@ ngx_module_t ngx_http_copy_filter_module = { }; -static ngx_http_output_body_filter_pt ngx_http_next_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; static ngx_int_t @@ -115,7 +115,8 @@ ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in) ctx->bufs = conf->bufs; ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module; - ctx->output_filter = (ngx_output_chain_filter_pt) ngx_http_next_filter; + ctx->output_filter = (ngx_output_chain_filter_pt) + ngx_http_next_body_filter; ctx->filter_ctx = r; #if (NGX_HAVE_FILE_AIO) @@ -292,7 +293,7 @@ ngx_http_copy_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) static ngx_int_t ngx_http_copy_filter_init(ngx_conf_t *cf) { - ngx_http_next_filter = ngx_http_top_body_filter; + ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_copy_filter; return NGX_OK; diff --git a/usr.sbin/nginx/src/http/ngx_http_core_module.c b/usr.sbin/nginx/src/http/ngx_http_core_module.c index 82bb1275c5e..04620229459 100644 --- a/usr.sbin/nginx/src/http/ngx_http_core_module.c +++ b/usr.sbin/nginx/src/http/ngx_http_core_module.c @@ -76,6 +76,10 @@ static ngx_uint_t ngx_http_gzip_quantity(u_char *p, u_char *last); static char *ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); #endif +#if (NGX_HAVE_OPENAT) +static char *ngx_http_disable_symlinks(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#endif static char *ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data); @@ -719,7 +723,7 @@ static ngx_command_t ngx_http_core_commands[] = { NULL }, { ngx_string("resolver"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, ngx_http_core_resolver, NGX_HTTP_LOC_CONF_OFFSET, 0, @@ -764,6 +768,17 @@ static ngx_command_t ngx_http_core_commands[] = { #endif +#if (NGX_HAVE_OPENAT) + + { ngx_string("disable_symlinks"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_http_disable_symlinks, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + +#endif + ngx_null_command }; @@ -1213,20 +1228,29 @@ ngx_http_core_try_files_phase(ngx_http_request_t *r, len = tf->name.len; } - /* 16 bytes are preallocation */ - reserve = ngx_abs((ssize_t) (len - r->uri.len)) + alias + 16; + if (!alias) { + reserve = len > r->uri.len ? len - r->uri.len : 0; - if (reserve > allocated) { +#if (NGX_PCRE) + } else if (clcf->regex) { + reserve = len; +#endif - /* we just need to allocate path and to copy a root */ + } else { + reserve = len > r->uri.len - alias ? len - (r->uri.len - alias) : 0; + } - if (ngx_http_map_uri_to_path(r, &path, &root, reserve) == NULL) { + if (reserve > allocated || !allocated) { + + /* 16 bytes are preallocation */ + allocated = reserve + 16; + + if (ngx_http_map_uri_to_path(r, &path, &root, allocated) == 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) { @@ -1298,6 +1322,11 @@ ngx_http_core_try_files_phase(ngx_http_request_t *r, of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; + if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { @@ -2456,7 +2485,6 @@ ngx_http_subrequest(ngx_http_request_t *r, sr->start_sec = tp->sec; sr->start_msec = tp->msec; - r->main->subrequests++; r->main->count++; *psr = sr; @@ -2510,6 +2538,7 @@ ngx_http_internal_redirect(ngx_http_request_t *r, #endif r->internal = 1; + r->valid_unparsed_uri = 0; r->add_uri_to_alias = 0; r->main->count++; @@ -2561,6 +2590,9 @@ ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name) r->content_handler = NULL; r->loc_conf = (*clcfp)->loc_conf; + /* clear the modules contexts */ + ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module); + ngx_http_update_location_config(r); cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); @@ -2616,6 +2648,56 @@ ngx_http_cleanup_add(ngx_http_request_t *r, size_t size) } +ngx_int_t +ngx_http_set_disable_symlinks(ngx_http_request_t *r, + ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of) +{ +#if (NGX_HAVE_OPENAT) + u_char *p; + ngx_str_t from; + + of->disable_symlinks = clcf->disable_symlinks; + + if (clcf->disable_symlinks_from == NULL) { + return NGX_OK; + } + + if (ngx_http_complex_value(r, clcf->disable_symlinks_from, &from) + != NGX_OK) + { + return NGX_ERROR; + } + + if (from.len == 0 + || from.len > path->len + || ngx_memcmp(path->data, from.data, from.len) != 0) + { + return NGX_OK; + } + + if (from.len == path->len) { + of->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF; + return NGX_OK; + } + + p = path->data + from.len; + + if (*p == '/') { + of->disable_symlinks_from = from.len; + return NGX_OK; + } + + p--; + + if (*p == '/') { + of->disable_symlinks_from = from.len - 1; + } +#endif + + return NGX_OK; +} + + static char * ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { @@ -3344,6 +3426,11 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf) #endif #endif +#if (NGX_HAVE_OPENAT) + clcf->disable_symlinks = NGX_CONF_UNSET_UINT; + clcf->disable_symlinks_from = NGX_CONF_UNSET_PTR; +#endif + return clcf; } @@ -3494,8 +3581,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_bitmask_value(conf->keepalive_disable, prev->keepalive_disable, (NGX_CONF_BITMASK_SET - |NGX_HTTP_KEEPALIVE_DISABLE_MSIE6 - |NGX_HTTP_KEEPALIVE_DISABLE_SAFARI)); + |NGX_HTTP_KEEPALIVE_DISABLE_MSIE6)); 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, @@ -3553,7 +3639,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) * to inherit it in all servers */ - prev->resolver = ngx_resolver_create(cf, NULL); + prev->resolver = ngx_resolver_create(cf, NULL, 0); if (prev->resolver == NULL) { return NGX_CONF_ERROR; } @@ -3627,6 +3713,13 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) #endif #endif +#if (NGX_HAVE_OPENAT) + ngx_conf_merge_uint_value(conf->disable_symlinks, prev->disable_symlinks, + NGX_DISABLE_SYMLINKS_OFF); + ngx_conf_merge_ptr_value(conf->disable_symlinks_from, + prev->disable_symlinks_from, NULL); +#endif + return NGX_CONF_OK; } @@ -3831,6 +3924,97 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #endif } + if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) { + + if (ngx_strcmp(&value[n].data[13], "on") == 0) { + lsopt.so_keepalive = 1; + + } else if (ngx_strcmp(&value[n].data[13], "off") == 0) { + lsopt.so_keepalive = 2; + + } else { + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + u_char *p, *end; + ngx_str_t s; + + end = value[n].data + value[n].len; + s.data = value[n].data + 13; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + lsopt.tcp_keepidle = ngx_parse_time(&s, 1); + if (lsopt.tcp_keepidle == (time_t) NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + lsopt.tcp_keepintvl = ngx_parse_time(&s, 1); + if (lsopt.tcp_keepintvl == (time_t) NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + if (s.data < end) { + s.len = end - s.data; + + lsopt.tcp_keepcnt = ngx_atoi(s.data, s.len); + if (lsopt.tcp_keepcnt == NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + if (lsopt.tcp_keepidle == 0 && lsopt.tcp_keepintvl == 0 + && lsopt.tcp_keepcnt == 0) + { + goto invalid_so_keepalive; + } + + lsopt.so_keepalive = 1; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"so_keepalive\" parameter accepts " + "only \"on\" or \"off\" on this platform"); + return NGX_CONF_ERROR; + +#endif + } + + lsopt.set = 1; + lsopt.bind = 1; + + continue; + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + invalid_so_keepalive: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid so_keepalive value: \"%s\"", + &value[n].data[13]); + return NGX_CONF_ERROR; +#endif + } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[n]); return NGX_CONF_ERROR; @@ -4429,7 +4613,7 @@ ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) s.data = value[i].data + 9; inactive = ngx_parse_time(&s, 1); - if (inactive < 0) { + if (inactive == (time_t) NGX_ERROR) { goto failed; } @@ -4457,7 +4641,7 @@ ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (max == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"open_file_cache\" must have the \"max\" parameter"); + "\"open_file_cache\" must have the \"max\" parameter"); return NGX_CONF_ERROR; } @@ -4475,7 +4659,7 @@ ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *clcf = conf; - ngx_str_t *value; + ngx_str_t *value, name; if (clcf->error_log) { return "is duplicate"; @@ -4483,7 +4667,14 @@ ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) value = cf->args->elts; - clcf->error_log = ngx_log_create(cf->cycle, &value[1]); + if (ngx_strcmp(value[1].data, "stderr") == 0) { + ngx_str_null(&name); + + } else { + name = value[1]; + } + + clcf->error_log = ngx_log_create(cf->cycle, &name); if (clcf->error_log == NULL) { return NGX_CONF_ERROR; } @@ -4516,24 +4707,16 @@ ngx_http_core_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return "invalid value"; } - if (clcf->keepalive_timeout == (ngx_msec_t) NGX_PARSE_LARGE_TIME) { - return "value must be less than 597 hours"; - } - if (cf->args->nelts == 2) { return NGX_CONF_OK; } clcf->keepalive_header = ngx_parse_time(&value[2], 1); - if (clcf->keepalive_header == NGX_ERROR) { + if (clcf->keepalive_header == (time_t) NGX_ERROR) { return "invalid value"; } - if (clcf->keepalive_header == NGX_PARSE_LARGE_TIME) { - return "value must be less than 68 years"; - } - return NGX_CONF_OK; } @@ -4558,7 +4741,6 @@ ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *clcf = conf; - ngx_url_t u; ngx_str_t *value; if (clcf->resolver) { @@ -4567,19 +4749,9 @@ ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) value = cf->args->elts; - ngx_memzero(&u, sizeof(ngx_url_t)); - - u.host = value[1]; - u.port = 53; - - if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V: %s", &u.host, u.err); - return NGX_CONF_ERROR; - } - - clcf->resolver = ngx_resolver_create(cf, &u.addrs[0]); + clcf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1); if (clcf->resolver == NULL) { - return NGX_OK; + return NGX_CONF_ERROR; } return NGX_CONF_OK; @@ -4688,6 +4860,100 @@ ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #endif +#if (NGX_HAVE_OPENAT) + +static char * +ngx_http_disable_symlinks(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; + ngx_http_compile_complex_value_t ccv; + + if (clcf->disable_symlinks != NGX_CONF_UNSET_UINT) { + return "is duplicate"; + } + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strcmp(value[i].data, "off") == 0) { + clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF; + continue; + } + + if (ngx_strcmp(value[i].data, "if_not_owner") == 0) { + clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_NOTOWNER; + continue; + } + + if (ngx_strcmp(value[i].data, "on") == 0) { + clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_ON; + continue; + } + + if (ngx_strncmp(value[i].data, "from=", 5) == 0) { + value[i].len -= 5; + value[i].data += 5; + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[i]; + ccv.complex_value = ngx_palloc(cf->pool, + sizeof(ngx_http_complex_value_t)); + if (ccv.complex_value == NULL) { + return NGX_CONF_ERROR; + } + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + clcf->disable_symlinks_from = ccv.complex_value; + + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (clcf->disable_symlinks == NGX_CONF_UNSET_UINT) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"off\", \"on\" " + "or \"if_not_owner\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 2) { + clcf->disable_symlinks_from = NULL; + return NGX_CONF_OK; + } + + if (clcf->disable_symlinks_from == NGX_CONF_UNSET_PTR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate parameters \"%V %V\"", + &value[1], &value[2]); + return NGX_CONF_ERROR; + } + + if (clcf->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"from=\" cannot be used with \"off\" parameter"); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + +#endif + + static char * ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data) { diff --git a/usr.sbin/nginx/src/http/ngx_http_core_module.h b/usr.sbin/nginx/src/http/ngx_http_core_module.h index ffe04a2075c..bfa0b419ece 100644 --- a/usr.sbin/nginx/src/http/ngx_http_core_module.h +++ b/usr.sbin/nginx/src/http/ngx_http_core_module.h @@ -78,6 +78,7 @@ typedef struct { #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:2; #endif + unsigned so_keepalive:2; int backlog; int rcvbuf; @@ -85,6 +86,11 @@ typedef struct { #if (NGX_HAVE_SETFIB) int setfib; #endif +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + int tcp_keepidle; + int tcp_keepintvl; + int tcp_keepcnt; +#endif #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) char *accept_filter; @@ -398,6 +404,11 @@ struct ngx_http_core_loc_conf_s { #endif #endif +#if (NGX_HAVE_OPENAT) + ngx_uint_t disable_symlinks; /* disable_symlinks */ + ngx_http_complex_value_t *disable_symlinks_from; +#endif + ngx_array_t *error_pages; /* error_page */ ngx_http_try_file_t *try_files; /* try_files */ @@ -499,6 +510,10 @@ ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *chain); ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *chain); +ngx_int_t ngx_http_set_disable_symlinks(ngx_http_request_t *r, + ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of); + + extern ngx_module_t ngx_http_core_module; extern ngx_uint_t ngx_http_max_module; diff --git a/usr.sbin/nginx/src/http/ngx_http_file_cache.c b/usr.sbin/nginx/src/http/ngx_http_file_cache.c index 68943a87bb4..bd6cebadd12 100644 --- a/usr.sbin/nginx/src/http/ngx_http_file_cache.c +++ b/usr.sbin/nginx/src/http/ngx_http_file_cache.c @@ -11,6 +11,9 @@ #include <ngx_md5.h> +static ngx_int_t ngx_http_file_cache_lock(ngx_http_request_t *r, + ngx_http_cache_t *c); +static void ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev); static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c); static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r, @@ -31,8 +34,7 @@ static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache); static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache); static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q, u_char *name); -static ngx_int_t - ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache); +static void ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache); static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path); static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, @@ -183,13 +185,13 @@ ngx_http_file_cache_create(ngx_http_request_t *r) return NGX_ERROR; } + cln->handler = ngx_http_file_cache_cleanup; + cln->data = c; + if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) { return NGX_ERROR; } - cln->handler = ngx_http_file_cache_cleanup; - cln->data = c; - if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) { return NGX_ERROR; } @@ -246,15 +248,24 @@ ngx_http_file_cache_open(ngx_http_request_t *r) c = r->cache; + if (c->waiting) { + return NGX_AGAIN; + } + if (c->buf) { return ngx_http_file_cache_read(r, c); } cache = c->file_cache; - cln = ngx_pool_cleanup_add(r->pool, 0); - if (cln == NULL) { - return NGX_ERROR; + if (c->node == NULL) { + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_http_file_cache_cleanup; + cln->data = c; } rc = ngx_http_file_cache_exists(cache, c); @@ -266,9 +277,6 @@ ngx_http_file_cache_open(ngx_http_request_t *r) return rc; } - cln->handler = ngx_http_file_cache_cleanup; - cln->data = c; - if (rc == NGX_AGAIN) { return NGX_HTTP_CACHE_SCARCE; } @@ -308,7 +316,7 @@ ngx_http_file_cache_open(ngx_http_request_t *r) } if (!test) { - return NGX_DECLINED; + goto done; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); @@ -332,7 +340,7 @@ ngx_http_file_cache_open(ngx_http_request_t *r) case NGX_ENOENT: case NGX_ENOTDIR: - return rv; + goto done; default: ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, @@ -356,6 +364,114 @@ ngx_http_file_cache_open(ngx_http_request_t *r) } return ngx_http_file_cache_read(r, c); + +done: + + if (rv == NGX_DECLINED) { + return ngx_http_file_cache_lock(r, c); + } + + return rv; +} + + +static ngx_int_t +ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c) +{ + ngx_msec_t now, timer; + ngx_http_file_cache_t *cache; + + if (!c->lock) { + return NGX_DECLINED; + } + + cache = c->file_cache; + + ngx_shmtx_lock(&cache->shpool->mutex); + + if (!c->node->updating) { + c->node->updating = 1; + c->updating = 1; + } + + ngx_shmtx_unlock(&cache->shpool->mutex); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache lock u:%d wt:%M", + c->updating, c->wait_time); + + if (c->updating) { + return NGX_DECLINED; + } + + c->waiting = 1; + + now = ngx_current_msec; + + if (c->wait_time == 0) { + c->wait_time = now + c->lock_timeout; + + c->wait_event.handler = ngx_http_file_cache_lock_wait_handler; + c->wait_event.data = r; + c->wait_event.log = r->connection->log; + } + + timer = c->wait_time - now; + + ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer); + + r->main->blocked++; + + return NGX_AGAIN; +} + + +static void +ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev) +{ + ngx_uint_t wait; + ngx_msec_t timer; + ngx_http_cache_t *c; + ngx_http_request_t *r; + ngx_http_file_cache_t *cache; + + r = ev->data; + c = r->cache; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "http file cache wait handler wt:%M cur:%M", + c->wait_time, ngx_current_msec); + + timer = c->wait_time - ngx_current_msec; + + if ((ngx_msec_int_t) timer <= 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "http file cache lock timeout"); + c->lock = 0; + goto wakeup; + } + + cache = c->file_cache; + wait = 0; + + ngx_shmtx_lock(&cache->shpool->mutex); + + if (c->node->updating) { + wait = 1; + } + + ngx_shmtx_unlock(&cache->shpool->mutex); + + if (wait) { + ngx_add_timer(ev, (timer > 500) ? 500 : timer); + return; + } + +wakeup: + + c->waiting = 0; + r->main->blocked--; + r->connection->write->handler(r->connection->write); } @@ -520,13 +636,19 @@ ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) ngx_shmtx_lock(&cache->shpool->mutex); - fcn = ngx_http_file_cache_lookup(cache, c->key); + fcn = c->node; + + if (fcn == NULL) { + fcn = ngx_http_file_cache_lookup(cache, c->key); + } if (fcn) { ngx_queue_remove(&fcn->queue); - fcn->uses++; - fcn->count++; + if (c->node == NULL) { + fcn->uses++; + fcn->count++; + } if (fcn->error) { @@ -623,6 +745,10 @@ ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path) c = r->cache; + if (c->file.name.len) { + return NGX_OK; + } + c->file.name.len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; @@ -954,6 +1080,10 @@ ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf) } } } + + if (c->wait_event.timer_set) { + ngx_del_timer(&c->wait_event); + } } @@ -1267,59 +1397,53 @@ ngx_http_file_cache_loader(void *data) static ngx_int_t -ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache) +ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path) +{ + return NGX_OK; +} + + +static ngx_int_t +ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) { - ngx_msec_t elapsed; + ngx_msec_t elapsed; + ngx_http_file_cache_t *cache; - if (cache->files++ > 100) { + cache = ctx->data; + + if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) { + (void) ngx_http_file_cache_delete_file(ctx, path); + } + if (++cache->files >= cache->loader_files) { + ngx_http_file_cache_loader_sleep(cache); + + } else { ngx_time_update(); elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last)); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "http file cache manager time: %M", elapsed); - - if (elapsed > 200) { - - /* - * if processing 100 files takes more than 200ms, - * it seems that many operations require disk i/o, - * therefore sleep 200ms - */ - - ngx_msleep(200); + "http file cache loader time elapsed: %M", elapsed); - ngx_time_update(); + if (elapsed >= cache->loader_threshold) { + ngx_http_file_cache_loader_sleep(cache); } - - cache->last = ngx_current_msec; - cache->files = 0; } return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK; } -static ngx_int_t -ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path) -{ - return NGX_OK; -} - - -static ngx_int_t -ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) +static void +ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache) { - ngx_http_file_cache_t *cache; + ngx_msleep(cache->loader_sleep); - cache = ctx->data; + ngx_time_update(); - if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) { - (void) ngx_http_file_cache_delete_file(ctx, path); - } - - return ngx_http_file_cache_loader_sleep(cache); + cache->last = ngx_current_msec; + cache->files = 0; } @@ -1468,6 +1592,8 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) time_t inactive; ssize_t size; ngx_str_t s, name, *value; + ngx_int_t loader_files; + ngx_msec_t loader_sleep, loader_threshold; ngx_uint_t i, n; ngx_http_file_cache_t *cache; @@ -1482,6 +1608,9 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } inactive = 600; + loader_files = 100; + loader_sleep = 50; + loader_threshold = 200; name.len = 0; size = 0; @@ -1571,7 +1700,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) s.data = value[i].data + 9; inactive = ngx_parse_time(&s, 1); - if (inactive < 0) { + if (inactive == (time_t) NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid inactive value \"%V\"", &value[i]); return NGX_CONF_ERROR; @@ -1595,6 +1724,48 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) continue; } + if (ngx_strncmp(value[i].data, "loader_files=", 13) == 0) { + + loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13); + if (loader_files == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid loader_files value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "loader_sleep=", 13) == 0) { + + s.len = value[i].len - 13; + s.data = value[i].data + 13; + + loader_sleep = ngx_parse_time(&s, 0); + if (loader_sleep == (ngx_msec_t) NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid loader_sleep value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "loader_threshold=", 17) == 0) { + + s.len = value[i].len - 17; + s.data = value[i].data + 17; + + loader_threshold = ngx_parse_time(&s, 0); + if (loader_threshold == (ngx_msec_t) NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid loader_threshold value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[i]); return NGX_CONF_ERROR; @@ -1612,6 +1783,9 @@ ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) cache->path->data = cache; cache->path->conf_file = cf->conf_file->file.name.data; cache->path->line = cf->conf_file->line; + cache->loader_files = loader_files; + cache->loader_sleep = loader_sleep; + cache->loader_threshold = loader_threshold; if (ngx_add_path(cf, &cache->path) != NGX_OK) { return NGX_CONF_ERROR; @@ -1665,7 +1839,7 @@ ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, n = cf->args->nelts - 1; valid = ngx_parse_time(&value[n], 1); - if (valid < 0) { + if (valid == (time_t) NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid time value \"%V\"", &value[n]); return NGX_CONF_ERROR; diff --git a/usr.sbin/nginx/src/http/ngx_http_parse.c b/usr.sbin/nginx/src/http/ngx_http_parse.c index 420c9275f4f..0a10a340ed9 100644 --- a/usr.sbin/nginx/src/http/ngx_http_parse.c +++ b/usr.sbin/nginx/src/http/ngx_http_parse.c @@ -111,7 +111,10 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) sw_schema, sw_schema_slash, sw_schema_slash_slash, + sw_host_start, sw_host, + sw_host_end, + sw_host_ip_literal, sw_port, sw_host_http_09, sw_after_slash_in_uri, @@ -324,14 +327,26 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) case sw_schema_slash_slash: switch (ch) { case '/': - r->host_start = p + 1; - state = sw_host; + state = sw_host_start; break; default: return NGX_HTTP_PARSE_INVALID_REQUEST; } break; + case sw_host_start: + + r->host_start = p; + + if (ch == '[') { + state = sw_host_ip_literal; + break; + } + + state = sw_host; + + /* fall through */ + case sw_host: c = (u_char) (ch | 0x20); @@ -343,6 +358,10 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) break; } + /* fall through */ + + case sw_host_end: + r->host_end = p; switch (ch) { @@ -367,6 +386,47 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) } break; + case sw_host_ip_literal: + + if (ch >= '0' && ch <= '9') { + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + break; + } + + switch (ch) { + case ':': + break; + case ']': + state = sw_host_end; + break; + case '-': + case '.': + case '_': + case '~': + /* unreserved */ + break; + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + /* sub-delims */ + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + case sw_port: if (ch >= '0' && ch <= '9') { break; @@ -1037,7 +1097,7 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes) /* * we use "ch = *p++" inside the cycle, but this operation is safe, - * because after the URI there is always at least one charcter: + * because after the URI there is always at least one character: * the line feed */ @@ -1093,6 +1153,7 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes) break; case '+': r->plus_in_uri = 1; + /* fall through */ default: *u++ = ch; break; @@ -1418,6 +1479,7 @@ ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b, return NGX_ERROR; } + r->http_major = ch - '0'; state = sw_major_digit; break; @@ -1432,6 +1494,7 @@ ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b, return NGX_ERROR; } + r->http_major = r->http_major * 10 + ch - '0'; break; /* the first digit of minor HTTP version */ @@ -1440,6 +1503,7 @@ ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b, return NGX_ERROR; } + r->http_minor = ch - '0'; state = sw_minor_digit; break; @@ -1454,6 +1518,7 @@ ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b, return NGX_ERROR; } + r->http_minor = r->http_minor * 10 + ch - '0'; break; /* HTTP status code */ @@ -1531,6 +1596,7 @@ done: status->end = p; } + status->http_version = r->http_major * 1000 + r->http_minor; r->state = sw_start; return NGX_OK; diff --git a/usr.sbin/nginx/src/http/ngx_http_postpone_filter_module.c b/usr.sbin/nginx/src/http/ngx_http_postpone_filter_module.c index cf49b2fa8ce..85c4b84d027 100644 --- a/usr.sbin/nginx/src/http/ngx_http_postpone_filter_module.c +++ b/usr.sbin/nginx/src/http/ngx_http_postpone_filter_module.c @@ -46,7 +46,7 @@ ngx_module_t ngx_http_postpone_filter_module = { }; -static ngx_http_output_body_filter_pt ngx_http_next_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; static ngx_int_t @@ -80,7 +80,7 @@ ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in) if (r->postponed == NULL) { if (in || c->buffered) { - return ngx_http_next_filter(r->main, in); + return ngx_http_next_body_filter(r->main, in); } return NGX_OK; @@ -116,7 +116,7 @@ ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in) "http postpone filter output \"%V?%V\"", &r->uri, &r->args); - if (ngx_http_next_filter(r->main, pr->out) == NGX_ERROR) { + if (ngx_http_next_body_filter(r->main, pr->out) == NGX_ERROR) { return NGX_ERROR; } } @@ -171,7 +171,7 @@ found: static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf) { - ngx_http_next_filter = ngx_http_top_body_filter; + ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_postpone_filter; return NGX_OK; diff --git a/usr.sbin/nginx/src/http/ngx_http_request.c b/usr.sbin/nginx/src/http/ngx_http_request.c index 04c4165dedd..052f379ac3d 100644 --- a/usr.sbin/nginx/src/http/ngx_http_request.c +++ b/usr.sbin/nginx/src/http/ngx_http_request.c @@ -612,6 +612,8 @@ ngx_http_ssl_handshake_handler(ngx_connection_t *c) c->ssl->no_wait_shutdown = 1; + c->log->action = "reading client request line"; + c->read->handler = ngx_http_process_request_line; /* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler; @@ -1175,7 +1177,7 @@ ngx_http_read_request_header(ngx_http_request_t *r) if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client closed prematurely connection"); + "client prematurely closed connection"); } if (n == 0 || n == NGX_ERROR) { @@ -1493,7 +1495,9 @@ ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, } else if (ngx_strstrn(user_agent, "Chrome/", 7 - 1)) { r->headers_in.chrome = 1; - } else if (ngx_strstrn(user_agent, "Safari/", 7 - 1)) { + } else if (ngx_strstrn(user_agent, "Safari/", 7 - 1) + && ngx_strstrn(user_agent, "Mac OS X", 8 - 1)) + { r->headers_in.safari = 1; } else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) { @@ -1675,56 +1679,85 @@ static ssize_t ngx_http_validate_host(ngx_http_request_t *r, u_char **host, size_t len, ngx_uint_t alloc) { - u_char *h, ch; - size_t i, last; - ngx_uint_t dot; + u_char *h, ch; + size_t i, dot_pos, host_len; + + enum { + sw_usual = 0, + sw_literal, + sw_rest + } state; + + dot_pos = len; + host_len = len; - last = len; h = *host; - dot = 0; + + state = sw_usual; for (i = 0; i < len; i++) { ch = h[i]; - if (ch == '.') { - if (dot) { + switch (ch) { + + case '.': + if (dot_pos == i - 1) { return 0; } + dot_pos = i; + break; - dot = 1; - continue; - } + case ':': + if (state == sw_usual) { + host_len = i; + state = sw_rest; + } + break; - dot = 0; + case '[': + if (i == 0) { + state = sw_literal; + } + break; - if (ch == ':') { - last = i; - continue; - } + case ']': + if (state == sw_literal) { + host_len = i + 1; + state = sw_rest; + } + break; - if (ngx_path_separator(ch) || ch == '\0') { + case '\0': return 0; - } - if (ch >= 'A' || ch < 'Z') { - alloc = 1; + default: + + if (ngx_path_separator(ch)) { + return 0; + } + + if (ch >= 'A' && ch <= 'Z') { + alloc = 1; + } + + break; } } - if (dot) { - last--; + if (dot_pos == host_len - 1) { + host_len--; } if (alloc) { - *host = ngx_pnalloc(r->pool, last) ; + *host = ngx_pnalloc(r->pool, host_len); if (*host == NULL) { return -1; } - ngx_strlow(*host, h, last); + ngx_strlow(*host, h, host_len); } - return last; + return host_len; } @@ -1981,6 +2014,7 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) if (r == c->data) { r->main->count--; + r->main->subrequests++; if (!r->logged) { @@ -2394,7 +2428,7 @@ closed: } ngx_log_error(NGX_LOG_INFO, c->log, err, - "client closed prematurely connection"); + "client prematurely closed connection"); ngx_http_finalize_request(r, 0); } diff --git a/usr.sbin/nginx/src/http/ngx_http_request.h b/usr.sbin/nginx/src/http/ngx_http_request.h index f9d449dd1de..70ca6097efa 100644 --- a/usr.sbin/nginx/src/http/ngx_http_request.h +++ b/usr.sbin/nginx/src/http/ngx_http_request.h @@ -10,7 +10,7 @@ #define NGX_HTTP_MAX_URI_CHANGES 10 -#define NGX_HTTP_MAX_SUBREQUESTS 50 +#define NGX_HTTP_MAX_SUBREQUESTS 200 /* must be 2^n */ #define NGX_HTTP_LC_HEADER_LEN 32 @@ -479,10 +479,10 @@ struct ngx_http_request_s { /* * instead of using the request context data in - * ngx_http_limit_zone_module and ngx_http_limit_req_module + * ngx_http_limit_conn_module and ngx_http_limit_req_module * we use the single bits in the request structure */ - unsigned limit_zone_set:1; + unsigned limit_conn_set:1; unsigned limit_req_set:1; #if 0 diff --git a/usr.sbin/nginx/src/http/ngx_http_request_body.c b/usr.sbin/nginx/src/http/ngx_http_request_body.c index ac09d56f197..bddd7c580a8 100644 --- a/usr.sbin/nginx/src/http/ngx_http_request_body.c +++ b/usr.sbin/nginx/src/http/ngx_http_request_body.c @@ -303,7 +303,7 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client closed prematurely connection"); + "client prematurely closed connection"); } if (n == 0 || n == NGX_ERROR) { diff --git a/usr.sbin/nginx/src/http/ngx_http_script.c b/usr.sbin/nginx/src/http/ngx_http_script.c index 77ac9a629f5..ce290f4ef67 100644 --- a/usr.sbin/nginx/src/http/ngx_http_script.c +++ b/usr.sbin/nginx/src/http/ngx_http_script.c @@ -1506,6 +1506,12 @@ ngx_http_script_file_code(ngx_http_script_engine_t *e) of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; + if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { + e->ip = ngx_http_script_exit; + e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; + return; + } + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { diff --git a/usr.sbin/nginx/src/http/ngx_http_upstream.c b/usr.sbin/nginx/src/http/ngx_http_upstream.c index ffbbc11ce91..70a4e166856 100644 --- a/usr.sbin/nginx/src/http/ngx_http_upstream.c +++ b/usr.sbin/nginx/src/http/ngx_http_upstream.c @@ -73,6 +73,8 @@ static void ngx_http_upstream_finalize_request(ngx_http_request_t *r, static ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_content_length(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t @@ -90,6 +92,11 @@ static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_connection(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t + ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t @@ -97,14 +104,14 @@ static ngx_int_t ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); -static ngx_int_t ngx_http_upstream_copy_content_length(ngx_http_request_t *r, - ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); @@ -150,9 +157,9 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = { ngx_http_upstream_copy_content_type, 0, 1 }, { ngx_string("Content-Length"), - ngx_http_upstream_process_header_line, + ngx_http_upstream_process_content_length, offsetof(ngx_http_upstream_headers_in_t, content_length), - ngx_http_upstream_copy_content_length, 0, 0 }, + ngx_http_upstream_ignore_header_line, 0, 0 }, { ngx_string("Date"), ngx_http_upstream_process_header_line, @@ -193,7 +200,7 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = { { ngx_string("Set-Cookie"), ngx_http_upstream_process_set_cookie, 0, - ngx_http_upstream_copy_header_line, 0, 1 }, + ngx_http_upstream_rewrite_set_cookie, 0, 1 }, { ngx_string("Content-Disposition"), ngx_http_upstream_ignore_header_line, 0, @@ -216,7 +223,7 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = { offsetof(ngx_http_headers_out_t, accept_ranges), 1 }, { ngx_string("Connection"), - ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_process_connection, 0, ngx_http_upstream_ignore_header_line, 0, 0 }, { ngx_string("Keep-Alive"), @@ -248,6 +255,10 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = { ngx_http_upstream_process_charset, 0, ngx_http_upstream_copy_header_line, 0, 0 }, + { ngx_string("Transfer-Encoding"), + ngx_http_upstream_process_transfer_encoding, 0, + ngx_http_upstream_ignore_header_line, 0, 0 }, + #if (NGX_HTTP_GZIP) { ngx_string("Content-Encoding"), ngx_http_upstream_process_header_line, @@ -400,6 +411,8 @@ ngx_http_upstream_create(ngx_http_request_t *r) r->cache = NULL; #endif + u->headers_in.content_length_n = -1; + return NGX_OK; } @@ -697,6 +710,9 @@ ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u) c->body_start = u->conf->buffer_size; c->file_cache = u->conf->cache->data; + c->lock = u->conf->cache_lock; + c->lock_timeout = u->conf->cache_lock_timeout; + u->cache_status = NGX_HTTP_CACHE_MISS; } @@ -804,6 +820,7 @@ ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u) u->buffer.pos += c->header_start; ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); + u->headers_in.content_length_n = -1; if (ngx_list_init(&u->headers_in.headers, r->pool, 8, sizeof(ngx_table_elt_t)) @@ -1140,8 +1157,20 @@ ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u) c->sendfile &= r->connection->sendfile; u->output.sendfile = c->sendfile; - c->pool = r->pool; + if (c->pool == NULL) { + + /* we need separate pool here to be able to cache SSL connections */ + + c->pool = ngx_create_pool(128, r->connection->log); + if (c->pool == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + c->log = r->connection->log; + c->pool->log = c->log; c->read->log = c->log; c->write->log = c->log; @@ -1167,7 +1196,7 @@ ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u) { /* * the r->request_body->buf can be reused for one request only, - * the subrequests should allocate their own temporay bufs + * the subrequests should allocate their own temporary bufs */ u->output.free = ngx_alloc_chain_link(r->pool); @@ -1286,7 +1315,10 @@ ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u) return NGX_ERROR; } + u->keepalive = 0; + ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); + u->headers_in.content_length_n = -1; if (ngx_list_init(&u->headers_in.headers, r->pool, 8, sizeof(ngx_table_elt_t)) @@ -1869,8 +1901,6 @@ ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u) r->method = NGX_HTTP_GET; } - r->valid_unparsed_uri = 0; - ngx_http_internal_redirect(r, uri, &args); ngx_http_finalize_request(r, NGX_DONE); return NGX_DONE; @@ -1928,14 +1958,9 @@ ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u) r->headers_out.status = u->headers_in.status_n; r->headers_out.status_line = u->headers_in.status_line; - u->headers_in.content_length_n = r->headers_out.content_length_n; + r->headers_out.content_length_n = u->headers_in.content_length_n; - if (r->headers_out.content_length_n != -1) { - u->length = (size_t) r->headers_out.content_length_n; - - } else { - u->length = NGX_MAX_SIZE_T_VALUE; - } + u->length = u->headers_in.content_length_n; return NGX_OK; } @@ -1999,6 +2024,11 @@ ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, } } + if (u->length == 0) { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; @@ -2119,7 +2149,7 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) return; } - if (u->peer.connection->read->ready) { + if (u->peer.connection->read->ready || u->length == 0) { ngx_http_upstream_process_non_buffered_upstream(r, u); } } @@ -2295,6 +2325,15 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) p->send_timeout = clcf->send_timeout; p->send_lowat = clcf->send_lowat; + p->length = -1; + + if (u->input_filter_init + && u->input_filter_init(p->input_ctx) != NGX_OK) + { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + u->read_event_handler = ngx_http_upstream_process_upstream; r->write_event_handler = ngx_http_upstream_process_downstream; @@ -2384,7 +2423,7 @@ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, return; } - ngx_chain_update_chains(&u->free_bufs, &u->busy_bufs, + ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs, &u->out_bufs, u->output.tag); } @@ -2405,10 +2444,6 @@ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, size = b->end - b->last; - if (size > u->length) { - size = u->length; - } - if (size && upstream->read->ready) { n = upstream->recv(upstream, b->last, size); @@ -2505,7 +2540,7 @@ ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes) cl->buf->last = b->last; cl->buf->tag = u->output.tag; - if (u->length == NGX_MAX_SIZE_T_VALUE) { + if (u->length == -1) { return NGX_OK; } @@ -2650,9 +2685,17 @@ ngx_http_upstream_process_request(ngx_http_request_t *r) } else if (p->upstream_eof) { - /* TODO: check length & update cache */ + tf = u->pipe->temp_file; - ngx_http_file_cache_update(r, u->pipe->temp_file); + if (u->headers_in.content_length_n == -1 + || u->headers_in.content_length_n + == tf->offset - (off_t) r->cache->body_start) + { + ngx_http_file_cache_update(r, tf); + + } else { + ngx_http_file_cache_free(r->cache, tf); + } } else if (p->upstream_error) { ngx_http_file_cache_free(r->cache, u->pipe->temp_file); @@ -2799,6 +2842,10 @@ ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) { status = 0; + /* TODO: inform balancer instead */ + + u->peer.tries++; + } else { switch(ft_type) { @@ -2873,6 +2920,10 @@ ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, } #endif + if (u->peer.connection->pool) { + ngx_destroy_pool(u->peer.connection->pool); + } + ngx_close_connection(u->peer.connection); u->peer.connection = NULL; } @@ -2968,6 +3019,10 @@ ngx_http_upstream_finalize_request(ngx_http_request_t *r, "close http upstream connection: %d", u->peer.connection->fd); + if (u->peer.connection->pool) { + ngx_destroy_pool(u->peer.connection->pool); + } + ngx_close_connection(u->peer.connection); } @@ -3065,6 +3120,21 @@ ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, static ngx_int_t +ngx_http_upstream_process_content_length(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_http_upstream_t *u; + + u = r->upstream; + + u->headers_in.content_length = h; + u->headers_in.content_length_n = ngx_atoof(h->value.data, h->value.len); + + return NGX_OK; +} + + +static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { @@ -3242,6 +3312,8 @@ ngx_http_upstream_process_accel_expires(ngx_http_request_t *r, switch (n) { case 0: u->cacheable = 0; + /* fall through */ + case NGX_ERROR: return NGX_OK; @@ -3343,6 +3415,40 @@ ngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h, static ngx_int_t +ngx_http_upstream_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + r->upstream->headers_in.connection = h; + + if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len, + (u_char *) "close", 5 - 1) + != NULL) + { + r->upstream->headers_in.connection_close = 1; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + r->upstream->headers_in.transfer_encoding = h; + + if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len, + (u_char *) "chunked", 7 - 1) + != NULL) + { + r->upstream->headers_in.chunked = 1; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { @@ -3450,26 +3556,6 @@ ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h, static ngx_int_t -ngx_http_upstream_copy_content_length(ngx_http_request_t *r, ngx_table_elt_t *h, - ngx_uint_t offset) -{ - ngx_table_elt_t *ho; - - ho = ngx_list_push(&r->headers_out.headers); - if (ho == NULL) { - return NGX_ERROR; - } - - *ho = *h; - - r->headers_out.content_length = ho; - r->headers_out.content_length_n = ngx_atoof(h->value.data, h->value.len); - - return NGX_OK; -} - - -static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { @@ -3588,6 +3674,27 @@ ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r, ngx_table_elt_t *h, static ngx_int_t +ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_table_elt_t *ho; + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = *h; + + if (r->upstream->rewrite_cookie) { + return r->upstream->rewrite_cookie(r, ho); + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { @@ -4163,7 +4270,7 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) fail_timeout = ngx_parse_time(&s, 1); - if (fail_timeout == NGX_ERROR) { + if (fail_timeout == (time_t) NGX_ERROR) { goto invalid; } @@ -4355,12 +4462,58 @@ ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, case NGX_DECLINED: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid address \"%V\"", &value[1]); + /* fall through */ + default: return NGX_CONF_ERROR; } } +char * +ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_array_t **a; + ngx_http_upstream_param_t *param; + + a = (ngx_array_t **) (p + cmd->offset); + + if (*a == NULL) { + *a = ngx_array_create(cf->pool, 4, sizeof(ngx_http_upstream_param_t)); + if (*a == NULL) { + return NGX_CONF_ERROR; + } + } + + param = ngx_array_push(*a); + if (param == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + param->key = value[1]; + param->value = value[2]; + param->skip_empty = 0; + + if (cf->args->nelts == 4) { + if (ngx_strcmp(value[3].data, "if_not_empty") != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[3]); + return NGX_CONF_ERROR; + } + + param->skip_empty = 1; + } + + return NGX_CONF_OK; +} + + ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf, ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev, diff --git a/usr.sbin/nginx/src/http/ngx_http_upstream.h b/usr.sbin/nginx/src/http/ngx_http_upstream.h index 17fe538bdbf..f32c9852269 100644 --- a/usr.sbin/nginx/src/http/ngx_http_upstream.h +++ b/usr.sbin/nginx/src/http/ngx_http_upstream.h @@ -166,6 +166,9 @@ typedef struct { ngx_uint_t cache_use_stale; ngx_uint_t cache_methods; + ngx_flag_t cache_lock; + ngx_msec_t cache_lock_timeout; + ngx_array_t *cache_valid; ngx_array_t *cache_bypass; ngx_array_t *no_cache; @@ -221,6 +224,7 @@ typedef struct { ngx_table_elt_t *location; ngx_table_elt_t *accept_ranges; ngx_table_elt_t *www_authenticate; + ngx_table_elt_t *transfer_encoding; #if (NGX_HTTP_GZIP) ngx_table_elt_t *content_encoding; @@ -229,6 +233,9 @@ typedef struct { off_t content_length_n; ngx_array_t cache_control; + + unsigned connection_close:1; + unsigned chunked:1; } ngx_http_upstream_headers_in_t; @@ -271,7 +278,7 @@ struct ngx_http_upstream_s { ngx_http_upstream_resolved_t *resolved; ngx_buf_t buffer; - size_t length; + off_t length; ngx_chain_t *out_bufs; ngx_chain_t *busy_bufs; @@ -292,6 +299,8 @@ struct ngx_http_upstream_s { ngx_int_t rc); ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix); + ngx_int_t (*rewrite_cookie)(ngx_http_request_t *r, + ngx_table_elt_t *h); ngx_msec_t timeout; @@ -312,6 +321,7 @@ struct ngx_http_upstream_s { #endif unsigned buffering:1; + unsigned keepalive:1; unsigned request_sent:1; unsigned header_sent:1; @@ -324,6 +334,13 @@ typedef struct { } ngx_http_upstream_next_t; +typedef struct { + ngx_str_t key; + ngx_str_t value; + ngx_uint_t skip_empty; +} ngx_http_upstream_param_t; + + ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -333,6 +350,8 @@ ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags); char *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf, ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev, ngx_str_t *default_hide_headers, ngx_hash_init_t *hash); diff --git a/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.c b/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.c index afc9b2e085a..f0fac873699 100644 --- a/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.c +++ b/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.c @@ -49,6 +49,13 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, n += server[i].naddrs; } + if (n == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no servers in upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t) + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1)); if (peers == NULL) { @@ -444,8 +451,8 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) break; } - if (now - peer->accessed > peer->fail_timeout) { - peer->fails = 0; + if (now - peer->checked > peer->fail_timeout) { + peer->checked = now; break; } @@ -492,8 +499,8 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) break; } - if (now - peer->accessed > peer->fail_timeout) { - peer->fails = 0; + if (now - peer->checked > peer->fail_timeout) { + peer->checked = now; break; } @@ -664,15 +671,16 @@ ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data, return; } + peer = &rrp->peers->peer[rrp->current]; + if (state & NGX_PEER_FAILED) { now = ngx_time(); - peer = &rrp->peers->peer[rrp->current]; - /* ngx_lock_mutex(rrp->peers->mutex); */ peer->fails++; peer->accessed = now; + peer->checked = now; if (peer->max_fails) { peer->current_weight -= peer->weight / peer->max_fails; @@ -687,6 +695,14 @@ ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data, } /* ngx_unlock_mutex(rrp->peers->mutex); */ + + } else { + + /* mark peer live if check passed */ + + if (peer->accessed < peer->checked) { + peer->fails = 0; + } } rrp->current++; diff --git a/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.h b/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.h index 6d285ab75e1..4b70f2f5bac 100644 --- a/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.h +++ b/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.h @@ -24,6 +24,7 @@ typedef struct { ngx_uint_t fails; time_t accessed; + time_t checked; ngx_uint_t max_fails; time_t fail_timeout; diff --git a/usr.sbin/nginx/src/http/ngx_http_variables.c b/usr.sbin/nginx/src/http/ngx_http_variables.c index a9eb0afb719..949cef9107c 100644 --- a/usr.sbin/nginx/src/http/ngx_http_variables.c +++ b/usr.sbin/nginx/src/http/ngx_http_variables.c @@ -34,6 +34,10 @@ 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); +#if (NGX_HAVE_TCP_INFO) +static ngx_int_t ngx_http_variable_tcpinfo(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +#endif static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -49,6 +53,8 @@ static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_document_root(ngx_http_request_t *r, @@ -106,7 +112,7 @@ static ngx_int_t ngx_http_variable_pid(ngx_http_request_t *r, /* * the $http_host, $http_user_agent, $http_referer, $http_via, * and $http_x_forwarded_for variables may be handled by generic - * ngx_http_variable_unknown_header_in(), but for perfomance reasons + * ngx_http_variable_unknown_header_in(), but for performance reasons * they are handled using dedicated entries */ @@ -158,6 +164,8 @@ static ngx_http_variable_t ngx_http_core_variables[] = { { ngx_string("scheme"), NULL, ngx_http_variable_scheme, 0, 0, 0 }, + { ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 }, + { ngx_string("request_uri"), NULL, ngx_http_variable_request, offsetof(ngx_http_request_t, unparsed_uri), 0, 0 }, @@ -255,6 +263,20 @@ static ngx_http_variable_t ngx_http_core_variables[] = { { ngx_string("pid"), NULL, ngx_http_variable_pid, 0, 0, 0 }, +#if (NGX_HAVE_TCP_INFO) + { ngx_string("tcpinfo_rtt"), NULL, ngx_http_variable_tcpinfo, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("tcpinfo_rttvar"), NULL, ngx_http_variable_tcpinfo, + 1, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("tcpinfo_snd_cwnd"), NULL, ngx_http_variable_tcpinfo, + 2, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("tcpinfo_rcv_space"), NULL, ngx_http_variable_tcpinfo, + 3, NGX_HTTP_VAR_NOCACHEABLE, 0 }, +#endif + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -380,7 +402,7 @@ ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name) v->flags = 0; v->index = cmcf->variables.nelts - 1; - return cmcf->variables.nelts - 1; + return v->index; } @@ -880,6 +902,61 @@ ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v, } +#if (NGX_HAVE_TCP_INFO) + +static ngx_int_t +ngx_http_variable_tcpinfo(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + struct tcp_info ti; + socklen_t len; + uint32_t value; + + len = sizeof(struct tcp_info); + if (getsockopt(r->connection->fd, IPPROTO_TCP, TCP_INFO, &ti, &len) == -1) { + v->not_found = 1; + return NGX_OK; + } + + v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN); + if (v->data == NULL) { + return NGX_ERROR; + } + + switch (data) { + case 0: + value = ti.tcpi_rtt; + break; + + case 1: + value = ti.tcpi_rttvar; + break; + + case 2: + value = ti.tcpi_snd_cwnd; + break; + + case 3: + value = ti.tcpi_rcv_space; + break; + + /* suppress warning */ + default: + value = 0; + break; + } + + v->len = ngx_sprintf(v->data, "%uD", value) - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + +#endif + + static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) @@ -1109,6 +1186,30 @@ ngx_http_variable_scheme(ngx_http_request_t *r, static ngx_int_t +ngx_http_variable_https(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ +#if (NGX_HTTP_SSL) + + if (r->connection->ssl) { + v->len = sizeof("on") - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "on"; + + return NGX_OK; + } + +#endif + + *v = ngx_http_variable_null_value; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { @@ -1172,10 +1273,13 @@ static ngx_int_t ngx_http_variable_realpath_root(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { + u_char *real; size_t len; ngx_str_t path; ngx_http_core_loc_conf_t *clcf; - u_char real[NGX_MAX_PATH]; +#if (NGX_HAVE_MAX_PATH) + u_char buffer[NGX_MAX_PATH]; +#endif clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); @@ -1197,7 +1301,15 @@ ngx_http_variable_realpath_root(ngx_http_request_t *r, } } - if (ngx_realpath(path.data, real) == NULL) { +#if (NGX_HAVE_MAX_PATH) + real = buffer; +#else + real = NULL; +#endif + + real = ngx_realpath(path.data, real); + + if (real == NULL) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, ngx_realpath_n " \"%s\" failed", path.data); return NGX_ERROR; @@ -1207,6 +1319,9 @@ ngx_http_variable_realpath_root(ngx_http_request_t *r, v->data = ngx_pnalloc(r->pool, len); if (v->data == NULL) { +#if !(NGX_HAVE_MAX_PATH) + ngx_free(real); +#endif return NGX_ERROR; } @@ -1217,6 +1332,10 @@ ngx_http_variable_realpath_root(ngx_http_request_t *r, ngx_memcpy(v->data, real, len); +#if !(NGX_HAVE_MAX_PATH) + ngx_free(real); +#endif + return NGX_OK; } diff --git a/usr.sbin/nginx/src/mail/ngx_mail.c b/usr.sbin/nginx/src/mail/ngx_mail.c index c94281a15a2..49ec2e05b4d 100644 --- a/usr.sbin/nginx/src/mail/ngx_mail.c +++ b/usr.sbin/nginx/src/mail/ngx_mail.c @@ -309,6 +309,12 @@ found: addr->ctx = listen->ctx; addr->bind = listen->bind; addr->wildcard = listen->wildcard; + addr->so_keepalive = listen->so_keepalive; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + addr->tcp_keepidle = listen->tcp_keepidle; + addr->tcp_keepintvl = listen->tcp_keepintvl; + addr->tcp_keepcnt = listen->tcp_keepcnt; +#endif #if (NGX_MAIL_SSL) addr->ssl = listen->ssl; #endif @@ -374,6 +380,13 @@ ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports) ls->log.data = &ls->addr_text; ls->log.handler = ngx_accept_log_error; + ls->keepalive = addr[i].so_keepalive; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + ls->keepidle = addr[i].tcp_keepidle; + ls->keepintvl = addr[i].tcp_keepintvl; + ls->keepcnt = addr[i].tcp_keepcnt; +#endif + #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) ls->ipv6only = addr[i].ipv6only; #endif diff --git a/usr.sbin/nginx/src/mail/ngx_mail.h b/usr.sbin/nginx/src/mail/ngx_mail.h index 46a42dff62c..1fceb42856e 100644 --- a/usr.sbin/nginx/src/mail/ngx_mail.h +++ b/usr.sbin/nginx/src/mail/ngx_mail.h @@ -41,6 +41,12 @@ typedef struct { #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:2; #endif + unsigned so_keepalive:2; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + int tcp_keepidle; + int tcp_keepintvl; + int tcp_keepcnt; +#endif } ngx_mail_listen_t; @@ -96,6 +102,12 @@ typedef struct { #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:2; #endif + unsigned so_keepalive:2; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + int tcp_keepidle; + int tcp_keepintvl; + int tcp_keepcnt; +#endif } ngx_mail_conf_addr_t; diff --git a/usr.sbin/nginx/src/mail/ngx_mail_core_module.c b/usr.sbin/nginx/src/mail/ngx_mail_core_module.c index c3528599cb0..f79913b0a46 100644 --- a/usr.sbin/nginx/src/mail/ngx_mail_core_module.c +++ b/usr.sbin/nginx/src/mail/ngx_mail_core_module.c @@ -25,6 +25,12 @@ static char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_conf_deprecated_t ngx_conf_deprecated_so_keepalive = { + ngx_conf_deprecated, "so_keepalive", + "so_keepalive\" parameter of the \"listen" +}; + + static ngx_command_t ngx_mail_core_commands[] = { { ngx_string("server"), @@ -53,7 +59,7 @@ static ngx_command_t ngx_mail_core_commands[] = { ngx_conf_set_flag_slot, NGX_MAIL_SRV_CONF_OFFSET, offsetof(ngx_mail_core_srv_conf_t, so_keepalive), - NULL }, + &ngx_conf_deprecated_so_keepalive }, { ngx_string("timeout"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, @@ -70,7 +76,7 @@ static ngx_command_t ngx_mail_core_commands[] = { NULL }, { ngx_string("resolver"), - NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, ngx_mail_core_resolver, NGX_MAIL_SRV_CONF_OFFSET, 0, @@ -447,6 +453,96 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #endif } + if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) { + + if (ngx_strcmp(&value[i].data[13], "on") == 0) { + ls->so_keepalive = 1; + + } else if (ngx_strcmp(&value[i].data[13], "off") == 0) { + ls->so_keepalive = 2; + + } else { + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + u_char *p, *end; + ngx_str_t s; + + end = value[i].data + value[i].len; + s.data = value[i].data + 13; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + ls->tcp_keepidle = ngx_parse_time(&s, 1); + if (ls->tcp_keepidle == (time_t) NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + ls->tcp_keepintvl = ngx_parse_time(&s, 1); + if (ls->tcp_keepintvl == (time_t) NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + if (s.data < end) { + s.len = end - s.data; + + ls->tcp_keepcnt = ngx_atoi(s.data, s.len); + if (ls->tcp_keepcnt == NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0 + && ls->tcp_keepcnt == 0) + { + goto invalid_so_keepalive; + } + + ls->so_keepalive = 1; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"so_keepalive\" parameter accepts " + "only \"on\" or \"off\" on this platform"); + return NGX_CONF_ERROR; + +#endif + } + + ls->bind = 1; + + continue; + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + invalid_so_keepalive: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid so_keepalive value: \"%s\"", + &value[i].data[13]); + return NGX_CONF_ERROR; +#endif + } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the invalid \"%V\" parameter", &value[i]); return NGX_CONF_ERROR; @@ -494,7 +590,6 @@ ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_mail_core_srv_conf_t *cscf = conf; - ngx_url_t u; ngx_str_t *value; value = cf->args->elts; @@ -508,19 +603,9 @@ ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } - ngx_memzero(&u, sizeof(ngx_url_t)); - - u.host = value[1]; - u.port = 53; - - if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V: %s", &u.host, u.err); - return NGX_CONF_ERROR; - } - - cscf->resolver = ngx_resolver_create(cf, &u.addrs[0]); + cscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1); if (cscf->resolver == NULL) { - return NGX_CONF_OK; + return NGX_CONF_ERROR; } return NGX_CONF_OK; diff --git a/usr.sbin/nginx/src/os/unix/ngx_daemon.c b/usr.sbin/nginx/src/os/unix/ngx_daemon.c index f18860bbe2f..ab672110838 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_daemon.c +++ b/usr.sbin/nginx/src/os/unix/ngx_daemon.c @@ -9,7 +9,8 @@ #include <ngx_core.h> -ngx_int_t ngx_daemon(ngx_log_t *log) +ngx_int_t +ngx_daemon(ngx_log_t *log) { int fd; diff --git a/usr.sbin/nginx/src/os/unix/ngx_darwin.h b/usr.sbin/nginx/src/os/unix/ngx_darwin.h index b3aba906be7..4d01b26ea54 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_darwin.h +++ b/usr.sbin/nginx/src/os/unix/ngx_darwin.h @@ -9,6 +9,7 @@ #define _NGX_DARWIN_H_INCLUDED_ +void ngx_debug_init(void); ngx_chain_t *ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit); @@ -16,5 +17,7 @@ extern int ngx_darwin_kern_osreldate; extern int ngx_darwin_hw_ncpu; extern u_long ngx_darwin_net_inet_tcp_sendspace; +extern ngx_uint_t ngx_debug_malloc; + #endif /* _NGX_DARWIN_H_INCLUDED_ */ diff --git a/usr.sbin/nginx/src/os/unix/ngx_darwin_config.h b/usr.sbin/nginx/src/os/unix/ngx_darwin_config.h index a24e43c2002..149778c29ad 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_darwin_config.h +++ b/usr.sbin/nginx/src/os/unix/ngx_darwin_config.h @@ -88,6 +88,7 @@ #define NGX_HAVE_OS_SPECIFIC_INIT 1 +#define NGX_HAVE_DEBUG_MALLOC 1 extern char **environ; diff --git a/usr.sbin/nginx/src/os/unix/ngx_darwin_init.c b/usr.sbin/nginx/src/os/unix/ngx_darwin_init.c index 5c542cda818..e3cc5fe3eec 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_darwin_init.c +++ b/usr.sbin/nginx/src/os/unix/ngx_darwin_init.c @@ -15,6 +15,8 @@ int ngx_darwin_hw_ncpu; int ngx_darwin_kern_ipc_somaxconn; u_long ngx_darwin_net_inet_tcp_sendspace; +ngx_uint_t ngx_debug_malloc; + static ngx_os_io_t ngx_darwin_io = { ngx_unix_recv, @@ -56,6 +58,34 @@ sysctl_t sysctls[] = { }; +void +ngx_debug_init() +{ +#if (NGX_DEBUG_MALLOC) + + /* + * MacOSX 10.6, 10.7: MallocScribble fills freed memory with 0x55 + * and fills allocated memory with 0xAA. + * MacOSX 10.4, 10.5: MallocScribble fills freed memory with 0x55, + * MallocPreScribble fills allocated memory with 0xAA. + * MacOSX 10.3: MallocScribble fills freed memory with 0x55, + * and no way to fill allocated memory. + */ + + setenv("MallocScribble", "1", 0); + + ngx_debug_malloc = 1; + +#else + + if (getenv("MallocScribble")) { + ngx_debug_malloc = 1; + } + +#endif +} + + ngx_int_t ngx_os_specific_init(ngx_log_t *log) { diff --git a/usr.sbin/nginx/src/os/unix/ngx_darwin_sendfile_chain.c b/usr.sbin/nginx/src/os/unix/ngx_darwin_sendfile_chain.c index ec247d21bfe..078d10b2429 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_darwin_sendfile_chain.c +++ b/usr.sbin/nginx/src/os/unix/ngx_darwin_sendfile_chain.c @@ -103,10 +103,8 @@ ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) prev = NULL; iov = NULL; - for (cl = in; - cl && header.nelts < IOV_MAX && send < limit; - cl = cl->next) - { + for (cl = in; cl && send < limit; cl = cl->next) { + if (ngx_buf_special(cl->buf)) { continue; } @@ -125,6 +123,10 @@ ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) iov->iov_len += (size_t) size; } else { + if (header.nelts >= IOV_MAX) { + break; + } + iov = ngx_array_push(&header); if (iov == NULL) { return NGX_CHAIN_ERROR; @@ -178,7 +180,7 @@ ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) prev = NULL; iov = NULL; - while (cl && header.nelts < IOV_MAX && send < limit) { + while (cl && send < limit) { if (ngx_buf_special(cl->buf)) { cl = cl->next; @@ -199,6 +201,10 @@ ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) iov->iov_len += (size_t) size; } else { + if (trailer.nelts >= IOV_MAX) { + break; + } + iov = ngx_array_push(&trailer); if (iov == NULL) { return NGX_CHAIN_ERROR; diff --git a/usr.sbin/nginx/src/os/unix/ngx_errno.h b/usr.sbin/nginx/src/os/unix/ngx_errno.h index 092d05a0b7c..78af40bfe50 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_errno.h +++ b/usr.sbin/nginx/src/os/unix/ngx_errno.h @@ -48,6 +48,11 @@ typedef int ngx_err_t; #define NGX_EILSEQ EILSEQ #define NGX_ENOMOREFILES 0 +#if (NGX_HAVE_OPENAT) +#define NGX_EMLINK EMLINK +#define NGX_ELOOP ELOOP +#endif + #if (__hpux__) #define NGX_EAGAIN EWOULDBLOCK #else diff --git a/usr.sbin/nginx/src/os/unix/ngx_files.h b/usr.sbin/nginx/src/os/unix/ngx_files.h index 12988245a08..9c97e2bb789 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_files.h +++ b/usr.sbin/nginx/src/os/unix/ngx_files.h @@ -76,6 +76,27 @@ typedef struct { #define NGX_FILE_APPEND O_WRONLY|O_APPEND #define NGX_FILE_NONBLOCK O_NONBLOCK +#if (NGX_HAVE_OPENAT) +#define NGX_FILE_NOFOLLOW O_NOFOLLOW + +#if defined(O_DIRECTORY) +#define NGX_FILE_DIRECTORY O_DIRECTORY +#else +#define NGX_FILE_DIRECTORY 0 +#endif + +#if defined(O_SEARCH) +#define NGX_FILE_SEARCH O_SEARCH|NGX_FILE_DIRECTORY + +#elif defined(O_EXEC) +#define NGX_FILE_SEARCH O_EXEC|NGX_FILE_DIRECTORY + +#else +#define NGX_FILE_SEARCH O_RDONLY|NGX_FILE_DIRECTORY +#endif + +#endif /* NGX_HAVE_OPENAT */ + #define NGX_FILE_DEFAULT_ACCESS 0644 #define NGX_FILE_OWNER_ACCESS 0600 @@ -179,14 +200,25 @@ void ngx_close_file_mapping(ngx_file_mapping_t *fm); #endif -#define ngx_realpath(p, r) realpath((char *) p, (char *) r) +#define ngx_realpath(p, r) (u_char *) realpath((char *) p, (char *) r) #define ngx_realpath_n "realpath()" #define ngx_getcwd(buf, size) (getcwd((char *) buf, size) != NULL) #define ngx_getcwd_n "getcwd()" #define ngx_path_separator(c) ((c) == '/') + +#if defined(PATH_MAX) + +#define NGX_HAVE_MAX_PATH 1 #define NGX_MAX_PATH PATH_MAX +#else + +#define NGX_MAX_PATH 4096 + +#endif + + #define NGX_DIR_MASK_LEN 0 @@ -325,6 +357,23 @@ ngx_int_t ngx_directio_off(ngx_fd_t fd); size_t ngx_fs_bsize(u_char *name); +#if (NGX_HAVE_OPENAT) + +#define ngx_openat_file(fd, name, mode, create, access) \ + openat(fd, (const char *) name, mode|create, access) + +#define ngx_openat_file_n "openat()" + +#define ngx_file_at_info(fd, name, sb, flag) \ + fstatat(fd, (const char *) name, sb, flag) + +#define ngx_file_at_info_n "fstatat()" + +#define NGX_AT_FDCWD (ngx_fd_t) AT_FDCWD + +#endif + + #define ngx_stderr STDERR_FILENO #define ngx_set_stderr(fd) dup2(fd, STDERR_FILENO) #define ngx_set_stderr_n "dup2(STDERR_FILENO)" diff --git a/usr.sbin/nginx/src/os/unix/ngx_freebsd.h b/usr.sbin/nginx/src/os/unix/ngx_freebsd.h index 95bf57a702f..4f93da55cd2 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_freebsd.h +++ b/usr.sbin/nginx/src/os/unix/ngx_freebsd.h @@ -9,6 +9,7 @@ #define _NGX_FREEBSD_H_INCLUDED_ +void ngx_debug_init(void); ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit); @@ -18,7 +19,7 @@ extern u_long ngx_freebsd_net_inet_tcp_sendspace; extern ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; extern ngx_uint_t ngx_freebsd_use_tcp_nopush; -extern ngx_uint_t ngx_freebsd_debug_malloc; +extern ngx_uint_t ngx_debug_malloc; #endif /* _NGX_FREEBSD_H_INCLUDED_ */ diff --git a/usr.sbin/nginx/src/os/unix/ngx_freebsd_config.h b/usr.sbin/nginx/src/os/unix/ngx_freebsd_config.h index f13793619b0..5b3ff278c01 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_freebsd_config.h +++ b/usr.sbin/nginx/src/os/unix/ngx_freebsd_config.h @@ -23,6 +23,7 @@ #include <grp.h> #include <dirent.h> #include <glob.h> +#include <time.h> #include <sys/param.h> /* ALIGN() */ #include <sys/mount.h> /* statfs() */ @@ -110,6 +111,7 @@ pid_t rfork_thread(int flags, void *stack, int (*func)(void *arg), void *arg); #define NGX_HAVE_OS_SPECIFIC_INIT 1 +#define NGX_HAVE_DEBUG_MALLOC 1 extern char **environ; diff --git a/usr.sbin/nginx/src/os/unix/ngx_freebsd_init.c b/usr.sbin/nginx/src/os/unix/ngx_freebsd_init.c index 02b3f2a7890..57af44a20cf 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_freebsd_init.c +++ b/usr.sbin/nginx/src/os/unix/ngx_freebsd_init.c @@ -23,7 +23,8 @@ int ngx_freebsd_machdep_hlt_logical_cpus; ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; ngx_uint_t ngx_freebsd_use_tcp_nopush; -ngx_uint_t ngx_freebsd_debug_malloc; + +ngx_uint_t ngx_debug_malloc; static ngx_os_io_t ngx_freebsd_io = { @@ -81,7 +82,7 @@ ngx_debug_init() malloc_options = "J"; #endif - ngx_freebsd_debug_malloc = 1; + ngx_debug_malloc = 1; #else char *mo; @@ -89,7 +90,7 @@ ngx_debug_init() mo = getenv("MALLOC_OPTIONS"); if (mo && ngx_strchr(mo, 'J')) { - ngx_freebsd_debug_malloc = 1; + ngx_debug_malloc = 1; } #endif } diff --git a/usr.sbin/nginx/src/os/unix/ngx_freebsd_rfork_thread.c b/usr.sbin/nginx/src/os/unix/ngx_freebsd_rfork_thread.c index 9506b9de3ea..530ec4a53bb 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_freebsd_rfork_thread.c +++ b/usr.sbin/nginx/src/os/unix/ngx_freebsd_rfork_thread.c @@ -11,14 +11,14 @@ /* * The threads implementation uses the rfork(RFPROC|RFTHREAD|RFMEM) syscall * to create threads. All threads use the stacks of the same size mmap()ed - * below the main stack. Thus the current thread id is determinated via + * below the main stack. Thus the current thread id is determined via * the stack pointer value. * * The mutex implementation uses the ngx_atomic_cmp_set() operation * to acquire a mutex and the SysV semaphore to wait on a mutex and to wake up * the waiting threads. The light mutex does not use semaphore, so after * spinning in the lock the thread calls sched_yield(). However the light - * mutecies are intended to be used with the "trylock" operation only. + * mutexes are intended to be used with the "trylock" operation only. * The SysV semop() is a cheap syscall, particularly if it has little sembuf's * and does not use SEM_UNDO. * diff --git a/usr.sbin/nginx/src/os/unix/ngx_freebsd_sendfile_chain.c b/usr.sbin/nginx/src/os/unix/ngx_freebsd_sendfile_chain.c index 26b326705c5..f58b5c20fe1 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_freebsd_sendfile_chain.c +++ b/usr.sbin/nginx/src/os/unix/ngx_freebsd_sendfile_chain.c @@ -18,7 +18,7 @@ * as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet, * and then again the 11 full 1460-bytes packets. * - * Threfore we use the TCP_NOPUSH option (similar to Linux's TCP_CORK) + * Therefore we use the TCP_NOPUSH option (similar to Linux's TCP_CORK) * to postpone the sending - it not only sends a header and the first part of * the file in one packet, but also sends the file pages in the full packets. * @@ -107,10 +107,8 @@ ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) prev = NULL; iov = NULL; - for (cl = in; - cl && header.nelts < IOV_MAX && send < limit; - cl = cl->next) - { + for (cl = in; cl && send < limit; cl = cl->next) { + if (ngx_buf_special(cl->buf)) { continue; } @@ -129,6 +127,10 @@ ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) iov->iov_len += (size_t) size; } else { + if (header.nelts >= IOV_MAX){ + break; + } + iov = ngx_array_push(&header); if (iov == NULL) { return NGX_CHAIN_ERROR; @@ -183,7 +185,7 @@ ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) prev = NULL; iov = NULL; - while (cl && header.nelts < IOV_MAX && send < limit) { + while (cl && send < limit) { if (ngx_buf_special(cl->buf)) { cl = cl->next; @@ -204,6 +206,10 @@ ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) iov->iov_len += (size_t) size; } else { + if (trailer.nelts >= IOV_MAX){ + break; + } + iov = ngx_array_push(&trailer); if (iov == NULL) { return NGX_CHAIN_ERROR; diff --git a/usr.sbin/nginx/src/os/unix/ngx_gcc_atomic_sparc64.h b/usr.sbin/nginx/src/os/unix/ngx_gcc_atomic_sparc64.h index d26e1307cb5..a84db354482 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_gcc_atomic_sparc64.h +++ b/usr.sbin/nginx/src/os/unix/ngx_gcc_atomic_sparc64.h @@ -15,7 +15,7 @@ * r0 = [r1]; * } * - * so "r0 == r2" means that the operation was successfull. + * so "r0 == r2" means that the operation was successful. * * * The "r" means the general register. diff --git a/usr.sbin/nginx/src/os/unix/ngx_linux_config.h b/usr.sbin/nginx/src/os/unix/ngx_linux_config.h index 7cf4eac4179..2834032dea0 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_linux_config.h +++ b/usr.sbin/nginx/src/os/unix/ngx_linux_config.h @@ -109,6 +109,7 @@ typedef struct iocb ngx_aiocb_t; #define NGX_HAVE_OS_SPECIFIC_INIT 1 +#define ngx_debug_init() extern char **environ; diff --git a/usr.sbin/nginx/src/os/unix/ngx_linux_sendfile_chain.c b/usr.sbin/nginx/src/os/unix/ngx_linux_sendfile_chain.c index 4f6fdf5876c..e8f3d5a894e 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_linux_sendfile_chain.c +++ b/usr.sbin/nginx/src/os/unix/ngx_linux_sendfile_chain.c @@ -89,10 +89,8 @@ ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) /* create the iovec and coalesce the neighbouring bufs */ - for (cl = in; - cl && header.nelts < IOV_MAX && send < limit; - cl = cl->next) - { + for (cl = in; cl && send < limit; cl = cl->next) { + if (ngx_buf_special(cl->buf)) { continue; } @@ -132,6 +130,10 @@ ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) iov->iov_len += (size_t) size; } else { + if (header.nelts >= IOV_MAX) { + break; + } + iov = ngx_array_push(&header); if (iov == NULL) { return NGX_CHAIN_ERROR; diff --git a/usr.sbin/nginx/src/os/unix/ngx_os.h b/usr.sbin/nginx/src/os/unix/ngx_os.h index 705bc5870c3..c646e2aa5a8 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_os.h +++ b/usr.sbin/nginx/src/os/unix/ngx_os.h @@ -32,7 +32,6 @@ typedef struct { } ngx_os_io_t; -void ngx_debug_init(void); ngx_int_t ngx_os_init(ngx_log_t *log); void ngx_os_status(ngx_log_t *log); ngx_int_t ngx_os_specific_init(ngx_log_t *log); diff --git a/usr.sbin/nginx/src/os/unix/ngx_posix_config.h b/usr.sbin/nginx/src/os/unix/ngx_posix_config.h index 263ee69f889..4d432a7e32e 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_posix_config.h +++ b/usr.sbin/nginx/src/os/unix/ngx_posix_config.h @@ -45,6 +45,7 @@ #include <grp.h> #include <dirent.h> #include <glob.h> +#include <time.h> #if (NGX_HAVE_SYS_PARAM_H) #include <sys/param.h> /* statfs() */ #endif @@ -126,6 +127,8 @@ typedef struct aiocb ngx_aiocb_t; #define NGX_LISTEN_BACKLOG 511 +#define ngx_debug_init() + #if (__FreeBSD__) && (__FreeBSD_version < 400017) diff --git a/usr.sbin/nginx/src/os/unix/ngx_process.c b/usr.sbin/nginx/src/os/unix/ngx_process.c index f771b5aeda3..5713ca8e1b3 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_process.c +++ b/usr.sbin/nginx/src/os/unix/ngx_process.c @@ -23,6 +23,7 @@ typedef struct { static void ngx_execute_proc(ngx_cycle_t *cycle, void *data); static void ngx_signal_handler(int signo); static void ngx_process_get_status(void); +static void ngx_unlock_mutexes(ngx_pid_t pid); int ngx_argc; @@ -498,17 +499,6 @@ ngx_process_get_status(void) } - if (ngx_accept_mutex_ptr) { - - /* - * unlock the accept mutex if the abnormally exited process - * held it - */ - - ngx_atomic_cmp_set(ngx_accept_mutex_ptr, pid, 0); - } - - one = 1; process = "unknown process"; @@ -546,6 +536,55 @@ ngx_process_get_status(void) process, pid, WEXITSTATUS(status)); ngx_processes[i].respawn = 0; } + + ngx_unlock_mutexes(pid); + } +} + + +static void +ngx_unlock_mutexes(ngx_pid_t pid) +{ + ngx_uint_t i; + ngx_shm_zone_t *shm_zone; + ngx_list_part_t *part; + ngx_slab_pool_t *sp; + + /* + * unlock the accept mutex if the abnormally exited process + * held it + */ + + if (ngx_accept_mutex_ptr) { + (void) ngx_shmtx_force_unlock(&ngx_accept_mutex, pid); + } + + /* + * unlock shared memory mutexes if held by the abnormally exited + * process + */ + + part = (ngx_list_part_t *) &ngx_cycle->shared_memory.part; + shm_zone = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + shm_zone = part->elts; + i = 0; + } + + sp = (ngx_slab_pool_t *) shm_zone[i].shm.addr; + + if (ngx_shmtx_force_unlock(&sp->mutex, pid)) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "shared memory zone \"%V\" was locked by %P", + &shm_zone[i].shm.name, pid); + } } } diff --git a/usr.sbin/nginx/src/os/unix/ngx_process.h b/usr.sbin/nginx/src/os/unix/ngx_process.h index bd0252bd85f..7b5e8c0c25e 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_process.h +++ b/usr.sbin/nginx/src/os/unix/ngx_process.h @@ -9,6 +9,7 @@ #define _NGX_PROCESS_H_INCLUDED_ +#include <ngx_setaffinity.h> #include <ngx_setproctitle.h> diff --git a/usr.sbin/nginx/src/os/unix/ngx_process_cycle.c b/usr.sbin/nginx/src/os/unix/ngx_process_cycle.c index 7718483017d..b9d4c8c7ec5 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_process_cycle.c +++ b/usr.sbin/nginx/src/os/unix/ngx_process_cycle.c @@ -63,7 +63,7 @@ ngx_int_t ngx_threads_n; #endif -u_long cpu_affinity; +uint64_t cpu_affinity; static u_char master_process[] = "master process"; @@ -251,6 +251,10 @@ ngx_master_process_cycle(ngx_cycle_t *cycle) ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_JUST_RESPAWN); ngx_start_cache_manager_processes(cycle, 1); + + /* allow new processes to start */ + ngx_msleep(100); + live = 1; ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); @@ -952,23 +956,10 @@ nochroot: } } -#if (NGX_HAVE_SCHED_SETAFFINITY) - if (cpu_affinity) { - ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, - "sched_setaffinity(0x%08Xl)", cpu_affinity); - - if (sched_setaffinity(0, sizeof(cpu_affinity), - (cpu_set_t *) &cpu_affinity) - == -1) - { - ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, - "sched_setaffinity(0x%08Xl) failed", cpu_affinity); - } + ngx_setaffinity(cpu_affinity, cycle->log); } -#endif - #if (NGX_HAVE_PR_SET_DUMPABLE) /* allow coredump after setuid() in Linux 2.4.x */ diff --git a/usr.sbin/nginx/src/os/unix/ngx_readv_chain.c b/usr.sbin/nginx/src/os/unix/ngx_readv_chain.c index 373c4027cb3..7b6badfa825 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_readv_chain.c +++ b/usr.sbin/nginx/src/os/unix/ngx_readv_chain.c @@ -71,6 +71,10 @@ ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain) iov->iov_len += chain->buf->end - chain->buf->last; } else { + if (vec.nelts >= IOV_MAX) { + break; + } + iov = ngx_array_push(&vec); if (iov == NULL) { return NGX_ERROR; @@ -195,6 +199,10 @@ ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain) iov->iov_len += chain->buf->end - chain->buf->last; } else { + if (vec.nelts >= IOV_MAX) { + break; + } + iov = ngx_array_push(&vec); if (iov == NULL) { return NGX_ERROR; diff --git a/usr.sbin/nginx/src/os/unix/ngx_setaffinity.c b/usr.sbin/nginx/src/os/unix/ngx_setaffinity.c new file mode 100644 index 00000000000..8f6cf35948a --- /dev/null +++ b/usr.sbin/nginx/src/os/unix/ngx_setaffinity.c @@ -0,0 +1,69 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> + + +#if (NGX_HAVE_CPUSET_SETAFFINITY) + +#include <sys/cpuset.h> + +void +ngx_setaffinity(uint64_t cpu_affinity, ngx_log_t *log) +{ + cpuset_t mask; + ngx_uint_t i; + + ngx_log_error(NGX_LOG_NOTICE, log, 0, + "cpuset_setaffinity(0x%08Xl)", cpu_affinity); + + CPU_ZERO(&mask); + i = 0; + do { + if (cpu_affinity & 1) { + CPU_SET(i, &mask); + } + i++; + cpu_affinity >>= 1; + } while (cpu_affinity); + + if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, + sizeof(cpuset_t), &mask) == -1) + { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "cpuset_setaffinity() failed"); + } +} + +#elif (NGX_HAVE_SCHED_SETAFFINITY) + +void +ngx_setaffinity(uint64_t cpu_affinity, ngx_log_t *log) +{ + cpu_set_t mask; + ngx_uint_t i; + + ngx_log_error(NGX_LOG_NOTICE, log, 0, + "sched_setaffinity(0x%08Xl)", cpu_affinity); + + CPU_ZERO(&mask); + i = 0; + do { + if (cpu_affinity & 1) { + CPU_SET(i, &mask); + } + i++; + cpu_affinity >>= 1; + } while (cpu_affinity); + + if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sched_setaffinity() failed"); + } +} + +#endif diff --git a/usr.sbin/nginx/src/os/unix/ngx_setaffinity.h b/usr.sbin/nginx/src/os/unix/ngx_setaffinity.h new file mode 100644 index 00000000000..33f5835dee6 --- /dev/null +++ b/usr.sbin/nginx/src/os/unix/ngx_setaffinity.h @@ -0,0 +1,23 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + +#ifndef _NGX_SETAFFINITY_H_INCLUDED_ +#define _NGX_SETAFFINITY_H_INCLUDED_ + + +#if (NGX_HAVE_SCHED_SETAFFINITY || NGX_HAVE_CPUSET_SETAFFINITY) + +#define NGX_HAVE_CPU_AFFINITY 1 + +void ngx_setaffinity(uint64_t cpu_affinity, ngx_log_t *log); + +#else + +#define ngx_setaffinity(cpu_affinity, log) + +#endif + + +#endif /* _NGX_SETAFFINITY_H_INCLUDED_ */ diff --git a/usr.sbin/nginx/src/os/unix/ngx_setproctitle.c b/usr.sbin/nginx/src/os/unix/ngx_setproctitle.c index a61fe1b9427..91afa519142 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_setproctitle.c +++ b/usr.sbin/nginx/src/os/unix/ngx_setproctitle.c @@ -21,7 +21,7 @@ * from argv[0] for our process title. * * The Solaris's standard /bin/ps does not show the changed process title. - * You have to use "/usr/ucb/ps -w" instead. Besides, the UCB ps dos not + * You have to use "/usr/ucb/ps -w" instead. Besides, the UCB ps does not * show a new title if its length less than the origin command line length. * To avoid it we append to a new title the origin command line in the * parenthesis. diff --git a/usr.sbin/nginx/src/os/unix/ngx_solaris_config.h b/usr.sbin/nginx/src/os/unix/ngx_solaris_config.h index 278bfd29556..e664ba826c0 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_solaris_config.h +++ b/usr.sbin/nginx/src/os/unix/ngx_solaris_config.h @@ -29,6 +29,7 @@ #include <grp.h> #include <dirent.h> #include <glob.h> +#include <time.h> #include <sys/statvfs.h> /* statvfs() */ #include <sys/filio.h> /* FIONBIO */ @@ -99,6 +100,7 @@ #define NGX_HAVE_OS_SPECIFIC_INIT 1 +#define ngx_debug_init() extern char **environ; diff --git a/usr.sbin/nginx/src/os/unix/ngx_solaris_sendfilev_chain.c b/usr.sbin/nginx/src/os/unix/ngx_solaris_sendfilev_chain.c index d68220dc32f..f800c15f5c9 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_solaris_sendfilev_chain.c +++ b/usr.sbin/nginx/src/os/unix/ngx_solaris_sendfilev_chain.c @@ -74,7 +74,6 @@ ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) send = 0; - complete = 0; vec.elts = sfvs; vec.size = sizeof(sendfilevec_t); @@ -87,6 +86,7 @@ ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) fprev = 0; sfv = NULL; eintr = 0; + complete = 0; sent = 0; prev_send = send; @@ -94,8 +94,8 @@ ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) /* create the sendfilevec and coalesce the neighbouring bufs */ - for (cl = in; cl && vec.nelts < IOV_MAX && send < limit; cl = cl->next) - { + for (cl = in; cl && send < limit; cl = cl->next) { + if (ngx_buf_special(cl->buf)) { continue; } @@ -113,6 +113,10 @@ ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) sfv->sfv_len += (size_t) size; } else { + if (vec.nelts >= IOV_MAX) { + break; + } + sfv = ngx_array_push(&vec); if (sfv == NULL) { return NGX_CHAIN_ERROR; @@ -147,6 +151,10 @@ ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) sfv->sfv_len += (size_t) size; } else { + if (vec.nelts >= IOV_MAX) { + break; + } + sfv = ngx_array_push(&vec); if (sfv == NULL) { return NGX_CHAIN_ERROR; diff --git a/usr.sbin/nginx/src/os/unix/ngx_writev_chain.c b/usr.sbin/nginx/src/os/unix/ngx_writev_chain.c index 579fdecc897..805982d6558 100644 --- a/usr.sbin/nginx/src/os/unix/ngx_writev_chain.c +++ b/usr.sbin/nginx/src/os/unix/ngx_writev_chain.c @@ -54,7 +54,6 @@ ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) } send = 0; - complete = 0; vec.elts = iovs; vec.size = sizeof(struct iovec); @@ -65,14 +64,15 @@ ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) prev = NULL; iov = NULL; eintr = 0; + complete = 0; prev_send = send; vec.nelts = 0; /* create the iovec and coalesce the neighbouring bufs */ - for (cl = in; cl && vec.nelts < IOV_MAX && send < limit; cl = cl->next) - { + for (cl = in; cl && send < limit; cl = cl->next) { + if (ngx_buf_special(cl->buf)) { continue; } @@ -93,6 +93,10 @@ ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) iov->iov_len += size; } else { + if (vec.nelts >= IOV_MAX) { + break; + } + iov = ngx_array_push(&vec); if (iov == NULL) { return NGX_CHAIN_ERROR; diff --git a/usr.sbin/nginx/src/pcre/pcre_study.c b/usr.sbin/nginx/src/pcre/pcre_study.c new file mode 100644 index 00000000000..61deb56f053 --- /dev/null +++ b/usr.sbin/nginx/src/pcre/pcre_study.c @@ -0,0 +1,1527 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_study(), along with local +supporting functions. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +#define SET_BIT(c) start_bits[c/8] |= (1 << (c&7)) + +/* Returns from set_start_bits() */ + +enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE, SSB_UNKNOWN }; + + + +/************************************************* +* Find the minimum subject length for a group * +*************************************************/ + +/* Scan a parenthesized group and compute the minimum length of subject that +is needed to match it. This is a lower bound; it does not mean there is a +string of that length that matches. In UTF8 mode, the result is in characters +rather than bytes. + +Arguments: + code pointer to start of group (the bracket) + startcode pointer to start of the whole pattern + options the compiling options + int RECURSE depth + +Returns: the minimum length + -1 if \C in UTF-8 mode or (*ACCEPT) was encountered + -2 internal error (missing capturing bracket) + -3 internal error (opcode not listed) +*/ + +static int +find_minlength(const pcre_uchar *code, const pcre_uchar *startcode, int options, + int recurse_depth) +{ +int length = -1; +/* PCRE_UTF16 has the same value as PCRE_UTF8. */ +BOOL utf = (options & PCRE_UTF8) != 0; +BOOL had_recurse = FALSE; +register int branchlength = 0; +register pcre_uchar *cc = (pcre_uchar *)code + 1 + LINK_SIZE; + +if (*code == OP_CBRA || *code == OP_SCBRA || + *code == OP_CBRAPOS || *code == OP_SCBRAPOS) cc += IMM2_SIZE; + +/* Scan along the opcodes for this branch. If we get to the end of the +branch, check the length against that of the other branches. */ + +for (;;) + { + int d, min; + pcre_uchar *cs, *ce; + register int op = *cc; + + switch (op) + { + case OP_COND: + case OP_SCOND: + + /* If there is only one branch in a condition, the implied branch has zero + length, so we don't add anything. This covers the DEFINE "condition" + automatically. */ + + cs = cc + GET(cc, 1); + if (*cs != OP_ALT) + { + cc = cs + 1 + LINK_SIZE; + break; + } + + /* Otherwise we can fall through and treat it the same as any other + subpattern. */ + + case OP_CBRA: + case OP_SCBRA: + case OP_BRA: + case OP_SBRA: + case OP_CBRAPOS: + case OP_SCBRAPOS: + case OP_BRAPOS: + case OP_SBRAPOS: + case OP_ONCE: + case OP_ONCE_NC: + d = find_minlength(cc, startcode, options, recurse_depth); + if (d < 0) return d; + branchlength += d; + do cc += GET(cc, 1); while (*cc == OP_ALT); + cc += 1 + LINK_SIZE; + break; + + /* ACCEPT makes things far too complicated; we have to give up. */ + + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + return -1; + + /* Reached end of a branch; if it's a ket it is the end of a nested + call. If it's ALT it is an alternation in a nested call. If it is END it's + the end of the outer call. All can be handled by the same code. If an + ACCEPT was previously encountered, use the length that was in force at that + time, and pass back the shortest ACCEPT length. */ + + case OP_ALT: + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_KETRPOS: + case OP_END: + if (length < 0 || (!had_recurse && branchlength < length)) + length = branchlength; + if (op != OP_ALT) return length; + cc += 1 + LINK_SIZE; + branchlength = 0; + had_recurse = FALSE; + break; + + /* Skip over assertive subpatterns */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do cc += GET(cc, 1); while (*cc == OP_ALT); + /* Fall through */ + + /* Skip over things that don't match chars */ + + case OP_REVERSE: + case OP_CREF: + case OP_NCREF: + case OP_RREF: + case OP_NRREF: + case OP_DEF: + case OP_CALLOUT: + case OP_SOD: + case OP_SOM: + case OP_EOD: + case OP_EODN: + case OP_CIRC: + case OP_CIRCM: + case OP_DOLL: + case OP_DOLLM: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + cc += PRIV(OP_lengths)[*cc]; + break; + + /* Skip over a subpattern that has a {0} or {0,x} quantifier */ + + case OP_BRAZERO: + case OP_BRAMINZERO: + case OP_BRAPOSZERO: + case OP_SKIPZERO: + cc += PRIV(OP_lengths)[*cc]; + do cc += GET(cc, 1); while (*cc == OP_ALT); + cc += 1 + LINK_SIZE; + break; + + /* Handle literal characters and + repetitions */ + + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_PLUS: + case OP_PLUSI: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_POSPLUS: + case OP_POSPLUSI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + branchlength++; + cc += 2; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: + branchlength++; + cc += (cc[1] == OP_PROP || cc[1] == OP_NOTPROP)? 4 : 2; + break; + + /* Handle exact repetitions. The count is already in characters, but we + need to skip over a multibyte character in UTF8 mode. */ + + case OP_EXACT: + case OP_EXACTI: + case OP_NOTEXACT: + case OP_NOTEXACTI: + branchlength += GET2(cc,1); + cc += 2 + IMM2_SIZE; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + case OP_TYPEEXACT: + branchlength += GET2(cc,1); + cc += 2 + IMM2_SIZE + ((cc[1 + IMM2_SIZE] == OP_PROP + || cc[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0); + break; + + /* Handle single-char non-literal matchers */ + + case OP_PROP: + case OP_NOTPROP: + cc += 2; + /* Fall through */ + + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ALLANY: + case OP_EXTUNI: + case OP_HSPACE: + case OP_NOT_HSPACE: + case OP_VSPACE: + case OP_NOT_VSPACE: + branchlength++; + cc++; + break; + + /* "Any newline" might match two characters, but it also might match just + one. */ + + case OP_ANYNL: + branchlength += 1; + cc++; + break; + + /* The single-byte matcher means we can't proceed in UTF-8 mode. (In + non-UTF-8 mode \C will actually be turned into OP_ALLANY, so won't ever + appear, but leave the code, just in case.) */ + + case OP_ANYBYTE: +#ifdef SUPPORT_UTF + if (utf) return -1; +#endif + branchlength++; + cc++; + break; + + /* For repeated character types, we have to test for \p and \P, which have + an extra two bytes of parameters. */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSQUERY: + if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2; + cc += PRIV(OP_lengths)[op]; + break; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEPOSUPTO: + if (cc[1 + IMM2_SIZE] == OP_PROP + || cc[1 + IMM2_SIZE] == OP_NOTPROP) cc += 2; + cc += PRIV(OP_lengths)[op]; + break; + + /* Check a class for variable quantification */ + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: + cc += GET(cc, 1) - PRIV(OP_lengths)[OP_CLASS]; + /* Fall through */ +#endif + + case OP_CLASS: + case OP_NCLASS: + cc += PRIV(OP_lengths)[OP_CLASS]; + + switch (*cc) + { + case OP_CRPLUS: + case OP_CRMINPLUS: + branchlength++; + /* Fall through */ + + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + cc++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + branchlength += GET2(cc,1); + cc += 1 + 2 * IMM2_SIZE; + break; + + default: + branchlength++; + break; + } + break; + + /* Backreferences and subroutine calls are treated in the same way: we find + the minimum length for the subpattern. A recursion, however, causes an + a flag to be set that causes the length of this branch to be ignored. The + logic is that a recursion can only make sense if there is another + alternation that stops the recursing. That will provide the minimum length + (when no recursion happens). A backreference within the group that it is + referencing behaves in the same way. + + If PCRE_JAVASCRIPT_COMPAT is set, a backreference to an unset bracket + matches an empty string (by default it causes a matching failure), so in + that case we must set the minimum length to zero. */ + + case OP_REF: + case OP_REFI: + if ((options & PCRE_JAVASCRIPT_COMPAT) == 0) + { + ce = cs = (pcre_uchar *)PRIV(find_bracket)(startcode, utf, GET2(cc, 1)); + if (cs == NULL) return -2; + do ce += GET(ce, 1); while (*ce == OP_ALT); + if (cc > cs && cc < ce) + { + d = 0; + had_recurse = TRUE; + } + else + { + d = find_minlength(cs, startcode, options, recurse_depth); + } + } + else d = 0; + cc += 1 + IMM2_SIZE; + + /* Handle repeated back references */ + + switch (*cc) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + min = 0; + cc++; + break; + + case OP_CRPLUS: + case OP_CRMINPLUS: + min = 1; + cc++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + min = GET2(cc, 1); + cc += 1 + 2 * IMM2_SIZE; + break; + + default: + min = 1; + break; + } + + branchlength += min * d; + break; + + /* We can easily detect direct recursion, but not mutual recursion. This is + caught by a recursion depth count. */ + + case OP_RECURSE: + cs = ce = (pcre_uchar *)startcode + GET(cc, 1); + do ce += GET(ce, 1); while (*ce == OP_ALT); + if ((cc > cs && cc < ce) || recurse_depth > 10) + had_recurse = TRUE; + else + { + branchlength += find_minlength(cs, startcode, options, recurse_depth + 1); + } + cc += 1 + LINK_SIZE; + break; + + /* Anything else does not or need not match a character. We can get the + item's length from the table, but for those that can match zero occurrences + of a character, we must take special action for UTF-8 characters. As it + happens, the "NOT" versions of these opcodes are used at present only for + ASCII characters, so they could be omitted from this list. However, in + future that may change, so we include them here so as not to leave a + gotcha for a future maintainer. */ + + case OP_UPTO: + case OP_UPTOI: + case OP_NOTUPTO: + case OP_NOTUPTOI: + case OP_MINUPTO: + case OP_MINUPTOI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + case OP_POSUPTO: + case OP_POSUPTOI: + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + + case OP_STAR: + case OP_STARI: + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_POSSTAR: + case OP_POSSTARI: + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + + case OP_QUERY: + case OP_QUERYI: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_MINQUERY: + case OP_MINQUERYI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + case OP_POSQUERY: + case OP_POSQUERYI: + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + + cc += PRIV(OP_lengths)[op]; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + /* Skip these, but we need to add in the name length. */ + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + case OP_THEN_ARG: + cc += PRIV(OP_lengths)[op] + cc[1]; + break; + + /* The remaining opcodes are just skipped over. */ + + case OP_CLOSE: + case OP_COMMIT: + case OP_FAIL: + case OP_PRUNE: + case OP_SET_SOM: + case OP_SKIP: + case OP_THEN: + cc += PRIV(OP_lengths)[op]; + break; + + /* This should not occur: we list all opcodes explicitly so that when + new ones get added they are properly considered. */ + + default: + return -3; + } + } +/* Control never gets here */ +} + + + +/************************************************* +* Set a bit and maybe its alternate case * +*************************************************/ + +/* Given a character, set its first byte's bit in the table, and also the +corresponding bit for the other version of a letter if we are caseless. In +UTF-8 mode, for characters greater than 127, we can only do the caseless thing +when Unicode property support is available. + +Arguments: + start_bits points to the bit map + p points to the character + caseless the caseless flag + cd the block with char table pointers + utf TRUE for UTF-8 / UTF-16 mode + +Returns: pointer after the character +*/ + +static const pcre_uchar * +set_table_bit(pcre_uint8 *start_bits, const pcre_uchar *p, BOOL caseless, + compile_data *cd, BOOL utf) +{ +unsigned int c = *p; + +#ifdef COMPILE_PCRE8 +SET_BIT(c); + +#ifdef SUPPORT_UTF +if (utf && c > 127) + { + GETCHARINC(c, p); +#ifdef SUPPORT_UCP + if (caseless) + { + pcre_uchar buff[6]; + c = UCD_OTHERCASE(c); + (void)PRIV(ord2utf)(c, buff); + SET_BIT(buff[0]); + } +#endif + return p; + } +#endif + +/* Not UTF-8 mode, or character is less than 127. */ + +if (caseless && (cd->ctypes[c] & ctype_letter) != 0) SET_BIT(cd->fcc[c]); +return p + 1; +#endif + +#ifdef COMPILE_PCRE16 +if (c > 0xff) + { + c = 0xff; + caseless = FALSE; + } +SET_BIT(c); + +#ifdef SUPPORT_UTF +if (utf && c > 127) + { + GETCHARINC(c, p); +#ifdef SUPPORT_UCP + if (caseless) + { + c = UCD_OTHERCASE(c); + if (c > 0xff) + c = 0xff; + SET_BIT(c); + } +#endif + return p; + } +#endif + +if (caseless && (cd->ctypes[c] & ctype_letter) != 0) SET_BIT(cd->fcc[c]); +return p + 1; +#endif +} + + + +/************************************************* +* Set bits for a positive character type * +*************************************************/ + +/* This function sets starting bits for a character type. In UTF-8 mode, we can +only do a direct setting for bytes less than 128, as otherwise there can be +confusion with bytes in the middle of UTF-8 characters. In a "traditional" +environment, the tables will only recognize ASCII characters anyway, but in at +least one Windows environment, some higher bytes bits were set in the tables. +So we deal with that case by considering the UTF-8 encoding. + +Arguments: + start_bits the starting bitmap + cbit type the type of character wanted + table_limit 32 for non-UTF-8; 16 for UTF-8 + cd the block with char table pointers + +Returns: nothing +*/ + +static void +set_type_bits(pcre_uint8 *start_bits, int cbit_type, int table_limit, + compile_data *cd) +{ +register int c; +for (c = 0; c < table_limit; c++) start_bits[c] |= cd->cbits[c+cbit_type]; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +if (table_limit == 32) return; +for (c = 128; c < 256; c++) + { + if ((cd->cbits[c/8] & (1 << (c&7))) != 0) + { + pcre_uchar buff[6]; + (void)PRIV(ord2utf)(c, buff); + SET_BIT(buff[0]); + } + } +#endif +} + + +/************************************************* +* Set bits for a negative character type * +*************************************************/ + +/* This function sets starting bits for a negative character type such as \D. +In UTF-8 mode, we can only do a direct setting for bytes less than 128, as +otherwise there can be confusion with bytes in the middle of UTF-8 characters. +Unlike in the positive case, where we can set appropriate starting bits for +specific high-valued UTF-8 characters, in this case we have to set the bits for +all high-valued characters. The lowest is 0xc2, but we overkill by starting at +0xc0 (192) for simplicity. + +Arguments: + start_bits the starting bitmap + cbit type the type of character wanted + table_limit 32 for non-UTF-8; 16 for UTF-8 + cd the block with char table pointers + +Returns: nothing +*/ + +static void +set_nottype_bits(pcre_uint8 *start_bits, int cbit_type, int table_limit, + compile_data *cd) +{ +register int c; +for (c = 0; c < table_limit; c++) start_bits[c] |= ~cd->cbits[c+cbit_type]; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +if (table_limit != 32) for (c = 24; c < 32; c++) start_bits[c] = 0xff; +#endif +} + + + +/************************************************* +* Create bitmap of starting bytes * +*************************************************/ + +/* This function scans a compiled unanchored expression recursively and +attempts to build a bitmap of the set of possible starting bytes. As time goes +by, we may be able to get more clever at doing this. The SSB_CONTINUE return is +useful for parenthesized groups in patterns such as (a*)b where the group +provides some optional starting bytes but scanning must continue at the outer +level to find at least one mandatory byte. At the outermost level, this +function fails unless the result is SSB_DONE. + +Arguments: + code points to an expression + start_bits points to a 32-byte table, initialized to 0 + utf TRUE if in UTF-8 / UTF-16 mode + cd the block with char table pointers + +Returns: SSB_FAIL => Failed to find any starting bytes + SSB_DONE => Found mandatory starting bytes + SSB_CONTINUE => Found optional starting bytes + SSB_UNKNOWN => Hit an unrecognized opcode +*/ + +static int +set_start_bits(const pcre_uchar *code, pcre_uint8 *start_bits, BOOL utf, + compile_data *cd) +{ +register int c; +int yield = SSB_DONE; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +int table_limit = utf? 16:32; +#else +int table_limit = 32; +#endif + +#if 0 +/* ========================================================================= */ +/* The following comment and code was inserted in January 1999. In May 2006, +when it was observed to cause compiler warnings about unused values, I took it +out again. If anybody is still using OS/2, they will have to put it back +manually. */ + +/* This next statement and the later reference to dummy are here in order to +trick the optimizer of the IBM C compiler for OS/2 into generating correct +code. Apparently IBM isn't going to fix the problem, and we would rather not +disable optimization (in this module it actually makes a big difference, and +the pcre module can use all the optimization it can get). */ + +volatile int dummy; +/* ========================================================================= */ +#endif + +do + { + BOOL try_next = TRUE; + const pcre_uchar *tcode = code + 1 + LINK_SIZE; + + if (*code == OP_CBRA || *code == OP_SCBRA || + *code == OP_CBRAPOS || *code == OP_SCBRAPOS) tcode += IMM2_SIZE; + + while (try_next) /* Loop for items in this branch */ + { + int rc; + + switch(*tcode) + { + /* If we reach something we don't understand, it means a new opcode has + been created that hasn't been added to this code. Hopefully this problem + will be discovered during testing. */ + + default: + return SSB_UNKNOWN; + + /* Fail for a valid opcode that implies no starting bits. */ + + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + case OP_ALLANY: + case OP_ANY: + case OP_ANYBYTE: + case OP_CIRC: + case OP_CIRCM: + case OP_CLOSE: + case OP_COMMIT: + case OP_COND: + case OP_CREF: + case OP_DEF: + case OP_DOLL: + case OP_DOLLM: + case OP_END: + case OP_EOD: + case OP_EODN: + case OP_EXTUNI: + case OP_FAIL: + case OP_MARK: + case OP_NCREF: + case OP_NOT: + case OP_NOTEXACT: + case OP_NOTEXACTI: + case OP_NOTI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + case OP_NOTPROP: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_NOTUPTO: + case OP_NOTUPTOI: + case OP_NOT_HSPACE: + case OP_NOT_VSPACE: + case OP_NRREF: + case OP_PROP: + case OP_PRUNE: + case OP_PRUNE_ARG: + case OP_RECURSE: + case OP_REF: + case OP_REFI: + case OP_REVERSE: + case OP_RREF: + case OP_SCOND: + case OP_SET_SOM: + case OP_SKIP: + case OP_SKIP_ARG: + case OP_SOD: + case OP_SOM: + case OP_THEN: + case OP_THEN_ARG: +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: +#endif + return SSB_FAIL; + + /* We can ignore word boundary tests. */ + + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + tcode++; + break; + + /* If we hit a bracket or a positive lookahead assertion, recurse to set + bits from within the subpattern. If it can't find anything, we have to + give up. If it finds some mandatory character(s), we are done for this + branch. Otherwise, carry on scanning after the subpattern. */ + + case OP_BRA: + case OP_SBRA: + case OP_CBRA: + case OP_SCBRA: + case OP_BRAPOS: + case OP_SBRAPOS: + case OP_CBRAPOS: + case OP_SCBRAPOS: + case OP_ONCE: + case OP_ONCE_NC: + case OP_ASSERT: + rc = set_start_bits(tcode, start_bits, utf, cd); + if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc; + if (rc == SSB_DONE) try_next = FALSE; else + { + do tcode += GET(tcode, 1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + } + break; + + /* If we hit ALT or KET, it means we haven't found anything mandatory in + this branch, though we might have found something optional. For ALT, we + continue with the next alternative, but we have to arrange that the final + result from subpattern is SSB_CONTINUE rather than SSB_DONE. For KET, + return SSB_CONTINUE: if this is the top level, that indicates failure, + but after a nested subpattern, it causes scanning to continue. */ + + case OP_ALT: + yield = SSB_CONTINUE; + try_next = FALSE; + break; + + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_KETRPOS: + return SSB_CONTINUE; + + /* Skip over callout */ + + case OP_CALLOUT: + tcode += 2 + 2*LINK_SIZE; + break; + + /* Skip over lookbehind and negative lookahead assertions */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do tcode += GET(tcode, 1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + break; + + /* BRAZERO does the bracket, but carries on. */ + + case OP_BRAZERO: + case OP_BRAMINZERO: + case OP_BRAPOSZERO: + rc = set_start_bits(++tcode, start_bits, utf, cd); + if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc; +/* ========================================================================= + See the comment at the head of this function concerning the next line, + which was an old fudge for the benefit of OS/2. + dummy = 1; + ========================================================================= */ + do tcode += GET(tcode,1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + break; + + /* SKIPZERO skips the bracket. */ + + case OP_SKIPZERO: + tcode++; + do tcode += GET(tcode,1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + break; + + /* Single-char * or ? sets the bit and tries the next item */ + + case OP_STAR: + case OP_MINSTAR: + case OP_POSSTAR: + case OP_QUERY: + case OP_MINQUERY: + case OP_POSQUERY: + tcode = set_table_bit(start_bits, tcode + 1, FALSE, cd, utf); + break; + + case OP_STARI: + case OP_MINSTARI: + case OP_POSSTARI: + case OP_QUERYI: + case OP_MINQUERYI: + case OP_POSQUERYI: + tcode = set_table_bit(start_bits, tcode + 1, TRUE, cd, utf); + break; + + /* Single-char upto sets the bit and tries the next */ + + case OP_UPTO: + case OP_MINUPTO: + case OP_POSUPTO: + tcode = set_table_bit(start_bits, tcode + 1 + IMM2_SIZE, FALSE, cd, utf); + break; + + case OP_UPTOI: + case OP_MINUPTOI: + case OP_POSUPTOI: + tcode = set_table_bit(start_bits, tcode + 1 + IMM2_SIZE, TRUE, cd, utf); + break; + + /* At least one single char sets the bit and stops */ + + case OP_EXACT: + tcode += IMM2_SIZE; + /* Fall through */ + case OP_CHAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + (void)set_table_bit(start_bits, tcode + 1, FALSE, cd, utf); + try_next = FALSE; + break; + + case OP_EXACTI: + tcode += IMM2_SIZE; + /* Fall through */ + case OP_CHARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_POSPLUSI: + (void)set_table_bit(start_bits, tcode + 1, TRUE, cd, utf); + try_next = FALSE; + break; + + /* Special spacing and line-terminating items. These recognize specific + lists of characters. The difference between VSPACE and ANYNL is that the + latter can match the two-character CRLF sequence, but that is not + relevant for finding the first character, so their code here is + identical. */ + + case OP_HSPACE: + SET_BIT(0x09); + SET_BIT(0x20); +#ifdef SUPPORT_UTF + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+00A0 */ + SET_BIT(0xE1); /* For U+1680, U+180E */ + SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */ + SET_BIT(0xE3); /* For U+3000 */ +#endif +#ifdef COMPILE_PCRE16 + SET_BIT(0xA0); + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + else +#endif /* SUPPORT_UTF */ + { + SET_BIT(0xA0); +#ifdef COMPILE_PCRE16 + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + try_next = FALSE; + break; + + case OP_ANYNL: + case OP_VSPACE: + SET_BIT(0x0A); + SET_BIT(0x0B); + SET_BIT(0x0C); + SET_BIT(0x0D); +#ifdef SUPPORT_UTF + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+0085 */ + SET_BIT(0xE2); /* For U+2028, U+2029 */ +#endif +#ifdef COMPILE_PCRE16 + SET_BIT(0x85); + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + else +#endif /* SUPPORT_UTF */ + { + SET_BIT(0x85); +#ifdef COMPILE_PCRE16 + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + try_next = FALSE; + break; + + /* Single character types set the bits and stop. Note that if PCRE_UCP + is set, we do not see these op codes because \d etc are converted to + properties. Therefore, these apply in the case when only characters less + than 256 are recognized to match the types. */ + + case OP_NOT_DIGIT: + set_nottype_bits(start_bits, cbit_digit, table_limit, cd); + try_next = FALSE; + break; + + case OP_DIGIT: + set_type_bits(start_bits, cbit_digit, table_limit, cd); + try_next = FALSE; + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + ensure it is set as not whitespace. */ + + case OP_NOT_WHITESPACE: + set_nottype_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] |= 0x08; + try_next = FALSE; + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + not set it from the table. */ + + case OP_WHITESPACE: + c = start_bits[1]; /* Save in case it was already set */ + set_type_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] = (start_bits[1] & ~0x08) | c; + try_next = FALSE; + break; + + case OP_NOT_WORDCHAR: + set_nottype_bits(start_bits, cbit_word, table_limit, cd); + try_next = FALSE; + break; + + case OP_WORDCHAR: + set_type_bits(start_bits, cbit_word, table_limit, cd); + try_next = FALSE; + break; + + /* One or more character type fudges the pointer and restarts, knowing + it will hit a single character type and stop there. */ + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: + tcode++; + break; + + case OP_TYPEEXACT: + tcode += 1 + IMM2_SIZE; + break; + + /* Zero or more repeats of character types set the bits and then + try again. */ + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEPOSUPTO: + tcode += IMM2_SIZE; /* Fall through */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPOSSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSQUERY: + switch(tcode[1]) + { + default: + case OP_ANY: + case OP_ALLANY: + return SSB_FAIL; + + case OP_HSPACE: + SET_BIT(0x09); + SET_BIT(0x20); +#ifdef COMPILE_PCRE8 + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+00A0 */ + SET_BIT(0xE1); /* For U+1680, U+180E */ + SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */ + SET_BIT(0xE3); /* For U+3000 */ +#endif +#ifdef COMPILE_PCRE16 + SET_BIT(0xA0); + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + else +#endif /* SUPPORT_UTF */ + SET_BIT(0xA0); + break; + + case OP_ANYNL: + case OP_VSPACE: + SET_BIT(0x0A); + SET_BIT(0x0B); + SET_BIT(0x0C); + SET_BIT(0x0D); +#ifdef COMPILE_PCRE8 + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+0085 */ + SET_BIT(0xE2); /* For U+2028, U+2029 */ +#endif +#ifdef COMPILE_PCRE16 + SET_BIT(0x85); + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + else +#endif /* SUPPORT_UTF */ + SET_BIT(0x85); + break; + + case OP_NOT_DIGIT: + set_nottype_bits(start_bits, cbit_digit, table_limit, cd); + break; + + case OP_DIGIT: + set_type_bits(start_bits, cbit_digit, table_limit, cd); + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + ensure it gets set as not whitespace. */ + + case OP_NOT_WHITESPACE: + set_nottype_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] |= 0x08; + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + avoid setting it. */ + + case OP_WHITESPACE: + c = start_bits[1]; /* Save in case it was already set */ + set_type_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] = (start_bits[1] & ~0x08) | c; + break; + + case OP_NOT_WORDCHAR: + set_nottype_bits(start_bits, cbit_word, table_limit, cd); + break; + + case OP_WORDCHAR: + set_type_bits(start_bits, cbit_word, table_limit, cd); + break; + } + + tcode += 2; + break; + + /* Character class where all the information is in a bit map: set the + bits and either carry on or not, according to the repeat count. If it was + a negative class, and we are operating with UTF-8 characters, any byte + with a value >= 0xc4 is a potentially valid starter because it starts a + character with a value > 255. */ + + case OP_NCLASS: +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 + if (utf) + { + start_bits[24] |= 0xf0; /* Bits for 0xc4 - 0xc8 */ + memset(start_bits+25, 0xff, 7); /* Bits for 0xc9 - 0xff */ + } +#endif +#ifdef COMPILE_PCRE16 + SET_BIT(0xFF); /* For characters > 255 */ +#endif + /* Fall through */ + + case OP_CLASS: + { + pcre_uint8 *map; + tcode++; + map = (pcre_uint8 *)tcode; + + /* In UTF-8 mode, the bits in a bit map correspond to character + values, not to byte values. However, the bit map we are constructing is + for byte values. So we have to do a conversion for characters whose + value is > 127. In fact, there are only two possible starting bytes for + characters in the range 128 - 255. */ + +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 + if (utf) + { + for (c = 0; c < 16; c++) start_bits[c] |= map[c]; + for (c = 128; c < 256; c++) + { + if ((map[c/8] && (1 << (c&7))) != 0) + { + int d = (c >> 6) | 0xc0; /* Set bit for this starter */ + start_bits[d/8] |= (1 << (d&7)); /* and then skip on to the */ + c = (c & 0xc0) + 0x40 - 1; /* next relevant character. */ + } + } + } + else +#endif + { + /* In non-UTF-8 mode, the two bit maps are completely compatible. */ + for (c = 0; c < 32; c++) start_bits[c] |= map[c]; + } + + /* Advance past the bit map, and act on what follows. For a zero + minimum repeat, continue; otherwise stop processing. */ + + tcode += 32 / sizeof(pcre_uchar); + switch (*tcode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + tcode++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (GET2(tcode, 1) == 0) tcode += 1 + 2 * IMM2_SIZE; + else try_next = FALSE; + break; + + default: + try_next = FALSE; + break; + } + } + break; /* End of bitmap class handling */ + + } /* End of switch */ + } /* End of try_next loop */ + + code += GET(code, 1); /* Advance to next branch */ + } +while (*code == OP_ALT); +return yield; +} + + + + + +/************************************************* +* Study a compiled expression * +*************************************************/ + +/* This function is handed a compiled expression that it must study to produce +information that will speed up the matching. It returns a pcre[16]_extra block +which then gets handed back to pcre_exec(). + +Arguments: + re points to the compiled expression + options contains option bits + errorptr points to where to place error messages; + set NULL unless error + +Returns: pointer to a pcre[16]_extra block, with study_data filled in and + the appropriate flags set; + NULL on error or if no optimization possible +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN pcre_extra * PCRE_CALL_CONVENTION +pcre_study(const pcre *external_re, int options, const char **errorptr) +#else +PCRE_EXP_DEFN pcre16_extra * PCRE_CALL_CONVENTION +pcre16_study(const pcre16 *external_re, int options, const char **errorptr) +#endif +{ +int min; +BOOL bits_set = FALSE; +pcre_uint8 start_bits[32]; +PUBL(extra) *extra = NULL; +pcre_study_data *study; +const pcre_uint8 *tables; +pcre_uchar *code; +compile_data compile_block; +const REAL_PCRE *re = (const REAL_PCRE *)external_re; + +*errorptr = NULL; + +if (re == NULL || re->magic_number != MAGIC_NUMBER) + { + *errorptr = "argument is not a compiled regular expression"; + return NULL; + } + +if ((re->flags & PCRE_MODE) == 0) + { +#ifdef COMPILE_PCRE8 + *errorptr = "argument is compiled in 16 bit mode"; +#else + *errorptr = "argument is compiled in 8 bit mode"; +#endif + return NULL; + } + +if ((options & ~PUBLIC_STUDY_OPTIONS) != 0) + { + *errorptr = "unknown or incorrect option bit(s) set"; + return NULL; + } + +code = (pcre_uchar *)re + re->name_table_offset + + (re->name_count * re->name_entry_size); + +/* For an anchored pattern, or an unanchored pattern that has a first char, or +a multiline pattern that matches only at "line starts", there is no point in +seeking a list of starting bytes. */ + +if ((re->options & PCRE_ANCHORED) == 0 && + (re->flags & (PCRE_FIRSTSET|PCRE_STARTLINE)) == 0) + { + int rc; + + /* Set the character tables in the block that is passed around */ + + tables = re->tables; + +#ifdef COMPILE_PCRE8 + if (tables == NULL) + (void)pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, + (void *)(&tables)); +#else + if (tables == NULL) + (void)pcre16_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, + (void *)(&tables)); +#endif + + compile_block.lcc = tables + lcc_offset; + compile_block.fcc = tables + fcc_offset; + compile_block.cbits = tables + cbits_offset; + compile_block.ctypes = tables + ctypes_offset; + + /* See if we can find a fixed set of initial characters for the pattern. */ + + memset(start_bits, 0, 32 * sizeof(pcre_uint8)); + rc = set_start_bits(code, start_bits, (re->options & PCRE_UTF8) != 0, + &compile_block); + bits_set = rc == SSB_DONE; + if (rc == SSB_UNKNOWN) + { + *errorptr = "internal error: opcode not recognized"; + return NULL; + } + } + +/* Find the minimum length of subject string. */ + +switch(min = find_minlength(code, code, re->options, 0)) + { + case -2: *errorptr = "internal error: missing capturing bracket"; return NULL; + case -3: *errorptr = "internal error: opcode not recognized"; return NULL; + default: break; + } + +/* If a set of starting bytes has been identified, or if the minimum length is +greater than zero, or if JIT optimization has been requested, get a +pcre[16]_extra block and a pcre_study_data block. The study data is put in the +latter, which is pointed to by the former, which may also get additional data +set later by the calling program. At the moment, the size of pcre_study_data +is fixed. We nevertheless save it in a field for returning via the +pcre_fullinfo() function so that if it becomes variable in the future, +we don't have to change that code. */ + +if (bits_set || min > 0 +#ifdef SUPPORT_JIT + || (options & PCRE_STUDY_JIT_COMPILE) != 0 +#endif + ) + { + extra = (PUBL(extra) *)(PUBL(malloc)) + (sizeof(PUBL(extra)) + sizeof(pcre_study_data)); + if (extra == NULL) + { + *errorptr = "failed to get memory"; + return NULL; + } + + study = (pcre_study_data *)((char *)extra + sizeof(PUBL(extra))); + extra->flags = PCRE_EXTRA_STUDY_DATA; + extra->study_data = study; + + study->size = sizeof(pcre_study_data); + study->flags = 0; + + /* Set the start bits always, to avoid unset memory errors if the + study data is written to a file, but set the flag only if any of the bits + are set, to save time looking when none are. */ + + if (bits_set) + { + study->flags |= PCRE_STUDY_MAPPED; + memcpy(study->start_bits, start_bits, sizeof(start_bits)); + } + else memset(study->start_bits, 0, 32 * sizeof(pcre_uint8)); + +#ifdef PCRE_DEBUG + if (bits_set) + { + pcre_uint8 *ptr = start_bits; + int i; + + printf("Start bits:\n"); + for (i = 0; i < 32; i++) + printf("%3d: %02x%s", i * 8, *ptr++, ((i + 1) & 0x7) != 0? " " : "\n"); + } +#endif + + /* Always set the minlength value in the block, because the JIT compiler + makes use of it. However, don't set the bit unless the length is greater than + zero - the interpretive pcre_exec() and pcre_dfa_exec() needn't waste time + checking the zero case. */ + + if (min > 0) + { + study->flags |= PCRE_STUDY_MINLEN; + study->minlength = min; + } + else study->minlength = 0; + + /* If JIT support was compiled and requested, attempt the JIT compilation. + If no starting bytes were found, and the minimum length is zero, and JIT + compilation fails, abandon the extra block and return NULL. */ + +#ifdef SUPPORT_JIT + extra->executable_jit = NULL; + if ((options & PCRE_STUDY_JIT_COMPILE) != 0) PRIV(jit_compile)(re, extra); + if (study->flags == 0 && (extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) == 0) + { +#ifdef COMPILE_PCRE8 + pcre_free_study(extra); +#endif +#ifdef COMPILE_PCRE16 + pcre16_free_study(extra); +#endif + extra = NULL; + } +#endif + } + +return extra; +} + + +/************************************************* +* Free the study data * +*************************************************/ + +/* This function frees the memory that was obtained by pcre_study(). + +Argument: a pointer to the pcre[16]_extra block +Returns: nothing +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN void +pcre_free_study(pcre_extra *extra) +#else +PCRE_EXP_DEFN void +pcre16_free_study(pcre16_extra *extra) +#endif +{ +if (extra == NULL) + return; +#ifdef SUPPORT_JIT +if ((extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 && + extra->executable_jit != NULL) + PRIV(jit_free)(extra->executable_jit); +#endif +PUBL(free)(extra); +} + +/* End of pcre_study.c */ |