changeset 410:cd9cb7a3ff9e

Merge with nginx 0.7.8.
author Maxim Dounin <mdounin@mdounin.ru>
date Mon, 11 Aug 2008 22:07:06 +0400
parents 52b28d322d76 (current diff) 040b8c84d040 (diff)
children 03a69004d77d
files .hgtags auto/modules src/mail/ngx_mail.h src/mail/ngx_mail_auth_http_module.c src/mail/ngx_mail_core_module.c src/mail/ngx_mail_handler.c src/mail/ngx_mail_imap_module.c src/mail/ngx_mail_proxy_module.c src/mail/ngx_mail_smtp_handler.c src/mail/ngx_mail_smtp_module.c
diffstat 186 files changed, 15470 insertions(+), 5254 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags
+++ b/.hgtags
@@ -170,3 +170,32 @@ 390b8f8309d6eb6771fec2467145bb42b6b71868
 5e3b425174f6aea903cfd1b7b5eb8d21bb780847 NGINX_0_6_9
 3a91bfeffabaa2cd1a89dab846803bc595d79460 NGINX_0_6_10
 3f511163d90862acd9a0d46304e910b515d6c52f NGINX_0_6_11
+1c519aff5c0cc50bbc6db813f93767d6cf4569bc NGINX_0_6_12
+cac46d125dc7c11ce3b7b5c2be0eb634d2e513a6 NGINX_0_6_13
+10cc350ed8a1a0c1a9f0c093f5bec0032ea9eac4 NGINX_0_6_14
+4276c2f1f434d6e7e350d9675b2fcc6efb99f79c NGINX_0_6_15
+eae74a780a84a4f89a835e17a840657cb6b1c556 NGINX_0_6_16
+05693816539c8788ed0060c52a94df2f0975a8bb NGINX_0_6_17
+e10168d6e371c8c2bf687eda4beba01ce6ae3684 NGINX_0_6_18
+5a1bb0129dff4d4fd697de3d70c119967618f0f9 NGINX_0_6_19
+84b8345f70d5c2c37e869f1b269c12bd0f55bc90 NGINX_0_6_20
+583decdb82a458bd5dd2e11c8accbb6ca30c6bd6 NGINX_0_6_21
+b743d290eb3bdec9f8f8ccdcaea8e899b5d781bc NGINX_0_6_22
+9121a0a91f470755095d53c8691d1c3ea58784b8 NGINX_0_6_23
+2b41fbc2e39e1d6c9ab4bd7c5de0191e4d27f574 NGINX_0_6_24
+54fad6c4b555ce02d963e0bc98c867301affb883 NGINX_0_6_25
+a39aab45a53f467d05443d25208b5d893de8f867 NGINX_0_6_26
+babd3d9efb625a2afe53f106195370bd9d5467ca NGINX_0_6_27
+6aeb6e11b9e796d06e1079cecf514d5c3bf5b47f NGINX_0_6_28
+9a242235a80a93be37aa0694220d9c11c27dda64 NGINX_0_6_29
+6639b93e81b2bd6a016b310953215108aad0c379 NGINX_0_6_30
+67fa3851697b2c2940bd437b00e576a2f1706568 NGINX_0_6_31
+edf1cb6c328eb13c27880d1f4cb5ea068f7aec37 NGINX_0_7_0
+820f6378fc007b8682ab891f6b793f7ebe97208a NGINX_0_7_1
+bc21d9cd9c5427ba2c8e096ca20a33ce4aa50d7c NGINX_0_7_2
+984bb0b1399bffe332ad1fe5cd8d4d7862fa18d8 NGINX_0_7_3
+12defd37f57883e4d59ef36837d03c9b68c3c3ad NGINX_0_7_4
+1172e6d6f40f26a5130a15f5b7adbd902216faf2 NGINX_0_7_5
+6de24473fa708acc3b2f13611f5c44b0e3a292c9 NGINX_0_7_6
+0b6053502c552a3021db417cb2dc6caac7ba1bdd NGINX_0_7_7
+34fb3a5735483bd22e77f90f305103307a813fc4 NGINX_0_7_8
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,537 @@
 
+Changes with nginx 0.7.7                                         30 Jul 2008
+
+    *) Change: now the EAGAIN error returned by connect() is not considered 
+       as temporary error.
+
+    *) Change: now the $ssl_client_cert variable value is a certificate 
+       with TAB character intended before each line except first one; an 
+       unchanged certificate is available in the $ssl_client_raw_cert 
+       variable.
+
+    *) Feature: the "ask" parameter in the "ssl_verify_client" directive.
+
+    *) Feature: byte-range processing improvements.
+       Thanks to Maxim Dounin.
+
+    *) Feature: the "directio" directive.
+
+    *) Feature: MacOSX 1.5 sendfile() support.
+
+    *) Bugfix: now in MacOSX and Cygwin locations are tested in case 
+       insensitive mode; however, the compare is provided by single-byte 
+       locales only.
+
+    *) Bugfix: mail proxy SSL connections hanged, if select, poll, or 
+       /dev/poll methods were used.
+
+    *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module.
+
+
+Changes with nginx 0.7.6                                         07 Jul 2008
+
+    *) Bugfix: now if variables are used in the "access_log" directive a 
+       request root existence is always tested.
+
+    *) Bugfix: the ngx_http_flv_module did not support several values in a 
+       query string.
+
+
+Changes with nginx 0.7.5                                         01 Jul 2008
+
+    *) Bugfixes in variables support in the "access_log" directive; the 
+       bugs had appeared in 0.7.4.
+
+    *) Bugfix: nginx could not be built --without-http_gzip_module; the bug 
+       had appeared in 0.7.3.
+       Thanks to Kirill A. Korinskiy.
+
+    *) Bugfix: if sub_filter and SSI were used together, then responses 
+       might were transferred incorrectly.
+
+
+Changes with nginx 0.7.4                                         30 Jun 2008
+
+    *) Feature: variables support in the "access_log" directive.
+
+    *) Feature: the "open_log_file_cache" directive.
+
+    *) Feature: the -g switch.
+
+    *) Feature: the "Expect" request header line support.
+
+    *) Bugfix: large SSI inclusions might be truncated.
+
+
+Changes with nginx 0.7.3                                         23 Jun 2008
+
+    *) Change: the "rss" extension MIME type has been changed to 
+       "application/rss+xml".
+
+    *) Change: now the "gzip_vary" directive turned on issues a 
+       "Vary: Accept-Encoding" header line for uncompressed responses too.
+
+    *) Feature: now the "rewrite" directive does a redirect automatically 
+       if the "https://" protocol is used.
+
+    *) Bugfix: the "proxy_pass" directive did not work with the HTTPS 
+       protocol; the bug had appeared in 0.6.9.
+
+
+Changes with nginx 0.7.2                                         16 Jun 2008
+
+    *) Feature: now nginx supports EDH key exchange ciphers.
+
+    *) Feature: the "ssl_dhparam" directive.
+
+    *) Feature: the $ssl_client_cert variable.
+       Thanks to Manlio Perillo.
+
+    *) Bugfix: after changing URI via a "rewrite" directive nginx did not 
+       search a new location; the bug had appeared in 0.7.1.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: nginx could not be built without PCRE library; the bug had 
+       appeared in 0.7.1.
+
+    *) Bugfix: when a request to a directory was redirected with the slash 
+       added, nginx dropped a query string from the original request.
+
+
+Changes with nginx 0.7.1                                         26 May 2008
+
+    *) Change: now locations are searched in a tree.
+
+    *) Change: the "optimize_server_names" directive was canceled due to 
+       the "server_name_in_redirect" directive introduction.
+
+    *) Change: some long deprecated directives are not supported anymore.
+
+    *) Change: the "none" parameter in the "ssl_session_cache" directive; 
+       now this is default parameter.
+       Thanks to Rob Mueller.
+
+    *) Bugfix: worker processes might not catch reconfiguration and log 
+       rotation signals.
+
+    *) Bugfix: nginx could not be built on latest Fedora 9 Linux.
+       Thanks to Roxis.
+
+
+Changes with nginx 0.7.0                                         19 May 2008
+
+    *) Change: now the 0x00-0x1F, '"' and '\' characters are escaped as 
+       \xXX in an access_log.
+       Thanks to Maxim Dounin.
+
+    *) Change: now nginx allows several "Host" request header line.
+
+    *) Feature: the "modified" flag in the "expires" directive.
+
+    *) Feature: the $uid_got and $uid_set variables may be used at any 
+       request processing stage.
+
+    *) Feature: the $hostname variable.
+       Thanks to Andrei Nigmatulin.
+
+    *) Feature: DESTDIR support.
+       Thanks to Todd A. Fisher and Andras Voroskoi.
+
+    *) Bugfix: a segmentation fault might occur in worker process on Linux, 
+       if keepalive was enabled.
+
+
+Changes with nginx 0.6.31                                        12 May 2008
+
+    *) Bugfix: nginx did not process FastCGI response if header was at the 
+       end of FastCGI record; the bug had appeared in 0.6.2.
+       Thanks to Sergey Serov.
+
+    *) Bugfix: a segmentation fault might occur in worker process if a file 
+       was deleted and the "open_file_cache_errors" directive was off.
+
+
+Changes with nginx 0.6.30                                        29 Apr 2008
+
+    *) Change: now if an "include" directive pattern does not match any 
+       file, then nginx does not issue an error.
+
+    *) Feature: now the time in directives may be specified without spaces, 
+       for example, "1h50m".
+
+    *) Bugfix: memory leaks if the "ssl_verify_client" directive was on.
+       Thanks to Chavelle Vincent.
+
+    *) Bugfix: the "sub_filter" directive might set text to change into 
+       output.
+
+    *) Bugfix: the "error_page" directive did not take into account 
+       arguments in redirected URI.
+
+    *) Bugfix: now nginx always opens files in binary mode under Cygwin.
+
+    *) Bugfix: nginx could not be built on OpenBSD; the bug had appeared in 
+       0.6.15.
+
+
+Changes with nginx 0.6.29                                        18 Mar 2008
+
+    *) Feature: the ngx_google_perftools_module.
+
+    *) Bugfix: the ngx_http_perl_module could not be built on 64-bit 
+       platforms; the bug had appeared in 0.6.27.
+
+
+Changes with nginx 0.6.28                                        13 Mar 2008
+
+    *) Bugfix: the rtsig method could not be built; the bug had appeared in 
+       0.6.27.
+
+
+Changes with nginx 0.6.27                                        12 Mar 2008
+
+    *) Change: now by default the rtsig method is not built on 
+       Linux 2.6.18+.
+
+    *) Change: now a request method is not changed while redirection to a 
+       named location via an "error_page" directive.
+
+    *) Feature: the "resolver" and "resolver_timeout" directives in SMTP 
+       proxy.
+
+    *) Feature: the "post_action" directive supports named locations.
+
+    *) Bugfix: a segmentation fault occurred in worker process, if a 
+       request was redirected from proxy, FastCGI, or memcached location to 
+       static named locations.
+
+    *) Bugfix: browsers did not repeat SSL handshake if there is no valid 
+       client certificate in first handshake. 
+       Thanks to Alexander V. Inyukhin.
+
+    *) Bugfix: if response code 495-497 was redirected via an "error_page" 
+       directive without code change, then nginx tried to allocate too many 
+       memory.
+
+    *) Bugfix: memory leak in long-lived non buffered connections.
+
+    *) Bugfix: memory leak in resolver.
+
+    *) Bugfix: a segmentation fault occurred in worker process, if a 
+       request was redirected from proxy, FastCGI, or memcached location to 
+       static named locations.
+
+    *) Bugfix: in the $proxy_host and $proxy_port variables caching.
+       Thanks to Sergey Bochenkov.
+
+    *) Bugfix: a "proxy_pass" directive with variables used incorrectly the 
+       same port as in another "proxy_pass" directive with the same host 
+       name and without variables.
+       Thanks to Sergey Bochenkov.
+
+    *) Bugfix: an alert "sendmsg() failed (9: Bad file descriptor)" on some 
+       64-bit platforms while reconfiguration.
+
+    *) Bugfix: a segmentation fault occurred in worker process, if empty 
+       stub block was used second time in SSI.
+
+    *) Bugfix: in copying URI part contained escaped symbols into arguments.
+
+
+Changes with nginx 0.6.26                                        11 Feb 2008
+
+    *) Bugfix: the "proxy_store" and "fastcgi_store" directives did not 
+       check a response length.
+
+    *) Bugfix: a segmentation fault occurred in worker process, if big 
+       value was used in a "expires" directive.
+       Thanks to Joaquin Cuenca Abela.
+
+    *) Bugfix: nginx incorrectly detected cache line size on Pentium 4.
+       Thanks to Gena Makhomed.
+
+    *) Bugfix: in proxied or FastCGI subrequests a client original method 
+       was used instead of the GET method.
+
+    *) Bugfix: socket leak in HTTPS mode if deferred accept was used.
+       Thanks to Ben Maurer.
+
+    *) Bugfix: nginx issued the bogus error message "SSL_shutdown() failed 
+       (SSL: )"; the bug had appeared in 0.6.23.
+
+    *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" 
+       error; the bug had appeared in 0.6.23.
+
+
+Changes with nginx 0.6.25                                        08 Jan 2008
+
+    *) Change: now the "server_name_in_redirect" directive is used instead 
+       of the "server_name" directive's special "*" parameter.
+
+    *) Change: now wildcard and regex names can be used as main name in a 
+       "server_name" directive.
+
+    *) Change: the "satisfy_any" directive was replaced by the "satisfy" 
+       directive.
+
+    *) Workaround: old worker processes might hog CPU after reconfiguration 
+       if they was run under Linux OpenVZ.
+
+    *) Feature: the "min_delete_depth" directive.
+
+    *) Bugfix: the COPY and MOVE methods did not work with single files.
+
+    *) Bugfix: the ngx_http_gzip_static_module did not allow the 
+       ngx_http_dav_module to work; the bug had appeared in 0.6.23.
+
+    *) Bugfix: socket leak in HTTPS mode if deferred accept was used.
+       Thanks to Ben Maurer.
+
+    *) Bugfix: nginx could not be built without PCRE library; the bug had 
+       appeared in 0.6.23.
+
+
+Changes with nginx 0.6.24                                        27 Dec 2007
+
+    *) Bugfix: a segmentation fault might occur in worker process if HTTPS 
+       was used; the bug had appeared in 0.6.23.
+
+
+Changes with nginx 0.6.23                                        27 Dec 2007
+
+    *) Change: the "off" parameter in the "ssl_session_cache" directive; 
+       now this is default parameter.
+
+    *) Change: the "open_file_cache_retest" directive was renamed to the 
+       "open_file_cache_valid".
+
+    *) Feature: the "open_file_cache_min_uses" directive.
+
+    *) Feature: the ngx_http_gzip_static_module.
+
+    *) Feature: the "gzip_disable" directive.
+
+    *) Feature: the "memcached_pass" directive may be used inside the "if" 
+       block.
+
+    *) Bugfix: a segmentation fault occurred in worker process, if the 
+       "memcached_pass" and "if" directives were used in the same location.
+
+    *) Bugfix: if a "satisfy_any on" directive was used and not all access 
+       and auth modules directives were set, then other given access and 
+       auth directives were not tested;
+
+    *) Bugfix: regex parameters in a "valid_referers" directive were not 
+       inherited from previous level.
+
+    *) Bugfix: a "post_action" directive did run if a request was completed 
+       with 499 status code.
+
+    *) Bugfix: optimization of 16K buffer usage in a SSL connection.
+       Thanks to Ben Maurer.
+
+    *) Bugfix: the STARTTLS in SMTP mode did not work.
+       Thanks to Oleg Motienko.
+
+    *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" 
+       error; the bug had appeared in 0.5.13.
+
+
+Changes with nginx 0.6.22                                        19 Dec 2007
+
+    *) Change: now all ngx_http_perl_module methods return values copied to 
+       perl's allocated memory.
+
+    *) Bugfix: if nginx was built with ngx_http_perl_module, the perl 
+       before 5.8.6 was used, and perl supported threads, then during 
+       reconfiguration the master process aborted; the bug had appeared in 
+       0.5.9.
+       Thanks to Boris Zhmurov.
+
+    *) Bugfix: the ngx_http_perl_module methods may get invalid values of 
+       the regex captures.
+
+    *) Bugfix: a segmentation fault occurred in worker process, if the 
+       $r->has_request_body() method was called for a request whose small 
+       request body was already received.
+
+    *) Bugfix: large_client_header_buffers did not freed before going to 
+       keep-alive state.
+       Thanks to Olexander Shtepa.
+
+    *) Bugfix: the last address was missed in the $upstream_addr variable; 
+       the bug had appeared in 0.6.18.
+
+    *) Bugfix: the "fastcgi_catch_stderr" directive did return error code; 
+       now it returns 502 code, that can be rerouted to a next server using 
+       the "fastcgi_next_upstream invalid_header" directive.
+
+    *) Bugfix: a segmentation fault occurred in master process if the 
+       "fastcgi_catch_stderr" directive was used; the bug had appeared in 
+       0.6.10.
+       Thanks to Manlio Perillo.
+
+
+Changes with nginx 0.6.21                                        03 Dec 2007
+
+    *) Change: if variable values used in a "proxy_pass" directive contain 
+       IP-addresses only, then a "resolver" directive is not mandatory. 
+       resolver
+
+    *) Bugfix: a segmentation fault might occur in worker process if a 
+       "proxy_pass" directive with URI-part was used; the bug had appeared 
+       in 0.6.19.
+
+    *) Bugfix: if resolver was used on platform that does not support 
+       kqueue, then nginx issued an alert "name is out of response".
+       Thanks to Andrei Nigmatulin.
+
+    *) Bugfix: if the $server_protocol was used in FastCGI parameters and a 
+       request line length was near to the "client_header_buffer_size" 
+       directive value, then nginx issued an alert "fastcgi: the request 
+       record is too big".
+
+    *) Bugfix: if a plain text HTTP/0.9 version request was made to HTTPS 
+       server, then nginx returned usual response.
+
+
+Changes with nginx 0.6.20                                        28 Nov 2007
+
+    *) Bugfix: a segmentation fault might occur in worker process if a 
+       "proxy_pass" directive with URI-part was used; the bug had appeared 
+       in 0.6.19.
+
+
+Changes with nginx 0.6.19                                        27 Nov 2007
+
+    *) Bugfix: the 0.6.18 version could not be built.
+
+
+Changes with nginx 0.6.18                                        27 Nov 2007
+
+    *) Change: now the ngx_http_userid_module adds start time microseconds 
+       to the cookie field contains a pid value.
+
+    *) Change: now the full request line instead of URI only is written to 
+       error_log.
+
+    *) Feature: variables support in the "proxy_pass" directive.
+
+    *) Feature: the "resolver" and "resolver_timeout" directives.
+
+    *) Feature: now the directive "add_header last-modified ''" deletes a 
+       "Last-Modified" response header line.
+
+    *) Bugfix: the "limit_rate" directive did not allow to use full 
+       throughput, even if limit value was very high.
+
+
+Changes with nginx 0.6.17                                        15 Nov 2007
+
+    *) Feature: the "If-Range" request header line support.
+       Thanks to Alexander V. Inyukhin.
+
+    *) Bugfix: URL double escaping in a redirect of the "msie_refresh" 
+       directive; the bug had appeared in 0.6.4.
+
+    *) Bugfix: the "autoindex" directive did not work with the "alias /" 
+       directive.
+
+    *) Bugfix: a segmentation fault might occur in worker process if 
+       subrequests were used.
+
+    *) Bugfix: the big responses may be transferred truncated if SSL and 
+       gzip were used.
+
+    *) Bugfix: the $status variable was equal to 0 if a proxied server 
+       returned response in HTTP/0.9 version.
+
+
+Changes with nginx 0.6.16                                        29 Oct 2007
+
+    *) Change: now the uname(2) is used on Linux instead of procfs.
+       Thanks to Ilya Novikov.
+
+    *) Bugfix: if the "?" character was in a "error_page" directive, then 
+       it was escaped in a proxied request; the bug had appeared in 0.6.11.
+
+    *) Bugfix: compatibility with mget.
+
+
+Changes with nginx 0.6.15                                        22 Oct 2007
+
+    *) Feature: Cygwin compatibility.
+       Thanks to Vladimir Kutakov.
+
+    *) Feature: the "merge_slashes" directive.
+
+    *) Feature: the "gzip_vary" directive.
+
+    *) Feature: the "server_tokens" directive.
+
+    *) Bugfix: nginx did not unescape URI in the "include" SSI command.
+
+    *) Bugfix: the segmentation fault was occurred on start or while 
+       reconfiguration if variable was used in the "charset" or 
+       "source_charset" directives.
+
+    *) Bugfix: nginx returned the 400 response on requests like 
+       "GET http://www.domain.com HTTP/1.0".
+       Thanks to James Oakley.
+
+    *) Bugfix: if request with request body was redirected using the 
+       "error_page" directive, then nginx tried to read the request body 
+       again; the bug had appeared in 0.6.7.
+
+    *) Bugfix: a segmentation fault occurred in worker process if no 
+       server_name was explicitly defined for server processing request; 
+       the bug had appeared in 0.6.7.
+
+
+Changes with nginx 0.6.14                                        15 Oct 2007
+
+    *) Change: now by default the "echo" SSI command uses entity encoding.
+
+    *) Feature: the "encoding" parameter in the "echo" SSI command.
+
+    *) Feature: the "access_log" directive may be used inside the 
+       "limit_except" block.
+
+    *) Bugfix: if all upstream servers were failed, then all servers had 
+       got weight the was equal one until servers became alive; the bug had 
+       appeared in 0.6.6.
+
+    *) Bugfix: a segmentation fault occurred in worker process if 
+       $date_local and $date_gmt were used outside the 
+       ngx_http_ssi_filter_module.
+
+    *) Bugfix: a segmentation fault might occur in worker process if debug 
+       log was enabled.
+       Thanks to Andrei Nigmatulin.
+
+    *) Bugfix: ngx_http_memcached_module did not set 
+       $upstream_response_time.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: a worker process may got caught in an endless loop, if the 
+       memcached was used.
+
+    *) Bugfix: nginx supported low case only "close" and "keep-alive" 
+       values in the "Connection" request header line; the bug had appeared 
+       in 0.6.11.
+
+    *) Bugfix: sub_filter did not work with empty substitution.
+
+    *) Bugfix: in sub_filter parsing.
+
+
+Changes with nginx 0.6.13                                        24 Sep 2007
+
+    *) Bugfix: nginx did not close directory file on HEAD request if 
+       autoindex was used.
+       Thanks to Arkadiusz Patyk.
+
+
 Changes with nginx 0.6.12                                        21 Sep 2007
 
     *) Change: mail proxy was split on three modules: pop3, imap and smtp.
@@ -9,7 +542,8 @@ Changes with nginx 0.6.12               
     *) Feature: the "smtp_greeting_delay" and "smtp_client_buffer" 
        directives of the ngx_mail_smtp_module.
 
-    *) Bugfix: the trailing wildcards did not work; bug appeared in 0.6.9.
+    *) Bugfix: the trailing wildcards did not work; the bug had appeared in 
+       0.6.9.
 
     *) Bugfix: nginx could not start on Solaris if the shared PCRE library 
        located in non-standard place was used.
@@ -32,7 +566,8 @@ Changes with nginx 0.6.11               
        lines.
 
     *) Bugfix: if the "max_fails" was set for upstream server, then after 
-       first failure server weight was always one; bug appeared in 0.6.6.
+       first failure server weight was always one; the bug had appeared in 
+       0.6.6.
 
 
 Changes with nginx 0.6.10                                        03 Sep 2007
@@ -40,7 +575,7 @@ Changes with nginx 0.6.10               
     *) Feature: the "open_file_cache", "open_file_cache_retest", and 
        "open_file_cache_errors" directives.
 
-    *) Bugfix: socket leak; bug appeared in 0.6.7.
+    *) Bugfix: socket leak; the bug had appeared in 0.6.7.
 
     *) Bugfix: a charset set by the "charset" directive was not appended to 
        the "Content-Type" header set by $r->send_http_header().
@@ -52,7 +587,7 @@ Changes with nginx 0.6.10               
 Changes with nginx 0.6.9                                         28 Aug 2007
 
     *) Bugfix: a worker process may got caught in an endless loop, if the 
-       HTTPS protocol was used; bug appeared in 0.6.7.
+       HTTPS protocol was used; the bug had appeared in 0.6.7.
 
     *) Bugfix: if server listened on two addresses or ports and trailing 
        wildcard was used, then nginx did not run.
@@ -60,7 +595,8 @@ Changes with nginx 0.6.9                
     *) Bugfix: the "ip_hash" directive might incorrectly mark servers as 
        down.
 
-    *) Bugfix: nginx could not be built on amd64; bug appeared in 0.6.8.
+    *) Bugfix: nginx could not be built on amd64; the bug had appeared in 
+       0.6.8.
 
 
 Changes with nginx 0.6.8                                         20 Aug 2007
@@ -75,8 +611,8 @@ Changes with nginx 0.6.8                
     *) Change: now nginx escapes "%" in $memcached_key variable.
 
     *) Bugfix: nginx used path relative to configuration prefix for 
-       non-absolute configuration file path specified in the "-c" key; bug 
-       appeared in 0.6.6.
+       non-absolute configuration file path specified in the "-c" key; the 
+       bug had appeared in 0.6.6.
 
     *) Bugfix: nginx did not work on FreeBSD/sparc64.
 
@@ -106,7 +642,7 @@ Changes with nginx 0.6.7                
     *) Feature: the "add_header Last-Modified ..." directive changes the 
        "Last-Modified" response header line.
 
-    *) Bugfix: if an response different than 200 was returned to an request 
+    *) Bugfix: if a response different than 200 was returned to a request 
        with body and connection went to the keep-alive state after the 
        request, then nginx returned 400 for the next request.
 
@@ -117,9 +653,9 @@ Changes with nginx 0.6.7                
        platforms except FreeBSD.
        Thanks to Jiang Hong.
 
-    *) Bugfix: a worker process may got caught in an endless loop, if an 
-       "server" inside "upstream" block was marked as "down"; bug appeared 
-       in 0.6.6.
+    *) Bugfix: a worker process may got caught in an endless loop, if a 
+       "server" inside "upstream" block was marked as "down"; the bug had 
+       appeared in 0.6.6.
 
     *) Bugfix: now Solaris sendfilev() is not used to transfer the client 
        request body to FastCGI-server via the unix domain socket.
@@ -144,7 +680,7 @@ Changes with nginx 0.6.6                
        for HTTP and HTTPS, then nginx used only one port - 80 or 443.
 
     *) Bugfix: fix building on Solaris/amd64 by Sun Studio 11 and early 
-       versions; bug appeared in 0.6.4.
+       versions; the bug had appeared in 0.6.4.
 
 
 Changes with nginx 0.6.5                                         23 Jul 2007
@@ -205,8 +741,8 @@ Changes with nginx 0.6.3                
        eventport method was used.
 
     *) Bugfix: the "proxy_ignore_client_abort" and 
-       "fastcgi_ignore_client_abort" directives did not work; bug appeared 
-       in 0.5.13.
+       "fastcgi_ignore_client_abort" directives did not work; the bug had 
+       appeared in 0.5.13.
 
 
 Changes with nginx 0.6.2                                         09 Jul 2007
@@ -238,7 +774,8 @@ Changes with nginx 0.6.0                
 Changes with nginx 0.5.25                                        11 Jun 2007
 
     *) Bugfix: nginx could not be built with the 
-       --without-http_rewrite_module parameter; bug appeared in 0.5.24.
+       --without-http_rewrite_module parameter; the bug had appeared in 
+       0.5.24.
 
 
 Changes with nginx 0.5.24                                        06 Jun 2007
@@ -247,7 +784,7 @@ Changes with nginx 0.5.24               
        was made using HTTP/0.9.
 
     *) Bugfix: a part of response body might be passed uncompressed if gzip 
-       was used; bug appeared in 0.5.23.
+       was used; the bug had appeared in 0.5.23.
 
 
 Changes with nginx 0.5.23                                        04 Jun 2007
@@ -270,8 +807,8 @@ Changes with nginx 0.5.23               
 
 Changes with nginx 0.5.22                                        29 May 2007
 
-    *) Bugfix: a big request body might not be passed to backend; bug 
-       appeared in 0.5.21.
+    *) Bugfix: a big request body might not be passed to backend; the bug 
+       had appeared in 0.5.21.
 
 
 Changes with nginx 0.5.21                                        28 May 2007
@@ -308,7 +845,8 @@ Changes with nginx 0.5.20               
        Studio.
        Thanks to Andrei Nigmatulin.
 
-    *) Bugfix: the ngx_http_perl_module could not built by Solaris make.
+    *) Bugfix: the ngx_http_perl_module could not be built by Solaris 
+       make.
        Thanks to Andrei Nigmatulin.
 
 
@@ -343,11 +881,12 @@ Changes with nginx 0.5.18               
 
     *) Bugfix: a segmentation fault occurred in master process after first 
        reconfiguration and receiving any signal if nginx was built with 
-       ngx_http_perl_module and perl did not support multiplicity; bug 
-       appeared in 0.5.9.
+       ngx_http_perl_module and perl did not support multiplicity; the bug 
+       had appeared in 0.5.9.
 
     *) Bugfix: if perl did not support multiplicity, then after 
-       reconfiguration perl code did not work; bug appeared in 0.3.38.
+       reconfiguration perl code did not work; the bug had appeared in 
+       0.3.38.
 
 
 Changes with nginx 0.5.17                                        02 Apr 2007
@@ -374,14 +913,14 @@ Changes with nginx 0.5.16               
 
     *) Bugfix: a segmentation fault might occur in worker process if a 
        charset was set in the "Content-Type" header line and the line has 
-       trailing ";"; bug appeared in 0.3.50.
+       trailing ";"; the bug had appeared in 0.3.50.
 
     *) Bugfix: the "[alert] zero size buf" error when FastCGI server was 
-       used and an request body written in a temporary file was multiple of 
+       used and a request body written in a temporary file was multiple of 
        32K.
 
     *) Bugfix: nginx could not be built on Solaris without the --with-debug 
-       option; bug appeared in 0.5.15.
+       option; the bug had appeared in 0.5.15.
 
 
 Changes with nginx 0.5.15                                        19 Mar 2007
@@ -437,17 +976,17 @@ Changes with nginx 0.5.13               
        send timeout only.
 
     *) Bugfix: nginx could not be built on platforms different from i386, 
-       amd64, sparc and ppc; bug appeared in 0.5.8.
+       amd64, sparc, and ppc; the bug had appeared in 0.5.8.
 
 
 Changes with nginx 0.5.12                                        12 Feb 2007
 
     *) Bugfix: nginx could not be built on platforms different from i386, 
-       amd64, sparc É ppc; bug appeared in 0.5.8.
+       amd64, sparc, and ppc; the bug had appeared in 0.5.8.
 
     *) Bugfix: a segmentation fault might occur in worker process if the 
-       temporarily files were used while working with FastCGI server; bug 
-       appeared in 0.5.8.
+       temporarily files were used while working with FastCGI server; the 
+       bug had appeared in 0.5.8.
 
     *) Bugfix: a segmentation fault might occur in worker process if the 
        $fastcgi_script_name variable was logged.
@@ -461,7 +1000,7 @@ Changes with nginx 0.5.11               
        Thanks to Chris McGrath.
 
     *) Bugfix: the response was incorrect if several ranges were requested; 
-       bug appeared in 0.5.6.
+       the bug had appeared in 0.5.6.
 
     *) Bugfix: the "create_full_put_path" directive could not create the 
        intermediate directories if no "dav_access" directive was set.
@@ -477,10 +1016,10 @@ Changes with nginx 0.5.11               
 Changes with nginx 0.5.10                                        26 Jan 2007
 
     *) Bugfix: while online executable file upgrade the new master process 
-       did not inherit the listening sockets; bug appeared in 0.5.9.
+       did not inherit the listening sockets; the bug had appeared in 0.5.9.
 
     *) Bugfix: a segmentation fault might occur in worker process if nginx 
-       was built with -O2 optimization; bug appeared in 0.5.1.
+       was built with -O2 optimization; the bug had appeared in 0.5.1.
 
 
 Changes with nginx 0.5.9                                         25 Jan 2007
@@ -516,7 +1055,7 @@ Changes with nginx 0.5.8                
 
     *) Bugfix: if the "proxy_buffering off" directive was used and a client 
        connection was non-active, then the connection was closed after send 
-       timeout; bug appeared in 0.4.7.
+       timeout; the bug had appeared in 0.4.7.
 
     *) Bugfix: if the "epoll" method was used and a client closed a 
        connection prematurely, then nginx closed the connection after a 
@@ -597,7 +1136,7 @@ Changes with nginx 0.5.4                
        directive, then nginx might report about configuration error.
 
     *) Bugfix: a segmentation fault might occur if the $host variable was 
-       used; bug appeared in 0.4.14.
+       used; the bug had appeared in 0.4.14.
 
 
 Changes with nginx 0.5.3                                         13 Dec 2006
@@ -614,8 +1153,8 @@ Changes with nginx 0.5.3                
 Changes with nginx 0.5.2                                         11 Dec 2006
 
     *) Bugfix: if the "proxy_pass" directive used the name of the 
-       "upstream" block, then nginx tried to resolve the name; bug appeared 
-       in 0.5.1.
+       "upstream" block, then nginx tried to resolve the name; the bug had 
+       appeared in 0.5.1.
 
 
 Changes with nginx 0.5.1                                         11 Dec 2006
@@ -623,19 +1162,20 @@ Changes with nginx 0.5.1                
     *) Bugfix: the "post_action" directive might not run after a 
        unsuccessful completion of a request.
 
-    *) Workaround: for Eudora for Mac; bug appeared in 0.4.11.
+    *) Workaround: for Eudora for Mac; the bug had appeared in 0.4.11.
        Thanks to Bron Gondwana.
 
     *) Bugfix: if the "upstream" name was used in the "fastcgi_pass", then 
-       the message "no port in upstream" was issued; bug appeared in 0.5.0.
+       the message "no port in upstream" was issued; the bug had appeared 
+       in 0.5.0.
 
     *) Bugfix: if the "proxy_pass" and "fastcgi_pass" directives used the 
        same servers but different ports, then these directives uses the 
-       first described port; bug appeared in 0.5.0.
+       first described port; the bug had appeared in 0.5.0.
 
     *) Bugfix: if the "proxy_pass" and "fastcgi_pass" directives used the 
        unix domain sockets, then these directives used first described 
-       socket; bug appeared in 0.5.0.
+       socket; the bug had appeared in 0.5.0.
 
     *) Bugfix: ngx_http_auth_basic_module ignored the user if it was in the 
        last line in the password file and there was no the carriage return, 
@@ -664,8 +1204,8 @@ Changes with nginx 0.5.0                
     *) Feature: the WAIT status in the "Auth-Status" header line of the 
        IMAP/POP3 proxy authentication server response.
 
-    *) Bugfix: nginx could not be built on 64-bit platforms; bug appeared 
-       in 0.4.14.
+    *) Bugfix: nginx could not be built on 64-bit platforms; the bug had 
+       appeared in 0.4.14.
 
 
 Changes with nginx 0.4.14                                        27 Nov 2006
@@ -676,12 +1216,12 @@ Changes with nginx 0.4.14               
        Linux, and NetBSD.
 
     *) Bugfix: ngx_http_perl_module did not work with perl built with the 
-       threads support; bug appeared in 0.3.38.
+       threads support; the bug had appeared in 0.3.38.
 
     *) Bugfix: ngx_http_perl_module did not work if perl was called 
        recursively.
 
-    *) Bugfix: nginx ignored a host name in an request line.
+    *) Bugfix: nginx ignored a host name in a request line.
 
     *) Bugfix: a worker process may got caught in an endless loop, if a 
        FastCGI server sent too many data to the stderr.
@@ -704,14 +1244,14 @@ Changes with nginx 0.4.13               
     *) Feature: the "limit_except" directive supports all WebDAV methods.
 
     *) Bugfix: if the "add_before_body" directive was used without the 
-       "add_after_body" directive, then an response did not transferred 
+       "add_after_body" directive, then a response did not transferred 
        complete.
 
     *) Bugfix: a large request body did not receive if the epoll method and 
        the deferred accept() were used.
 
     *) Bugfix: a charset could not be set for ngx_http_autoindex_module 
-       responses; bug appeared in 0.3.50.
+       responses; the bug had appeared in 0.3.50.
 
     *) Bugfix: the "[alert] zero size buf" error when FastCGI server was 
        used;
@@ -719,8 +1259,8 @@ Changes with nginx 0.4.13               
     *) Bugfix: the --group= configuration parameter was ignored.
        Thanks to Thomas Moschny.
 
-    *) Bugfix: the 50th subrequest in SSI response did not work; bug 
-       appeared in 0.3.50.
+    *) Bugfix: the 50th subrequest in SSI response did not work; the bug 
+       had appeared in 0.3.50.
 
 
 Changes with nginx 0.4.12                                        31 Oct 2006
@@ -741,7 +1281,7 @@ Changes with nginx 0.4.11               
        method.
 
     *) Bugfix: if the APOP was enabled in the POP3 proxy, then the 
-       USER/PASS commands might not work; bug appeared in 0.4.10.
+       USER/PASS commands might not work; the bug had appeared in 0.4.10.
 
 
 Changes with nginx 0.4.10                                        23 Oct 2006
@@ -756,10 +1296,10 @@ Changes with nginx 0.4.10               
        variable was used in the "map" directive.
 
     *) Bugfix: the ngx_http_flv_module did not support the byte ranges for 
-       full responses; bug appeared in 0.4.7.
-
-    *) Bugfix: nginx could not be built on Debian amd64; bug appeared in 
-       0.4.9.
+       full responses; the bug had appeared in 0.4.7.
+
+    *) Bugfix: nginx could not be built on Debian amd64; the bug had 
+       appeared in 0.4.9.
 
 
 Changes with nginx 0.4.9                                         13 Oct 2006
@@ -773,8 +1313,8 @@ Changes with nginx 0.4.9                
 Changes with nginx 0.4.8                                         11 Oct 2006
 
     *) Bugfix: if an "include" SSI command were before another "include" 
-       SSI command with an "wait" parameter, then the "wait" parameter 
-       might not work.
+       SSI command with a "wait" parameter, then the "wait" parameter might 
+       not work.
 
     *) Bugfix: the ngx_http_flv_module added the FLV header to the full 
        responses.
@@ -791,8 +1331,8 @@ Changes with nginx 0.4.7                
        variables.
 
     *) Bugfix: if an "include" SSI command were before another "include" 
-       SSI command with an "wait" parameter, then the "wait" parameter 
-       might not work.
+       SSI command with a "wait" parameter, then the "wait" parameter might 
+       not work.
 
     *) Bugfix: if the "proxy_buffering off" directive was used or while 
        working with memcached the connections might not be closed on 
@@ -812,14 +1352,14 @@ Changes with nginx 0.4.6                
        $r->headers_out("Content-Length", ...) method.
 
     *) Bugfix: after redirecting error by an "error_page" directive any 
-       ngx_http_rewrite_module directive returned this error code; bug 
-       appeared in 0.4.4.
+       ngx_http_rewrite_module directive returned this error code; the bug 
+       had appeared in 0.4.4.
 
 
 Changes with nginx 0.4.5                                         02 Oct 2006
 
-    *) Bugfix: nginx could not be built on Linux and Solaris; bug appeared 
-       in 0.4.4.
+    *) Bugfix: nginx could not be built on Linux and Solaris; the bug had 
+       appeared in 0.4.4.
 
 
 Changes with nginx 0.4.4                                         02 Oct 2006
@@ -851,10 +1391,10 @@ Changes with nginx 0.4.3                
     *) Feature: the ngx_http_browser_module.
 
     *) Bugfix: a segmentation fault may occur while redirecting the 400 
-       error to the proxied server using an "proxy_pass" directive.
+       error to the proxied server using a "proxy_pass" directive.
 
     *) Bugfix: a segmentation fault occurred if an unix domain socket was 
-       used in an "proxy_pass" directive; bug appeared in 0.3.47.
+       used in a "proxy_pass" directive; the bug had appeared in 0.3.47.
 
     *) Bugfix: SSI did work with memcached and nonbuffered responses.
 
@@ -863,8 +1403,8 @@ Changes with nginx 0.4.3                
 
 Changes with nginx 0.4.2                                         14 Sep 2006
 
-    *) Bugfix: the O_NOATIME flag support on Linux was canceled; bug 
-       appeared in 0.4.1.
+    *) Bugfix: the O_NOATIME flag support on Linux was canceled; the bug 
+       had appeared in 0.4.1.
 
 
 Changes with nginx 0.4.1                                         14 Sep 2006
@@ -898,7 +1438,7 @@ Changes with nginx 0.4.0                
 
     *) Bugfix: a segmentation fault occurred if there was an "index" 
        directive with variables and the first index name was without 
-       variables; bug appeared in 0.1.29.
+       variables; the bug had appeared in 0.1.29.
 
 
 Changes with nginx 0.3.61                                        28 Aug 2006
@@ -916,7 +1456,7 @@ Changes with nginx 0.3.61               
 Changes with nginx 0.3.60                                        18 Aug 2006
 
     *) Bugfix: a worker process may got caught in an endless loop while an 
-       error redirection; bug appeared in 0.3.59.
+       error redirection; the bug had appeared in 0.3.59.
 
 
 Changes with nginx 0.3.59                                        16 Aug 2006
@@ -928,7 +1468,7 @@ Changes with nginx 0.3.59               
 
     *) Bugfix: the "error_page" directive did not changes the 
        "Content-Type" header line after the "X-Accel-Redirect" was used; 
-       bug appeared in 0.3.58.
+       the bug had appeared in 0.3.58.
 
 
 Changes with nginx 0.3.58                                        14 Aug 2006
@@ -969,7 +1509,7 @@ Changes with nginx 0.3.56               
     *) Feature: the "if" directive supports the "-d", "!-d", "-e", "!-e", 
        "-x", and "!-x" operators.
 
-    *) Bugfix: a segmentation fault occurred if an request returned an 
+    *) Bugfix: a segmentation fault occurred if a request returned a 
        redirect and some sent to client header lines were logged in the 
        access log.
 
@@ -996,8 +1536,8 @@ Changes with nginx 0.3.55               
        upstream.
 
     *) Bugfix: on some condition while reconfiguration character codes 
-       inside the "charset_map" may be treated invalid; bug appeared in 
-       0.3.50.
+       inside the "charset_map" may be treated invalid; the bug had 
+       appeared in 0.3.50.
 
 
 Changes with nginx 0.3.54                                        11 Jul 2006
@@ -1016,8 +1556,8 @@ Changes with nginx 0.3.54               
     *) Bugfix: the $upstream_response_time variable had the time of the 
        first request to a backend only.
 
-    *) Bugfix: nginx could not be built on amd64 platform; bug appeared in 
-       0.3.53.
+    *) Bugfix: nginx could not be built on amd64 platform; the bug had 
+       appeared in 0.3.53.
 
 
 Changes with nginx 0.3.53                                        07 Jul 2006
@@ -1050,10 +1590,10 @@ Changes with nginx 0.3.52               
        return the 405 error.
 
     *) Bugfix: the worker process may got caught in an endless loop if the 
-       limit rate was used; bug appeared in 0.3.37.
+       limit rate was used; the bug had appeared in 0.3.37.
 
     *) Bugfix: ngx_http_charset_module logged "unknown charset" alert, even 
-       if the recoding was not needed; bug appeared in 0.3.50.
+       if the recoding was not needed; the bug had appeared in 0.3.50.
 
     *) Bugfix: if a code response of the PUT request was 409, then a 
        temporary file was not removed.
@@ -1062,7 +1602,7 @@ Changes with nginx 0.3.52               
 Changes with nginx 0.3.51                                        30 Jun 2006
 
     *) Bugfix: the "<" symbols might disappeared some conditions in the 
-       SSI; bug appeared in 0.3.50.
+       SSI; the bug had appeared in 0.3.50.
 
 
 Changes with nginx 0.3.50                                        28 Jun 2006
@@ -1107,10 +1647,11 @@ Changes with nginx 0.3.48               
 
     *) Bugfix: the internal redirect always transform client's HTTP method 
        to GET, now the transformation is made for the "X-Accel-Redirect" 
-       redirects only and if the method is not HEAD; bug appeared in 0.3.42.
+       redirects only and if the method is not HEAD; the bug had appeared 
+       in 0.3.42.
 
     *) Bugfix: the ngx_http_perl_module could not be built, if the perl was 
-       built with the threads support; bug appeared in 0.3.46.
+       built with the threads support; the bug had appeared in 0.3.46.
 
 
 Changes with nginx 0.3.47                                        23 May 2006
@@ -1147,7 +1688,8 @@ Changes with nginx 0.3.45               
     *) Change: the &deg; symbol codes were changed in koi-win conversion 
        table.
 
-    *) Feature: the euro É N symbols were added to koi-win conversion table.
+    *) Feature: the euro and N symbols were added to koi-win conversion 
+       table.
 
     *) Bugfix: if nginx distributed the requests among several backends and 
        some backend failed, then requests intended for this backend was 
@@ -1224,9 +1766,9 @@ Changes with nginx 0.3.39               
 
     *) Bugfix: the active connection counter increased on the exceeding of 
        the connection limit specified by the "worker_connections" 
-       directive; bug appeared in 0.2.0.
-
-    *) Bugfix: the limit rate might not work on some condition; bug 
+       directive; the bug had appeared in 0.2.0.
+
+    *) Bugfix: the limit rate might not work on some condition; the bug had 
        appeared in 0.3.38.
 
 
@@ -1300,7 +1842,7 @@ Changes with nginx 0.3.36               
 Changes with nginx 0.3.35                                        22 Mar 2006
 
     *) Bugfix: the accept-filter and the TCP_DEFER_ACCEPT option were set 
-       for first "listen" directive only; bug appeared in 0.3.31.
+       for first "listen" directive only; the bug had appeared in 0.3.31.
 
     *) Bugfix: in the "proxy_pass" directive without the URI part in a 
        subrequest.
@@ -1325,7 +1867,7 @@ Changes with nginx 0.3.33               
 Changes with nginx 0.3.32                                        11 Mar 2006
 
     *) Bugfix: the debug logging on startup and reconfiguration time was 
-       removed; bug appeared in 0.3.31.
+       removed; the bug had appeared in 0.3.31.
 
 
 Changes with nginx 0.3.31                                        10 Mar 2006
@@ -1343,7 +1885,8 @@ Changes with nginx 0.3.31               
 
     *) Bugfix: if there were several "listen" directives listening one 
        various addresses inside one server, then server names like 
-       "*.domain.tld" worked for first address only; bug appeared in 0.3.18.
+       "*.domain.tld" worked for first address only; the bug had appeared 
+       in 0.3.18.
 
     *) Bugfix: if the HTTPS protocol was used in the "proxy_pass" directive 
        and the request body was in temporarily file then the request was 
@@ -1361,7 +1904,7 @@ Changes with nginx 0.3.30               
        ngx_http_ssi_filter_module.
 
     *) Bugfix: nginx could not be built on i386 platform, if the PIC was 
-       used; bug appeared in 0.3.27.
+       used; the bug had appeared in 0.3.27.
 
 
 Changes with nginx 0.3.29                                        20 Feb 2006
@@ -1437,8 +1980,8 @@ Changes with nginx 0.3.26               
 Changes with nginx 0.3.25                                        01 Feb 2006
 
     *) Bugfix: the segmentation fault was occurred on start or while 
-       reconfiguration if there was invalid configuration; bug appeared in 
-       0.3.24.
+       reconfiguration if there was invalid configuration; the bug had 
+       appeared in 0.3.24.
 
 
 Changes with nginx 0.3.24                                        01 Feb 2006
@@ -1454,8 +1997,8 @@ Changes with nginx 0.3.24               
        location.
 
     *) Bugfix: on 64-bit platforms segmentation fault may occurred on start 
-       if the many names were used in the "server_name" directives; bug 
-       appeared in 0.3.18.
+       if the many names were used in the "server_name" directives; the bug 
+       had appeared in 0.3.18.
 
 
 Changes with nginx 0.3.23                                        24 Jan 2006
@@ -1478,8 +2021,8 @@ Changes with nginx 0.3.22               
        canceled.
 
     *) Bugfix: segmentation fault was occurred if the "none" or "blocked" 
-       values was specified in the "valid_referers" directive; bug appeared 
-       in 0.3.18.
+       values was specified in the "valid_referers" directive; the bug had 
+       appeared in 0.3.18.
 
 
 Changes with nginx 0.3.21                                        16 Jan 2006
@@ -1530,10 +2073,10 @@ Changes with nginx 0.3.18               
        ngx_http_map_module.
 
     *) Bugfix: segmentation fault was occurred if configuration file did 
-       not exist; bug appeared in 0.3.12.
+       not exist; the bug had appeared in 0.3.12.
 
     *) Bugfix: on 64-bit platforms segmentation fault may occurred on 
-       start; bug appeared in 0.3.16.
+       start; the bug had appeared in 0.3.16.
 
 
 Changes with nginx 0.3.17                                        18 Dec 2005
@@ -1544,8 +2087,8 @@ Changes with nginx 0.3.17               
     *) Feature: the "map" directive supports domain names in the 
        ".domain.tld" form.
 
-    *) Bugfix: the timeouts were not used in SSL handshake; bug appeared in 
-       0.2.4.
+    *) Bugfix: the timeouts were not used in SSL handshake; the bug had 
+       appeared in 0.2.4.
 
     *) Bugfix: in the HTTPS protocol in the "proxy_pass" directive.
 
@@ -1571,11 +2114,11 @@ Changes with nginx 0.3.16               
     *) Bugfix: the "config timefmt" SSI command set incorrect time format.
 
     *) Bugfix: nginx did not close connection to IMAP/POP3 backend for the 
-       SSL connections; bug appeared in 0.3.13.
+       SSL connections; the bug had appeared in 0.3.13.
        Thanks to Rob Mueller.
 
-    *) Bugfix: segmentation fault may occurred in at SSL shutdown; bug 
-       appeared in 0.3.13.
+    *) Bugfix: segmentation fault may occurred in at SSL shutdown; the bug 
+       had appeared in 0.3.13.
 
 
 Changes with nginx 0.3.15                                        07 Dec 2005
@@ -1591,8 +2134,8 @@ Changes with nginx 0.3.15               
 
 Changes with nginx 0.3.14                                        05 Dec 2005
 
-    *) Bugfix: in the 304 response the body was transferred; bug appeared 
-       in 0.3.13.
+    *) Bugfix: in the 304 response the body was transferred; the bug had 
+       appeared in 0.3.13.
 
 
 Changes with nginx 0.3.13                                        05 Dec 2005
@@ -1608,7 +2151,7 @@ Changes with nginx 0.3.13               
        request body to FastCGI-server via the unix domain socket.
 
     *) Bugfix: the "auth_basic" directive did not disable the 
-       authorization; bug appeared in 0.3.11.
+       authorization; the bug had appeared in 0.3.11.
 
 
 Changes with nginx 0.3.12                                        26 Nov 2005
@@ -1629,7 +2172,7 @@ Changes with nginx 0.3.12               
     *) Feature: the "proxy_buffering" directive.
 
     *) Bugfix: the changes in accept mutex handling when the "rtsig" method 
-       was used; bug appeared in 0.3.0.
+       was used; the bug had appeared in 0.3.0.
 
     *) Bugfix: if the client sent the "Transfer-Encoding: chunked" header 
        line, then nginx returns the 411 error.
@@ -1640,7 +2183,7 @@ Changes with nginx 0.3.12               
 
     *) Bugfix: if the "combined" format was explicitly specified in the 
        "access_log" directive, then the empty lines was written to the log; 
-       bug appeared in 0.3.8.
+       the bug had appeared in 0.3.8.
 
     *) Bugfix: nginx did not run on the sparc platform under any OS except 
        Solaris.
@@ -1652,7 +2195,7 @@ Changes with nginx 0.3.12               
 Changes with nginx 0.3.11                                        15 Nov 2005
 
     *) Bugfix: nginx did not pass the client request headers and body while 
-       proxying; bug appeared in 0.3.10.
+       proxying; the bug had appeared in 0.3.10.
 
 
 Changes with nginx 0.3.10                                        15 Nov 2005
@@ -1691,7 +2234,7 @@ Changes with nginx 0.3.10               
 Changes with nginx 0.3.9                                         10 Nov 2005
 
     *) Bugfix: nginx considered URI as unsafe if two any symbols was 
-       between two slashes; bug appeared in 0.3.8.
+       between two slashes; the bug had appeared in 0.3.8.
 
 
 Changes with nginx 0.3.8                                         09 Nov 2005
@@ -1731,8 +2274,8 @@ Changes with nginx 0.3.8                
 
     *) Bugfix: if the request URI was changes by the "rewrite" directive 
        and the request was proxied in location given by regular expression, 
-       then the incorrect request was transferred to backend; bug appeared 
-       in 0.2.6.
+       then the incorrect request was transferred to backend; the bug had 
+       appeared in 0.2.6.
 
     *) Bugfix: the "expires" directive did not remove the previous 
        "Expires" header.
@@ -1753,7 +2296,7 @@ Changes with nginx 0.3.7                
     *) Feature: the "access_log" supports the "buffer=" parameter.
 
     *) Bugfix: nginx could not be built on platforms different from i386, 
-       amd64, sparc É ppc; bug appeared in 0.3.2.
+       amd64, sparc, and ppc; the bug had appeared in 0.3.2.
 
 
 Changes with nginx 0.3.6                                         24 Oct 2005
@@ -1764,7 +2307,8 @@ Changes with nginx 0.3.6                
     *) Feature: the "log_format" supports the variables in the $name form.
 
     *) Bugfix: if at least in one server was no the "listen" directive, 
-       then nginx did not listen on the 80 port; bug appeared in 0.3.3.
+       then nginx did not listen on the 80 port; the bug had appeared in 
+       0.3.3.
 
     *) Bugfix: if the URI part is omitted in "proxy_pass" directive, the 
        the 80 port was always used.
@@ -1773,10 +2317,10 @@ Changes with nginx 0.3.6                
 Changes with nginx 0.3.5                                         21 Oct 2005
 
     *) Bugfix: the segmentation fault may occurred if the IMAP/POP3 login 
-       was changed by authorization server; bug appeared in 0.2.2.
+       was changed by authorization server; the bug had appeared in 0.2.2.
 
     *) Bugfix: the accept mutex did not work and all connections were 
-       handled by one process; bug appeared in 0.3.3.
+       handled by one process; the bug had appeared in 0.3.3.
 
     *) Bugfix: the timeout did not work if the "rtsig" method and the 
        "timer_resolution" directive were used.
@@ -1784,8 +2328,8 @@ Changes with nginx 0.3.5                
 
 Changes with nginx 0.3.4                                         19 Oct 2005
 
-    *) Bugfix: nginx could not be built on Linux 2.4+ and MacOS X; bug 
-       appeared in 0.3.3.
+    *) Bugfix: nginx could not be built on Linux 2.4+ and MacOS X; the bug 
+       had appeared in 0.3.3.
 
 
 Changes with nginx 0.3.3                                         19 Oct 2005
@@ -1806,7 +2350,7 @@ Changes with nginx 0.3.3                
        the CLOSED state.
 
     *) Bugfix: the mime type may be incorrectly set to default value for 
-       index file with variable in the name; bug appeared in 0.3.0.
+       index file with variable in the name; the bug had appeared in 0.3.0.
 
     *) Feature: the "timer_resolution" directive.
 
@@ -1838,7 +2382,8 @@ Changes with nginx 0.3.2                
 Changes with nginx 0.3.1                                         10 Oct 2005
 
     *) Bugfix: the segmentation fault occurred when the signal queue 
-       overflowed if the "rtsig" method was used; bug appeared in 0.2.0.
+       overflowed if the "rtsig" method was used; the bug had appeared in 
+       0.2.0.
 
     *) Change: correct handling of the "\\", "\"", "\'", and "\$" pairs in 
        SSI.
@@ -1872,7 +2417,7 @@ Changes with nginx 0.2.6                
     *) Bugfix: if the "set" directive set the ngx_http_geo_module variable 
        in some configuration part, the this variable was not available in 
        other configuration parts and the "using uninitialized variable" 
-       error was occurred; bug appeared in 0.2.2.
+       error was occurred; the bug had appeared in 0.2.2.
 
 
 Changes with nginx 0.2.5                                         04 Oct 2005
@@ -1894,17 +2439,17 @@ Changes with nginx 0.2.4                
     *) Feature: the ngx_http_ssi_module supports "$var=text", "$var!=text", 
        "$var=/text/", and "$var!=/text/" expressions in the "if" command.
 
-    *) Bugfix: in proxying location without trailing slash; bug appeared in 
-       0.1.44.
+    *) Bugfix: in proxying location without trailing slash; the bug had 
+       appeared in 0.1.44.
 
     *) Bugfix: the segmentation fault may occurred if the "rtsig" method 
-       was used; bug appeared in 0.2.0.
+       was used; the bug had appeared in 0.2.0.
 
 
 Changes with nginx 0.2.3                                         30 Sep 2005
 
     *) Bugfix: nginx could not be built without the --with-debug option; 
-       bug appeared in 0.2.2.
+       the bug had appeared in 0.2.2.
 
 
 Changes with nginx 0.2.2                                         30 Sep 2005
@@ -1933,8 +2478,8 @@ Changes with nginx 0.2.2                
 Changes with nginx 0.2.1                                         23 Sep 2005
 
     *) Bugfix: if all backend using in load-balancing failed after one 
-       error, then nginx may got caught in an endless loop; bug appeared in 
-       0.2.0.
+       error, then nginx may got caught in an endless loop; the bug had 
+       appeared in 0.2.0.
 
 
 Changes with nginx 0.2.0                                         23 Sep 2005
@@ -2012,7 +2557,7 @@ Changes with nginx 0.1.43               
     *) Bugfix: the segmentation fault occurred or the worker process may 
        got caught in an endless loop if the proxied or FastCGI server sent 
        the "Cache-Control" header line and the "expires" directive was 
-       used; in the proxied mode the bug appeared in 0.1.29.
+       used; in the proxied mode the the bug had appeared in 0.1.29.
 
 
 Changes with nginx 0.1.42                                        23 Aug 2005
@@ -2022,7 +2567,7 @@ Changes with nginx 0.1.42               
        occurred in the ngx_http_proxy_module.
 
     *) Bugfix: the "limit_rate" directive did not work inside the "if" 
-       block; bug appeared in 0.1.38.
+       block; the bug had appeared in 0.1.38.
 
 
 Changes with nginx 0.1.41                                        25 Jul 2005
@@ -2037,7 +2582,7 @@ Changes with nginx 0.1.40               
        information did not logged in the error log.
 
     *) Bugfix: the "Set-Cookie" header line was not transferred when the 
-       "X-Accel-Redirect" was used; bug appeared in 0.1.39.
+       "X-Accel-Redirect" was used; the bug had appeared in 0.1.39.
 
     *) Bugfix: the "Content-Disposition" header line was not transferred 
        when the "X-Accel-Redirect" was used.
@@ -2059,8 +2604,8 @@ Changes with nginx 0.1.39               
        transferred while the 401 response code redirecting.
 
     *) Bugfix: the ngx_http_proxy_module and ngx_http_fastcgi_module may 
-       close a connection before anything was transferred to a client; bug 
-       appeared in 0.1.38.
+       close a connection before anything was transferred to a client; the 
+       bug had appeared in 0.1.38.
 
     *) Workaround: the Linux glibc crypt_r() initialization bug.
 
@@ -2069,17 +2614,17 @@ Changes with nginx 0.1.39               
 
     *) Bugfix: if the backend response had the "Location" header line and 
        nginx should not rewrite this line, then the 500 code response body 
-       was transferred; bug appeared in 0.1.29.
+       was transferred; the bug had appeared in 0.1.29.
 
     *) Bugfix: some directives of the ngx_http_proxy_module and 
        ngx_http_fastcgi_module were not inherited from the server to the 
-       location level; bug appeared in 0.1.29.
+       location level; the bug had appeared in 0.1.29.
 
     *) Bugfix: the ngx_http_ssl_module did not support the certificate 
        chain.
 
     *) Bugfix: the ngx_http_autoindex_module did not show correctly the 
-       long file names; bug appeared in 0.1.38.
+       long file names; the bug had appeared in 0.1.38.
 
     *) Bugfixes in IMAP/POP3 proxy in interaction with a backend at the 
        login state.
@@ -2107,8 +2652,8 @@ Changes with nginx 0.1.38               
        than one remote subrequest.
 
     *) Bugfix: nginx treated the backend response as invalid if the status 
-       line in the header was transferred in two packets; bug appeared in 
-       0.1.29.
+       line in the header was transferred in two packets; the bug had 
+       appeared in 0.1.29.
 
     *) Feature: the "ssi_types" directive.
 
@@ -2162,7 +2707,7 @@ Changes with nginx 0.1.35               
     *) Feature: the "port_in_redirect" directive.
 
     *) Bugfix: the segmentation fault was occurred if the backend response 
-       header was in several packets; bug appeared in 0.1.29.
+       header was in several packets; the bug had appeared in 0.1.29.
 
     *) Bugfix: if more than 10 servers were configured or some server did 
        not use the "listen" directive, then the segmentation fault was 
@@ -2172,7 +2717,8 @@ Changes with nginx 0.1.35               
        bigger than the temporary file.
 
     *) Bugfix: nginx returned the 400 response on requests like 
-       "GET http://www.domain.com/uri HTTP/1.0"; bug appeared in 0.1.28.
+       "GET http://www.domain.com/uri HTTP/1.0"; the bug had appeared in 
+       0.1.28.
 
 
 Changes with nginx 0.1.34                                        26 May 2005
@@ -2192,7 +2738,7 @@ Changes with nginx 0.1.34               
 Changes with nginx 0.1.33                                        23 May 2005
 
     *) Bugfix: nginx could not be built with the --without-pcre parameter; 
-       bug appeared in 0.1.29.
+       the bug had appeared in 0.1.29.
 
     *) Bugfix: 3, 4, 7, and 8 the "proxy_set_header" directives in one 
        level cause the bus fault on start up.
@@ -2206,7 +2752,7 @@ Changes with nginx 0.1.33               
 Changes with nginx 0.1.32                                        19 May 2005
 
     *) Bugfix: the arguments were omitted in the redirects, issued by the 
-       "rewrite" directive; bug appeared in 0.1.29.
+       "rewrite" directive; the bug had appeared in 0.1.29.
 
     *) Feature: the "if" directive supports the captures in regular 
        expressions.
@@ -2227,7 +2773,7 @@ Changes with nginx 0.1.31               
     *) Bugfix: errors while using SSI and gzipping.
 
     *) Bugfix: the redirect with the 301 code was transferred without 
-       response body; bug appeared in 0.1.30.
+       response body; the bug had appeared in 0.1.30.
 
 
 Changes with nginx 0.1.30                                        14 May 2005
@@ -2239,7 +2785,8 @@ Changes with nginx 0.1.30               
 
     *) Bugfix: if the length of the response part received at once from 
        proxied or FastCGI server was equal to 500, then nginx returns the 
-       500 response code; in proxy mode the bug appeared in 0.1.29 only.
+       500 response code; in proxy mode the the bug had appeared in 0.1.29 
+       only.
 
     *) Bugfix: nginx did not consider the directives with 8 or 9 parameters 
        as invalid.
@@ -2318,7 +2865,7 @@ Changes with nginx 0.1.29               
        returned the 408 response.
 
     *) Bugfix: the segmentation fault was occurred if the backend sent an 
-       invalid line in response header; bug appeared in 0.1.26.
+       invalid line in response header; the bug had appeared in 0.1.26.
 
     *) Bugfix: the segmentation fault may occurred in FastCGI fault 
        tolerance configuration.
@@ -2438,7 +2985,7 @@ Changes with nginx 0.1.23               
        server name of the "server_name" directive.
 
     *) Bugfix: nginx could not be built on platforms different from i386, 
-       amd64, sparc É ppc; bug appeared in 0.1.22.
+       amd64, sparc, and ppc; the bug had appeared in 0.1.22.
 
     *) Bugfix: the ngx_http_autoindex_module now shows the information not 
        about the symlink, but about file or directory it points to.
@@ -2453,7 +3000,7 @@ Changes with nginx 0.1.22               
        connections statistics if the proxying or FastCGI server were used.
 
     *) Bugfix: the installation paths were incorrectly quoted on Linux and 
-       Solaris; bug appeared in 0.1.21.
+       Solaris; the bug had appeared in 0.1.21.
 
 
 Changes with nginx 0.1.21                                        22 Feb 2005
@@ -2538,7 +3085,8 @@ Changes with nginx 0.1.17               
        static page, then the segmentation fault occurred.
 
     *) Bugfix: if in a proxied "Location" header was a relative URL, then a 
-       host name and a slash were added to them; bug appeared in 0.1.14.
+       host name and a slash were added to them; the bug had appeared in 
+       0.1.14.
 
     *) Bugfix: the system error message was not logged on Linux.
 
@@ -2563,7 +3111,7 @@ Changes with nginx 0.1.16               
     *) Feature: the rewrite directive supports the arguments rewriting.
 
     *) Bugfix: the response code 400 was returned for the POST request with 
-       the "Content-Length: 0" header; bug appeared in 0.1.14.
+       the "Content-Length: 0" header; the bug had appeared in 0.1.14.
 
 
 Changes with nginx 0.1.15                                        19 Jan 2005
@@ -2584,8 +3132,8 @@ Changes with nginx 0.1.15               
        to use the regular expressions in locations.
 
     *) Bugfix: the directive "proxy_preserve_host  on" adds port 80 to the 
-       "Host" headers, if upstream listen on port 80; bug appeared in 
-       0.1.14.
+       "Host" headers, if upstream listen on port 80; the bug had appeared 
+       in 0.1.14.
 
     *) Bugfix: the same paths in autoconfiguration parameters 
        --http-client-body-temp-path=PATH and --http-proxy-temp-path=PATH, 
@@ -2611,7 +3159,8 @@ Changes with nginx 0.1.14               
        fastcgi_max_temp_file_size, fastcgi_temp_file_write_size, 
        fastcgi_next_upstream, and fastcgi_x_powered_by.
 
-    *) Bugfix: the "[alert] zero size buf" error; bug appeared in 0.1.3.
+    *) Bugfix: the "[alert] zero size buf" error; the bug had appeared in 
+       0.1.3.
 
     *) Change: the URI must be specified after the host name in the 
        proxy_pass directive.
@@ -2696,7 +3245,7 @@ Changes with nginx 0.1.10               
 
     *) Bugfix: if the request without arguments contains "//", "/./", 
        "/../" or "%XX" then the lost character in the request line was 
-       lost; bug appeared in 0.1.9.
+       lost; the bug had appeared in 0.1.9.
 
     *) Bugfix: the fix in 0.1.9 for the files bigger than 2G on Linux did 
        not work.
@@ -2714,7 +3263,8 @@ Changes with nginx 0.1.9                
        does not support sendfile64().
 
     *) Bugfix: while the build configuration on Linux the 
-       --with-poll_module parameter was required; bug appeared in 0.1.8.
+       --with-poll_module parameter was required; the bug had appeared in 
+       0.1.8.
 
 
 Changes with nginx 0.1.8                                         20 Nov 2004
@@ -2730,7 +3280,7 @@ Changes with nginx 0.1.8                
 Changes with nginx 0.1.7                                         12 Nov 2004
 
     *) Bugfix: on FreeBSD the segmentation fault may occur if the size of 
-       the transferred file was changed; bug appeared in 0.1.5.
+       the transferred file was changed; the bug had appeared in 0.1.5.
 
 
 Changes with nginx 0.1.6                                         11 Nov 2004
@@ -2789,13 +3339,13 @@ Changes with nginx 0.1.2                
     *) Bugfix: the portability improvements.
 
     *) Bugfix: if configuration file was set in command line, the 
-       reconfiguration was impossible; bug appeared in 0.1.1.
+       reconfiguration was impossible; the bug had appeared in 0.1.1.
 
     *) Bugfix: proxy module may get caught in an endless loop when sendfile 
        is not used.
 
     *) Bugfix: with sendfile the response was not recoded according to the 
-       charset module directives; bug appeared in 0.1.1.
+       charset module directives; the bug had appeared in 0.1.1.
 
     *) Bugfix: very seldom bug in the kqueue processing.
 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,556 @@
 
+éÚÍÅÎÅÎÉÑ × nginx 0.7.7                                           30.07.2008
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÏÛÉÂËÁ EAGAIN ÐÒÉ ×ÙÚÏ×Å connect() ÎÅ ÓÞÉÔÁÅÔÓÑ 
+       ×ÒÅÍÅÎÎÏÊ.
+
+    *) éÚÍÅÎÅÎÉÅ: ÚÎÁÞÅÎÉÅÍ ÐÅÒÅÍÅÎÎÏÊ $ssl_client_cert ÔÅÐÅÒØ Ñ×ÌÑÅÔÓÑ 
+       ÓÅÒÔÉÆÉËÁÔ, ÐÅÒÅÄ ËÁÖÄÏÊ ÓÔÒÏËÏÊ ËÏÔÏÒÏÇÏ, ËÒÏÍÅ ÐÅÒ×ÏÊ, ×ÓÔÁ×ÌÑÅÔÓÑ 
+       ÓÉÍ×ÏÌ ÔÁÂÕÌÑÃÉÉ; ÎÅÉÚÍÅΣÎÎÙÊ ÓÅÒÔÉÆÉËÁÔ ÄÏÓÔÕÐÅÎ ÞÅÒÅÚ ÐÅÒÅÍÅÎÎÕÀ 
+       $ssl_client_raw_cert.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒ ask ÄÉÒÅËÔÉ×Ù ssl_verify_client.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÕÌÕÞÛÅÎÉÑ × ÏÂÒÁÂÏÔËÅ byte-range.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á directio.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÏÄÄÅÒÖËÁ sendfile() × MacOSX 1.5.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × MacOSX É Cygwin ÐÒÉ ÐÒÏ×ÅÒËÅ location'Ï× ÔÅÐÅÒØ 
+       ÄÅÌÁÅÔÓÑ ÓÒÁ×ÎÅÎÉÅ ÂÅÚ ÕÞ£ÔÁ ÒÅÇÉÓÔÒÁ ÓÉÍ×ÏÌÏ×; ÏÄÎÁËÏ, ÓÒÁ×ÎÅÎÉÅ 
+       ÏÇÒÁÎÉÞÅÎÏ ÔÏÌØËÏ ÏÄÎÏÂÁÊÔÎÙÍÉ locale'ÑÍÉ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÓÏÅÄÉÎÅÎÉÑ ÐÏÞÔÏ×ÏÇÏ ÐÒÏËÓÉ-ÓÅÒ×ÅÒÁ ÚÁ×ÉÓÁÌÉ × ÒÅÖÉÍÅ 
+       SSL, ÅÓÌÉ ÉÓÐÏÌØÚÏ×ÁÌÉÓØ ÍÅÔÏÄÙ select, poll ÉÌÉ /dev/poll.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÛÉÂËÉ ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ËÏÄÉÒÏ×ËÉ UTF-8 × 
+       ngx_http_autoindex_module.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.6                                           07.07.2008
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÅÒÅÍÅÎÎÙÈ × ÄÉÒÅËÔÉ×Å 
+       access_log ×ÓÅÇÄÁ ÐÒÏ×ÅÒÑÅÔÓÑ ÓÕÝÅÓÔ×Ï×ÁÎÉÉ root'Á ÄÌÑ ÚÁÐÒÏÓÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÏÄÕÌØ ngx_http_flv_module ÎÅ ÐÏÄÄÅÒÖÉ×ÁÌ ÎÅÓËÏÌØËÏ 
+       ÚÎÁÞÅÎÉÊ × ÁÒÇÕÍÅÎÔÁÈ ÚÁÐÒÏÓÁ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.5                                           01.07.2008
+
+    *) éÓÐÒÁ×ÌÅÎÉÑ × ÐÏÄÄÅÒÖËÅ ÐÅÒÅÍÅÎÎÙÈ × ÄÉÒÅËÔÉ×Å access_log; ÏÛÉÂËÉ 
+       ÐÏÑ×ÉÌÁÓØ × 0.7.4.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ Ó ÐÁÒÁÍÅÔÒÏÍ 
+       --without-http_gzip_module; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.3.
+       óÐÁÓÉÂÏ ëÉÒÉÌÌÕ ëÏÒÉÎÓËÏÍÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÓÏ×ÍÅÓÔÎÏÍ ÉÓÐÏÌØÚÏ×ÁÎÉÉ sub_filter É SSI ÏÔ×ÅÔÙ 
+       ÍÏÇÌÉ ÐÅÒÅÄÁ×ÁÔØÓÑ ÎÅ×ÅÒÎÏ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.4                                           30.06.2008
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á access_log ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÅÒÅÍÅÎÎÙÅ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á open_log_file_cache.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ËÌÀÞ -g.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÏÄÄÅÒÖËÁ ÓÔÒÏËÉ "Expect" × ÚÁÇÏÌÏ×ËÅ ÚÁÐÒÏÓÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÂÏÌØÛÉÅ ×ËÌÀÞÅÎÉÑ × SSI ÍÏÇÌÉ ÐÅÒÅÄÁ×ÁÌÉÓØ ÎÅ ÐÏÌÎÏÓÔØÀ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.3                                           23.06.2008
+
+    *) éÚÍÅÎÅÎÉÅ: MIME-ÔÉÐ ÄÌÑ ÒÁÓÛÉÒÅÎÉÑ rss ÉÚÍÅΣΠÎÁ 
+       "application/rss+xml".
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÄÉÒÅËÔÉ×Á "gzip_vary on" ×ÙÄÁ£Ô ÓÔÒÏËÕ 
+       "Vary: Accept-Encoding" × ÚÁÇÏÌÏ×ËÅ ÏÔ×ÅÔÁ É ÄÌÑ ÎÅÓÖÁÔÙÈ ÏÔ×ÅÔÏ×.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÒÏÔÏËÏÌÁ "https://" × 
+       ÄÉÒÅËÔÉ×Å rewrite Á×ÔÏÍÁÔÉÞÅÓËÉ ÄÅÌÁÅÔÓÑ ÒÅÄÉÒÅËÔ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á proxy_pass ÎÅ ÒÁÂÏÔÁÌÁ Ó ÐÒÏÔÏËÏÌÏÍ HTTPS; 
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.9.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.2                                           16.06.2008
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ nginx ÐÏÄÄÅÒÖÉ×ÁÅÔ ÛÉÆÒÙ Ó ÏÂÍÅÎÏÍ EDH-ËÌÀÞÁÍÉ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á ssl_dhparam.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÁÑ $ssl_client_cert.
+       óÐÁÓÉÂÏ Manlio Perillo.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÏÓÌÅ ÉÚÍÅÎÅÎÉÑ URI Ó ÐÏÍÏÝØÀ ÄÉÒÅËÔÉ×Ù rewrite nginx 
+       ÎÅ ÉÓËÁÌ ÎÏ×ÙÊ location; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.1.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÂÅÚ ÂÉÂÌÉÏÔÅËÉ PCRE; ÏÛÉÂËÁ 
+       ÐÏÑ×ÉÌÁÓØ × 0.7.1.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÒÅÄÉÒÅËÔÅ ÚÁÐÒÏÓÁ Ë ËÁÔÁÌÏÇÕ Ó ÄÏÂÁ×ÌÅÎÉÅÍ ÓÌÜÛÁ 
+       nginx ÎÅ ÄÏÂÁ×ÌÑÌ ÁÒÇÕÍÅÎÔÙ ÉÚ ÏÒÉÇÉÎÁÌØÎÏÇÏ ÚÁÐÒÏÓÁ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.1                                           26.05.2008
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÐÏÉÓË location'Á ÄÅÌÁÅÔÓÑ Ó ÐÏÍÏÝØÀ ÄÅÒÅ×Á.
+
+    *) éÚÍÅÎÅÎÉÅ: ÄÉÒÅËÔÉ×Á optimize_server_names ÕÐÒÁÚÄÎÅÎÁ × Ó×ÑÚÉ Ó 
+       ÐÏÑ×ÌÅÎÉÅÍ ÄÉÒÅËÔÉ×Ù server_name_in_redirect.
+
+    *) éÚÍÅÎÅÎÉÅ: ÎÅËÏÔÏÒÙÅ ÄÁ×ÎÏ ÕÓÔÁÒÅ×ÛÉÅ ÄÉÒÅËÔÉ×Ù ÂÏÌØÛÅ ÎÅ 
+       ÐÏÄÄÅÒÖÉ×ÁÀÔÓÑ.
+
+    *) éÚÍÅÎÅÎÉÅ: ÐÁÒÁÍÅÔÒ "none" × ÄÉÒÅËÔÉ×Å ssl_session_cache; ÔÅÐÅÒØ 
+       ÜÔÏÔ ÐÁÒÁÍÅÔÒ ÉÓÐÏÌØÚÕÅÔÓÑ ÐÏ ÕÍÏÌÞÁÎÉÀ.
+       óÐÁÓÉÂÏ Rob Mueller.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÒÁÂÏÞÉÅ ÐÒÏÃÅÓÓÙ ÍÏÇÌÉ ÎÅ ÒÅÁÇÉÒÏ×ÁÔØ ÎÁ ÓÉÇÎÁÌÙ 
+       ÐÅÒÅËÏÎÆÉÇÕÒÁÃÉÉ É ÒÏÔÁÃÉÉ ÌÏÇÏ×.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÎÁ ÐÏÓÌÅÄÎÉÈ Fedora 9 Linux.
+       óÐÁÓÉÂÏ Roxis.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.0                                           19.05.2008
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÓÉÍ×ÏÌÙ 0x00-0x1F, '"' É '\' × access_log 
+       ÚÁÐÉÓÙ×ÁÀÔÓÑ × ×ÉÄÅ \xXX.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ nginx ÒÁÚÒÅÛÁÅÔ ÎÅÓËÏÌØËÏ ÓÔÒÏË "Host" × ÚÁÇÏÌÏ×ËÅ 
+       ÚÁÐÒÏÓÁ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á expires ÐÏÄÄÅÒÖÉ×ÁÅÔ ÆÌÁÇ modified.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÙÅ $uid_got É $uid_set ÍÏÖÎÏ ÉÓÐÏÌØÚÏ×ÁÔØ ÎÁ 
+       ÌÀÂÏÊ ÓÔÁÄÉÉ ÏÂÒÁÂÏÔËÉ ÚÁÐÒÏÓÁ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÁÑ $hostname.
+       óÐÁÓÉÂÏ áÎÄÒÅÀ îÉÇÍÁÔÕÌÉÎÕ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÏÄÄÅÒÖËÁ DESTDIR.
+       óÐÁÓÉÂÏ Todd A. Fisher É Andras Voroskoi.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ keepalive ÎÁ Linux × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ 
+       ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.31                                          12.05.2008
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÏÂÒÁÂÁÔÙ×ÁÌ ÏÔ×ÅÔ FastCGI-ÓÅÒ×ÅÒÁ, ÅÓÌÉ ÓÔÒÏËÁ 
+       ÚÁÇÏÌÏ×ËÁ ÏÔ×ÅÔ ÂÙÌÁ × ËÏÎÃÅ ÚÁÐÉÓÉ FastCGI; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 
+       0.6.2.
+       óÐÁÓÉÂÏ óÅÒÇÅÀ óÅÒÏ×Õ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÕÄÁÌÅÎÉÉ ÆÁÊÌÁ É ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÄÉÒÅËÔÉ×Ù 
+       open_file_cache_errors off × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ 
+       segmentation fault.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.30                                          29.04.2008
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ, ÅÓÌÉ ÍÁÓËÅ, ÚÁÄÁÎÎÏÊ × ÄÉÒÅËÔÉ×Å include, ÎÅ 
+       ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ ÎÉ ÏÄÉÎ ÆÁÊÌ, ÔÏ nginx ÎÅ ×ÙÄÁ£Ô ÏÛÉÂËÕ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ×ÒÅÍÑ × ÄÉÒÅËÔÉ×ÁÈ ÍÏÖÎÏ ÚÁÄÁ×ÁÔØ ÂÅÚ ÐÒÏÂÅÌÁ, 
+       ÎÁÐÒÉÍÅÒ, "1h50m".
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÕÔÅÞÅË ÐÁÍÑÔÉ, ÅÓÌÉ ÄÉÒÅËÔÉ×Á ssl_verify_client ÉÍÅÌÁ 
+       ÚÎÁÞÅÎÉÅ on.
+       óÐÁÓÉÂÏ Chavelle Vincent.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á sub_filter ÍÏÇÌÁ ×ÓÔÁ×ÌÑÔØ ÚÁÍÅÎÑÅÍÙÊ ÔÅËÓÔ × 
+       ×Ù×ÏÄ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á error_page ÎÅ ×ÏÓÐÒÉÎÉÍÁÌÁ ÐÁÒÁÍÅÔÒÙ × 
+       ÐÅÒÅÎÁÐÒÁ×ÌÑÅÍÏÍ URI.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÐÒÉ ÓÂÏÒËÅ Ó Cygwin nginx ×ÓÅÇÄÁ ÏÔËÒÙ×ÁÅÔ ÆÁÊÌÙ 
+       × ÂÉÎÁÒÎÏÍ ÒÅÖÉÍÅ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÐÏÄ OpenBSD; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 
+       0.6.15.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.29                                          18.03.2008
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÍÏÄÕÌØ ngx_google_perftools_module.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÏÄÕÌØ ngx_http_perl_module ÎÅ ÓÏÂÉÒÁÌÓÑ ÎÁ 64-ÂÉÔÎÙÈ 
+       ÐÌÁÔÆÏÒÍÁÈ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.27.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.28                                          13.03.2008
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÅÔÏÄ rtsig ÎÅ ÓÏÂÉÒÁÌÓÑ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.27.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.27                                          12.03.2008
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÎÁ Linux 2.6.18+ ÐÏ ÕÍÏÌÞÁÎÉÀ ÎÅ ÓÏÂÉÒÁÅÔÓÑ ÍÅÔÏÄ 
+       rtsig.
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÐÒÉ ÐÅÒÅÎÁÐÒÁ×ÌÅÎÉÉ ÚÁÐÒÏÓÁ × ÉÍÅÎÏ×ÁÎÎÙÊ location 
+       Ó ÐÏÍÏÝØÀ ÄÉÒÅËÔÉ×Ù error_page ÍÅÔÏÄ ÚÁÐÒÏÓÁ ÎÅ ÉÚÍÅÎÑÅÔÓÑ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù resolver É resolver_timeout × SMTP 
+       ÐÒÏËÓÉ-ÓÅÒ×ÅÒÅ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á post_action ÐÏÄÄÅÒÖÉ×ÁÅÔ ÉÍÅÎÏ×ÁÎÎÙÅ 
+       location'Ù.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÐÅÒÅÎÁÐÒÁ×ÌÅÎÉÉ ÚÁÐÒÏÓÁ ÉÚ location'Á c 
+       ÏÂÒÁÂÏÔÞÉËÏÍ proxy, FastCGI ÉÌÉ memcached × ÉÍÅÎÏ×ÁÎÎÙÊ location ÓÏ 
+       ÓÔÁÔÉÞÅÓËÉÍ ÏÂÒÁÂÏÔÞÉËÏÍ × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation 
+       fault.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÂÒÁÕÚÅÒÙ ÎÅ ÐÏ×ÔÏÒÑÌÉ SSL handshake, ÅÓÌÉ ÐÒÉ ÐÅÒ×ÏÍ 
+       handshake ÎÅ ÏËÁÚÁÌÏÓØ ÐÒÁ×ÉÌØÎÏÇÏ ËÌÉÅÎÔÓËÏÇÏ ÓÅÒÔÉÆÉËÁÔÁ. 
+       óÐÁÓÉÂÏ áÌÅËÓÁÎÄÒÕ éÎÀÈÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÐÅÒÅÎÁÐÒÁ×ÌÅÎÉÉ ÏÛÉÂÏË 495-497 Ó ÐÏÍÏÝØÀ ÄÉÒÅËÔÉ×Ù 
+       error_page ÂÅÚ ÉÚÍÅÎÅÎÉÑ ËÏÄÁ ÏÛÉÂËÉ nginx ÐÙÔÁÌÓÑ ×ÙÄÅÌÉÔØ ÏÞÅÎØ 
+       ÍÎÏÇÏ ÐÁÍÑÔÉ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÕÔÅÞËÉ ÐÁÍÑÔÉ × ÄÏÌÇÏÖÉ×ÕÝÉÈ ÎÅÂÕÆÆÅÒÉÚÉÒÏ×ÁÎÎÙÈ 
+       ÓÏÅÄÉÎÅÎÉÑÈ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÕÔÅÞËÉ ÐÁÍÑÔÉ × resolver'Å.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÐÅÒÅÎÁÐÒÁ×ÌÅÎÉÉ ÚÁÐÒÏÓÁ ÉÚ location'Á c 
+       ÏÂÒÁÂÏÔÞÉËÏÍ proxy × ÄÒÕÇÏÊ location Ó ÏÂÒÁÂÏÔÞÉËÏÍ proxy × ÒÁÂÏÞÅÍ 
+       ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation fault.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÛÉÂËÉ × ËÜÛÉÒÏ×ÁÎÉÉ ÐÅÒÅÍÅÎÎÙÈ $proxy_host É 
+       $proxy_port.
+       óÐÁÓÉÂÏ óÅÒÇÅÀ âÏÞÅÎËÏ×Õ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á proxy_pass Ó ÐÅÒÅÍÅÎÎÙÍÉ ÉÓÐÏÌØÚÏ×ÁÌÁ ÐÏÒÔ, 
+       ÏÐÉÓÁÎÎÏÊ × ÄÒÕÇÏÊ ÄÉÒÅËÔÉ×Å proxy_pass ÂÅÚ ÐÅÒÅÍÅÎÎÙÈ, ÎÏ Ó ÔÁËÉÍ 
+       ÖÅ ÉÍÅÎÅÍ ÈÏÓÔÁ.
+       óÐÁÓÉÂÏ óÅÒÇÅÀ âÏÞÅÎËÏ×Õ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ×Ï ×ÒÅÍÑ ÐÅÒÅËÏÎÆÉÇÕÒÁÃÉÉ ÎÁ ÎÅËÏÔÏÒÙÈ 64-ÂÉÔÎÏÍ 
+       ÐÌÁÔÆÏÒÍÁÈ × ÌÏÇ ÚÁÐÉÓÙ×ÁÌÓÑ alert "sendmsg() failed (9: Bad file 
+       descriptor)".
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÐÏ×ÔÏÒÎÏÍ ÉÓÐÏÌØÚÏ×ÁÎÉÉ × SSI ÐÕÓÔÏÇÏ block'Á × 
+       ËÁÞÅÓÔ×Å ÚÁÇÌÕÛËÉ × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation fault.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÛÉÂËÉ ÐÒÉ ËÏÐÉÒÏ×ÁÎÉÉ ÞÁÓÔÉ URI, ÓÏÄÅÒÖÁÝÅÇÏ 
+       ÜËÒÁÎÉÒÏ×ÁÎÎÙÅ ÓÉÍ×ÏÌÙ, × ÁÒÇÕÍÅÎÔÙ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.26                                          11.02.2008
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù proxy_store É fastcgi_store ÎÅ ÐÒÏ×ÅÒÑÌÉ 
+       ÄÌÉÎÕ ÏÔ×ÅÔÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÂÏÌØÛÏÇÏ ÚÎÁÞÅÎÉÑ × ÄÉÒÅËÔÉ×Å expires 
+       × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation fault.
+       óÐÁÓÉÂÏ Joaquin Cuenca Abela.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ×ÅÒÎÏ ÏÐÒÅÄÅÌÑÌ ÄÌÉÎÕ ÓÔÒÏËÉ ËÜÛÁ ÎÁ 
+       Pentium 4.
+       óÐÁÓÉÂÏ Gena Makhomed.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÐÒÏËÓÉÒÏ×ÁÎÎÙÈ ÐÏÄÚÁÐÒÏÓÁÈ É ÐÏÄÚÁÐÒÏÓÁÈ Ë 
+       FastCGI-ÓÅÒ×ÅÒÕ ×ÍÅÓÔÏ ÍÅÔÏÄÁ GET ÉÓÐÏÌØÚÏ×ÁÌÓÑ ÏÒÉÇÉÎÁÌØÎÙÊ ÍÅÔÏÄ 
+       ËÌÉÅÎÔÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÕÔÅÞËÉ ÓÏËÅÔÏ× × ÒÅÖÉÍÅ HTTPS ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ 
+       ÏÔÌÏÖÅÎÎÏÇÏ accept'Á.
+       óÐÁÓÉÂÏ Ben Maurer.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ×ÙÄÁ×ÁÌ ÏÛÉÂÏÞÎÏÅ ÓÏÏÂÝÅÎÉÅ "SSL_shutdown() 
+       failed (SSL: )"; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.23.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ HTTPS ÚÁÐÒÏÓÙ ÍÏÇÌÉ ÚÁ×ÅÒÛÁÔØÓÑ Ó 
+       ÏÛÉÂËÏÊ "bad write retry"; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.23.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.25                                          08.01.2008
+
+    *) éÚÍÅÎÅÎÉÅ: ×ÍÅÓÔÏ ÓÐÅÃÉÁÌØÎÏÇÏ ÐÁÒÁÍÅÔÒÁ "*" × ÄÉÒÅËÔÉ×Å server_name 
+       ÔÅÐÅÒØ ÉÓÐÏÌØÚÕÅÔÓÑ ÄÉÒÅËÔÉ×Á server_name_in_redirect.
+
+    *) éÚÍÅÎÅÎÉÅ: × ËÁÞÅÓÔ×Å ÏÓÎÏ×ÎÏÇÏ ÉÍÅÎÉ × ÄÉÒÅËÔÉ×Å server_name ÔÅÐÅÒØ 
+       ÍÏÖÎÏ ÉÓÐÏÌØÚÏ×ÁÔØ ÉÍÅÎÁ Ó ÍÁÓËÁÍÉ É ÒÅÇÕÌÑÒÎÙÍÉ ×ÙÒÁÖÅÎÉÑÍÉ.
+
+    *) éÚÍÅÎÅÎÉÅ: ÄÉÒÅËÔÉ×Á satisfy_any ÚÁÍÅÎÅÎÁ ÄÉÒÅËÔÉ×ÏÊ satisfy.
+
+    *) éÚÍÅÎÅÎÉÅ: ÐÏÓÌÅ ÐÅÒÅËÏÎÆÉÇÕÒÁÃÉÉ ÓÔÁÒÙÅ ÒÁÂÏÞÉÅ ÐÒÏÃÅÓÓ ÍÏÇÌÉ 
+       ÓÉÌØÎÏ ÎÁÇÒÕÖÁÔØ ÐÒÏÃÅÓÓÏÒ ÐÒÉ ÚÁÐÕÓËÅ ÐÏÄ Linux OpenVZ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á min_delete_depth.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÅÔÏÄÙ COPY É MOVE ÎÅ ÒÁÂÏÔÁÌÉ Ó ÏÄÉÎÏÞÎÙÍÉ ÆÁÊÌÁÍÉ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÏÄÕÌØ ngx_http_gzip_static_module ÎÅ ÐÏÚ×ÏÌÑÌ ÒÁÂÏÔÁÔØ 
+       ÍÏÄÕÌÀ ngx_http_dav_module; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.23.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÕÔÅÞËÉ ÓÏËÅÔÏ× × ÒÅÖÉÍÅ HTTPS ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ 
+       ÏÔÌÏÖÅÎÎÏÇÏ accept'Á.
+       óÐÁÓÉÂÏ Ben Maurer.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÂÅÚ ÂÉÂÌÉÏÔÅËÉ PCRE; ÏÛÉÂËÁ 
+       ÐÏÑ×ÉÌÁÓØ × 0.6.23.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.24                                          27.12.2007
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ HTTPS × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ 
+       ÐÒÏÉÚÏÊÔÉ segmentation fault; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.23.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.23                                          27.12.2007
+
+    *) éÚÍÅÎÅÎÉÅ: ÐÁÒÁÍÅÔÒ "off" × ÄÉÒÅËÔÉ×Å ssl_session_cache; ÔÅÐÅÒØ ÜÔÏÔ 
+       ÐÁÒÁÍÅÔÒ ÉÓÐÏÌØÚÕÅÔÓÑ ÐÏ ÕÍÏÌÞÁÎÉÀ.
+
+    *) éÚÍÅÎÅÎÉÅ: ÄÉÒÅËÔÉ×Á open_file_cache_retest ÐÅÒÅÉÍÅÎÏ×ÁÎÁ × 
+       open_file_cache_valid.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á open_file_cache_min_uses.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÍÏÄÕÌØ ngx_http_gzip_static_module.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á gzip_disable.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Õ memcached_pass ÍÏÖÎÏ ÉÓÐÏÌØÚÏ×ÁÔØ ×ÎÕÔÒÉ ÂÌÏËÁ 
+       if.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ×ÎÕÔÒÉ ÏÄÎÏÇÏ location'Á ÉÓÐÏÌØÚÏ×ÁÌÉÓØ ÄÉÒÅËÔÉ×Ù 
+       "memcached_pass" É "if", ÔÏ × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ 
+       segmentation fault.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÄÉÒÅËÔÉ×Ù satisfy_any on" ÂÙÌÉ 
+       ÚÁÄÁÎÙ ÄÉÒÅËÔÉ×Ù ÎÅ ×ÓÅÈ ÍÏÄÕÌÅÊ ÄÏÓÔÕÐÁ, ÔÏ ÚÁÄÁÎÎÙÅ ÄÉÒÅËÔÉ×Ù ÎÅ 
+       ÐÒÏ×ÅÒÑÌÉÓØ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒÙ, ÚÁÄÁÎÎÙÅ ÒÅÇÕÌÑÒÎÙÍ ×ÙÒÁÖÅÎÉÅÍ × ÄÉÒÅËÔÉ×Å 
+       valid_referers, ÎÅ ÎÁÓÌÅÄÏ×ÁÌÁÓØ Ó ÐÒÅÄÙÄÕÝÅÇÏ ÕÒÏ×ÎÑ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á post_action ÎÅ ÒÁÂÏÔÁÌÁ, ÅÓÌÉ ÚÁÐÒÏÓ 
+       ÚÁ×ÅÒÛÁÌÓÑ Ó ËÏÄÏÍ 499.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÐÔÉÍÉÚÁÃÉÑ ÉÓÐÏÌØÚÏ×ÁÎÉÑ 16K ÂÕÆÅÒÁ ÄÌÑ 
+       SSL-ÓÏÅÄÉÎÅÎÉÑ.
+       óÐÁÓÉÂÏ Ben Maurer.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: STARTTLS × ÒÅÖÉÍÅ SMTP ÎÅ ÒÁÂÏÔÁÌ.
+       óÐÁÓÉÂÏ ïÌÅÇÕ íÏÔÉÅÎËÏ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ HTTPS ÚÁÐÒÏÓÙ ÍÏÇÌÉ ÚÁ×ÅÒÛÁÔØÓÑ Ó 
+       ÏÛÉÂËÏÊ "bad write retry"; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.5.13.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.22                                          19.12.2007
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ×ÓÅ ÍÅÔÏÄÙ ÍÏÄÕÌÑ ngx_http_perl_module ×ÏÚ×ÒÁÝÁÀÔ 
+       ÚÎÁÞÅÎÉÑ, ÓËÏÐÉÒÏ×ÁÎÎÙÅ × ÐÁÍÑÔØ, ×ÙÄÅÌÅÎÎÕÀ perl'ÏÍ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ nginx ÂÙÌ ÓÏÂÒÁÎ Ó ÍÏÄÕÌÅÍ ngx_http_perl_module, 
+       ÉÓÐÏÌØÚÏ×ÁÌÓÑ perl ÄÏ ×ÅÒÓÉÉ 5.8.6 É perl ÐÏÄÄÅÒÖÉ×ÁÌ ÐÏÔÏËÉ, ÔÏ ×Ï 
+       ×ÒÅÍÑ ÐÅÒÅËÏÎÆÉÇÕÒÁÃÉÉ ÏÓÎÏ×ÎÏÊ ÐÒÏÃÅÓÓ Á×ÁÒÉÊÎÏ ×ÙÈÏÄÉÌ; ÏÛÉÂËÁ 
+       ÐÏÑ×ÉÌÁÓØ × 0.5.9.
+       óÐÁÓÉÂÏ âÏÒÉÓÕ öÍÕÒÏ×Õ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÍÅÔÏÄÙ ÍÏÄÕÌÑ ngx_http_perl_module ÍÏÇÌÉ ÐÅÒÅÄÁ×ÁÔØÓÑ 
+       ÎÅ×ÅÒÎÙÅ ÒÅÚÕÌØÔÁÔÙ ×ÙÄÅÌÅÎÉÑ × ÒÅÇÕÌÑÒÎÙÈ ×ÙÒÁÖÅÎÉÑÈ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ÍÅÔÏÄ $r->has_request_body() ×ÙÚÙ×ÁÌÓÑ ÄÌÑ 
+       ÚÁÐÒÏÓÁ, Õ ËÏÔÏÒÏÇÏ ÎÅÂÏÌØÛÏÅ ÔÅÌÏ ÚÁÐÒÏÓÁ ÂÙÌÏ ÕÖÅ ÐÏÌÎÏÓÔØÀ 
+       ÐÏÌÕÞÅÎÏ, ÔÏ × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation fault.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: large_client_header_buffers ÎÅ ÏÓ×ÏÂÏÖÄÁÌÉÓØ ÐÅÒÅÄ 
+       ÐÅÒÅÈÏÄÏÍ × ÓÏÓÔÏÑÎÉÅ keep-alive.
+       óÐÁÓÉÂÏ ïÌÅËÓÁÎÄÒÕ ûÔÅÐÅ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÐÅÒÅÍÅÎÎÏÊ $upstream_addr ÎÅ ÚÁÐÉÓÙ×ÁÌÓÑ ÐÏÓÌÅÄÎÉÊ 
+       ÁÄÒÅÓ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.18.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á fastcgi_catch_stderr ÎÅ ×ÏÚ×ÒÁÝÁÌÁ ÏÛÉÂËÕ; 
+       ÔÅÐÅÒØ ÏÎÁ ×ÏÚ×ÒÁÝÁÅÔ ÏÛÉÂËÕ 502, ËÏÔÏÒÕÀ ÍÏÖÎÏ ÎÁÐÒÁ×ÉÔØ ÎÁ 
+       ÓÌÅÄÕÀÝÉÊ ÓÅÒ×ÅÒ Ó ÐÏÍÏÝØÀ "fastcgi_next_upstream invalid_header".
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÄÉÒÅËÔÉ×Ù fastcgi_catch_stderr × 
+       ÏÓÎÏ×ÎÏÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation fault; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 
+       0.6.10.
+       óÐÁÓÉÂÏ Manlio Perillo.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.21                                          03.12.2007
+
+    *) éÚÍÅÎÅÎÉÅ: ÅÓÌÉ × ÚÎÁÞÅÎÉÑÈ ÐÅÒÅÍÅÎÎÙÈ ÄÉÒÅËÔÉ×Ù proxy_pass 
+       ÉÓÐÏÌØÚÕÀÔÓÑ ÔÏÌØËÏ IP-ÁÄÒÅÓÁ, ÔÏ ÕËÁÚÙ×ÁÔØ resolver ÎÅ ÎÕÖÎÏ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÄÉÒÅËÔÉ×Ù proxy_pass c URI-ÞÁÓÔØÀ × 
+       ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ 
+       × 0.6.19.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ resolver ÉÓÐÏÌØÚÏ×ÁÌÓÑ ÎÁ ÐÌÁÔÆÏÒÍÁÈ, ÎÅ 
+       ÐÏÄÄÅÒÖÉ×ÁÀÝÉÈ ÍÅÔÏÄ kqueue, ÔÏ nginx ×ÙÄÁ×ÁÌ alert "name is out of 
+       response".
+       óÐÁÓÉÂÏ áÎÄÒÅÀ îÉÇÍÁÔÕÌÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ðÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÅÒÅÍÅÎÎÏÊ $server_protocol × 
+       FastCGI-ÐÁÒÁÍÅÔÒÁÈ É ÚÁÐÒÏÓÅ, ÄÌÉÎÁ ËÏÔÏÒÏÇÏ ÂÙÌÁ ÂÌÉÚËÁ Ë ÚÎÁÞÅÎÉÀ 
+       ÄÉÒÅËÔÉ×Ù client_header_buffer_size, nginx ×ÙÄÁ×ÁÌ alert "fastcgi: 
+       the request record is too big".
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÏÂÙÞÎÏÍ ÚÁÐÒÏÓÅ ×ÅÒÓÉÉ HTTP/0.9 Ë HTTPS ÓÅÒ×ÅÒÕ 
+       nginx ×ÏÚ×ÒÁÝÁÌ ÏÂÙÞÎÙÊ ÏÔ×ÅÔ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.20                                          28.11.2007
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÄÉÒÅËÔÉ×Ù proxy_pass c URI-ÞÁÓÔØÀ × 
+       ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ 
+       × 0.6.19.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.19                                          27.11.2007
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ×ÅÒÓÉÑ 0.6.18 ÎÅ ÓÏÂÉÒÁÌÁÓØ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.18                                          27.11.2007
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÍÏÄÕÌØ ngx_http_userid_module × ÐÏÌÅ ËÕËÉ Ó 
+       ÎÏÍÅÒÏÍ ÐÒÏÃÅÓÓÁ ÄÏÂÁ×ÌÑÅÔ ÍÉËÒÏÓÅËÕÎÄÙ ÎÁ ×ÒÅÍÑ ÓÔÁÒÔÁ.
+
+    *) éÚÍÅÎÅÎÉÅ: × error_log ÔÅÐÅÒØ ÚÁÐÉÓÙ×ÁÅÔÓÑ ÐÏÌÎÁÑ ÓÔÒÏËÁ ÚÁÐÒÏÓÁ 
+       ×ÍÅÓÔÏ ÔÏÌØËÏ URI.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á proxy_pass ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÅÒÅÍÅÎÎÙÅ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù resolver É resolver_timeout.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÄÉÒÅËÔÉ×Á "add_header last-modified ''" ÕÄÁÌÑÅÔ × 
+       ÚÁÇÏÌÏ×ËÅ ÏÔ×ÅÔÁ ÓÔÒÏËÕ "Last-Modified".
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á limit_rate ÎÅ ÐÏÚ×ÏÌÑÌÁ ÐÅÒÅÄÁ×ÁÔØ ÎÁ ÐÏÌÎÏÊ 
+       ÓËÏÒÏÓÔÉ, ÄÁÖÅ ÅÓÌÉ ÂÙÌ ÕËÁÚÁÎ ÏÞÅÎØ ÂÏÌØÛÏÊ ÌÉÍÉÔ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.17                                          15.11.2007
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÏÄÄÅÒÖËÁ ÓÔÒÏËÉ "If-Range" × ÚÁÇÏÌÏ×ËÅ ÚÁÐÒÏÓÁ.
+       óÐÁÓÉÂÏ áÌÅËÓÁÎÄÒÕ éÎÀÈÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÄÉÒÅËÔÉ×Ù msie_refresh ÐÏ×ÔÏÒÎÏ 
+       ÜËÒÁÎÉÒÏ×ÁÌÉÓØ ÕÖÅ ÜËÒÁÎÉÒÏ×ÁÎÎÙÅ ÓÉÍ×ÏÌÙ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.4.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á autoindex ÎÅ ÒÁÂÏÔÁÌÁ ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ 
+       "alias /".
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÏÄÚÁÐÒÏÓÏ× × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ 
+       ÐÒÏÉÚÏÊÔÉ segmentation fault.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ SSL É gzip ÂÏÌØÛÉÅ ÏÔ×ÅÔÙ ÍÏÇÌÉ 
+       ÐÅÒÅÄÁ×ÁÔØÓÑ ÎÅ ÐÏÌÎÏÓÔØÀ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ÏÔ×ÅÔ ÐÒÏËÓÉÒÏ×ÁÎÎÏÇÏ ÓÅÒ×ÅÒÁ ÂÙÌ ×ÅÒÓÉÉ HTTP/0.9, 
+       ÔÏ ÐÅÒÅÍÅÎÎÁÑ $status ÂÙÌÁ ÒÁ×ÎÁ 0.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.16                                          29.10.2007
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÎÁ Linux ÉÓÐÏÌØÚÕÅÔÓÑ uname(2) ×ÍÅÓÔÏ procfs.
+       óÐÁÓÉÂÏ éÌØÅ îÏ×ÉËÏ×Õ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ × ÄÉÒÅËÔÉ×Å error_page ÉÓÐÏÌØÚÏ×ÁÌÓÑ ÓÉÍ×ÏÌ "?", 
+       ÔÏ ÏÎ ÜËÒÁÎÉÒÏ×ÁÌÓÑ ÐÒÉ ÐÒÏËÓÉÒÏ×ÁÎÉÉ ÚÁÐÒÏÓÁ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 
+       0.6.11.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÓÏ×ÍÅÓÔÉÍÏÓÔØ Ó mget.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.15                                          22.10.2007
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÓÏ×ÍÅÓÔÉÍÏÓÔØ Ó Cygwin.
+       óÐÁÓÉÂÏ ÷ÌÁÄÉÍÉÒÕ ëÕÔÁËÏ×Õ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á merge_slashes.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á gzip_vary.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á server_tokens.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÒÁÓËÏÄÉÒÏ×ÁÌ URI × ËÏÍÁÎÄÅ SSI include.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÅÒÅÍÅÎÎÏÊ × ÄÉÒÅËÔÉ×ÁÈ charset ÉÌÉ 
+       source_charset ÎÁ ÓÔÁÒÔÅ ÉÌÉ ×Ï ×ÒÅÍÑ ÐÅÒÅËÏÎÆÉÇÕÒÁÃÉÉ ÐÒÏÉÓÈÏÄÉÌ 
+       segmentation fault,
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ×ÏÚ×ÒÁÝÁÌ ÏÛÉÂËÕ 400 ÎÁ ÚÁÐÒÏÓÙ ×ÉÄÁ 
+       "GET http://www.domain.com HTTP/1.0".
+       óÐÁÓÉÂÏ James Oakley.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÏÓÌÅ ÐÅÒÅÎÁÐÒÁ×ÌÅÎÉÑ ÚÁÐÒÏÓÁ Ó ÔÅÌÏÍ ÚÁÐÒÏÓÁ Ó ÐÏÍÏÝØÀ 
+       ÄÉÒÅËÔÉ×Ù error_page nginx ÐÙÔÁÌÓÑ ÓÎÏ×Á ÐÒÏÞÉÔÁÔØ ÔÅÌÏ ÚÁÐÒÏÓÁ; 
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.7.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation fault, ÅÓÌÉ 
+       Õ ÓÅÒ×ÅÒÁ, ÏÂÒÁÂÁÔÙ×ÁÀÝÅÍÕ ÚÁÐÒÏÓ, ÎÅ ÂÙÌ Ñ×ÎÏ ÏÐÒÅÄẠ̊Π
+       server_name; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.7.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.14                                          15.10.2007
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÐÏ ÕÍÏÌÞÁÎÉÀ ËÏÍÁÎÄÁ SSI echo ÉÓÐÏÌØÚÕÅÔ 
+       ËÏÄÉÒÏ×ÁÎÉÅ entity.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒ encoding × ËÏÍÁÎÄÅ SSI echo.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Õ access_log ÍÏÖÎÏ ÉÓÐÏÌØÚÏ×ÁÔØ ×ÎÕÔÒÉ ÂÌÏËÁ 
+       limit_except.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ×ÓÅ ÓÅÒ×ÅÒÁ ÁÐÓÔÒÉÍÁ ÏËÁÚÙ×ÁÌÉÓØ ÎÅÄÏÓÔÕÐÎÙÍÉ, ÔÏ 
+       ÄÏ ×ÏÓÓÔÁÎÏ×ÌÅÎÉÑ ÒÁÂÏÔÏÓÐÏÓÏÂÎÏÓÔÉ Õ ×ÓÅÈ ÓÅÒ×ÅÒÏ× ×ÅÓ ÓÔÁÎÏ×ÉÌÓÑ 
+       ÒÁ×ÎÙÍ ÏÄÎÏÍÕ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.6.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÅÒÅÍÅÎÎÙÈ $date_local É $date_gmt 
+       ×ÎÅ ÍÏÄÕÌÑ ngx_http_ssi_filter_module × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ 
+       segmentation fault.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ×ËÌÀÞ£ÎÎÏÍ ÏÔÌÁÄÏÞÎÏÍ ÌÏÇÅ × ÒÁÂÏÞÅÍ 
+       ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault.
+       óÐÁÓÉÂÏ áÎÄÒÅÀ îÉÇÍÁÔÕÌÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ngx_http_memcached_module ÎÅ ÕÓÔÁÎÁ×ÌÉ×ÁÌ 
+       upstream_response_time.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÒÁÂÏÞÉÊ ÐÒÏÃÅÓÓ ÍÏÇ ÚÁÃÉËÌÉÔØÓÑ ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ 
+       memcached.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÒÁÓÐÏÚÎÁ×ÁÌ ÐÁÒÁÍÅÔÒÙ "close" É "keep-alive" × 
+       ÓÔÒÏËÅ "Connection" × ÚÁÇÏÌÏ×ËÅ ÚÁÐÒÏÓÁ ÔÏÌØËÏ, ÅÓÌÉ ÏÎÉ ÂÙÌÉ × 
+       ÎÉÖÎÅÍ ÒÅÇÉÓÔÒÅ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.11.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: sub_filter ÎÅ ÒÁÂÏÔÁÌ Ó ÐÕÓÔÏÊ ÓÔÒÏËÏÊ ÚÁÍÅÎÙ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÐÁÒÓÉÎÇÅ sub_filter.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.6.13                                          24.09.2007
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÚÁËÒÙ×ÁÌ ÆÁÊÌ ËÁÔÁÌÏÇÁ ÄÌÑ ÚÁÐÒÏÓÁ HEAD, ÅÓÌÉ 
+       ÉÓÐÏÌØÚÏ×ÁÌÓÑ autoindex
+       óÐÁÓÉÂÏ Arkadiusz Patyk.
+
+
 éÚÍÅÎÅÎÉÑ × nginx 0.6.12                                          21.09.2007
 
     *) éÚÍÅÎÅÎÉÅ: ÐÏÞÔÏ×ÙÊ ÐÒÏËÓÉ-ÓÅÒ×ÅÒ ÒÁÚÄẠ̊ΠÎÁ ÔÒÉ ÍÏÄÕÌÑ: pop3, imap 
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2002-2007 Igor Sysoev
+ * Copyright (C) 2002-2008 Igor Sysoev
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
--- a/auto/cc/msvc
+++ b/auto/cc/msvc
@@ -2,7 +2,7 @@
 # Copyright (C) Igor Sysoev
 
 
-# MSVC 6.0 SP2, MSVC Toolkit 2003 (7.1)
+# MSVC 6.0 SP2, MSVC Toolkit 2003 (7.1), MSVC 2005 Express Edition SP1 (8.0)
 
 # optimizations
 
@@ -91,17 +91,17 @@ CORE_LIBS="$CORE_LIBS kernel32.lib user3
 CORE_LINK="$CORE_LINK -subsystem:windows -entry:mainCRTStartup"
 
 # debug
-CFLAGS="$CFLAGS -Yd"
-CORE_LINK="$CORE_LINK -debug -debugtype:coff"
+if [ $NGX_CC_NAME != msvc8 ]; then
+   CFLAGS="$CFLAGS -Zi"
+   CORE_LINK="$CORE_LINK -debug"
+fi
 
 
 # precompiled headers
-if [ $NGX_CC_NAME != msvc7 ]; then
-    CORE_DEPS="$CORE_DEPS $NGX_OBJS/ngx_config.pch"
-    NGX_PCH="$NGX_OBJS/ngx_config.pch"
-    NGX_BUILD_PCH="-Ycngx_config.h -Fp$NGX_OBJS/ngx_config.pch"
-    NGX_USE_PCH="-Yungx_config.h -Fp$NGX_OBJS/ngx_config.pch"
-fi
+CORE_DEPS="$CORE_DEPS $NGX_OBJS/ngx_config.pch"
+NGX_PCH="$NGX_OBJS/ngx_config.pch"
+NGX_BUILD_PCH="-Ycngx_config.h -Fp$NGX_OBJS/ngx_config.pch"
+NGX_USE_PCH="-Yungx_config.h -Fp$NGX_OBJS/ngx_config.pch"
 
 
 # the resource file
--- a/auto/cc/name
+++ b/auto/cc/name
@@ -25,6 +25,13 @@ fi
 
 if [ "$CC" = cl ]; then
     if `$NGX_WINE $CC -v 2>&1 \
+        | grep '^Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14' \
+        >/dev/null 2>&1`; then
+
+        NGX_CC_NAME=msvc8
+        echo " + using Microsoft Visual C++ 8 compiler"
+
+    else if `$NGX_WINE $CC -v 2>&1 \
         | grep '^Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13' \
         >/dev/null 2>&1`; then
 
@@ -35,6 +42,7 @@ if [ "$CC" = cl ]; then
         NGX_CC_NAME=msvc
         echo " + using Microsoft Visual C++ compiler"
     fi
+    fi
 
 else
 if [ "$CC" = wcl386 ]; then
--- a/auto/feature
+++ b/auto/feature
@@ -19,7 +19,9 @@ if test -n "$ngx_feature_name"; then
 fi
 
 if test -n "$ngx_feature_path"; then
-    ngx_feature_inc_path="-I $ngx_feature_path"
+    for ngx_temp in $ngx_feature_path; do
+        ngx_feature_inc_path="$ngx_feature_inc_path -I $ngx_temp"
+    done
 fi
 
 cat << END > $NGX_AUTOTEST.c
--- a/auto/headers
+++ b/auto/headers
@@ -6,3 +6,4 @@ ngx_include="unistd.h";    . auto/includ
 ngx_include="inttypes.h";  . auto/include
 ngx_include="limits.h";    . auto/include
 ngx_include="sys/filio.h"; . auto/include
+ngx_include="crypt.h";     . auto/include
--- a/auto/install
+++ b/auto/install
@@ -17,47 +17,53 @@ fi
 
 cat << END                                                    >> $NGX_MAKEFILE
 
-install:	$NGX_OBJS${ngx_dirsep}nginx${ngx_binext}	\
+install:	$NGX_OBJS${ngx_dirsep}nginx${ngx_binext} \
 		$NGX_INSTALL_PERL_MODULES
-	test -d '$NGX_PREFIX' || mkdir -p '$NGX_PREFIX'
+	test -d '\$(DESTDIR)$NGX_PREFIX' || mkdir -p '\$(DESTDIR)$NGX_PREFIX'
 
-	test -d '`dirname "$NGX_SBIN_PATH"`' \
-		|| mkdir -p '`dirname "$NGX_SBIN_PATH"`'
-	test ! -f '$NGX_SBIN_PATH' || mv '$NGX_SBIN_PATH' '$NGX_SBIN_PATH.old'
-	cp $NGX_OBJS/nginx '$NGX_SBIN_PATH'
+	test -d '\$(DESTDIR)`dirname "$NGX_SBIN_PATH"`' \
+		|| mkdir -p '\$(DESTDIR)`dirname "$NGX_SBIN_PATH"`'
+	test ! -f '\$(DESTDIR)$NGX_SBIN_PATH' \
+		|| mv '\$(DESTDIR)$NGX_SBIN_PATH' \
+			'\$(DESTDIR)$NGX_SBIN_PATH.old'
+	cp $NGX_OBJS/nginx '\$(DESTDIR)$NGX_SBIN_PATH'
 
-	test -d '$NGX_CONF_PREFIX' || mkdir -p '$NGX_CONF_PREFIX'
+	test -d '\$(DESTDIR)$NGX_CONF_PREFIX' \
+		|| mkdir -p '\$(DESTDIR)$NGX_CONF_PREFIX'
 
-	cp conf/koi-win '$NGX_CONF_PREFIX'
-	cp conf/koi-utf '$NGX_CONF_PREFIX'
-	cp conf/win-utf '$NGX_CONF_PREFIX'
+	cp conf/koi-win '\$(DESTDIR)$NGX_CONF_PREFIX'
+	cp conf/koi-utf '\$(DESTDIR)$NGX_CONF_PREFIX'
+	cp conf/win-utf '\$(DESTDIR)$NGX_CONF_PREFIX'
 
-	test -f '$NGX_CONF_PREFIX/mime.types' \
-		|| cp conf/mime.types '$NGX_CONF_PREFIX'
-	cp conf/mime.types '$NGX_CONF_PREFIX/mime.types.default'
+	test -f '\$(DESTDIR)$NGX_CONF_PREFIX/mime.types' \
+		|| cp conf/mime.types '\$(DESTDIR)$NGX_CONF_PREFIX'
+	cp conf/mime.types '\$(DESTDIR)$NGX_CONF_PREFIX/mime.types.default'
 
-	test -f '$NGX_CONF_PREFIX/fastcgi_params' \
-		|| cp conf/fastcgi_params '$NGX_CONF_PREFIX'
-	cp conf/fastcgi_params '$NGX_CONF_PREFIX/fastcgi_params.default'
+	test -f '\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi_params' \
+		|| cp conf/fastcgi_params '\$(DESTDIR)$NGX_CONF_PREFIX'
+	cp conf/fastcgi_params \
+		'\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi_params.default'
 
-	test -f '$NGX_CONF_PATH' || cp conf/nginx.conf '$NGX_CONF_PREFIX'
-	cp conf/nginx.conf '$NGX_CONF_PREFIX/nginx.conf.default'
+	test -f '\$(DESTDIR)$NGX_CONF_PATH' \
+		|| cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PREFIX'
+	cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PREFIX/nginx.conf.default'
 
-	test -d '`dirname "$NGX_PID_PATH"`' \
-		|| mkdir -p '`dirname "$NGX_PID_PATH"`'
+	test -d '\$(DESTDIR)`dirname "$NGX_PID_PATH"`' \
+		|| mkdir -p '\$(DESTDIR)`dirname "$NGX_PID_PATH"`'
 
-	test -d '`dirname "$NGX_HTTP_LOG_PATH"`' || \
-		mkdir -p '`dirname "$NGX_HTTP_LOG_PATH"`'
+	test -d '\$(DESTDIR)`dirname "$NGX_HTTP_LOG_PATH"`' || \
+		mkdir -p '\$(DESTDIR)`dirname "$NGX_HTTP_LOG_PATH"`'
 
-	test -d '$NGX_PREFIX/html' || cp -r html '$NGX_PREFIX'
+	test -d '\$(DESTDIR)$NGX_PREFIX/html' \
+		|| cp -r html '\$(DESTDIR)$NGX_PREFIX'
 END
 
 
-if test -n "$NGX_ERROR_LOG_PATH"; then
+if test -n "\$(DESTDIR)$NGX_ERROR_LOG_PATH"; then
     cat << END                                                >> $NGX_MAKEFILE
 
-	test -d '`dirname "$NGX_ERROR_LOG_PATH"`' || \
-		mkdir -p '`dirname "$NGX_ERROR_LOG_PATH"`'
+	test -d '\$(DESTDIR)`dirname "$NGX_ERROR_LOG_PATH"`' || \
+		mkdir -p '\$(DESTDIR)`dirname "$NGX_ERROR_LOG_PATH"`'
 END
 
 fi
--- a/auto/lib/conf
+++ b/auto/lib/conf
@@ -16,6 +16,7 @@ if [ $USE_MD5 = YES ]; then
         have=NGX_HAVE_OPENSSL_MD5_H . auto/have
         have=NGX_OPENSSL_MD5 . auto/have
         MD5=YES
+        MD5_LIB=OpenSSL
 
     else
         . auto/lib/md5/conf
@@ -28,6 +29,7 @@ if [ $USE_SHA1 = YES ]; then
     if [ $OPENSSL != NONE -a $OPENSSL != NO ]; then
         have=NGX_HAVE_OPENSSL_SHA1_H . auto/have
         SHA1=YES
+        SHA1_LIB=OpenSSL
 
     else
         . auto/lib/sha1/conf
@@ -39,6 +41,14 @@ if [ $USE_ZLIB = YES ]; then
     . auto/lib/zlib/conf
 fi
 
+if [ $USE_LIBXSLT = YES ]; then
+    . auto/lib/libxslt/conf
+fi
+
 if [ $USE_PERL = YES ]; then
     . auto/lib/perl/conf
 fi
+
+if [ $NGX_GOOGLE_PERFTOOLS = YES ]; then
+    . auto/lib/google-perftools/conf
+fi
new file mode 100644
--- /dev/null
+++ b/auto/lib/google-perftools/conf
@@ -0,0 +1,33 @@
+
+# Copyright (C) Igor Sysoev
+
+
+    ngx_feature="Google perftools"
+    ngx_feature_name=
+    ngx_feature_run=no
+    ngx_feature_incs=
+    ngx_feature_path=
+    ngx_feature_libs="-lprofiler"
+    ngx_feature_test="ProfilerStop()"
+    . auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+    # FreeBSD port
+
+    ngx_feature="Google perftools in /usr/local/"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lprofiler"
+    else
+        ngx_feature_libs="-L/usr/local/lib -lprofiler"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+    CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+fi
new file mode 100644
--- /dev/null
+++ b/auto/lib/libxslt/conf
@@ -0,0 +1,78 @@
+
+# Copyright (C) Igor Sysoev
+
+
+    ngx_feature="libxslt"
+    ngx_feature_name=
+    ngx_feature_run=no
+    ngx_feature_incs="#include <libxml/parser.h>
+                      #include <libxml/tree.h>
+                      #include <libxslt/xslt.h>
+                      #include <libxslt/xsltInternals.h>
+                      #include <libxslt/transform.h>
+                      #include <libxslt/xsltutils.h>"
+    ngx_feature_path="/usr/include/libxml2"
+    ngx_feature_libs="-lxml2 -lxslt"
+    ngx_feature_test="xmlParserCtxtPtr    ctxt = NULL;
+                      xsltStylesheetPtr   sheet = NULL;
+                      xmlDocPtr           doc;
+                      doc = xmlParseChunk(ctxt, NULL, 0, 0);
+                      xsltApplyStylesheet(sheet, doc, NULL);"
+    . auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+    # FreeBSD port
+
+    ngx_feature="libxslt in /usr/local/"
+    ngx_feature_path="/usr/local/include/libxml2 /usr/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lxml2 -lxslt"
+    else
+        ngx_feature_libs="-L/usr/local/lib -lxml2 -lxslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # NetBSD port
+
+    ngx_feature="libxslt in /usr/pkg/"
+    ngx_feature_path="/usr/pkg/include/libxml2 /usr/pkg/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lxml2 -lxslt"
+    else
+        ngx_feature_libs="-L/usr/pkg/lib -lxml2 -lxslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # MacPorts
+
+    ngx_feature="libxslt in /opt/local/"
+    ngx_feature_path="/opt/local/include/libxml2 /opt/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lxml2 -lxslt"
+    else
+        ngx_feature_libs="-L/opt/local/lib -lxml2 -lxslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+    CORE_INCS="$CORE_INCS $ngx_feature_path"
+    CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+fi
--- a/auto/lib/md5/conf
+++ b/auto/lib/md5/conf
@@ -45,6 +45,7 @@ if [ $MD5 != NONE ]; then
 else
 
     if [ "$NGX_PLATFORM" != win32 ]; then
+
         MD5=NO
 
         # Solaris 8/9
@@ -58,55 +59,43 @@ else
         ngx_feature_test="MD5_CTX md5; MD5Init(&md5)"
         . auto/feature
 
-        if [ $ngx_found = yes ]; then
-            CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
-            MD5=YES
-            MD5_LIB=md5
-            ngx_found=no
+        ngx_md5_lib="system md5"
 
-        else
+        if [ $ngx_found = no ]; then
+
             # FreeBSD
 
             ngx_feature="rsaref md library"
-            ngx_feature_name=
-            ngx_feature_run=no
-            ngx_feature_incs="#include <md5.h>"
-            ngx_feature_path=
             ngx_feature_libs="-lmd"
-            ngx_feature_test="MD5_CTX md5; MD5Init(&md5)"
             . auto/feature
+
+            ngx_md5_lib="system md"
         fi
 
+        if [ $ngx_found = no ]; then
+
+            # OpenSSL crypto library
+
+            ngx_feature="OpenSSL md5 crypto library"
+            ngx_feature_name="NGX_OPENSSL_MD5"
+            ngx_feature_incs="#include <openssl/md5.h>"
+            ngx_feature_libs="-lcrypto"
+            ngx_feature_test="MD5_CTX md5; MD5_Init(&md5)"
+            . auto/feature
+
+            ngx_md5_lib="system crypto"
+
+            if [ $ngx_found = yes ]; then
+                have=NGX_HAVE_OPENSSL_MD5_H . auto/have
+            fi
+        fi
 
         if [ $ngx_found = yes ]; then
             CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
             MD5=YES
-            MD5_LIB=md
-            ngx_found=no
-
-        else
-            if [ $MD5 = NO ]; then
-
-                # OpenSSL crypto library
-
-                ngx_feature="OpenSSL md5 crypto library"
-                ngx_feature_name="NGX_OPENSSL_MD5"
-                ngx_feature_run=no
-                ngx_feature_incs="#include <openssl/md5.h>"
-                ngx_feature_path=
-                ngx_feature_libs="-lcrypto"
-                ngx_feature_test="MD5_CTX md5; MD5_Init(&md5)"
-                . auto/feature
-            fi
+            MD5_LIB=$ngx_md5_lib
         fi
 
-
-        if [ $ngx_found = yes ]; then
-            have=NGX_HAVE_OPENSSL_MD5_H . auto/have
-            CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
-            MD5=YES
-            MD5_LIB=crypto
-        fi
     fi
 
 fi
--- a/auto/lib/pcre/conf
+++ b/auto/lib/pcre/conf
@@ -84,6 +84,7 @@ if [ $PCRE != NONE ]; then
 else
 
     if [ "$NGX_PLATFORM" != win32 ]; then
+
         PCRE=NO
 
         ngx_feature="PCRE library"
@@ -95,20 +96,11 @@ else
         ngx_feature_test="pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL)"
         . auto/feature
 
-        if [ $ngx_found = yes ]; then
-            CORE_DEPS="$CORE_DEPS $REGEX_DEPS"
-            CORE_SRCS="$CORE_SRCS $REGEX_SRCS"
-            CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
-            PCRE=YES
-            ngx_found=no
+        if [ $ngx_found = no ]; then
 
-        else
             # FreeBSD port
 
             ngx_feature="PCRE library in /usr/local/"
-            ngx_feature_name="NGX_PCRE"
-            ngx_feature_run=no
-            ngx_feature_incs="#include <pcre.h>"
             ngx_feature_path="/usr/local/include"
 
             if [ $NGX_RPATH = YES ]; then
@@ -117,8 +109,49 @@ else
                 ngx_feature_libs="-L/usr/local/lib -lpcre"
             fi
 
-            ngx_feature_test="pcre *re;
-                              re = pcre_compile(NULL, 0, NULL, 0, NULL)"
+            . auto/feature
+        fi
+
+        if [ $ngx_found = no ]; then
+
+            # RedHat RPM, Solaris package
+
+            ngx_feature="PCRE library in /usr/include/pcre/"
+            ngx_feature_path="/usr/include/pcre"
+            ngx_feature_libs="-lpcre"
+
+            . auto/feature
+        fi
+
+        if [ $ngx_found = no ]; then
+
+            # NetBSD port
+
+            ngx_feature="PCRE library in /usr/pkg/"
+            ngx_feature_path="/usr/pkg/include"
+
+            if [ $NGX_RPATH = YES ]; then
+                ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lpcre"
+            else
+                ngx_feature_libs="-L/usr/pkg/lib -lpcre"
+            fi
+
+            . auto/feature
+        fi
+
+        if [ $ngx_found = no ]; then
+
+            # MacPorts
+
+            ngx_feature="PCRE library in /opt/local/"
+            ngx_feature_path="/opt/local/include"
+
+            if [ $NGX_RPATH = YES ]; then
+                ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lpcre"
+            else
+                ngx_feature_libs="-L/opt/local/lib -lpcre"
+            fi
+
             . auto/feature
         fi
 
@@ -128,94 +161,6 @@ else
             CORE_INCS="$CORE_INCS $ngx_feature_path"
             CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
             PCRE=YES
-            ngx_found=no
-
-        else
-            # Linux package
-
-            if [ $PCRE = NO ]; then
-
-                ngx_feature="PCRE library in /usr/include/pcre/"
-                ngx_feature_name="NGX_PCRE"
-                ngx_feature_run=no
-                ngx_feature_incs="#include <pcre.h>"
-                ngx_feature_path="/usr/include/pcre"
-                ngx_feature_libs="-lpcre"
-                ngx_feature_test="pcre *re;
-                                  re = pcre_compile(NULL, 0, NULL, 0, NULL)"
-                . auto/feature
-            fi
-        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
-            ngx_found=no
-
-        else
-            # NetBSD port
-
-            if [ $PCRE = NO ]; then
-
-                ngx_feature="PCRE library in /usr/pkg/"
-                ngx_feature_name="NGX_PCRE"
-                ngx_feature_run=no
-                ngx_feature_incs="#include <pcre.h>"
-                ngx_feature_path="/usr/pkg/include"
-
-                if [ $NGX_RPATH = YES ]; then
-                    ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lpcre"
-                else
-                    ngx_feature_libs="-L/usr/pkg/lib -lpcre"
-                fi
-
-                ngx_feature_test="pcre *re;
-                                  re = pcre_compile(NULL, 0, NULL, 0, NULL)"
-                . auto/feature
-            fi
-        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
-            ngx_found=no
-
-        else
-            # MacPorts
-
-            if [ $PCRE = NO ]; then
-
-                ngx_feature="PCRE library in /opt/local/"
-                ngx_feature_name="NGX_PCRE"
-                ngx_feature_run=no
-                ngx_feature_incs="#include <pcre.h>"
-                ngx_feature_path="/opt/local/include"
-
-                if [ $NGX_RPATH = YES ]; then
-                    ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lpcre"
-                else
-                    ngx_feature_libs="-L/opt/local/lib -lpcre"
-                fi
-
-                ngx_feature_test="pcre *re;
-                                  re = pcre_compile(NULL, 0, NULL, 0, NULL)"
-                . auto/feature
-            fi
-        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
-            ngx_found=no
         fi
 
     fi
--- a/auto/lib/sha1/conf
+++ b/auto/lib/sha1/conf
@@ -35,6 +35,7 @@ if [ $SHA1 != NONE ]; then
 else
 
     if [ "$NGX_PLATFORM" != win32 ]; then
+
         SHA1=NO
 
         # FreeBSD
@@ -48,35 +49,28 @@ else
         ngx_feature_test="SHA_CTX sha1; SHA1_Init(&sha1)"
         . auto/feature
 
+        ngx_sha1_lib="system md"
+
+        if [ $ngx_found = no ]; then
+
+            # OpenSSL crypto library
+
+            ngx_feature="OpenSSL sha1 crypto library"
+            ngx_feature_incs="#include <openssl/sha.h>"
+            ngx_feature_libs="-lcrypto"
+            . auto/feature
+
+            ngx_sha1_lib="system crypto"
+
+            if [ $ngx_found = yes ]; then
+                have=NGX_HAVE_OPENSSL_SHA1_H . auto/have
+            fi
+        fi
 
         if [ $ngx_found = yes ]; then
             CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
             SHA1=YES
-            SHA1_LIB=md
-            ngx_found=no
-
-        else
-            if [ $SHA1 = NO ]; then
-
-                # OpenSSL crypto library
-
-                ngx_feature="OpenSSL sha1 crypto library"
-                ngx_feature_name=
-                ngx_feature_run=no
-                ngx_feature_incs="#include <openssl/sha.h>"
-                ngx_feature_path=
-                ngx_feature_libs="-lcrypto"
-                ngx_feature_test="SHA_CTX sha1; SHA1_Init(&sha1)"
-                . auto/feature
-            fi
-        fi
-
-
-        if [ $ngx_found = yes ]; then
-            have=NGX_HAVE_OPENSSL_SHA1_H . auto/have
-            CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
-            SHA1=YES
-            SHA1_LIB=crypto
+            SHA1_LIB=$ngx_sha1_lib
         fi
     fi
 
--- a/auto/make
+++ b/auto/make
@@ -6,7 +6,8 @@ mkdir -p $NGX_OBJS/src/core $NGX_OBJS/sr
          $NGX_OBJS/src/os/unix $NGX_OBJS/src/os/win32 \
          $NGX_OBJS/src/http $NGX_OBJS/src/http/modules \
 	 $NGX_OBJS/src/http/modules/perl \
-         $NGX_OBJS/src/mail
+         $NGX_OBJS/src/mail \
+         $NGX_OBJS/src/misc
 
 
 ngx_objs_dir=$NGX_OBJS$ngx_regex_dirsep
@@ -127,6 +128,9 @@ END
 fi
 
 
+ngx_all_srcs="$ngx_all_srcs $NGX_MISC_SRCS"
+
+
 if test -n "$NGX_ADDON_SRCS"; then
 
 cat << END                                                >> $NGX_MAKEFILE
@@ -309,6 +313,32 @@ END
 fi
 
 
+# the misc sources
+
+if test -n "$NGX_MISC_SRCS"; then
+
+    ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) $ngx_use_pch \$(ALL_INCS)"
+
+    for ngx_src in $NGX_MISC_SRCS
+    do
+        ngx_src=`echo $ngx_src | sed -e "s/\//$ngx_regex_dirsep/g"`
+        ngx_obj=`echo $ngx_src \
+            | sed -e "s#^\(.*\.\)cpp\\$#$ngx_objs_dir\1$ngx_objext#g" \
+                  -e "s#^\(.*\.\)cc\\$#$ngx_objs_dir\1$ngx_objext#g" \
+                  -e "s#^\(.*\.\)c\\$#$ngx_objs_dir\1$ngx_objext#g" \
+                  -e "s#^\(.*\.\)S\\$#$ngx_objs_dir\1$ngx_objext#g"`
+
+        cat << END                                            >> $NGX_MAKEFILE
+
+$ngx_obj:	\$(CORE_DEPS) $ngx_cont$ngx_src
+	$ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX
+
+END
+     done
+
+fi
+
+
 # the addons sources
 
 if test -n "$NGX_ADDON_SRCS"; then
--- a/auto/modules
+++ b/auto/modules
@@ -76,6 +76,9 @@ fi
 
 
 # the module order is important
+#     ngx_http_static_module
+#     ngx_http_gzip_static_module
+#     ngx_http_dav_module
 #     ngx_http_autoindex_module
 #     ngx_http_index_module
 #
@@ -92,6 +95,8 @@ fi
 #     ngx_http_postpone_filter
 #     ngx_http_charset_filter
 #     ngx_http_ssi_filter
+#         ngx_http_xslt_filter
+#         ngx_http_sub_filter
 #         ngx_http_addition_filter
 #         ngx_http_userid_filter
 #         ngx_http_headers_filter
@@ -113,7 +118,7 @@ fi
 
 if [ $HTTP_POSTPONE = YES ]; then
     HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_POSTPONE_FILTER_MODULE"
-    HTTP_SRCS="$HTTP_SRCS $HTPP_POSTPONE_FILTER_SRCS"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_POSTPONE_FILTER_SRCS"
 fi
 
 if [ $HTTP_CHARSET = YES ]; then
@@ -129,6 +134,12 @@ if [ $HTTP_SSI = YES ]; then
     HTTP_SRCS="$HTTP_SRCS $HTTP_SSI_SRCS"
 fi
 
+if [ $HTTP_XSLT = YES ]; then
+    USE_LIBXSLT=YES
+    HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_XSLT_FILTER_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_XSLT_SRCS"
+fi
+
 if [ $HTTP_SUB = YES ]; then
     HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SUB_FILTER_MODULE"
     HTTP_SRCS="$HTTP_SRCS $HTTP_SUB_SRCS"
@@ -146,6 +157,12 @@ fi
 
 HTTP_MODULES="$HTTP_MODULES $HTTP_STATIC_MODULE"
 
+if [ $HTTP_GZIP_STATIC = YES ]; then
+    have=NGX_HTTP_GZIP . auto/have
+    HTTP_MODULES="$HTTP_MODULES $HTTP_GZIP_STATIC_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_GZIP_STATIC_SRCS"
+fi
+
 if [ $HTTP_DAV = YES ]; then
     have=NGX_HTTP_DAV . auto/have
     HTTP_MODULES="$HTTP_MODULES $HTTP_DAV_MODULE"
@@ -272,8 +289,8 @@ fi
 
 # STUB
 #USE_MD5=YES
-#HTTP_SRCS="$HTTP_SRCS $HTPP_CACHE_SRCS"
-#HTTP_SRCS="$HTTP_SRCS $HTPP_FILE_CACHE_SRCS"
+#HTTP_SRCS="$HTTP_SRCS $HTTP_CACHE_SRCS"
+#HTTP_SRCS="$HTTP_SRCS $HTTP_FILE_CACHE_SRCS"
 
 if [ $HTTP_STUB_STATUS = YES ]; then
     have=NGX_STAT_STUB . auto/have
@@ -371,6 +388,12 @@ if [ $MAIL = YES ]; then
 fi
 
 
+if [ $NGX_GOOGLE_PERFTOOLS = YES ]; then
+    modules="$modules $NGX_GOOGLE_PERFTOOLS_MODULE"
+    NGX_MISC_SRCS="$NGX_MISC_SRCS $NGX_GOOGLE_PERFTOOLS_SRCS"
+fi
+
+
 cat << END                                    > $NGX_MODULES_C
 
 #include <ngx_config.h>
--- a/auto/options
+++ b/auto/options
@@ -56,6 +56,7 @@ HTTP_SSL=NO
 HTTP_SSI=YES
 HTTP_POSTPONE=NO
 HTTP_REALIP=NO
+HTTP_XSLT=NO
 HTTP_SUB=NO
 HTTP_ADDITION=NO
 HTTP_DAV=NO
@@ -76,6 +77,7 @@ HTTP_LIMIT_ZONE=YES
 HTTP_EMPTY_GIF=YES
 HTTP_BROWSER=YES
 HTTP_FLV=NO
+HTTP_GZIP_STATIC=NO
 HTTP_UPSTREAM_IP_HASH=YES
 
 # STUB
@@ -114,6 +116,10 @@ ZLIB_ASM=NO
 USE_PERL=NO
 NGX_PERL=perl
 
+USE_LIBXSLT=NO
+
+NGX_GOOGLE_PERFTOOLS=NO
+
 NGX_CPU_CACHE_LINE=
 
 
@@ -147,8 +153,8 @@ do
         --without-poll_module)           EVENT_POLL=NONE            ;;
         --with-aio_module)               EVENT_AIO=YES              ;;
 
-        --with-threads=*)                USE_THREADS="$value"       ;;
-        --with-threads)                  USE_THREADS="pthreads"     ;;
+        #--with-threads=*)                USE_THREADS="$value"       ;;
+        #--with-threads)                  USE_THREADS="pthreads"     ;;
 
         --without-http)                  HTTP=NO                    ;;
         --http-log-path=*)               NGX_HTTP_LOG_PATH="$value" ;;
@@ -159,9 +165,11 @@ do
         --with-http_ssl_module)          HTTP_SSL=YES               ;;
         --with-http_realip_module)       HTTP_REALIP=YES            ;;
         --with-http_addition_module)     HTTP_ADDITION=YES          ;;
+        --with-http_xslt_module)         HTTP_XSLT=YES              ;;
         --with-http_sub_module)          HTTP_SUB=YES               ;;
         --with-http_dav_module)          HTTP_DAV=YES               ;;
         --with-http_flv_module)          HTTP_FLV=YES               ;;
+        --with-http_gzip_static_module)  HTTP_GZIP_STATIC=YES       ;;
 
         --without-http_charset_module)   HTTP_CHARSET=NO            ;;
         --without-http_gzip_module)      HTTP_GZIP=NO               ;;
@@ -199,6 +207,8 @@ do
         --without-mail_imap_module)      MAIL_IMAP=NO               ;;
         --without-mail_smtp_module)      MAIL_SMTP=NO               ;;
 
+        --with-google_perftools_module)  NGX_GOOGLE_PERFTOOLS=YES   ;;
+
         --add-module=*)                  NGX_ADDONS="$NGX_ADDONS $value" ;;
 
         --with-cc=*)                     CC="$value"                ;;
@@ -270,9 +280,11 @@ cat << END
   --with-http_ssl_module             enable ngx_http_ssl_module
   --with-http_realip_module          enable ngx_http_realip_module
   --with-http_addition_module        enable ngx_http_addition_module
+  --with-http_xslt_module            enable ngx_http_xslt_module
   --with-http_sub_module             enable ngx_http_sub_module
   --with-http_dav_module             enable ngx_http_dav_module
   --with-http_flv_module             enable ngx_http_flv_module
+  --with-http_gzip_static_module     enable ngx_http_gzip_static_module
   --with-http_stub_status_module     enable ngx_http_stub_status_module
 
   --without-http_charset_module      disable ngx_http_charset_module
@@ -308,8 +320,13 @@ cat << END
 
   --without-http                     disable HTTP server
 
-  --with-mail                        enable IMAP4/POP3/SMTP proxy module
+  --with-mail                        enable POP3/IMAP4/SMTP proxy module
   --with-mail_ssl_module             enable ngx_mail_ssl_module
+  --without-mail_pop3_module         disable ngx_mail_pop3_module
+  --without-mail_imap_module         disable ngx_mail_imap_module
+  --without-mail_smtp_module         disable ngx_mail_smtp_module
+
+  --with-google_perftools_module     enable ngx_google_perftools_module
 
   --add-module=PATH                  enable an external module
 
--- a/auto/os/conf
+++ b/auto/os/conf
@@ -18,6 +18,10 @@ case "$NGX_PLATFORM" in
         . auto/os/solaris
     ;;
 
+    Darwin:*)
+        . auto/os/darwin
+    ;;
+
     win32)
         . auto/os/win32
     ;;
@@ -36,24 +40,6 @@ case "$NGX_PLATFORM" in
 '
     ;;
 
-    Darwin:*)
-        have=NGX_DARWIN . auto/have_headers
-        have=NGX_HAVE_INHERITED_NONBLOCK . auto/have
-        CORE_INCS="$UNIX_INCS"
-        CORE_DEPS="$UNIX_DEPS $POSIX_DEPS"
-        CORE_SRCS="$UNIX_SRCS"
-
-        ngx_feature="atomic(3)"
-        ngx_feature_name=NGX_DARWIN_ATOMIC
-        ngx_feature_run=no
-        ngx_feature_incs="#include <libkern/OSAtomic.h>"
-        ngx_feature_path=
-        ngx_feature_libs=
-        ngx_feature_test="int32_t  lock, n;
-                          n = OSAtomicCompareAndSwap32Barrier(0, 1, lock)"
-        . auto/feature
-    ;;
-
     HP-UX:*)
         # HP/UX
         have=NGX_HPUX . auto/have_headers
new file mode 100644
--- /dev/null
+++ b/auto/os/darwin
@@ -0,0 +1,115 @@
+
+# Copyright (C) Igor Sysoev
+
+
+have=NGX_DARWIN . auto/have_headers
+
+CORE_INCS="$UNIX_INCS"
+CORE_DEPS="$UNIX_DEPS $DARWIN_DEPS"
+CORE_SRCS="$UNIX_SRCS $DARWIN_SRCS"
+
+
+
+ngx_spacer='
+'
+
+# kqueue
+
+echo " + kqueue found"
+have=NGX_HAVE_KQUEUE . auto/have
+have=NGX_HAVE_CLEAR_EVENT . auto/have
+EVENT_MODULES="$EVENT_MODULES $KQUEUE_MODULE"
+CORE_SRCS="$CORE_SRCS $KQUEUE_SRCS"
+EVENT_FOUND=YES
+NGX_KQUEUE_CHECKED=YES
+
+ngx_feature="kqueue's EVFILT_TIMER"
+ngx_feature_name="NGX_HAVE_TIMER_EVENT"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/event.h>
+                  #include <sys/time.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int      kq;
+                  struct kevent    kev;
+                  struct timespec  ts;
+
+                  if ((kq = kqueue()) == -1) return 1;
+
+                  kev.ident = 0;
+                  kev.filter = EVFILT_TIMER;
+                  kev.flags = EV_ADD|EV_ENABLE;
+                  kev.fflags = 0;
+                  kev.data = 1000;
+                  kev.udata = 0;
+
+                  ts.tv_sec = 0;
+                  ts.tv_nsec = 0;
+
+                  if (kevent(kq, &kev, 1, &kev, 1, &ts) == -1) return 1;
+
+                  if (kev.flags & EV_ERROR) return 1;"
+
+. auto/feature
+
+
+ngx_feature="Darwin 64-bit kqueue millisecond timeout bug"
+ngx_feature_name=NGX_DARWIN_KEVENT_BUG
+ngx_feature_run=bug
+ngx_feature_incs="#include <sys/event.h>
+                  #include <sys/time.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int  kq;
+                  struct kevent    kev;
+                  struct timespec  ts;
+                  struct timeval   tv, tv0;
+
+                  kq = kqueue();
+
+                  ts.tv_sec = 0;
+                  ts.tv_nsec = 999000000;
+
+                  gettimeofday(&tv, 0);
+                  kevent(kq, NULL, 0, &kev, 1, &ts);
+                  gettimeofday(&tv0, 0);
+                  timersub(&tv0, &tv, &tv);
+
+                  if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;"
+
+. auto/feature
+
+
+# sendfile()
+
+CC_AUX_FLAGS="$CC_AUX_FLAGS"
+ngx_feature="sendfile()"
+ngx_feature_name="NGX_HAVE_SENDFILE"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/types.h>
+                  #include <sys/socket.h>
+                  #include <sys/uio.h>
+                  #include <sys/errno.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int s = 0, fd = 1;
+                  off_t n; off_t off = 0;
+                  n = sendfile(s, fd, off, &n, NULL, 0);
+                  if (n == -1 && errno == ENOSYS) return 1"
+. auto/feature
+
+if [ $ngx_found = yes ]; then
+    have=NGX_HAVE_SENDFILE . auto/have
+    CORE_SRCS="$CORE_SRCS $DARWIN_SENDFILE_SRCS"
+fi
+
+
+ngx_feature="atomic(3)"
+ngx_feature_name=NGX_DARWIN_ATOMIC
+ngx_feature_run=no
+ngx_feature_incs="#include <libkern/OSAtomic.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int32_t  lock, n;
+                  n = OSAtomicCompareAndSwap32Barrier(0, 1, lock)"
+. auto/feature
--- a/auto/os/features
+++ b/auto/os/features
@@ -122,36 +122,6 @@ if test -z "$NGX_KQUEUE_CHECKED"; then
                   if (kev.flags & EV_ERROR) return 1;"
 
         . auto/feature
-
-
-        if [ "$NGX_SYSTEM" = "Darwin" ]; then
-
-            ngx_feature="Darwin 64-bit kqueue millisecond timeout bug"
-            ngx_feature_name=NGX_DARWIN_KEVENT_BUG
-            ngx_feature_run=bug
-            ngx_feature_incs="#include <sys/event.h>
-#include <sys/time.h>"
-            ngx_feature_path=
-            ngx_feature_libs=
-            ngx_feature_test="int  kq;
-                  struct kevent    kev;
-                  struct timespec  ts;
-                  struct timeval   tv, tv0;
-
-                  kq = kqueue();
-
-                  ts.tv_sec = 0;
-                  ts.tv_nsec = 999000000;
-
-                  gettimeofday(&tv, 0);
-                  kevent(kq, NULL, 0, &kev, 1, &ts);
-                  gettimeofday(&tv0, 0);
-                  timersub(&tv0, &tv, &tv);
-
-                  if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;"
-
-            . auto/feature
-        fi
     fi
 fi
 
@@ -200,3 +170,34 @@ if [ $ngx_found = no ]; then
         CRYPT_LIB="-lcrypt"
     fi
 fi
+
+
+ngx_feature="O_DIRECT"
+ngx_feature_name="NGX_HAVE_O_DIRECT"
+ngx_feature_run=no
+ngx_feature_incs="#include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="fcntl(0, F_SETFL, O_DIRECT);"
+. auto/feature
+
+
+ngx_feature="F_NOCACHE"
+ngx_feature_name="NGX_HAVE_F_NOCACHE"
+ngx_feature_run=no
+ngx_feature_incs="#include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="fcntl(0, F_NOCACHE, 1);"
+. auto/feature
+
+
+ngx_feature="directio()"
+ngx_feature_name="NGX_HAVE_DIRECTIO"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/types.h>
+                  #include <sys/fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="directio(0, DIRECTIO_ON);"
+. auto/feature
--- a/auto/os/linux
+++ b/auto/os/linux
@@ -16,18 +16,18 @@ CC_AUX_FLAGS="$CC_AUX_FLAGS -D_GNU_SOURC
 
 # Linux kernel version
 
-version=`grep "#define LINUX_VERSION_CODE" /usr/include/linux/version.h \
-         | sed -e 's/^.* \(.*\)$/\1/'`
+version=$((`uname -r \
+         | sed 's/^\([^.]*\)\.\([^.]*\)\.\([^.-]*\).*/\1*256*256+\2*256+\3/'`))
 
 version=${version:-0}
 
 
-# enable the rt signals on Linux 2.2.19 and onward
+# enable the rt signals on Linux between 2.2.19 and 2.6.17
 
-if [ $version -ge 131609 -o $EVENT_RTSIG = YES ]; then
+if [ \( $version -ge 131603 -a $version -lt 132626 \) -o $EVENT_RTSIG = YES ]
+then
     echo " + rt signals found"
     have=NGX_HAVE_RTSIG . auto/have
-    have=NGX_HAVE_POLL . auto/have
     EVENT_MODULES="$EVENT_MODULES $RTSIG_MODULE"
     CORE_SRCS="$CORE_SRCS $RTSIG_SRCS"
     EVENT_FOUND=YES
--- a/auto/sources
+++ b/auto/sources
@@ -15,12 +15,15 @@ CORE_DEPS="src/core/nginx.h \
            src/core/ngx_list.h \
            src/core/ngx_hash.h \
            src/core/ngx_buf.h \
+           src/core/ngx_queue.h \
            src/core/ngx_string.h \
            src/core/ngx_parse.h \
            src/core/ngx_inet.h \
            src/core/ngx_file.h \
            src/core/ngx_crc.h \
            src/core/ngx_crc32.h \
+           src/core/ngx_md5.h \
+           src/core/ngx_sha1.h \
            src/core/ngx_rbtree.h \
            src/core/ngx_radix_tree.h \
            src/core/ngx_slab.h \
@@ -29,6 +32,7 @@ CORE_DEPS="src/core/nginx.h \
            src/core/ngx_connection.h \
            src/core/ngx_cycle.h \
            src/core/ngx_conf_file.h \
+           src/core/ngx_resolver.h \
            src/core/ngx_open_file_cache.h \
            src/core/ngx_garbage_collector.h"
 
@@ -40,6 +44,7 @@ CORE_SRCS="src/core/nginx.c \
            src/core/ngx_list.c \
            src/core/ngx_hash.c \
            src/core/ngx_buf.c \
+           src/core/ngx_queue.c \
            src/core/ngx_output_chain.c \
            src/core/ngx_string.c \
            src/core/ngx_parse.c \
@@ -56,6 +61,7 @@ CORE_SRCS="src/core/nginx.c \
            src/core/ngx_spinlock.c \
            src/core/ngx_cpuinfo.c \
            src/core/ngx_conf_file.c \
+           src/core/ngx_resolver.c \
            src/core/ngx_open_file_cache.c \
            src/core/ngx_garbage_collector.c"
 
@@ -159,6 +165,7 @@ UNIX_SRCS="$CORE_SRCS $EVENT_SRCS \
             src/os/unix/ngx_socket.c \
             src/os/unix/ngx_recv.c \
             src/os/unix/ngx_readv_chain.c \
+            src/os/unix/ngx_udp_recv.c \
             src/os/unix/ngx_send.c \
             src/os/unix/ngx_writev_chain.c \
             src/os/unix/ngx_channel.c \
@@ -172,7 +179,7 @@ UNIX_SRCS="$CORE_SRCS $EVENT_SRCS \
 
 POSIX_DEPS=src/os/unix/ngx_posix_config.h
 
-FREEBSD_DEPS=src/os/unix/ngx_freebsd_config.h
+FREEBSD_DEPS="src/os/unix/ngx_freebsd_config.h src/os/unix/ngx_freebsd.h"
 FREEBSD_SRCS=src/os/unix/ngx_freebsd_init.c
 FREEBSD_SENDFILE_SRCS=src/os/unix/ngx_freebsd_sendfile_chain.c
 FREEBSD_RFORK_DEPS="src/os/unix/ngx_freebsd_rfork_thread.h"
@@ -181,16 +188,21 @@ FREEBSD_RFORK_THREAD_SRCS="src/os/unix/r
 
 PTHREAD_SRCS="src/os/unix/ngx_pthread_thread.c"
 
-LINUX_DEPS=src/os/unix/ngx_linux_config.h
+LINUX_DEPS="src/os/unix/ngx_linux_config.h src/os/unix/ngx_linux.h"
 LINUX_SRCS=src/os/unix/ngx_linux_init.c
 LINUX_SENDFILE_SRCS=src/os/unix/ngx_linux_sendfile_chain.c
 
 
-SOLARIS_DEPS=src/os/unix/ngx_solaris_config.h
+SOLARIS_DEPS="src/os/unix/ngx_solaris_config.h src/os/unix/ngx_solaris.h"
 SOLARIS_SRCS=src/os/unix/ngx_solaris_init.c
 SOLARIS_SENDFILEV_SRCS=src/os/unix/ngx_solaris_sendfilev_chain.c
 
 
+DARWIN_DEPS="src/os/unix/ngx_darwin_config.h src/os/unix/ngx_darwin.h"
+DARWIN_SRCS=src/os/unix/ngx_darwin_init.c
+DARWIN_SENDFILE_SRCS=src/os/unix/ngx_darwin_sendfile_chain.c
+
+
 WIN32_INCS="$CORE_INCS $EVENT_INCS src/os/win32"
 
 WIN32_DEPS="$CORE_DEPS $EVENT_DEPS \
@@ -224,6 +236,7 @@ WIN32_SRCS="$CORE_SRCS $EVENT_SRCS \
             src/os/win32/ngx_socket.c \
             src/os/win32/ngx_wsarecv.c \
             src/os/win32/ngx_wsarecv_chain.c \
+            src/os/win32/ngx_udp_wsarecv.c \
             src/os/win32/ngx_wsasend_chain.c \
             src/os/win32/ngx_win32_init.c \
             src/os/win32/ngx_user.c \
@@ -300,10 +313,10 @@ HTTP_SRCS="src/http/ngx_http.c \
 # STUB
 HTTP_SRCS="$HTTP_SRCS src/http/ngx_http_busy_lock.c"
 
-HTPP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c
+HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c
 
-HTPP_CACHE_SRCS=src/http/ngx_http_cache.c
-HTPP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c
+HTTP_CACHE_SRCS=src/http/ngx_http_cache.c
+HTTP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c
 
 
 HTTP_CHARSET_FILTER_MODULE=ngx_http_charset_filter_module
@@ -319,6 +332,10 @@ HTTP_SSI_DEPS=src/http/modules/ngx_http_
 HTTP_SSI_SRCS=src/http/modules/ngx_http_ssi_filter_module.c
 
 
+HTTP_XSLT_FILTER_MODULE=ngx_http_xslt_filter_module
+HTTP_XSLT_SRCS=src/http/modules/ngx_http_xslt_filter_module.c
+
+
 HTTP_SUB_FILTER_MODULE=ngx_http_sub_filter_module
 HTTP_SUB_SRCS=src/http/modules/ngx_http_sub_filter_module.c
 
@@ -410,6 +427,10 @@ HTTP_FLV_MODULE=ngx_http_flv_module
 HTTP_FLV_SRCS=src/http/modules/ngx_http_flv_module.c
 
 
+HTTP_GZIP_STATIC_MODULE=ngx_http_gzip_static_module
+HTTP_GZIP_STATIC_SRCS=src/http/modules/ngx_http_gzip_static_module.c
+
+
 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
 
@@ -449,3 +470,7 @@ MAIL_AUTH_HTTP_SRCS="src/mail/ngx_mail_a
 
 MAIL_PROXY_MODULE="ngx_mail_proxy_module"
 MAIL_PROXY_SRCS="src/mail/ngx_mail_proxy_module.c"
+
+NGX_GOOGLE_PERFTOOLS_MODULE=ngx_google_perftools_module
+NGX_GOOGLE_PERFTOOLS_SRCS=src/misc/ngx_google_perftools_module.c
+
--- a/auto/summary
+++ b/auto/summary
@@ -21,15 +21,15 @@ echo
 echo "Configuration summary"
 
 
-case $USE_THREADS in
-    rfork)         echo "  + using rfork()ed threads" ;;
-    pthreads)      echo "  + using libpthread threads library" ;;
-    libthr)        echo "  + using FreeBSD libthr threads library" ;;
-    libc_r)        echo "  + using FreeBSD libc_r threads library" ;;
-    linuxthreads)  echo "  + using FreeBSD LinuxThreads port library" ;;
-    NO)            echo "  + threads are not used" ;;
-    *)             echo "  + using lib$USE_THREADS threads library" ;;
-esac
+#case $USE_THREADS in
+#    rfork)         echo "  + using rfork()ed threads" ;;
+#    pthreads)      echo "  + using libpthread threads library" ;;
+#    libthr)        echo "  + using FreeBSD libthr threads library" ;;
+#    libc_r)        echo "  + using FreeBSD libc_r threads library" ;;
+#    linuxthreads)  echo "  + using FreeBSD LinuxThreads port library" ;;
+#    NO)            echo "  + threads are not used" ;;
+#    *)             echo "  + using lib$USE_THREADS threads library" ;;
+#esac
 
 if [ $USE_PCRE = DISABLED ]; then
     echo "  + PCRE library is disabled"
@@ -51,26 +51,14 @@ case $OPENSSL in
 esac
 
 case $MD5 in
-    YES)
-        case $OPENSSL in
-            NONE|NO)  echo "  + md5: using system $MD5_LIB library" ;;
-            *)        echo "  + md5: using OpenSSL library" ;;
-        esac
-        ;;
-
+    YES)   echo "  + md5: using $MD5_LIB library" ;;
     NONE)  echo "  + md5 library is not used" ;;
     NO)    echo "  + md5 library is not found" ;;
     *)     echo "  + using md5 library: $MD5" ;;
 esac
 
 case $SHA1 in
-    YES)
-        case $OPENSSL in
-            NONE|NO)  echo "  + sha1: using system $SHA1_LIB library" ;;
-            *)        echo "  + sha1: using OpenSSL library" ;;
-        esac
-        ;;
-
+    YES)   echo "  + sha1: using $SHA1_LIB library" ;;
     NONE)  echo "  + sha1 library is not used" ;;
     NO)    echo "  + sha1 library is not found" ;;
     *)     echo "  + using sha1 library: $SHA1" ;;
--- a/conf/mime.types
+++ b/conf/mime.types
@@ -2,11 +2,12 @@
 types {
     text/html                             html htm shtml;
     text/css                              css;
-    text/xml                              xml rss;
+    text/xml                              xml;
     image/gif                             gif;
     image/jpeg                            jpeg jpg;
     application/x-javascript              js;
     application/atom+xml                  atom;
+    application/rss+xml                   rss;
 
     text/mathml                           mml;
     text/plain                            txt;
--- a/conf/nginx.conf
+++ b/conf/nginx.conf
@@ -63,6 +63,7 @@ http {
         # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
         #
         #location ~ \.php$ {
+        #    root           html;
         #    fastcgi_pass   127.0.0.1:9000;
         #    fastcgi_index  index.php;
         #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -273,9 +273,11 @@ main(int argc, char *const *argv)
         return 1;
     }
 
-    /* ngx_crc32_init() requires ngx_cacheline_size set in ngx_os_init() */
+    /*
+     * ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init()
+     */
 
-    if (ngx_crc32_init() != NGX_OK) {
+    if (ngx_crc32_table_init() != NGX_OK) {
         return 1;
     }
 
@@ -635,8 +637,7 @@ ngx_getopt(ngx_cycle_t *cycle, int argc,
         case 'c':
             if (argv[i + 1] == NULL) {
                 ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
-                              "the option: \"%s\" requires file name",
-                              argv[i]);
+                              "the option \"-c\" requires file name");
                 return NGX_ERROR;
             }
 
@@ -644,6 +645,17 @@ ngx_getopt(ngx_cycle_t *cycle, int argc,
             cycle->conf_file.len = ngx_strlen(cycle->conf_file.data);
             break;
 
+        case 'g':
+            if (argv[i + 1] == NULL) {
+                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+                              "the option \"-g\" requires parameter");
+                return NGX_ERROR;
+            }
+
+            cycle->conf_param.data = (u_char *) argv[++i];
+            cycle->conf_param.len = ngx_strlen(cycle->conf_param.data);
+            break;
+
         default:
             ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                           "invalid option: \"%s\"", argv[i]);
@@ -756,12 +768,6 @@ ngx_core_module_init_conf(ngx_cycle_t *c
 {
     ngx_core_conf_t  *ccf = conf;
 
-#if !(NGX_WIN32)
-    ngx_str_t       lock_file;
-    struct group   *grp;
-    struct passwd  *pwd;
-#endif
-
     ngx_conf_init_value(ccf->daemon, 1);
     ngx_conf_init_value(ccf->master, 1);
     ngx_conf_init_msec_value(ccf->timer_resolution, 0);
@@ -794,6 +800,8 @@ ngx_core_module_init_conf(ngx_cycle_t *c
 #if !(NGX_WIN32)
 
     if (ccf->user == (uid_t) NGX_CONF_UNSET_UINT && geteuid() == 0) {
+        struct group   *grp;
+        struct passwd  *pwd;
 
         ngx_set_errno(0);
         pwd = getpwnam(NGX_USER);
@@ -828,7 +836,7 @@ ngx_core_module_init_conf(ngx_cycle_t *c
 
     ccf->oldpid.len = ccf->pid.len + sizeof(NGX_OLDPID_EXT);
 
-    ccf->oldpid.data = ngx_palloc(cycle->pool, ccf->oldpid.len);
+    ccf->oldpid.data = ngx_pnalloc(cycle->pool, ccf->oldpid.len);
     if (ccf->oldpid.data == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -846,6 +854,9 @@ ngx_core_module_init_conf(ngx_cycle_t *c
         return NGX_CONF_ERROR;
     }
 
+    {
+    ngx_str_t  lock_file;
+
     lock_file = cycle->old_cycle->lock_file;
 
     if (lock_file.len) {
@@ -869,7 +880,7 @@ ngx_core_module_init_conf(ngx_cycle_t *c
 
     } else {
         cycle->lock_file.len = ccf->lock_file.len + 1;
-        cycle->lock_file.data = ngx_palloc(cycle->pool,
+        cycle->lock_file.data = ngx_pnalloc(cycle->pool,
                                       ccf->lock_file.len + sizeof(".accept"));
         if (cycle->lock_file.data == NULL) {
             return NGX_CONF_ERROR;
@@ -879,6 +890,7 @@ ngx_core_module_init_conf(ngx_cycle_t *c
                               ccf->lock_file.len),
                    ".accept", sizeof(".accept"));
     }
+    }
 
 #endif
 
@@ -1076,7 +1088,7 @@ ngx_set_cpu_affinity(ngx_conf_t *cf, ngx
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "invalid character \"%c\" in \"worker_cpu_affinity\"",
                           ch);
-            return NGX_CONF_ERROR ;
+            return NGX_CONF_ERROR;
         }
     }
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.6.12"
+#define NGINX_VERSION      "0.7.8"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_array.c
+++ b/src/core/ngx_array.c
@@ -39,12 +39,12 @@ ngx_array_destroy(ngx_array_t *a)
 
     p = a->pool;
 
-    if ((u_char *) a->elts + a->size * a->nalloc == p->last) {
-        p->last -= a->size * a->nalloc;
+    if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) {
+        p->d.last -= a->size * a->nalloc;
     }
 
-    if ((u_char *) a + sizeof(ngx_array_t) == p->last) {
-        p->last = (u_char *) a;
+    if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) {
+        p->d.last = (u_char *) a;
     }
 }
 
@@ -64,14 +64,15 @@ ngx_array_push(ngx_array_t *a)
 
         p = a->pool;
 
-        if ((u_char *) a->elts + size == p->last && p->last + a->size <= p->end)
+        if ((u_char *) a->elts + size == p->d.last
+            && p->d.last + a->size <= p->d.end)
         {
             /*
              * the array allocation is the last in the pool
              * and there is space for new allocation
              */
 
-            p->last += a->size;
+            p->d.last += a->size;
             a->nalloc++;
 
         } else {
@@ -111,15 +112,15 @@ ngx_array_push_n(ngx_array_t *a, ngx_uin
 
         p = a->pool;
 
-        if ((u_char *) a->elts + a->size * a->nalloc == p->last
-            && p->last + size <= p->end)
+        if ((u_char *) a->elts + a->size * a->nalloc == p->d.last
+            && p->d.last + size <= p->d.end)
         {
             /*
              * the array allocation is the last in the pool
              * and there is space for new allocation
              */
 
-            p->last += size;
+            p->d.last += size;
             a->nalloc += n;
 
         } else {
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -58,14 +58,52 @@ static int argument_number[] = {
 
 
 char *
+ngx_conf_param(ngx_conf_t *cf)
+{
+    ngx_str_t        *param;
+    ngx_buf_t         b;
+    ngx_conf_file_t   conf_file;
+
+    param = &cf->cycle->conf_param;
+
+    if (param->len == 0) {
+        return NGX_CONF_OK;
+    }
+
+    ngx_memzero(&conf_file, sizeof(ngx_conf_file_t));
+
+    ngx_memzero(&b, sizeof(ngx_buf_t));
+
+    b.start = param->data;
+    b.pos = param->data;
+    b.last = param->data + param->len;
+    b.end = b.last;
+    b.temporary = 1;
+
+    conf_file.file.fd = NGX_INVALID_FILE;
+    conf_file.file.name.data = (u_char *) "command line";
+    conf_file.line = 1;
+
+    cf->conf_file = &conf_file;
+    cf->conf_file->buffer = &b;
+
+    return ngx_conf_parse(cf, NULL);
+}
+
+
+char *
 ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
 {
     char             *rv;
     ngx_fd_t          fd;
     ngx_int_t         rc;
     ngx_buf_t        *b;
-    ngx_uint_t        block;
     ngx_conf_file_t  *prev;
+    enum {
+        parse_file = 0,
+        parse_block,
+        parse_param
+    } type;
 
 #if (NGX_SUPPRESS_WARN)
     fd = NGX_INVALID_FILE;
@@ -117,13 +155,17 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t
         cf->conf_file->file.name.len = filename->len;
         cf->conf_file->file.name.data = filename->data;
         cf->conf_file->file.offset = 0;
-        cf->conf_file->file.log = cf->log;;
+        cf->conf_file->file.log = cf->log;
         cf->conf_file->line = 1;
 
-        block = 0;
+        type = parse_file;
+
+    } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
+
+        type = parse_block;
 
     } else {
-        block = 1;
+        type = parse_param;
     }
 
 
@@ -145,24 +187,38 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t
         }
 
         if (rc == NGX_CONF_BLOCK_DONE) {
-            if (!block) {
+
+            if (type != parse_block) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\"");
                 goto failed;
             }
 
-            block = 0;
+            goto done;
         }
 
-        if (rc == NGX_CONF_FILE_DONE && block) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "unexpected end of file, expecting \"}\"");
-            goto failed;
+        if (rc == NGX_CONF_FILE_DONE) {
+
+            if (type == parse_block) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "unexpected end of file, expecting \"}\"");
+                goto failed;
+            }
+
+            goto done;
         }
 
-        if (rc != NGX_OK && rc != NGX_CONF_BLOCK_START) {
-            goto done;
+        if (rc == NGX_CONF_BLOCK_START) {
+
+            if (type == parse_param) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "block directives are not supported "
+                                   "in -g option");
+                goto failed;
+            }
         }
 
+        /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */
+
         if (cf->handler) {
 
             /*
@@ -398,10 +454,19 @@ ngx_conf_read_token(ngx_conf_t *cf)
     for ( ;; ) {
 
         if (b->pos >= b->last) {
+
             if (cf->conf_file->file.offset
                                  >= ngx_file_size(&cf->conf_file->file.info))
             {
                 if (cf->args->nelts > 0) {
+
+                    if (cf->conf_file->file.fd == NGX_INVALID_FILE) {
+                        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                           "unexpected end of parameter, "
+                                           "expecting \";\"");
+                        return NGX_ERROR;
+                    }
+
                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "unexpected end of file, "
                                   "expecting \";\" or \"}\"");
@@ -574,7 +639,7 @@ ngx_conf_read_token(ngx_conf_t *cf)
                     return NGX_ERROR;
                 }
 
-                word->data = ngx_palloc(cf->pool, b->pos - start + 1);
+                word->data = ngx_pnalloc(cf->pool, b->pos - start + 1);
                 if (word->data == NULL) {
                     return NGX_ERROR;
                 }
@@ -633,7 +698,7 @@ ngx_conf_include(ngx_conf_t *cf, ngx_com
 {
     char        *rv;
     ngx_int_t    n;
-    ngx_str_t   *value, file;
+    ngx_str_t   *value, file, name;
     ngx_glob_t   gl;
 
     value = cf->args->elts;
@@ -645,10 +710,18 @@ ngx_conf_include(ngx_conf_t *cf, ngx_com
         return NGX_CONF_ERROR;
     }
 
+    if (strpbrk((char *) file.data, "*?[") == NULL) {
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
+
+        return ngx_conf_parse(cf, &file);
+    }
+
     ngx_memzero(&gl, sizeof(ngx_glob_t));
 
     gl.pattern = file.data;
     gl.log = cf->log;
+    gl.test = 1;
 
     if (ngx_open_glob(&gl) != NGX_OK) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
@@ -659,12 +732,15 @@ ngx_conf_include(ngx_conf_t *cf, ngx_com
     rv = NGX_CONF_OK;
 
     for ( ;; ) {
-        n = ngx_read_glob(&gl, &file);
+        n = ngx_read_glob(&gl, &name);
 
         if (n != NGX_OK) {
             break;
         }
 
+        file.len = name.len++;
+        file.data = ngx_pstrdup(cf->pool, &name);
+
         ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
 
         rv = ngx_conf_parse(cf, &file);
@@ -715,7 +791,7 @@ ngx_conf_full_name(ngx_cycle_t *cycle, n
     }
 
     name->len = len + old.len;
-    name->data = ngx_palloc(cycle->pool, name->len + 1);
+    name->data = ngx_pnalloc(cycle->pool, name->len + 1);
     if (name->data == NULL) {
         return  NGX_ERROR;
     }
@@ -934,7 +1010,7 @@ ngx_conf_set_str_array_slot(ngx_conf_t *
 
     a = (ngx_array_t **) (p + cmd->offset);
 
-    if (*a == NULL) {
+    if (*a == NGX_CONF_UNSET_PTR) {
         *a = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
         if (*a == NULL) {
             return NGX_CONF_ERROR;
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -317,6 +317,7 @@ char *ngx_conf_check_num_bounds(ngx_conf
 #define addressof(addr)  ((int) &addr)
 
 
+char *ngx_conf_param(ngx_conf_t *cf);
 char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename);
 
 
--- a/src/core/ngx_config.h
+++ b/src/core/ngx_config.h
@@ -29,6 +29,10 @@
 #include <ngx_solaris_config.h>
 
 
+#elif (NGX_DARWIN)
+#include <ngx_darwin_config.h>
+
+
 #elif (NGX_WIN32)
 #include <ngx_win32_config.h>
 
@@ -116,10 +120,11 @@ typedef intptr_t        ngx_flag_t;
 #define INET_ADDRSTRLEN  16
 #endif
 
-#define NGX_MAXHOSTNAMELEN 64
-/*
-#define NGX_MAXHOSTNAMELEN MAXHOSTNAMELEN
-*/
+#ifdef MAXHOSTNAMELEN
+#define NGX_MAXHOSTNAMELEN  MAXHOSTNAMELEN
+#else
+#define NGX_MAXHOSTNAMELEN  256
+#endif
 
 
 #if ((__GNU__ == 2) && (__GNUC_MINOR__ < 8))
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -36,7 +36,7 @@ ngx_listening_inet_stream_socket(ngx_con
     sin->sin_port = htons(port);
 
 
-    ls->addr_text.data = ngx_palloc(cf->pool,
+    ls->addr_text.data = ngx_pnalloc(cf->pool,
                                     INET_ADDRSTRLEN - 1 + sizeof(":65535") - 1);
     if (ls->addr_text.data == NULL) {
         return NULL;
@@ -106,8 +106,8 @@ ngx_set_inherited_sockets(ngx_cycle_t *c
 
         ls[i].addr_text_max_len = INET_ADDRSTRLEN;
 
-        ls[i].addr_text.data = ngx_palloc(cycle->pool, INET_ADDRSTRLEN - 1
-                                                       + sizeof(":65535") - 1);
+        ls[i].addr_text.data = ngx_pnalloc(cycle->pool,
+                                   INET_ADDRSTRLEN - 1 + sizeof(":65535") - 1);
         if (ls[i].addr_text.data == NULL) {
             return NGX_ERROR;
         }
@@ -229,7 +229,7 @@ ngx_open_listening_sockets(ngx_cycle_t *
 
     /* TODO: configurable try number */
 
-    for (tries = 5 ; tries; tries--) {
+    for (tries = 5; tries; tries--) {
         failed = 0;
 
         /* for each listening socket */
@@ -537,13 +537,21 @@ ngx_close_listening_sockets(ngx_cycle_t 
 
         c = ls[i].connection;
 
-        if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
-            if (c->read->active) {
+        if (c->read->active) {
+            if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
                 ngx_del_conn(c, NGX_CLOSE_EVENT);
-            }
+
+            } else if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {
 
-        } else {
-            if (c->read->active) {
+                /*
+                 * it seems that Linux-2.6.x OpenVZ sends events
+                 * for closed shared listening sockets unless
+                 * the events was explicity deleted
+                 */
+
+                ngx_del_event(c->read, NGX_READ_EVENT, 0);
+
+            } else {
                 ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
             }
         }
@@ -653,6 +661,8 @@ ngx_free_connection(ngx_connection_t *c)
 void
 ngx_close_connection(ngx_connection_t *c)
 {
+    ngx_err_t     err;
+    ngx_uint_t    log_error, level;
     ngx_socket_t  fd;
 
     if (c->fd == -1) {
@@ -725,6 +735,8 @@ ngx_close_connection(ngx_connection_t *c
 
 #endif
 
+    log_error = c->log_error;
+
     ngx_free_connection(c);
 
     fd = c->fd;
@@ -732,9 +744,31 @@ ngx_close_connection(ngx_connection_t *c
 
     if (ngx_close_socket(fd) == -1) {
 
+        err = ngx_socket_errno;
+
+        if (err == NGX_ECONNRESET || err == NGX_ENOTCONN) {
+
+            switch (log_error) {
+
+            case NGX_ERROR_INFO:
+                level = NGX_LOG_INFO;
+                break;
+
+            case NGX_ERROR_ERR:
+                level = NGX_LOG_ERR;
+                break;
+
+            default:
+                level = NGX_LOG_CRIT;
+            }
+
+        } else {
+            level = NGX_LOG_CRIT;
+        }
+
         /* we use ngx_cycle->log because c->log was in c->pool */
 
-        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
+        ngx_log_error(level, ngx_cycle->log, err,
                       ngx_close_socket_n " %d failed", fd);
     }
 }
@@ -759,6 +793,9 @@ ngx_connection_error(ngx_connection_t *c
         || err == NGX_ENOTCONN
         || err == NGX_ETIMEDOUT
         || err == NGX_ECONNREFUSED
+        || err == NGX_ENETDOWN
+        || err == NGX_ENETUNREACH
+        || err == NGX_EHOSTDOWN
         || err == NGX_EHOSTUNREACH)
     {
         switch (c->log_error) {
@@ -773,11 +810,11 @@ ngx_connection_error(ngx_connection_t *c
             break;
 
         default:
-            level = NGX_LOG_CRIT;
+            level = NGX_LOG_ALERT;
         }
 
     } else {
-        level = NGX_LOG_CRIT;
+        level = NGX_LOG_ALERT;
     }
 
     ngx_log_error(level, c->log, err, text);
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -50,6 +50,7 @@ typedef void (*ngx_connection_handler_pt
 #include <ngx_alloc.h>
 #include <ngx_palloc.h>
 #include <ngx_buf.h>
+#include <ngx_queue.h>
 #include <ngx_array.h>
 #include <ngx_list.h>
 #include <ngx_hash.h>
@@ -71,6 +72,7 @@ typedef void (*ngx_connection_handler_pt
 #endif
 #include <ngx_process_cycle.h>
 #include <ngx_conf_file.h>
+#include <ngx_resolver.h>
 #include <ngx_open_file_cache.h>
 #include <ngx_os.h>
 #include <ngx_connection.h>
--- a/src/core/ngx_cpuinfo.c
+++ b/src/core/ngx_cpuinfo.c
@@ -92,13 +92,22 @@ ngx_cpuinfo(void)
 
     if (ngx_strcmp(vendor, "GenuineIntel") == 0) {
 
-        switch (cpu[0] & 0xf00) {
+        switch ((cpu[0] & 0xf00) >> 8) {
 
         /* Pentium */
         case 5:
+            ngx_cacheline_size = 32;
+            break;
+
         /* Pentium Pro, II, III */
         case 6:
             ngx_cacheline_size = 32;
+
+            if ((cpu[0] & 0xf0) >= 0xd0) {
+                /* Intel Core */
+                ngx_cacheline_size = 64;
+            }
+
             break;
 
         /*
--- a/src/core/ngx_crc32.c
+++ b/src/core/ngx_crc32.c
@@ -102,7 +102,7 @@ uint32_t *ngx_crc32_table_short = ngx_cr
 
 
 ngx_int_t
-ngx_crc32_init(void)
+ngx_crc32_table_init(void)
 {
     void  *p;
 
--- a/src/core/ngx_crc32.h
+++ b/src/core/ngx_crc32.h
@@ -49,7 +49,30 @@ ngx_crc32_long(u_char *p, size_t len)
 }
 
 
-ngx_int_t ngx_crc32_init(void);
+#define ngx_crc32_init(crc)                                                   \
+    crc = 0xffffffff
+
+
+static ngx_inline void
+ngx_crc32_update(uint32_t *crc, u_char *p, size_t len)
+{
+    uint32_t  c;
+
+    c = *crc;
+
+    while (len--) {
+        c = ngx_crc32_table256[(c ^ *p++) & 0xff] ^ (c >> 8);
+    }
+
+    *crc = c;
+}
+
+
+#define ngx_crc32_final(crc)                                                  \
+    crc ^= 0xffffffff
+
+
+ngx_int_t ngx_crc32_table_init(void);
 
 
 #endif /* _NGX_CRC32_H_INCLUDED_ */
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -42,20 +42,22 @@ static ngx_str_t  error_log = ngx_null_s
 ngx_cycle_t *
 ngx_init_cycle(ngx_cycle_t *old_cycle)
 {
-    void               *rv;
-    u_char             *lock_file;
-    ngx_uint_t          i, n;
-    ngx_log_t          *log;
-    ngx_conf_t          conf;
-    ngx_pool_t         *pool;
-    ngx_cycle_t        *cycle, **old;
-    ngx_shm_zone_t     *shm_zone, *oshm_zone;
-    ngx_slab_pool_t    *shpool;
-    ngx_list_part_t    *part, *opart;
-    ngx_open_file_t    *file;
-    ngx_listening_t    *ls, *nls;
-    ngx_core_conf_t    *ccf, *old_ccf;
-    ngx_core_module_t  *module;
+    void                *rv;
+    char               **senv, **env;
+    u_char              *lock_file;
+    ngx_uint_t           i, n;
+    ngx_log_t           *log;
+    ngx_conf_t           conf;
+    ngx_pool_t          *pool;
+    ngx_cycle_t         *cycle, **old;
+    ngx_shm_zone_t      *shm_zone, *oshm_zone;
+    ngx_slab_pool_t     *shpool;
+    ngx_list_part_t     *part, *opart;
+    ngx_open_file_t     *file;
+    ngx_listening_t     *ls, *nls;
+    ngx_core_conf_t     *ccf, *old_ccf;
+    ngx_core_module_t   *module;
+    char                 hostname[NGX_MAXHOSTNAMELEN];
 
     log = old_cycle->log;
 
@@ -79,7 +81,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
 
 
     cycle->conf_file.len = old_cycle->conf_file.len;
-    cycle->conf_file.data = ngx_palloc(pool, old_cycle->conf_file.len + 1);
+    cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1);
     if (cycle->conf_file.data == NULL) {
         ngx_destroy_pool(pool);
         return NULL;
@@ -88,6 +90,16 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
                 old_cycle->conf_file.len + 1);
 
 
+    cycle->conf_param.len = old_cycle->conf_param.len;
+    cycle->conf_param.data = ngx_pnalloc(pool, old_cycle->conf_param.len);
+    if (cycle->conf_param.data == NULL) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+    ngx_memcpy(cycle->conf_param.data, old_cycle->conf_param.data,
+               old_cycle->conf_param.len);
+
+
     n = old_cycle->pathes.nelts ? old_cycle->pathes.nelts : 10;
 
     cycle->pathes.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *));
@@ -169,6 +181,26 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     }
 
 
+    if (gethostname(hostname, NGX_MAXHOSTNAMELEN) == -1) {
+        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "gethostname() failed");
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+
+    /* on Linux gethostname() silently truncates name that does not fit */
+
+    hostname[NGX_MAXHOSTNAMELEN - 1] = '\0';
+    cycle->hostname.len = ngx_strlen(hostname);
+
+    cycle->hostname.data = ngx_pnalloc(pool, cycle->hostname.len);
+    if (cycle->hostname.data == NULL) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+
+    ngx_memcpy(cycle->hostname.data, hostname, cycle->hostname.len);
+
+
     for (i = 0; ngx_modules[i]; i++) {
         if (ngx_modules[i]->type != NGX_CORE_MODULE) {
             continue;
@@ -187,6 +219,9 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     }
 
 
+    senv = environ;
+
+
     ngx_memzero(&conf, sizeof(ngx_conf_t));
     /* STUB: init array ? */
     conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t));
@@ -213,6 +248,11 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     log->log_level = NGX_LOG_DEBUG_ALL;
 #endif
 
+    if (ngx_conf_param(&conf) != NGX_CONF_OK) {
+        ngx_destroy_cycle_pools(&conf);
+        return NULL;
+    }
+
     if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
         ngx_destroy_cycle_pools(&conf);
         return NULL;
@@ -435,8 +475,8 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
 
 #else
 
-        lock_file = ngx_palloc(cycle->pool,
-                               cycle->lock_file.len + shm_zone[i].name.len);
+        lock_file = ngx_pnalloc(cycle->pool,
+                                cycle->lock_file.len + shm_zone[i].name.len);
 
         if (lock_file == NULL) {
             goto failed;
@@ -694,9 +734,20 @@ old_shm_zone_done:
 
     if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) {
 
+        /*
+         * perl_destruct() frees environ if it is not the same as it was at
+         * perl_construct() time.  So we have saved an previous cycle
+         * environment before ngx_conf_parse() where it will be changed.
+         */
+
+        env = environ;
+        environ = senv;
+
         ngx_destroy_pool(old_cycle->pool);
         cycle->old_cycle = NULL;
 
+        environ = env;
+
         return cycle;
     }
 
@@ -938,9 +989,6 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx
     ngx_uint_t        i;
     ngx_list_part_t  *part;
     ngx_open_file_t  *file;
-#if !(NGX_WIN32)
-    ngx_file_info_t   fi;
-#endif
 
     part = &cycle->open_files.part;
     file = part->elts;
@@ -996,6 +1044,7 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx
         }
 #else
         if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) {
+            ngx_file_info_t  fi;
 
             if (ngx_file_info((const char *) file[i].name.data, &fi) == -1) {
                 ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
--- a/src/core/ngx_cycle.h
+++ b/src/core/ngx_cycle.h
@@ -60,8 +60,10 @@ struct ngx_cycle_s {
     ngx_cycle_t              *old_cycle;
 
     ngx_str_t                 conf_file;
+    ngx_str_t                 conf_param;
     ngx_str_t                 root;
     ngx_str_t                 lock_file;
+    ngx_str_t                 hostname;
 };
 
 
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -46,7 +46,7 @@ ngx_create_temp_file(ngx_file_t *file, n
 
     file->name.len = path->name.len + 1 + path->len + 10;
 
-    file->name.data = ngx_palloc(pool, file->name.len + 1);
+    file->name.data = ngx_pnalloc(pool, file->name.len + 1);
     if (file->name.data == NULL) {
         return NGX_ERROR;
     }
@@ -61,16 +61,19 @@ ngx_create_temp_file(ngx_file_t *file, n
 
     n = (uint32_t) ngx_next_temp_number(0);
 
+    cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
+    if (cln == NULL) {
+        return NGX_ERROR;
+    }
+
     for ( ;; ) {
         (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len,
                            "%010uD%Z", n);
 
-        ngx_create_hashed_filename(file, path);
+        ngx_create_hashed_filename(path, file->name.data, file->name.len);
 
-        cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
-        if (cln == NULL) {
-            return NGX_ERROR;
-        }
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
+                       "hashed path: %s", file->name.data);
 
         file->fd = ngx_open_tempfile(file->name.data, persistent, access);
 
@@ -117,31 +120,27 @@ ngx_create_temp_file(ngx_file_t *file, n
 
 
 void
-ngx_create_hashed_filename(ngx_file_t *file, ngx_path_t *path)
+ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len)
 {
-    size_t      name, pos, level;
-    ngx_uint_t  i;
+    size_t      i, level;
+    ngx_uint_t  n;
 
-    name = file->name.len;
-    pos = path->name.len + 1;
+    i = path->name.len + 1;
 
-    file->name.data[path->name.len + path->len]  = '/';
+    file[path->name.len + path->len]  = '/';
 
-    for (i = 0; i < 3; i++) {
-        level = path->level[i];
+    for (n = 0; n < 3; n++) {
+        level = path->level[n];
 
         if (level == 0) {
             break;
         }
 
-        name -= level;
-        file->name.data[pos - 1] = '/';
-        ngx_memcpy(&file->name.data[pos], &file->name.data[name], level);
-        pos += level + 1;
+        len -= level;
+        file[i - 1] = '/';
+        ngx_memcpy(&file[i], &file[len], level);
+        i += level + 1;
     }
-
-    ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
-                   "hashed path: %s", file->name.data);
 }
 
 
@@ -425,9 +424,6 @@ ngx_create_pathes(ngx_cycle_t *cycle, ng
     ngx_err_t         err;
     ngx_uint_t        i;
     ngx_path_t      **path;
-#if !(NGX_WIN32)
-    ngx_file_info_t   fi;
-#endif
 
     path = cycle->pathes.elts;
     for (i = 0; i < cycle->pathes.nelts; i++) {
@@ -447,6 +443,8 @@ ngx_create_pathes(ngx_cycle_t *cycle, ng
         }
 
 #if !(NGX_WIN32)
+        {
+        ngx_file_info_t   fi;
 
         if (ngx_file_info((const char *) path[i]->name.data, &fi) == -1) {
             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
@@ -474,7 +472,7 @@ ngx_create_pathes(ngx_cycle_t *cycle, ng
                 return NGX_ERROR;
             }
         }
-
+        }
 #endif
     }
 
@@ -483,6 +481,115 @@ ngx_create_pathes(ngx_cycle_t *cycle, ng
 
 
 ngx_int_t
+ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext)
+{
+    ngx_err_t  err;
+
+#if !(NGX_WIN32)
+
+    if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+                      ngx_change_file_access_n " \"%s\" failed", src->data);
+        err = 0;
+        goto failed;
+    }
+
+#endif
+
+    if (ext->time != -1) {
+        if (ngx_set_file_time(src->data, ext->fd, ext->time) != NGX_OK) {
+            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+                          ngx_set_file_time_n " \"%s\" failed", src->data);
+            err = 0;
+            goto failed;
+        }
+    }
+
+    if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
+        return NGX_OK;
+    }
+
+    err = ngx_errno;
+
+    if (err == NGX_ENOENT) {
+
+        if (!ext->create_path) {
+            goto failed;
+        }
+
+        err = ngx_create_full_path(to->data, ngx_dir_access(ext->access));
+
+        if (err) {
+            ngx_log_error(NGX_LOG_CRIT, ext->log, err,
+                          ngx_create_dir_n " \"%s\" failed", to->data);
+            err = 0;
+            goto failed;
+        }
+
+        if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
+            return NGX_OK;
+        }
+
+        err = ngx_errno;
+        goto failed;
+    }
+
+#if (NGX_WIN32)
+
+    if (err == NGX_EEXIST) {
+        if (ngx_win32_rename_file(src, to, ext->log) == NGX_OK) {
+
+            if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
+                return NGX_OK;
+            }
+
+            err = ngx_errno;
+
+        } else {
+            err = 0;
+        }
+    }
+
+#endif
+
+failed:
+
+    if (ext->delete_file) {
+        if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+                          ngx_delete_file_n " \"%s\" failed", src->data);
+        }
+    }
+
+    if (err) {
+        ngx_log_error(NGX_LOG_CRIT, ext->log, err,
+                      ngx_rename_file_n " \"%s\" to \"%s\" failed",
+                      src->data, to->data);
+    }
+
+    return NGX_ERROR;
+}
+
+
+/*
+ * ctx->init_handler() - see ctx->alloc
+ * ctx->file_handler() - file handler
+ * ctx->pre_tree_handler() - handler is called before entering directory
+ * ctx->post_tree_handler() - handler is called after leaving directory
+ * ctx->spec_handler() - special (socket, FIFO, etc.) file handler
+ *
+ * ctx->data - some data structure, it may be the same on all levels, or
+ *     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()
+ *
+ * ctx->log - a log
+ *
+ * on fatal (memory) error handler must return NGX_ABORT to stop walking tree
+ */
+
+ngx_int_t
 ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree)
 {
     void       *data, *prev;
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -57,6 +57,18 @@ typedef struct {
 } ngx_temp_file_t;
 
 
+typedef struct {
+    ngx_uint_t          access;
+    time_t              time;
+    ngx_fd_t            fd;
+
+    unsigned            create_path:1;
+    unsigned            delete_file:1;
+
+    ngx_log_t          *log;
+} ngx_ext_rename_file_t;
+
+
 typedef struct ngx_tree_ctx_s  ngx_tree_ctx_t;
 
 typedef ngx_int_t (*ngx_tree_init_handler_pt) (void *ctx, void *prev);
@@ -84,11 +96,13 @@ ssize_t ngx_write_chain_to_temp_file(ngx
 ngx_int_t ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path,
     ngx_pool_t *pool, ngx_uint_t persistent, ngx_uint_t clean,
     ngx_uint_t access);
-void ngx_create_hashed_filename(ngx_file_t *file, ngx_path_t *path);
+void ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len);
 ngx_int_t ngx_create_path(ngx_file_t *file, ngx_path_t *path);
 ngx_err_t ngx_create_full_path(u_char *dir, ngx_uint_t access);
 ngx_int_t ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot);
 ngx_int_t ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user);
+ngx_int_t ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to,
+    ngx_ext_rename_file_t *ext);
 ngx_int_t ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree);
 
 void ngx_init_temp_number(void);
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -390,9 +390,7 @@ found:
         elt->value = names[n].value;
         elt->len = (u_char) names[n].key.len;
 
-        for (i = 0; i < names[n].key.len; i++) {
-            elt->name[i] = ngx_tolower(names[n].key.data[i]);
-        }
+        ngx_strlow(elt->name, names[n].key.data, names[n].key.len);
 
         test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
     }
@@ -622,6 +620,24 @@ ngx_hash_key_lc(u_char *data, size_t len
 }
 
 
+ngx_uint_t
+ngx_hash_strlow(u_char *dst, u_char *src, size_t n)
+{
+    ngx_uint_t  key;
+
+    key = 0;
+
+    while (n--) {
+        *dst = ngx_tolower(*src);
+        key = ngx_hash(key, *dst);
+        dst++;
+        src++;
+    }
+
+    return key;
+}
+
+
 ngx_int_t
 ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)
 {
@@ -796,12 +812,7 @@ wildcard:
 
     /* wildcard hash */
 
-    k = 0;
-
-    for (i = skip; i < last; i++) {
-        key->data[i] = ngx_tolower(key->data[i]);
-        k = ngx_hash(k, key->data[i]);
-    }
+    k = ngx_hash_strlow(&key->data[skip], &key->data[skip], last - skip);
 
     k %= ha->hsize;
 
@@ -839,7 +850,7 @@ wildcard:
         }
 
         name->len = last - 1;
-        name->data = ngx_palloc(ha->temp_pool, name->len);
+        name->data = ngx_pnalloc(ha->temp_pool, name->len);
         if (name->data == NULL) {
             return NGX_ERROR;
         }
@@ -855,7 +866,7 @@ wildcard:
          *      and ".example.com" to "com.example\0"
          */
 
-        p = ngx_palloc(ha->temp_pool, last);
+        p = ngx_pnalloc(ha->temp_pool, last);
         if (p == NULL) {
             return NGX_ERROR;
         }
@@ -891,7 +902,7 @@ wildcard:
 
         last++;
 
-        p = ngx_palloc(ha->temp_pool, last);
+        p = ngx_pnalloc(ha->temp_pool, last);
         if (p == NULL) {
             return NGX_ERROR;
         }
@@ -944,7 +955,7 @@ wildcard:
     }
 
     name->len = last - skip;
-    name->data = ngx_palloc(ha->temp_pool, name->len);
+    name->data = ngx_pnalloc(ha->temp_pool, name->len);
     if (name->data == NULL) {
         return NGX_ERROR;
     }
--- a/src/core/ngx_hash.h
+++ b/src/core/ngx_hash.h
@@ -110,6 +110,8 @@ ngx_int_t ngx_hash_wildcard_init(ngx_has
 #define ngx_hash(key, c)   ((ngx_uint_t) key * 31 + c)
 ngx_uint_t ngx_hash_key(u_char *data, size_t len);
 ngx_uint_t ngx_hash_key_lc(u_char *data, size_t len);
+ngx_uint_t ngx_hash_strlow(u_char *dst, u_char *src, size_t n);
+
 
 ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type);
 ngx_int_t ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key,
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -8,6 +8,54 @@
 #include <ngx_core.h>
 
 
+static size_t ngx_sprint_uchar(u_char *text, u_char c, size_t len);
+
+
+/* AF_INET only */
+
+in_addr_t
+ngx_inet_addr(u_char *text, size_t len)
+{
+    u_char      *p, c;
+    in_addr_t    addr;
+    ngx_uint_t   octet, n;
+
+    addr = 0;
+    octet = 0;
+    n = 0;
+
+    for (p = text; p < text + len; p++) {
+
+        c = *p;
+
+        if (c >= '0' && c <= '9') {
+            octet = octet * 10 + (c - '0');
+            continue;
+        }
+
+        if (c == '.' && octet < 256) {
+            addr = (addr << 8) + octet;
+            octet = 0;
+            n++;
+            continue;
+        }
+
+        return INADDR_NONE;
+    }
+
+    if (n != 3) {
+        return INADDR_NONE;
+    }
+
+    if (octet < 256) {
+        addr = (addr << 8) + octet;
+        return htonl(addr);
+    }
+
+    return INADDR_NONE;
+}
+
+
 /*
  * ngx_sock_ntop() and ngx_inet_ntop() may be implemented as
  * "ngx_sprintf(text, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3])", however,
@@ -18,50 +66,6 @@
  * than using FreeBSD libc's snprintf().
  */
 
-
-static ngx_inline size_t
-ngx_sprint_uchar(u_char *text, u_char c, size_t len)
-{
-    size_t      n;
-    ngx_uint_t  c1, c2;
-
-    n = 0;
-
-    if (len == n) {
-        return n;
-    }
-
-    c1 = c / 100;
-
-    if (c1) {
-        *text++ = (u_char) (c1 + '0');
-        n++;
-
-        if (len == n) {
-            return n;
-        }
-    }
-
-    c2 = (c % 100) / 10;
-
-    if (c1 || c2) {
-        *text++ = (u_char) (c2 + '0');
-        n++;
-
-        if (len == n) {
-            return n;
-        }
-    }
-
-    c2 = c % 10;
-
-    *text++ = (u_char) (c2 + '0');
-    n++;
-
-    return n;
-}
-
-
 /* AF_INET only */
 
 size_t
@@ -118,6 +122,7 @@ ngx_sock_ntop(int family, struct sockadd
     return n;
 }
 
+
 size_t
 ngx_inet_ntop(int family, void *addr, u_char *text, size_t len)
 {
@@ -171,6 +176,49 @@ ngx_inet_ntop(int family, void *addr, u_
 }
 
 
+static size_t
+ngx_sprint_uchar(u_char *text, u_char c, size_t len)
+{
+    size_t      n;
+    ngx_uint_t  c1, c2;
+
+    n = 0;
+
+    if (len == n) {
+        return n;
+    }
+
+    c1 = c / 100;
+
+    if (c1) {
+        *text++ = (u_char) (c1 + '0');
+        n++;
+
+        if (len == n) {
+            return n;
+        }
+    }
+
+    c2 = (c % 100) / 10;
+
+    if (c1 || c2) {
+        *text++ = (u_char) (c2 + '0');
+        n++;
+
+        if (len == n) {
+            return n;
+        }
+    }
+
+    c2 = c % 10;
+
+    *text = (u_char) (c2 + '0');
+    n++;
+
+    return n;
+}
+
+
 /* AF_INET only */
 
 ngx_int_t
@@ -225,7 +273,7 @@ ngx_ptocidr(ngx_str_t *text, void *cidr)
 
 
 ngx_int_t
-ngx_parse_url(ngx_conf_t *cf, ngx_url_t *u)
+ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u)
 {
     u_char              *p, *host, *port_start;
     size_t               len, port_len;
@@ -273,12 +321,12 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t 
             return NGX_ERROR;
         }
 
-        u->addrs = ngx_pcalloc(cf->pool, sizeof(ngx_peer_addr_t));
+        u->addrs = ngx_pcalloc(pool, sizeof(ngx_peer_addr_t));
         if (u->addrs == NULL) {
             return NGX_ERROR;
         }
 
-        saun = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_un));
+        saun = ngx_pcalloc(pool, sizeof(struct sockaddr_un));
         if (saun == NULL) {
             return NGX_ERROR;
         }
@@ -372,6 +420,9 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t 
             return NGX_ERROR;
         }
 
+        u->port_text.len = port_len;
+        u->port_text.data = port_start;
+
     } else {
         port = ngx_atoi(p, len);
 
@@ -408,12 +459,12 @@ no_port:
 
         if (u->host.len) {
 
-           host = ngx_palloc(cf->temp_pool, u->host.len + 1);
-           if (host == NULL) {
-               return NGX_ERROR;
-           }
+            host = ngx_alloc(u->host.len + 1, pool->log);
+            if (host == NULL) {
+                return NGX_ERROR;
+            }
 
-           (void) ngx_cpystrn(host, u->host.data, u->host.len + 1);
+            (void) ngx_cpystrn(host, u->host.data, u->host.len + 1);
 
             u->addr.in_addr = inet_addr((const char *) host);
 
@@ -421,6 +472,7 @@ no_port:
                 h = gethostbyname((const char *) host);
 
                 if (h == NULL || h->h_addr_list[0] == NULL) {
+                    ngx_free(host);
                     u->err = "host not found";
                     return NGX_ERROR;
                 }
@@ -428,6 +480,8 @@ no_port:
                 u->addr.in_addr = *(in_addr_t *) (h->h_addr_list[0]);
             }
 
+            ngx_free(host);
+
         } else {
             u->addr.in_addr = INADDR_ANY;
         }
@@ -453,7 +507,7 @@ no_port:
         return NGX_ERROR;
     }
 
-    if (ngx_inet_resolve_host(cf, u) != NGX_OK) {
+    if (ngx_inet_resolve_host(pool, u) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -462,7 +516,7 @@ no_port:
 
 
 ngx_int_t
-ngx_inet_resolve_host(ngx_conf_t *cf, ngx_url_t *u)
+ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)
 {
     u_char              *p, *host;
     size_t               len;
@@ -471,7 +525,7 @@ ngx_inet_resolve_host(ngx_conf_t *cf, ng
     struct hostent      *h;
     struct sockaddr_in  *sin;
 
-    host = ngx_palloc(cf->temp_pool, u->host.len + 1);
+    host = ngx_alloc(u->host.len + 1, pool->log);
     if (host == NULL) {
         return NGX_ERROR;
     }
@@ -485,6 +539,8 @@ ngx_inet_resolve_host(ngx_conf_t *cf, ng
     if (in_addr == INADDR_NONE) {
         h = gethostbyname((char *) host);
 
+        ngx_free(host);
+
         if (h == NULL || h->h_addr_list[0] == NULL) {
             u->err = "host not found";
             return NGX_ERROR;
@@ -499,7 +555,7 @@ ngx_inet_resolve_host(ngx_conf_t *cf, ng
 
         /* MP: ngx_shared_palloc() */
 
-        u->addrs = ngx_pcalloc(cf->pool, i * sizeof(ngx_peer_addr_t));
+        u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_peer_addr_t));
         if (u->addrs == NULL) {
             return NGX_ERROR;
         }
@@ -508,7 +564,7 @@ ngx_inet_resolve_host(ngx_conf_t *cf, ng
 
         for (i = 0; h->h_addr_list[i] != NULL; i++) {
 
-            sin = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in));
+            sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
             if (sin == NULL) {
                 return NGX_ERROR;
             }
@@ -522,7 +578,7 @@ ngx_inet_resolve_host(ngx_conf_t *cf, ng
 
             len = INET_ADDRSTRLEN - 1 + 1 + sizeof(":65536") - 1;
 
-            p = ngx_palloc(cf->pool, len);
+            p = ngx_pnalloc(pool, len);
             if (p == NULL) {
                 return NGX_ERROR;
             }
@@ -535,14 +591,16 @@ ngx_inet_resolve_host(ngx_conf_t *cf, ng
 
     } else {
 
+        ngx_free(host);
+
         /* MP: ngx_shared_palloc() */
 
-        u->addrs = ngx_pcalloc(cf->pool, sizeof(ngx_peer_addr_t));
+        u->addrs = ngx_pcalloc(pool, sizeof(ngx_peer_addr_t));
         if (u->addrs == NULL) {
             return NGX_ERROR;
         }
 
-        sin = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in));
+        sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
         if (sin == NULL) {
             return NGX_ERROR;
         }
@@ -556,7 +614,7 @@ ngx_inet_resolve_host(ngx_conf_t *cf, ng
         u->addrs[0].sockaddr = (struct sockaddr *) sin;
         u->addrs[0].socklen = sizeof(struct sockaddr_in);
 
-        p = ngx_palloc(cf->pool, u->host.len + sizeof(":65536") - 1);
+        p = ngx_pnalloc(pool, u->host.len + sizeof(":65536") - 1);
         if (p == NULL) {
             return NGX_ERROR;
         }
--- a/src/core/ngx_inet.h
+++ b/src/core/ngx_inet.h
@@ -35,6 +35,7 @@ typedef struct {
 
     ngx_str_t         url;
     ngx_str_t         host;
+    ngx_str_t         port_text;
     ngx_str_t         uri;
 
     in_port_t         port;
@@ -58,11 +59,12 @@ typedef struct {
 } ngx_url_t;
 
 
+in_addr_t ngx_inet_addr(u_char *text, size_t len);
 size_t ngx_sock_ntop(int family, struct sockaddr *sa, u_char *text, size_t len);
 size_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len);
 ngx_int_t ngx_ptocidr(ngx_str_t *text, void *cidr);
-ngx_int_t ngx_parse_url(ngx_conf_t *cf, ngx_url_t *u);
-ngx_int_t ngx_inet_resolve_host(ngx_conf_t *cf, ngx_url_t *u);
+ngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u);
+ngx_int_t ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u);
 
 
 
--- a/src/core/ngx_log.h
+++ b/src/core/ngx_log.h
@@ -195,9 +195,6 @@ void ngx_cdecl ngx_log_debug_core(ngx_lo
 
 /*********************************/
 
-#define ngx_log_alloc_log(pool, log)  ngx_palloc(pool, log, sizeof(ngx_log_t))
-#define ngx_log_copy_log(new, old)    ngx_memcpy(new, old, sizeof(ngx_log_t))
-
 ngx_log_t *ngx_log_init(void);
 ngx_log_t *ngx_log_create_errlog(ngx_cycle_t *cycle, ngx_array_t *args);
 char *ngx_set_error_log_levels(ngx_conf_t *cf, ngx_log_t *log);
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_md5.h
@@ -0,0 +1,40 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_MD5_H_INCLUDED_
+#define _NGX_MD5_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (NGX_HAVE_OPENSSL_MD5_H)
+#include <openssl/md5.h>
+#else
+#include <md5.h>
+#endif
+
+
+typedef MD5_CTX  ngx_md5_t;
+
+
+#if (NGX_OPENSSL_MD5)
+
+#define ngx_md5_init    MD5_Init
+#define ngx_md5_update  MD5_Update
+#define ngx_md5_final   MD5_Final
+
+#else
+
+#define ngx_md5_init    MD5Init
+#define ngx_md5_update  MD5Update
+#define ngx_md5_final   MD5Final
+
+#endif
+
+
+#endif /* _NGX_MD5_H_INCLUDED_ */
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -18,22 +18,27 @@
 
 
 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);
+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);
 static void ngx_close_cached_file(ngx_open_file_cache_t *cache,
-    ngx_cached_open_file_t *file, ngx_log_t *log);
-static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,
-    ngx_log_t *log);
+    ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log);
+static void ngx_open_file_del_event(ngx_cached_open_file_t *file);
 static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,
     ngx_uint_t n, ngx_log_t *log);
 static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+static ngx_cached_open_file_t *
+    ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
+    uint32_t hash);
 static void ngx_open_file_cache_remove(ngx_event_t *ev);
 
 
 ngx_open_file_cache_t *
 ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
 {
-    ngx_rbtree_node_t      *sentinel;
     ngx_pool_cleanup_t     *cln;
     ngx_open_file_cache_t  *cache;
 
@@ -42,22 +47,10 @@ ngx_open_file_cache_init(ngx_pool_t *poo
         return NULL;
     }
 
-    cache->list_head.prev = NULL;
-    cache->list_head.next = &cache->list_tail;
-
-    cache->list_tail.prev = &cache->list_head;
-    cache->list_tail.next = NULL;
+    ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
+                    ngx_open_file_cache_rbtree_insert_value);
 
-    sentinel = ngx_palloc(pool, sizeof(ngx_rbtree_node_t));
-    if (sentinel == NULL) {
-        return NULL;
-    }
-
-    ngx_rbtree_sentinel_init(sentinel);
-
-    cache->rbtree.root = sentinel;
-    cache->rbtree.sentinel = sentinel;
-    cache->rbtree.insert = ngx_open_file_cache_rbtree_insert_value;
+    ngx_queue_init(&cache->expire_queue);
 
     cache->current = 0;
     cache->max = max;
@@ -80,6 +73,7 @@ ngx_open_file_cache_cleanup(void *data)
 {
     ngx_open_file_cache_t  *cache = data;
 
+    ngx_queue_t             *q;
     ngx_cached_open_file_t  *file;
 
     ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
@@ -87,14 +81,15 @@ ngx_open_file_cache_cleanup(void *data)
 
     for ( ;; ) {
 
-        file = cache->list_tail.prev;
-
-        if (file == &cache->list_head) {
+        if (ngx_queue_empty(&cache->expire_queue)) {
             break;
         }
 
-        file->next->prev = file->prev;
-        file->prev->next = file->next;
+        q = ngx_queue_last(&cache->expire_queue);
+
+        file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
+
+        ngx_queue_remove(q);
 
         ngx_rbtree_delete(&cache->rbtree, &file->node);
 
@@ -106,7 +101,7 @@ ngx_open_file_cache_cleanup(void *data)
         if (!file->err && !file->is_dir) {
             file->close = 1;
             file->count = 0;
-            ngx_close_cached_file(cache, file, ngx_cycle->log);
+            ngx_close_cached_file(cache, file, 0, ngx_cycle->log);
 
         } else {
             ngx_free(file->name);
@@ -135,13 +130,12 @@ ngx_open_cached_file(ngx_open_file_cache
     time_t                          now;
     uint32_t                        hash;
     ngx_int_t                       rc;
-    ngx_rbtree_node_t              *node, *sentinel;
     ngx_pool_cleanup_t             *cln;
     ngx_cached_open_file_t         *file;
     ngx_pool_cleanup_file_t        *clnf;
-    ngx_open_file_cache_event_t    *fev;
     ngx_open_file_cache_cleanup_t  *ofcln;
 
+    of->fd = NGX_INVALID_FILE;
     of->err = 0;
 
     if (cache == NULL) {
@@ -170,151 +164,144 @@ ngx_open_cached_file(ngx_open_file_cache
         return NGX_ERROR;
     }
 
-    hash = ngx_crc32_long(name->data, name->len);
-
-    node = cache->rbtree.root;
-    sentinel = cache->rbtree.sentinel;
-
     now = ngx_time();
 
-    while (node != sentinel) {
+    hash = ngx_crc32_long(name->data, name->len);
+
+    file = ngx_open_file_lookup(cache, name, hash);
+
+    if (file) {
+
+        file->uses++;
+
+        ngx_queue_remove(&file->queue);
 
-        if (hash < node->key) {
-            node = node->left;
-            continue;
-        }
+        if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {
+
+            /* file was not used often enough to keep open */
+
+            rc = ngx_open_and_stat_file(name->data, of, pool->log);
 
-        if (hash > node->key) {
-            node = node->right;
-            continue;
+            if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
+                goto failed;
+            }
+
+            goto add_event;
         }
 
-        /* hash == node->key */
-
-        do {
-            file = (ngx_cached_open_file_t *) node;
-
-            rc = ngx_strcmp(name->data, file->name);
-
-            if (rc == 0) {
-
-                file->next->prev = file->prev;
-                file->prev->next = file->next;
-
-                if (file->event || now - file->created < of->retest) {
-                    if (file->err == 0) {
-                        of->fd = file->fd;
-                        of->uniq = file->uniq;
-                        of->mtime = file->mtime;
-                        of->size = file->size;
-
-                        of->is_dir = file->is_dir;
-                        of->is_file = file->is_file;
-                        of->is_link = file->is_link;
-                        of->is_exec = file->is_exec;
+        if (file->use_event
+            || (file->event == NULL
+                && (of->uniq == 0 || of->uniq == file->uniq)
+                && now - file->created < of->valid))
+        {
+            if (file->err == 0) {
 
-                        if (!file->is_dir) {
-                            file->count++;
-                        }
-
-                    } else {
-                        of->err = file->err;
-                    }
-
-                    goto found;
-                }
+                of->fd = file->fd;
+                of->uniq = file->uniq;
+                of->mtime = file->mtime;
+                of->size = file->size;
 
-                ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
-                               "retest open file: %s, fd:%d, c:%d, e:%d",
-                               file->name, file->fd, file->count, file->err);
-
-                if (file->is_dir) {
+                of->is_dir = file->is_dir;
+                of->is_file = file->is_file;
+                of->is_link = file->is_link;
+                of->is_exec = file->is_exec;
 
-                    /*
-                     * chances that directory became file are very small
-                     * so test_dir flag allows to use a single ngx_file_info()
-                     * syscall instead of three syscalls
-                     */
-
-                    of->test_dir = 1;
+                if (!file->is_dir) {
+                    file->count++;
+                    ngx_open_file_add_event(cache, file, of, pool->log);
                 }
 
-                rc = ngx_open_and_stat_file(name->data, of, pool->log);
+            } else {
+                of->err = file->err;
+            }
+
+            goto found;
+        }
+
+        ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
+                       "retest open file: %s, fd:%d, c:%d, e:%d",
+                       file->name, file->fd, file->count, file->err);
+
+        if (file->is_dir) {
+
+            /*
+             * chances that directory became file are very small
+             * so test_dir flag allows to use a single syscall
+             * in ngx_file_info() instead of three syscalls
+             */
+
+            of->test_dir = 1;
+        }
+
+        of->fd = file->fd;
+        of->uniq = file->uniq;
 
-                if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
-                    goto failed;
+        rc = ngx_open_and_stat_file(name->data, of, pool->log);
+
+        if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
+            goto failed;
+        }
+
+        if (of->is_dir) {
+
+            if (file->is_dir || file->err) {
+                goto update;
+            }
+
+            /* file became directory */
+
+        } else if (of->err == 0) {  /* file */
+
+            if (file->is_dir || file->err) {
+                goto add_event;
+            }
+
+            if (of->uniq == file->uniq) {
+
+                file->count++;
+
+                if (file->event) {
+                    file->use_event = 1;
                 }
 
-                if (of->is_dir) {
-                    if (file->is_dir || file->err) {
-                        goto update;
-                    }
-
-                    /* file became directory */
-
-                } else if (of->err == 0) {  /* file */
-
-                    if (file->is_dir || file->err) {
-                        goto update;
-                    }
+                goto renew;
+            }
 
-                    if (of->uniq == file->uniq
-                        && of->mtime == file->mtime
-                        && of->size == file->size)
-                    {
-                        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);
-                        }
-
-                        of->fd = file->fd;
-                        file->count++;
-
-                        goto renew;
-                    }
+            /* file was changed */
 
-                    /* file was changed */
-
-                } else { /* error to cache */
-
-                    if (file->err || file->is_dir) {
-                        goto update;
-                    }
-
-                    /* file was removed, etc. */
-                }
+        } else { /* error to cache */
 
-                if (file->count == 0) {
-                    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);
-                    }
-
-                    goto update;
-                }
-
-                ngx_rbtree_delete(&cache->rbtree, &file->node);
-
-                cache->current--;
-
-                file->close = 1;
-
-                goto create;
+            if (file->err || file->is_dir) {
+                goto update;
             }
 
-            node = (rc < 0) ? node->left : node->right;
+            /* file was removed, etc. */
+        }
+
+        if (file->count == 0) {
+
+            ngx_open_file_del_event(file);
 
-        } while (node != sentinel && hash == node->key);
+            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);
+            }
 
-        break;
+            goto add_event;
+        }
+
+        ngx_rbtree_delete(&cache->rbtree, &file->node);
+
+        cache->current--;
+
+        file->close = 1;
+
+        goto create;
     }
 
     /* not found */
 
-    file = NULL;
-
     rc = ngx_open_and_stat_file(name->data, of, pool->log);
 
     if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
@@ -349,51 +336,16 @@ create:
 
     cache->current++;
 
+    file->uses = 1;
     file->count = 0;
+    file->event = NULL;
+
+add_event:
+
+    ngx_open_file_add_event(cache, file, of, pool->log);
 
 update:
 
-    if (of->events
-        && (ngx_event_flags & NGX_USE_VNODE_EVENT)
-        && of->fd != NGX_INVALID_FILE)
-    {
-        file->event = ngx_calloc(sizeof(ngx_event_t), pool->log);
-        if (file->event== NULL) {
-            goto failed;
-        }
-
-        fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), pool->log);
-        if (fev == NULL) {
-            goto failed;
-        }
-
-        fev->fd = of->fd;
-        fev->file = file;
-        fev->cache = cache;
-
-        file->event->handler = ngx_open_file_cache_remove;
-        file->event->data = fev;
-
-        /*
-         * although vnode event may be called while ngx_cycle->poll
-         * destruction; however, cleanup procedures are run before any
-         * memory freeing and events will be canceled.
-         */
-
-        file->event->log = ngx_cycle->log;
-
-        if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)
-            != NGX_OK)
-        {
-            ngx_free(file->event->data);
-            ngx_free(file->event);
-            goto failed;
-        }
-
-    } else {
-        file->event = NULL;
-    }
-
     file->fd = of->fd;
     file->err = of->err;
 
@@ -422,16 +374,11 @@ found:
 
     file->accessed = now;
 
-    /* add to the inactive list head */
+    ngx_queue_insert_head(&cache->expire_queue, &file->queue);
 
-    file->next = cache->list_head.next;
-    file->next->prev = file;
-    file->prev = &cache->list_head;
-    cache->list_head.next = file;
-
-    ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
-                   "cached open file: %s, fd:%d, c:%d, e:%d",
-                   file->name, file->fd, file->count, file->err);
+    ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,
+                   "cached open file: %s, fd:%d, c:%d, e:%d, u:%d",
+                   file->name, file->fd, file->count, file->err, file->uses);
 
     if (of->err == 0) {
 
@@ -441,6 +388,7 @@ found:
 
             ofcln->cache = cache;
             ofcln->file = file;
+            ofcln->min_uses = of->min_uses;
             ofcln->log = pool->log;
         }
 
@@ -451,18 +399,27 @@ found:
 
 failed:
 
-    if (file && file->count == 0) {
+    if (file) {
         ngx_rbtree_delete(&cache->rbtree, &file->node);
 
         cache->current--;
 
-        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", file->name);
+        if (file->count == 0) {
+
+            if (file->fd != NGX_INVALID_FILE) {
+                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",
+                                  file->name);
+                }
+            }
+
+            ngx_free(file->name);
+            ngx_free(file);
+
+        } else {
+            file->close = 1;
         }
-
-        ngx_free(file->name);
-        ngx_free(file);
     }
 
     if (of->fd != NGX_INVALID_FILE) {
@@ -482,34 +439,38 @@ ngx_open_and_stat_file(u_char *name, ngx
     ngx_fd_t         fd;
     ngx_file_info_t  fi;
 
-    of->fd = NGX_INVALID_FILE;
-
-    if (of->test_dir) {
+    if (of->fd != NGX_INVALID_FILE) {
 
         if (ngx_file_info(name, &fi) == -1) {
-            of->err = ngx_errno;
-
-            return NGX_ERROR;
+            goto failed;
         }
 
-        of->uniq = ngx_file_uniq(&fi);
-        of->mtime = ngx_file_mtime(&fi);
-        of->size = ngx_file_size(&fi);
-        of->is_dir = ngx_is_dir(&fi);
-        of->is_file = ngx_is_file(&fi);
-        of->is_link = ngx_is_link(&fi);
-        of->is_exec = ngx_is_exec(&fi);
+        if (of->uniq == ngx_file_uniq(&fi)) {
+            goto done;
+        }
+
+    } else if (of->test_dir) {
+
+        if (ngx_file_info(name, &fi) == -1) {
+            goto failed;
+        }
 
         if (of->is_dir) {
-            return NGX_OK;
+            goto done;
         }
     }
 
-    fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+    if (!of->log) {
+        fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+
+    } else {
+        fd = ngx_open_file(name, NGX_FILE_RDWR,
+                           NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND,
+                           NGX_FILE_DEFAULT_ACCESS);
+    }
 
     if (fd == NGX_INVALID_FILE) {
-        of->err = ngx_errno;
-        return NGX_ERROR;
+        goto failed;
     }
 
     if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
@@ -521,6 +482,8 @@ ngx_open_and_stat_file(u_char *name, ngx
                           ngx_close_file_n " \"%s\" failed", name);
         }
 
+        of->fd = NGX_INVALID_FILE;
+
         return NGX_ERROR;
     }
 
@@ -530,10 +493,21 @@ ngx_open_and_stat_file(u_char *name, ngx
                           ngx_close_file_n " \"%s\" failed", name);
         }
 
-        fd = NGX_INVALID_FILE;
+        of->fd = NGX_INVALID_FILE;
+
+    } else {
+        of->fd = fd;
+
+        if (of->directio <= ngx_file_size(&fi)) {
+            if (ngx_directio(fd) == -1) {
+                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                              ngx_directio_n " \"%s\" failed", name);
+            }
+        }
     }
 
-    of->fd = fd;
+done:
+
     of->uniq = ngx_file_uniq(&fi);
     of->mtime = ngx_file_mtime(&fi);
     of->size = ngx_file_size(&fi);
@@ -543,6 +517,82 @@ ngx_open_and_stat_file(u_char *name, ngx
     of->is_exec = ngx_is_exec(&fi);
 
     return NGX_OK;
+
+failed:
+
+    of->fd = NGX_INVALID_FILE;
+    of->err = ngx_errno;
+
+    return NGX_ERROR;
+}
+
+
+/*
+ * we ignore any possible event setting error and
+ * fallback to usual periodic file retests
+ */
+
+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)
+{
+    ngx_open_file_cache_event_t  *fev;
+
+    if (!(ngx_event_flags & NGX_USE_VNODE_EVENT)
+        || !of->events
+        || file->event
+        || of->fd == NGX_INVALID_FILE
+        || file->uses < of->min_uses)
+    {
+        return;
+    }
+
+    file->use_event = 0;
+
+    file->event = ngx_calloc(sizeof(ngx_event_t), log);
+    if (file->event== NULL) {
+        return;
+    }
+
+    fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log);
+    if (fev == NULL) {
+        ngx_free(file->event);
+        file->event = NULL;
+        return;
+    }
+
+    fev->fd = of->fd;
+    fev->file = file;
+    fev->cache = cache;
+
+    file->event->handler = ngx_open_file_cache_remove;
+    file->event->data = fev;
+
+    /*
+     * although vnode event may be called while ngx_cycle->poll
+     * destruction, however, cleanup procedures are run before any
+     * memory freeing and events will be canceled.
+     */
+
+    file->event->log = ngx_cycle->log;
+
+    if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)
+        != NGX_OK)
+    {
+        ngx_free(file->event->data);
+        ngx_free(file->event);
+        file->event = NULL;
+        return;
+    }
+
+    /*
+     * we do not set file->use_event here because there may be a race
+     * condition: a file may be deleted between opening the file and
+     * adding event, so we rely upon event notification only after
+     * one file revalidation on next file access
+     */
+
+    return;
 }
 
 
@@ -553,7 +603,7 @@ ngx_open_file_cleanup(void *data)
 
     c->file->count--;
 
-    ngx_close_cached_file(c->cache, c->file, c->log);
+    ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);
 
     /* drop one or two expired open files */
     ngx_expire_old_cached_files(c->cache, 1, c->log);
@@ -562,50 +612,43 @@ ngx_open_file_cleanup(void *data)
 
 static void
 ngx_close_cached_file(ngx_open_file_cache_t *cache,
-    ngx_cached_open_file_t *file, ngx_log_t *log)
+    ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log)
 {
-    ngx_log_debug4(NGX_LOG_DEBUG_CORE, log, 0,
-                   "close cached open file: %s, fd:%d, c:%d, %d",
-                   file->name, file->fd, file->count, file->close);
+    ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0,
+                   "close cached open file: %s, fd:%d, c:%d, u:%d, %d",
+                   file->name, file->fd, file->count, file->uses, file->close);
 
     if (!file->close) {
 
         file->accessed = ngx_time();
 
-        if (cache->list_head.next != file) {
+        ngx_queue_remove(&file->queue);
 
-            /* delete from inactive list */
-
-            file->next->prev = file->prev;
-            file->prev->next = file->next;
+        ngx_queue_insert_head(&cache->expire_queue, &file->queue);
 
-            /* add to the inactive list head */
-
-            file->next = cache->list_head.next;
-            file->next->prev = file;
-            file->prev = &cache->list_head;
-            cache->list_head.next = file;
+        if (file->uses >= min_uses || file->count) {
+            return;
         }
-
-        return;
     }
 
-    if (file->event) {
-        (void) ngx_del_event(file->event, NGX_VNODE_EVENT,
-                             file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);
-
-        ngx_free(file->event->data);
-        ngx_free(file->event);
-        file->event = NULL;
-    }
+    ngx_open_file_del_event(file);
 
     if (file->count) {
         return;
     }
 
-    if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                      ngx_close_file_n " \"%s\" failed", file->name);
+    if (file->fd != NGX_INVALID_FILE) {
+
+        if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                          ngx_close_file_n " \"%s\" failed", file->name);
+        }
+
+        file->fd = NGX_INVALID_FILE;
+    }
+
+    if (!file->close) {
+        return;
     }
 
     ngx_free(file->name);
@@ -614,10 +657,28 @@ ngx_close_cached_file(ngx_open_file_cach
 
 
 static void
+ngx_open_file_del_event(ngx_cached_open_file_t *file)
+{
+    if (file->event == NULL) {
+        return;
+    }
+
+    (void) ngx_del_event(file->event, NGX_VNODE_EVENT,
+                         file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);
+
+    ngx_free(file->event->data);
+    ngx_free(file->event);
+    file->event = NULL;
+    file->use_event = 0;
+}
+
+
+static void
 ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,
     ngx_log_t *log)
 {
     time_t                   now;
+    ngx_queue_t             *q;
     ngx_cached_open_file_t  *file;
 
     now = ngx_time();
@@ -630,18 +691,19 @@ ngx_expire_old_cached_files(ngx_open_fil
 
     while (n < 3) {
 
-        file = cache->list_tail.prev;
-
-        if (file == &cache->list_head) {
+        if (ngx_queue_empty(&cache->expire_queue)) {
             return;
         }
 
+        q = ngx_queue_last(&cache->expire_queue);
+
+        file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
+
         if (n++ != 0 && now - file->accessed <= cache->inactive) {
             return;
         }
 
-        file->next->prev = file->prev;
-        file->prev->next = file->next;
+        ngx_queue_remove(q);
 
         ngx_rbtree_delete(&cache->rbtree, &file->node);
 
@@ -652,7 +714,7 @@ ngx_expire_old_cached_files(ngx_open_fil
 
         if (!file->err && !file->is_dir) {
             file->close = 1;
-            ngx_close_cached_file(cache, file, log);
+            ngx_close_cached_file(cache, file, 0, log);
 
         } else {
             ngx_free(file->name);
@@ -703,6 +765,51 @@ ngx_open_file_cache_rbtree_insert_value(
 }
 
 
+static ngx_cached_open_file_t *
+ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
+    uint32_t hash)
+{
+    ngx_int_t                rc;
+    ngx_rbtree_node_t       *node, *sentinel;
+    ngx_cached_open_file_t  *file;
+
+    node = cache->rbtree.root;
+    sentinel = cache->rbtree.sentinel;
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        /* hash == node->key */
+
+        do {
+            file = (ngx_cached_open_file_t *) node;
+
+            rc = ngx_strcmp(name->data, file->name);
+
+            if (rc == 0) {
+                return file;
+            }
+
+            node = (rc < 0) ? node->left : node->right;
+
+        } while (node != sentinel && hash == node->key);
+
+        break;
+    }
+
+    return NULL;
+}
+
+
 static void
 ngx_open_file_cache_remove(ngx_event_t *ev)
 {
@@ -712,8 +819,7 @@ ngx_open_file_cache_remove(ngx_event_t *
     fev = ev->data;
     file = fev->file;
 
-    file->next->prev = file->prev;
-    file->prev->next = file->next;
+    ngx_queue_remove(&file->queue);
 
     ngx_rbtree_delete(&fev->cache->rbtree, &file->node);
 
@@ -721,10 +827,11 @@ ngx_open_file_cache_remove(ngx_event_t *
 
     /* NGX_ONESHOT_EVENT was already deleted */
     file->event = NULL;
+    file->use_event = 0;
 
     file->close = 1;
 
-    ngx_close_cached_file(fev->cache, file, ev->log);
+    ngx_close_cached_file(fev->cache, file, 0, ev->log);
 
     /* free memory only when fev->cache and fev->file are already not needed */
 
--- a/src/core/ngx_open_file_cache.h
+++ b/src/core/ngx_open_file_cache.h
@@ -17,11 +17,15 @@ typedef struct {
     ngx_file_uniq_t          uniq;
     time_t                   mtime;
     off_t                    size;
+    off_t                    directio;
     ngx_err_t                err;
 
-    time_t                   retest;
+    time_t                   valid;
+
+    ngx_uint_t               min_uses;
 
     unsigned                 test_dir:1;
+    unsigned                 log:1;
     unsigned                 errors:1;
     unsigned                 events:1;
 
@@ -36,8 +40,7 @@ typedef struct ngx_cached_open_file_s  n
 
 struct ngx_cached_open_file_s {
     ngx_rbtree_node_t        node;
-    ngx_cached_open_file_t  *prev;
-    ngx_cached_open_file_t  *next;
+    ngx_queue_t              queue;
 
     u_char                  *name;
     time_t                   created;
@@ -49,8 +52,11 @@ struct ngx_cached_open_file_s {
     off_t                    size;
     ngx_err_t                err;
 
+    uint32_t                 uses;
+
     unsigned                 count:24;
     unsigned                 close:1;
+    unsigned                 use_event:1;
 
     unsigned                 is_dir:1;
     unsigned                 is_file:1;
@@ -63,8 +69,8 @@ struct ngx_cached_open_file_s {
 
 typedef struct {
     ngx_rbtree_t             rbtree;
-    ngx_cached_open_file_t   list_head;
-    ngx_cached_open_file_t   list_tail;
+    ngx_rbtree_node_t        sentinel;
+    ngx_queue_t              expire_queue;
 
     ngx_uint_t               current;
     ngx_uint_t               max;
@@ -75,6 +81,7 @@ typedef struct {
 typedef struct {
     ngx_open_file_cache_t   *cache;
     ngx_cached_open_file_t  *file;
+    ngx_uint_t               min_uses;
     ngx_log_t               *log;
 } ngx_open_file_cache_cleanup_t;
 
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -32,6 +32,7 @@ ngx_output_chain(ngx_output_chain_ctx_t 
     size_t        size;
     ngx_int_t     rc, last;
     ngx_uint_t    recycled;
+    ngx_buf_t    *b;
     ngx_chain_t  *cl, *out, **last_out;
 
     if (ctx->in == NULL && ctx->busy == NULL) {
@@ -161,13 +162,29 @@ ngx_output_chain(ngx_output_chain_ctx_t 
                         }
                     }
 
-                    ctx->buf = ngx_create_temp_buf(ctx->pool, size);
-                    if (ctx->buf == NULL) {
+                    b = ngx_calloc_buf(ctx->pool);
+                    if (b == NULL) {
                         return NGX_ERROR;
                     }
 
-                    ctx->buf->tag = ctx->tag;
-                    ctx->buf->recycled = recycled;
+                    /*
+                     * allocate block aligned to a disk sector size
+                     * to enable O_DIRECT
+                     */
+
+                    b->start = ngx_pmemalign(ctx->pool, size, 512);
+                    if (b->start == NULL) {
+                        return NGX_ERROR;
+                    }
+
+                    b->pos = b->start;
+                    b->last = b->start;
+                    b->end = b->last + size;
+                    b->temporary = 1;
+                    b->tag = ctx->tag;
+                    b->recycled = recycled;
+
+                    ctx->buf = b;
                     ctx->allocated++;
                 }
             }
@@ -433,8 +450,11 @@ ngx_chain_writer(void *data, ngx_chain_t
 {
     ngx_chain_writer_ctx_t *ctx = data;
 
-    off_t         size;
-    ngx_chain_t  *cl;
+    off_t              size;
+    ngx_chain_t       *cl;
+    ngx_connection_t  *c;
+
+    c = ctx->connection;
 
     for (size = 0; in; in = in->next) {
 
@@ -446,7 +466,7 @@ ngx_chain_writer(void *data, ngx_chain_t
 
         size += ngx_buf_size(in->buf);
 
-        ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->connection->log, 0,
+        ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
                        "chain writer buf fl:%d s:%uO",
                        in->buf->flush, ngx_buf_size(in->buf));
 
@@ -461,7 +481,7 @@ ngx_chain_writer(void *data, ngx_chain_t
         ctx->last = &cl->next;
     }
 
-    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->connection->log, 0,
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
                    "chain writer in: %p", ctx->out);
 
     for (cl = ctx->out; cl; cl = cl->next) {
@@ -476,14 +496,13 @@ ngx_chain_writer(void *data, ngx_chain_t
         size += ngx_buf_size(cl->buf);
     }
 
-    if (size == 0 && !ctx->connection->buffered) {
+    if (size == 0 && !c->buffered) {
         return NGX_OK;
     }
 
-    ctx->out = ctx->connection->send_chain(ctx->connection, ctx->out,
-                                           ctx->limit);
+    ctx->out = c->send_chain(c, ctx->out, ctx->limit);
 
-    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->connection->log, 0,
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
                    "chain writer out: %p", ctx->out);
 
     if (ctx->out == NGX_CHAIN_ERROR) {
@@ -493,7 +512,7 @@ ngx_chain_writer(void *data, ngx_chain_t
     if (ctx->out == NULL) {
         ctx->last = &ctx->out;
 
-        if (!ctx->connection->buffered) {
+        if (!c->buffered) {
             return NGX_OK;
         }
     }
--- a/src/core/ngx_palloc.c
+++ b/src/core/ngx_palloc.c
@@ -8,6 +8,10 @@
 #include <ngx_core.h>
 
 
+static void *ngx_palloc_block(ngx_pool_t *pool, size_t size);
+static void *ngx_palloc_large(ngx_pool_t *pool, size_t size);
+
+
 ngx_pool_t *
 ngx_create_pool(size_t size, ngx_log_t *log)
 {
@@ -18,11 +22,15 @@ ngx_create_pool(size_t size, ngx_log_t *
         return NULL;
     }
 
-    p->last = (u_char *) p + sizeof(ngx_pool_t);
-    p->end = (u_char *) p + size;
+    p->d.last = (u_char *) p + sizeof(ngx_pool_t);
+    p->d.end = (u_char *) p + size;
+    p->d.next = NULL;
+
+    size = size - sizeof(ngx_pool_t);
+    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
+
     p->current = p;
     p->chain = NULL;
-    p->next = NULL;
     p->large = NULL;
     p->cleanup = NULL;
     p->log = log;
@@ -40,6 +48,8 @@ ngx_destroy_pool(ngx_pool_t *pool)
 
     for (c = pool->cleanup; c; c = c->next) {
         if (c->handler) {
+            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
+                           "run cleanup: %p", c);
             c->handler(c->data);
         }
     }
@@ -60,9 +70,9 @@ ngx_destroy_pool(ngx_pool_t *pool)
      * so we can not use this log while the free()ing the pool
      */
 
-    for (p = pool, n = pool->next; /* void */; p = n, n = n->next) {
+    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
         ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
-                       "free: %p, unused: %uz", p, p->end - p->last);
+                       "free: %p, unused: %uz", p, p->d.end - p->d.last);
 
         if (n == NULL) {
             break;
@@ -71,7 +81,7 @@ ngx_destroy_pool(ngx_pool_t *pool)
 
 #endif
 
-    for (p = pool, n = pool->next; /* void */; p = n, n = n->next) {
+    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
         ngx_free(p);
 
         if (n == NULL) {
@@ -84,82 +94,136 @@ ngx_destroy_pool(ngx_pool_t *pool)
 void *
 ngx_palloc(ngx_pool_t *pool, size_t size)
 {
-    u_char            *m;
-    ngx_pool_t        *p, *n, *current;
-    ngx_pool_large_t  *large;
+    u_char      *m;
+    ngx_pool_t  *p;
 
-    if (size <= (size_t) NGX_MAX_ALLOC_FROM_POOL
-        && size <= (size_t) (pool->end - (u_char *) pool)
-                   - (size_t) ngx_align_ptr(sizeof(ngx_pool_t), NGX_ALIGNMENT))
-    {
+    if (size <= pool->max) {
+
         p = pool->current;
-        current = p;
-
-        for ( ;; ) {
-
-#if (NGX_HAVE_NONALIGNED)
 
-            /*
-             * allow non-aligned memory blocks for small allocations (1, 2,
-             * or 3 bytes) and for odd length strings (struct's have aligned
-             * size)
-             */
+        do {
+            m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
 
-            if (size < sizeof(int) || (size & 1)) {
-                m = p->last;
+            if ((size_t) (p->d.end - m) >= size) {
+                p->d.last = m + size;
 
-            } else
-#endif
-
-            {
-                m = ngx_align_ptr(p->last, NGX_ALIGNMENT);
+                return m;
             }
 
-            if ((size_t) (p->end - m) >= size) {
-                p->last = m + size;
+            p = p->d.next;
+
+        } while (p);
+
+        return ngx_palloc_block(pool, size);
+    }
+
+    return ngx_palloc_large(pool, size);
+}
+
+
+void *
+ngx_pnalloc(ngx_pool_t *pool, size_t size)
+{
+    u_char      *m;
+    ngx_pool_t  *p;
+
+    if (size <= pool->max) {
+
+        p = pool->current;
+
+        do {
+            m = p->d.last;
+
+            if ((size_t) (p->d.end - m) >= size) {
+                p->d.last = m + size;
 
                 return m;
             }
 
-            if ((size_t) (p->end - m) < NGX_ALIGNMENT) {
-                current = p->next;
-            }
+            p = p->d.next;
+
+        } while (p);
 
-            if (p->next == NULL) {
-                break;
-            }
+        return ngx_palloc_block(pool, size);
+    }
 
-            p = p->next;
-            pool->current = current;
-        }
+    return ngx_palloc_large(pool, size);
+}
+
 
-        /* allocate a new pool block */
-
-        n = ngx_create_pool((size_t) (p->end - (u_char *) p), p->log);
-        if (n == NULL) {
-            return NULL;
-        }
+static void *
+ngx_palloc_block(ngx_pool_t *pool, size_t size)
+{
+    u_char      *m;
+    size_t       psize;
+    ngx_pool_t  *p, *new, *current;
 
-        pool->current = current ? current : n;
+    psize = (size_t) (pool->d.end - (u_char *) pool);
 
-        p->next = n;
-        m = ngx_align_ptr(n->last, NGX_ALIGNMENT);
-        n->last = m + size;
-
-        return m;
+    m = ngx_alloc(psize, pool->log);
+    if (m == NULL) {
+        return NULL;
     }
 
-#if 0
-    p = ngx_memalign(ngx_pagesize, size, pool->log);
-    if (p == NULL) {
-        return NULL;
+    new = (ngx_pool_t *) m;
+
+    new->d.end = m + psize;
+    new->d.next = NULL;
+
+    m += sizeof(ngx_pool_data_t);
+    new->d.last = m + size;
+
+    current = pool->current;
+
+    for (p = current; p->d.next; p = p->d.next) {
+        if ((size_t) (p->d.end - p->d.last) < NGX_ALIGNMENT) {
+            current = p->d.next;
+        }
     }
-#else
+
+    p->d.next = new;
+
+    pool->current = current ? current : new;
+
+    return m;
+}
+
+
+static void *
+ngx_palloc_large(ngx_pool_t *pool, size_t size)
+{
+    void              *p;
+    ngx_pool_large_t  *large;
+
     p = ngx_alloc(size, pool->log);
     if (p == NULL) {
         return NULL;
     }
-#endif
+
+    large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
+    if (large == NULL) {
+        ngx_free(p);
+        return NULL;
+    }
+
+    large->alloc = p;
+    large->next = pool->large;
+    pool->large = large;
+
+    return p;
+}
+
+
+void *
+ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
+{
+    void              *p;
+    ngx_pool_large_t  *large;
+
+    p = ngx_memalign(alignment, size, pool->log);
+    if (p == NULL) {
+        return NULL;
+    }
 
     large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
     if (large == NULL) {
@@ -245,8 +309,8 @@ ngx_pool_cleanup_file(void *data)
 {
     ngx_pool_cleanup_file_t  *c = data;
 
-    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "run cleanup: %p, fd:%d",
-                   c, c->fd);
+    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d",
+                   c->fd);
 
     if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
         ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
@@ -262,8 +326,8 @@ ngx_pool_delete_file(void *data)
 
     ngx_err_t  err;
 
-    ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, c->log, 0, "run cleanup: %p, fd:%d %s",
-                   c, c->fd, c->name);
+    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s",
+                   c->fd, c->name);
 
     if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {
         err = ngx_errno;
--- a/src/core/ngx_palloc.h
+++ b/src/core/ngx_palloc.h
@@ -43,12 +43,18 @@ struct ngx_pool_large_s {
 };
 
 
-struct ngx_pool_s {
+typedef struct {
     u_char               *last;
     u_char               *end;
+    ngx_pool_t           *next;
+} ngx_pool_data_t;
+
+
+struct ngx_pool_s {
+    ngx_pool_data_t       d;
+    size_t                max;
     ngx_pool_t           *current;
     ngx_chain_t          *chain;
-    ngx_pool_t           *next;
     ngx_pool_large_t     *large;
     ngx_pool_cleanup_t   *cleanup;
     ngx_log_t            *log;
@@ -69,7 +75,9 @@ ngx_pool_t *ngx_create_pool(size_t size,
 void ngx_destroy_pool(ngx_pool_t *pool);
 
 void *ngx_palloc(ngx_pool_t *pool, size_t size);
+void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
 void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
+void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
 ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);
 
 
--- a/src/core/ngx_parse.c
+++ b/src/core/ngx_parse.c
@@ -11,15 +11,15 @@
 ssize_t
 ngx_parse_size(ngx_str_t *line)
 {
-    u_char     last;
+    u_char     unit;
     size_t     len;
     ssize_t    size;
     ngx_int_t  scale;
 
     len = line->len;
-    last = line->data[len - 1];
+    unit = line->data[len - 1];
 
-    switch (last) {
+    switch (unit) {
     case 'K':
     case 'k':
         len--;
@@ -50,15 +50,15 @@ ngx_parse_size(ngx_str_t *line)
 off_t
 ngx_parse_offset(ngx_str_t *line)
 {
-    u_char     last;
+    u_char     unit;
     off_t      offset;
     size_t     len;
     ngx_int_t  scale;
 
     len = line->len;
-    last = line->data[len - 1];
+    unit = line->data[len - 1];
 
-    switch (last) {
+    switch (unit) {
     case 'K':
     case 'k':
         len--;
@@ -93,12 +93,11 @@ ngx_parse_offset(ngx_str_t *line)
 
 
 ngx_int_t
-ngx_parse_time(ngx_str_t *line, ngx_int_t sec)
+ngx_parse_time(ngx_str_t *line, ngx_uint_t sec)
 {
-    size_t       len;
-    u_char      *start, last;
+    u_char      *p, *last;
     ngx_int_t    value, total, scale;
-    ngx_uint_t   max, i;
+    ngx_uint_t   max, valid;
     enum {
         st_start = 0,
         st_year,
@@ -112,39 +111,30 @@ ngx_parse_time(ngx_str_t *line, ngx_int_
         st_last
     } step;
 
-
-    start = line->data;
-    len = 0;
+    valid = 0;
+    value = 0;
     total = 0;
     step = sec ? st_start : st_month;
+    scale = sec ? 1 : 1000;
 
-    for (i = 0; /* void */ ; i++) {
+    p = line->data;
+    last = p + line->len;
 
-        if (i < line->len) {
-            if (line->data[i] != ' ') {
-                len++;
-                continue;
-            }
+    while (p < last) {
 
-            if (line->data[i] == ' ' && len == 0) {
-                start = &line->data[i + 1];
-                continue;
-            }
+        if (*p >= '0' && *p <= '9') {
+            value = value * 10 + (*p++ - '0');
+            valid = 1;
+            continue;
         }
 
-        if (len == 0) {
-            break;
-        }
+        switch (*p++) {
 
-        last = line->data[i - 1];
-
-        switch (last) {
         case 'y':
             if (step > st_start) {
                 return NGX_ERROR;
             }
             step = st_year;
-            len--;
             max = 68;
             scale = 60 * 60 * 24 * 365;
             break;
@@ -154,7 +144,6 @@ ngx_parse_time(ngx_str_t *line, ngx_int_
                 return NGX_ERROR;
             }
             step = st_month;
-            len--;
             max = 828;
             scale = 60 * 60 * 24 * 30;
             break;
@@ -164,7 +153,6 @@ ngx_parse_time(ngx_str_t *line, ngx_int_
                 return NGX_ERROR;
             }
             step = st_week;
-            len--;
             max = 3550;
             scale = 60 * 60 * 24 * 7;
             break;
@@ -174,7 +162,6 @@ ngx_parse_time(ngx_str_t *line, ngx_int_
                 return NGX_ERROR;
             }
             step = st_day;
-            len--;
             max = 24855;
             scale = 60 * 60 * 24;
             break;
@@ -184,52 +171,49 @@ ngx_parse_time(ngx_str_t *line, ngx_int_
                 return NGX_ERROR;
             }
             step = st_hour;
-            len--;
             max = 596523;
             scale = 60 * 60;
             break;
 
         case 'm':
+            if (*p == 's') {
+                if (sec || step > st_sec) {
+                    return NGX_ERROR;
+                }
+                p++;
+                step = st_msec;
+                max = 2147483647;
+                scale = 1;
+                break;
+            }
+
             if (step > st_hour) {
                 return NGX_ERROR;
             }
             step = st_min;
-            len--;
             max = 35791394;
             scale = 60;
             break;
 
         case 's':
-            len--;
-
-            if (line->data[i - 2] == 'm') {
-                if (sec || step > st_sec) {
-                    return NGX_ERROR;
-                }
-                step = st_msec;
-                len--;
-                max = 2147483647;
-                scale = 1;
-                break;
-            }
-
             if (step > st_min) {
                 return NGX_ERROR;
             }
-
             step = st_sec;
             max = 2147483647;
             scale = 1;
             break;
 
-        default:
+        case ' ':
+            if (step > st_min) {
+                return NGX_ERROR;
+            }
             step = st_last;
             max = 2147483647;
             scale = 1;
-        }
+            break;
 
-        value = ngx_atoi(start, len);
-        if (value == NGX_ERROR) {
+        default:
             return NGX_ERROR;
         }
 
@@ -238,23 +222,27 @@ ngx_parse_time(ngx_str_t *line, ngx_int_
             max /= 1000;
         }
 
-        if ((u_int) value > max) {
-            return NGX_PARSE_LARGE_TIME;
+        if ((ngx_uint_t) value > max) {
+            return NGX_ERROR;
         }
 
         total += value * scale;
 
-        if ((u_int) total > 2147483647) {
-            return NGX_PARSE_LARGE_TIME;
+        if ((ngx_uint_t) total > 2147483647) {
+            return NGX_ERROR;
         }
 
-        if (i >= line->len) {
-            break;
+        value = 0;
+        scale = sec ? 1 : 1000;
+
+        while (p < last && *p == ' ') {
+            p++;
         }
-
-        len = 0;
-        start = &line->data[i + 1];
     }
 
-    return total;
+    if (valid) {
+        return total + value * scale;
+    }
+
+    return NGX_ERROR;
 }
--- a/src/core/ngx_parse.h
+++ b/src/core/ngx_parse.h
@@ -17,7 +17,7 @@
 
 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_int_t sec);
+ngx_int_t ngx_parse_time(ngx_str_t *line, ngx_uint_t sec);
 
 
 #endif /* _NGX_PARSE_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_queue.c
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * find the middle queue element if the queue has odd number of elements
+ * or the first element of the queue's second part otherwise
+ */
+
+ngx_queue_t *
+ngx_queue_middle(ngx_queue_t *queue)
+{
+    ngx_queue_t  *middle, *next;
+
+    middle = ngx_queue_head(queue);
+
+    if (middle == ngx_queue_last(queue)) {
+        return middle;
+    }
+
+    next = ngx_queue_head(queue);
+
+    for ( ;; ) {
+        middle = ngx_queue_next(middle);
+
+        next = ngx_queue_next(next);
+
+        if (next == ngx_queue_last(queue)) {
+            return middle;
+        }
+
+        next = ngx_queue_next(next);
+
+        if (next == ngx_queue_last(queue)) {
+            return middle;
+        }
+    }
+}
+
+
+/* the stable insertion sort */
+
+void
+ngx_queue_sort(ngx_queue_t *queue,
+    ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))
+{
+    ngx_queue_t  *q, *prev, *next;
+
+    q = ngx_queue_head(queue);
+
+    if (q == ngx_queue_last(queue)) {
+        return;
+    }
+
+    for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) {
+
+        prev = ngx_queue_prev(q);
+        next = ngx_queue_next(q);
+
+        ngx_queue_remove(q);
+
+        do {
+            if (cmp(prev, q) <= 0) {
+                break;
+            }
+
+            prev = ngx_queue_prev(prev);
+
+        } while (prev != ngx_queue_sentinel(queue));
+
+        ngx_queue_insert_after(prev, q);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_queue.h
@@ -0,0 +1,111 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#ifndef _NGX_QUEUE_H_INCLUDED_
+#define _NGX_QUEUE_H_INCLUDED_
+
+
+typedef struct ngx_queue_s  ngx_queue_t;
+
+struct ngx_queue_s {
+    ngx_queue_t  *prev;
+    ngx_queue_t  *next;
+};
+
+
+#define ngx_queue_init(q)                                                     \
+    (q)->prev = q;                                                            \
+    (q)->next = q
+
+
+#define ngx_queue_empty(h)                                                    \
+    (h == (h)->prev)
+
+
+#define ngx_queue_insert_head(h, x)                                           \
+    (x)->next = (h)->next;                                                    \
+    (x)->next->prev = x;                                                      \
+    (x)->prev = h;                                                            \
+    (h)->next = x
+
+
+#define ngx_queue_insert_after   ngx_queue_insert_head
+
+
+#define ngx_queue_insert_tail(h, x)                                           \
+    (x)->prev = (h)->prev;                                                    \
+    (x)->prev->next = x;                                                      \
+    (x)->next = h;                                                            \
+    (h)->prev = x
+
+
+#define ngx_queue_head(h)                                                     \
+    (h)->next
+
+
+#define ngx_queue_last(h)                                                     \
+    (h)->prev
+
+
+#define ngx_queue_sentinel(h)                                                 \
+    (h)
+
+
+#define ngx_queue_next(q)                                                     \
+    (q)->next
+
+
+#define ngx_queue_prev(q)                                                     \
+    (q)->prev
+
+
+#if (NGX_DEBUG)
+
+#define ngx_queue_remove(x)                                                   \
+    (x)->next->prev = (x)->prev;                                              \
+    (x)->prev->next = (x)->next;                                              \
+    (x)->prev = NULL;                                                         \
+    (x)->next = NULL
+
+#else
+
+#define ngx_queue_remove(x)                                                   \
+    (x)->next->prev = (x)->prev;                                              \
+    (x)->prev->next = (x)->next
+
+#endif
+
+
+#define ngx_queue_split(h, q, n)                                              \
+    (n)->prev = (h)->prev;                                                    \
+    (n)->prev->next = n;                                                      \
+    (n)->next = q;                                                            \
+    (h)->prev = (q)->prev;                                                    \
+    (h)->prev->next = h;                                                      \
+    (q)->prev = n;
+
+
+#define ngx_queue_add(h, n)                                                   \
+    (h)->prev->next = (n)->next;                                              \
+    (n)->next->prev = (h)->prev;                                              \
+    (h)->prev = (n)->prev;                                                    \
+    (h)->prev->next = h;
+
+
+#define ngx_queue_data(q, type, link)                                         \
+    (type *) ((u_char *) q - offsetof(type, link))
+
+
+ngx_queue_t *ngx_queue_middle(ngx_queue_t *queue);
+void ngx_queue_sort(ngx_queue_t *queue,
+    ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *));
+
+
+#endif /* _NGX_QUEUE_H_INCLUDED_ */
--- a/src/core/ngx_rbtree.c
+++ b/src/core/ngx_rbtree.c
@@ -97,28 +97,20 @@ void
 ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
     ngx_rbtree_node_t *sentinel)
 {
+    ngx_rbtree_node_t  **p;
+
     for ( ;; ) {
 
-        if (node->key < temp->key) {
-
-            if (temp->left == sentinel) {
-                temp->left = node;
-                break;
-            }
-
-            temp = temp->left;
+        p = (node->key < temp->key) ? &temp->left : &temp->right;
 
-        } else {
+        if (*p == sentinel) {
+            break;
+        }
 
-            if (temp->right == sentinel) {
-                temp->right = node;
-                break;
-            }
-
-            temp = temp->right;
-        }
+        temp = *p;
     }
 
+    *p = node;
     node->parent = temp;
     node->left = sentinel;
     node->right = sentinel;
@@ -130,6 +122,8 @@ void
 ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
     ngx_rbtree_node_t *sentinel)
 {
+    ngx_rbtree_node_t  **p;
+
     for ( ;; ) {
 
         /*
@@ -139,29 +133,20 @@ ngx_rbtree_insert_timer_value(ngx_rbtree
          * The comparison takes into account that overflow.
          */
 
-        if ((ngx_rbtree_key_int_t) node->key - (ngx_rbtree_key_int_t) temp->key
-            < 0)
-        {
-            /*  node->key < temp->key */
+        /*  node->key < temp->key */
 
-            if (temp->left == sentinel) {
-                temp->left = node;
-                break;
-            }
+        p = ((ngx_rbtree_key_int_t) node->key - (ngx_rbtree_key_int_t) temp->key
+              < 0)
+            ? &temp->left : &temp->right;
 
-            temp = temp->left;
-
-        } else {
+        if (*p == sentinel) {
+            break;
+        }
 
-            if (temp->right == sentinel) {
-                temp->right = node;
-                break;
-            }
-
-            temp = temp->right;
-        }
+        temp = *p;
     }
 
+    *p = node;
     node->parent = temp;
     node->left = sentinel;
     node->right = sentinel;
@@ -257,13 +242,13 @@ ngx_rbtree_delete(ngx_thread_volatile ng
         if (subst->right != sentinel) {
             subst->right->parent = subst;
         }
+    }
 
-        /* DEBUG stuff */
-        node->left = NULL;
-        node->right = NULL;
-        node->parent = NULL;
-        node->key = 0;
-    }
+    /* DEBUG stuff */
+    node->left = NULL;
+    node->right = NULL;
+    node->parent = NULL;
+    node->key = 0;
 
     if (red) {
         return;
--- a/src/core/ngx_rbtree.h
+++ b/src/core/ngx_rbtree.h
@@ -40,6 +40,13 @@ struct ngx_rbtree_s {
 };
 
 
+#define ngx_rbtree_init(tree, s, i)                                           \
+    ngx_rbtree_sentinel_init(s);                                              \
+    (tree)->root = s;                                                         \
+    (tree)->sentinel = s;                                                     \
+    (tree)->insert = i
+
+
 void ngx_rbtree_insert(ngx_thread_volatile ngx_rbtree_t *tree,
     ngx_rbtree_node_t *node);
 void ngx_rbtree_delete(ngx_thread_volatile ngx_rbtree_t *tree,
--- a/src/core/ngx_regex.c
+++ b/src/core/ngx_regex.c
@@ -114,6 +114,39 @@ ngx_regex_exec(ngx_regex_t *re, ngx_str_
 }
 
 
+ngx_int_t
+ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log)
+{
+    ngx_int_t         n;
+    ngx_uint_t        i;
+    ngx_regex_elt_t  *re;
+
+    re = a->elts;
+
+    for (i = 0; i < a->nelts; i++) {
+
+        n = ngx_regex_exec(re[i].regex, s, NULL, 0);
+
+        if (n == NGX_REGEX_NO_MATCHED) {
+            continue;
+        }
+
+        if (n < 0) {
+            ngx_log_error(NGX_LOG_ALERT, log, 0,
+                          ngx_regex_exec_n " failed: %d on \"%V\" using \"%s\"",
+                          n, s, re[i].name);
+            return NGX_ERROR;
+        }
+
+        /* match */
+
+        return NGX_OK;
+    }
+
+    return NGX_DECLINED;
+}
+
+
 static void * ngx_libc_cdecl
 ngx_regex_malloc(size_t size)
 {
--- a/src/core/ngx_regex.h
+++ b/src/core/ngx_regex.h
@@ -20,12 +20,20 @@
 
 typedef pcre  ngx_regex_t;
 
+typedef struct {
+    ngx_regex_t   *regex;
+    u_char        *name;
+} ngx_regex_elt_t;
+
+
 void ngx_regex_init(void);
 ngx_regex_t *ngx_regex_compile(ngx_str_t *pattern, ngx_int_t options,
     ngx_pool_t *pool, ngx_str_t *err);
 ngx_int_t ngx_regex_capture_count(ngx_regex_t *re);
 ngx_int_t ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures,
     ngx_int_t size);
+ngx_int_t ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log);
+
 
 #define ngx_regex_exec_n           "pcre_exec()"
 #define ngx_regex_capture_count_n  "pcre_fullinfo()"
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_resolver.c
@@ -0,0 +1,2133 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#define NGX_RESOLVER_UDP_SIZE   4096
+
+
+typedef struct {
+    u_char  ident_hi;
+    u_char  ident_lo;
+    u_char  flags_hi;
+    u_char  flags_lo;
+    u_char  nqs_hi;
+    u_char  nqs_lo;
+    u_char  nan_hi;
+    u_char  nan_lo;
+    u_char  nns_hi;
+    u_char  nns_lo;
+    u_char  nar_hi;
+    u_char  nar_lo;
+} ngx_resolver_query_t;
+
+
+typedef struct {
+    u_char  type_hi;
+    u_char  type_lo;
+    u_char  class_hi;
+    u_char  class_lo;
+} ngx_resolver_qs_t;
+
+
+typedef struct {
+    u_char  type_hi;
+    u_char  type_lo;
+    u_char  class_hi;
+    u_char  class_lo;
+    u_char  ttl[4];
+    u_char  len_hi;
+    u_char  len_lo;
+} ngx_resolver_an_t;
+
+
+ngx_int_t ngx_udp_connect(ngx_udp_connection_t *uc);
+
+
+static void ngx_resolver_cleanup(void *data);
+static void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree);
+static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r,
+    ngx_resolver_ctx_t *ctx);
+static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree,
+    ngx_queue_t *queue);
+static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r,
+    ngx_resolver_node_t *rn);
+static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_node_t *rn,
+    ngx_resolver_ctx_t *ctx);
+static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_node_t *rn,
+    ngx_resolver_ctx_t *ctx);
+static void ngx_resolver_resend_handler(ngx_event_t *ev);
+static time_t ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree,
+    ngx_queue_t *queue);
+static void ngx_resolver_read_response(ngx_event_t *rev);
+static void ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf,
+    size_t n);
+static void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,
+    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans);
+static void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,
+    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan);
+static ngx_resolver_node_t *ngx_resolver_lookup_name(ngx_resolver_t *r,
+    ngx_str_t *name, uint32_t hash);
+static ngx_resolver_node_t *ngx_resolver_lookup_addr(ngx_resolver_t *r,
+    in_addr_t addr);
+static void ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+static ngx_int_t ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name,
+    u_char *buf, u_char *src, u_char *last);
+static void ngx_resolver_timeout_handler(ngx_event_t *ev);
+static void ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn);
+static void *ngx_resolver_alloc(ngx_resolver_t *r, size_t size);
+static void *ngx_resolver_calloc(ngx_resolver_t *r, size_t size);
+static void ngx_resolver_free(ngx_resolver_t *r, void *p);
+static void ngx_resolver_free_locked(ngx_resolver_t *r, void *p);
+static void *ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size);
+
+
+/* STUB: ngx_peer_addr_t * */
+
+ngx_resolver_t *
+ngx_resolver_create(ngx_conf_t *cf, ngx_peer_addr_t *addr)
+{
+    ngx_resolver_t        *r;
+    ngx_pool_cleanup_t    *cln;
+    ngx_udp_connection_t  *uc;
+
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        return NULL;
+    }
+
+    cln->handler = ngx_resolver_cleanup;
+
+    r = ngx_calloc(sizeof(ngx_resolver_t), cf->log);
+    if (r == NULL) {
+        return NULL;
+    }
+
+    cln->data = r;
+
+    r->event = ngx_calloc(sizeof(ngx_event_t), cf->log);
+    if (r->event == NULL) {
+        return NULL;
+    }
+
+    ngx_rbtree_init(&r->name_rbtree, &r->name_sentinel,
+                    ngx_resolver_rbtree_insert_value);
+
+    ngx_rbtree_init(&r->addr_rbtree, &r->addr_sentinel,
+                    ngx_rbtree_insert_value);
+
+    ngx_queue_init(&r->name_resend_queue);
+    ngx_queue_init(&r->addr_resend_queue);
+
+    ngx_queue_init(&r->name_expire_queue);
+    ngx_queue_init(&r->addr_expire_queue);
+
+    r->event->handler = ngx_resolver_resend_handler;
+    r->event->data = r;
+    r->event->log = cf->cycle->new_log;
+    r->ident = -1;
+
+    r->resend_timeout = 5;
+    r->expire = 30;
+    r->valid = 300;
+
+    r->log = cf->cycle->new_log;
+    r->log_level = NGX_LOG_ALERT;
+
+    if (addr) {
+        uc = ngx_calloc(sizeof(ngx_udp_connection_t), cf->log);
+        if (uc == NULL) {
+            return NULL;
+        }
+
+        r->udp_connection = uc;
+
+        uc->sockaddr = addr->sockaddr;
+        uc->socklen = addr->socklen;
+        uc->server = addr->name;
+        uc->log = cf->cycle->new_log;
+    }
+
+    return r;
+}
+
+
+static void
+ngx_resolver_cleanup(void *data)
+{
+    ngx_resolver_t  *r = data;
+
+    if (r) {
+        ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+                       "cleanup resolver");
+
+        ngx_resolver_cleanup_tree(r, &r->name_rbtree);
+
+        ngx_resolver_cleanup_tree(r, &r->addr_rbtree);
+
+        if (r->event) {
+            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);
+        }
+
+        ngx_free(r);
+    }
+}
+
+
+static void
+ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree)
+{
+    ngx_resolver_ctx_t   *ctx, *next;
+    ngx_resolver_node_t  *rn;
+
+    while (tree->root != tree->sentinel) {
+
+        rn = (ngx_resolver_node_t *) ngx_rbtree_min(tree->root, tree->sentinel);
+
+        ngx_queue_remove(&rn->queue);
+
+        for (ctx = rn->waiting; ctx; ctx = next) {
+            next = ctx->next;
+
+            if (ctx->event) {
+                ngx_resolver_free(r, ctx->event);
+            }
+
+            ngx_resolver_free(r, ctx);
+        }
+
+        ngx_rbtree_delete(tree, &rn->node);
+
+        ngx_resolver_free_node(r, rn);
+    }
+}
+
+
+ngx_resolver_ctx_t *
+ngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp)
+{
+    in_addr_t            addr;
+    ngx_resolver_ctx_t  *ctx;
+
+    if (temp) {
+        addr = ngx_inet_addr(temp->name.data, temp->name.len);
+
+        if (addr != INADDR_NONE) {
+            temp->resolver = r;
+            temp->state = NGX_OK;
+            temp->naddrs = 1;
+            temp->addrs = &temp->addr;
+            temp->addr = addr;
+            temp->quick = 1;
+
+            return temp;
+        }
+    }
+
+    if (r->udp_connection == NULL) {
+        return NGX_NO_RESOLVER;
+    }
+
+    ctx = ngx_resolver_calloc(r, sizeof(ngx_resolver_ctx_t));
+
+    if (ctx) {
+        ctx->resolver = r;
+    }
+
+    return ctx;
+}
+
+
+ngx_int_t
+ngx_resolve_name(ngx_resolver_ctx_t *ctx)
+{
+    ngx_int_t        rc;
+    ngx_resolver_t  *r;
+
+    r = ctx->resolver;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,
+                   "resolve: \"%V\"", &ctx->name);
+
+    if (ctx->quick) {
+        ctx->handler(ctx);
+        return NGX_OK;
+    }
+
+    /* lock name mutex */
+
+    rc = ngx_resolve_name_locked(r, ctx);
+
+    if (rc == NGX_OK) {
+        return NGX_OK;
+    }
+
+    /* unlock name mutex */
+
+    if (rc == NGX_AGAIN) {
+        return NGX_OK;
+    }
+
+    /* NGX_ERROR */
+
+    if (ctx->event) {
+        ngx_resolver_free(r, ctx->event);
+    }
+
+    ngx_resolver_free(r, ctx);
+
+    return NGX_ERROR;
+}
+
+
+void
+ngx_resolve_name_done(ngx_resolver_ctx_t *ctx)
+{
+    uint32_t              hash;
+    ngx_resolver_t       *r;
+    ngx_resolver_ctx_t   *w, **p;
+    ngx_resolver_node_t  *rn;
+
+    r = ctx->resolver;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,
+                   "resolve name done: %i", ctx->state);
+
+    if (ctx->quick) {
+        return;
+    }
+
+    if (ctx->event && ctx->event->timer_set) {
+        ngx_del_timer(ctx->event);
+    }
+
+    /* lock name mutex */
+
+    if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
+
+        hash = ngx_crc32_short(ctx->name.data, ctx->name.len);
+
+        rn = ngx_resolver_lookup_name(r, &ctx->name, hash);
+
+        if (rn) {
+            p = &rn->waiting;
+            w = rn->waiting;
+
+            while (w) {
+                if (w == ctx) {
+                    *p = w->next;
+
+                    goto done;
+                }
+
+                p = &w->next;
+                w = w->next;
+            }
+        }
+
+        ngx_log_error(NGX_LOG_ALERT, r->log, 0,
+                      "could not cancel %V resolving", &ctx->name);
+    }
+
+done:
+
+    ngx_resolver_expire(r, &r->name_rbtree, &r->name_expire_queue);
+
+    /* unlock name mutex */
+
+    /* lock alloc mutex */
+
+    if (ctx->event) {
+        ngx_resolver_free_locked(r, ctx->event);
+    }
+
+    ngx_resolver_free_locked(r, ctx);
+
+    /* unlock alloc mutex */
+}
+
+
+/* NGX_RESOLVE_A only */
+
+static ngx_int_t
+ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
+{
+    uint32_t              hash;
+    in_addr_t             addr, *addrs;
+    ngx_int_t             rc;
+    ngx_uint_t            naddrs;
+    ngx_resolver_ctx_t   *next;
+    ngx_resolver_node_t  *rn;
+
+    hash = ngx_crc32_short(ctx->name.data, ctx->name.len);
+
+    rn = ngx_resolver_lookup_name(r, &ctx->name, hash);
+
+    if (rn) {
+
+        if (rn->valid >= ngx_time()) {
+
+            ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached");
+
+            ngx_queue_remove(&rn->queue);
+
+            rn->expire = ngx_time() + r->expire;
+
+            ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);
+
+            naddrs = rn->naddrs;
+
+            if (naddrs) {
+
+                /* NGX_RESOLVE_A answer */
+
+                if (naddrs != 1) {
+                    addr = 0;
+                    addrs = ngx_resolver_dup(r, rn->u.addrs,
+                                             naddrs * sizeof(in_addr_t));
+                    if (addrs == NULL) {
+                        return NGX_ERROR;
+                    }
+
+                } else {
+                    addr = rn->u.addr;
+                    addrs = NULL;
+                }
+
+                ctx->next = rn->waiting;
+                rn->waiting = NULL;
+
+                /* unlock name mutex */
+
+                do {
+                    ctx->state = NGX_OK;
+                    ctx->naddrs = naddrs;
+                    ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs;
+                    ctx->addr = addr;
+                    next = ctx->next;
+
+                    ctx->handler(ctx);
+
+                    ctx = next;
+                } while (ctx);
+
+                if (addrs) {
+                    ngx_resolver_free(r, addrs);
+                }
+
+                return NGX_OK;
+            }
+
+            /* NGX_RESOLVE_CNAME */
+
+            if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) {
+
+                ctx->name.len = rn->cnlen;
+                ctx->name.data = rn->u.cname;
+
+                return ngx_resolve_name_locked(r, ctx);
+            }
+
+            ctx->next = rn->waiting;
+            rn->waiting = NULL;
+
+            /* unlock name mutex */
+
+            do {
+                ctx->state = NGX_RESOLVE_NXDOMAIN;
+                next = ctx->next;
+
+                ctx->handler(ctx);
+
+                ctx = next;
+            } while (ctx);
+
+            return NGX_OK;
+        }
+
+        if (rn->waiting) {
+
+            ctx->next = rn->waiting;
+            rn->waiting = ctx;
+
+            return NGX_AGAIN;
+        }
+
+        ngx_queue_remove(&rn->queue);
+
+        /* lock alloc mutex */
+
+        ngx_resolver_free_locked(r, rn->query);
+        rn->query = NULL;
+
+        if (rn->cnlen) {
+            ngx_resolver_free_locked(r, rn->u.cname);
+        }
+
+        if (rn->naddrs > 1) {
+            ngx_resolver_free_locked(r, rn->u.addrs);
+        }
+
+        /* unlock alloc mutex */
+
+    } else {
+
+        rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t));
+        if (rn == NULL) {
+            return NGX_ERROR;
+        }
+
+        rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len);
+        if (rn->name == NULL) {
+            ngx_resolver_free(r, rn);
+            return NGX_ERROR;
+        }
+
+        rn->node.key = hash;
+        rn->nlen = (u_short) ctx->name.len;
+        rn->query = NULL;
+
+        ngx_rbtree_insert(&r->name_rbtree, &rn->node);
+    }
+
+    rc = ngx_resolver_create_name_query(rn, ctx);
+
+    if (rc == NGX_ERROR) {
+        goto failed;
+    }
+
+    if (rc == NGX_DECLINED) {
+        ngx_rbtree_delete(&r->name_rbtree, &rn->node);
+
+        ngx_resolver_free(r, rn->query);
+        ngx_resolver_free(r, rn->name);
+        ngx_resolver_free(r, rn);
+
+        ctx->state = NGX_RESOLVE_NXDOMAIN;
+        ctx->handler(ctx);
+
+        return NGX_OK;
+    }
+
+    if (ngx_resolver_send_query(r, rn) != NGX_OK) {
+        goto failed;
+    }
+
+    if (ctx->event == NULL) {
+        ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t));
+        if (ctx->event == NULL) {
+            goto failed;
+        }
+
+        ctx->event->handler = ngx_resolver_timeout_handler;
+        ctx->event->data = ctx;
+        ctx->event->log = r->log;
+        ctx->ident = -1;
+
+        ngx_add_timer(ctx->event, ctx->timeout);
+    }
+
+    if (ngx_queue_empty(&r->name_resend_queue)) {
+        ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));
+    }
+
+    rn->expire = ngx_time() + r->resend_timeout;
+
+    ngx_queue_insert_head(&r->name_resend_queue, &rn->queue);
+
+    rn->cnlen = 0;
+    rn->naddrs = 0;
+    rn->valid = 0;
+    rn->waiting = ctx;
+
+    ctx->state = NGX_AGAIN;
+
+    return NGX_AGAIN;
+
+failed:
+
+    ngx_rbtree_delete(&r->name_rbtree, &rn->node);
+
+    if (rn->query) {
+        ngx_resolver_free(r, rn->query);
+    }
+
+    ngx_resolver_free(r, rn->name);
+
+    ngx_resolver_free(r, rn);
+
+    return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_resolve_addr(ngx_resolver_ctx_t *ctx)
+{
+    ngx_resolver_t       *r;
+    ngx_resolver_node_t  *rn;
+
+    r = ctx->resolver;
+
+    ctx->addr = ntohl(ctx->addr);
+
+    /* lock addr mutex */
+
+    rn = ngx_resolver_lookup_addr(r, ctx->addr);
+
+    if (rn) {
+
+        if (rn->valid >= ngx_time()) {
+
+            ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached");
+
+            ngx_queue_remove(&rn->queue);
+
+            rn->expire = ngx_time() + r->expire;
+
+            ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue);
+
+            ctx->name.len = rn->nlen;
+            ctx->name.data = ngx_resolver_dup(r, rn->name, rn->nlen);
+            if (ctx->name.data == NULL) {
+                goto failed;
+            }
+
+            /* unlock addr mutex */
+
+            ctx->state = NGX_OK;
+
+            ctx->handler(ctx);
+
+            ngx_resolver_free(r, ctx->name.data);
+
+            return NGX_OK;
+        }
+
+        if (rn->waiting) {
+
+            ctx->next = rn->waiting;
+            rn->waiting = ctx;
+
+            return NGX_AGAIN;
+        }
+
+        ngx_queue_remove(&rn->queue);
+
+        ngx_resolver_free(r, rn->query);
+        rn->query = NULL;
+
+    } else {
+        rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t));
+        if (rn == NULL) {
+            goto failed;
+        }
+
+        rn->node.key = ctx->addr;
+        rn->query = NULL;
+
+        ngx_rbtree_insert(&r->addr_rbtree, &rn->node);
+    }
+
+    if (ngx_resolver_create_addr_query(rn, ctx) != NGX_OK) {
+        goto failed;
+    }
+
+    if (ngx_resolver_send_query(r, rn) != NGX_OK) {
+        goto failed;
+    }
+
+    ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t));
+    if (ctx->event == NULL) {
+        goto failed;
+    }
+
+    ctx->event->handler = ngx_resolver_timeout_handler;
+    ctx->event->data = ctx;
+    ctx->event->log = r->log;
+    ctx->ident = -1;
+
+    ngx_add_timer(ctx->event, ctx->timeout);
+
+    if (ngx_queue_empty(&r->addr_resend_queue)) {
+        ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));
+    }
+
+    rn->expire = ngx_time() + r->resend_timeout;
+
+    ngx_queue_insert_head(&r->addr_resend_queue, &rn->queue);
+
+    rn->cnlen = 0;
+    rn->naddrs = 0;
+    rn->name = NULL;
+    rn->nlen = 0;
+    rn->valid = 0;
+    rn->waiting = ctx;
+
+    /* unlock addr mutex */
+
+    ctx->state = NGX_AGAIN;
+
+    return NGX_OK;
+
+failed:
+
+    if (rn) {
+        ngx_rbtree_delete(&r->addr_rbtree, &rn->node);
+
+        if (rn->query) {
+            ngx_resolver_free(r, rn->query);
+        }
+
+        ngx_resolver_free(r, rn);
+    }
+
+    /* unlock addr mutex */
+
+    if (ctx->event) {
+        ngx_resolver_free(r, ctx->event);
+    }
+
+    ngx_resolver_free(r, ctx);
+
+    return NGX_ERROR;
+}
+
+
+void
+ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)
+{
+    in_addr_t             addr;
+    ngx_resolver_t       *r;
+    ngx_resolver_ctx_t   *w, **p;
+    ngx_resolver_node_t  *rn;
+
+    r = ctx->resolver;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,
+                   "resolve addr done: %i", ctx->state);
+
+    if (ctx->event && ctx->event->timer_set) {
+        ngx_del_timer(ctx->event);
+    }
+
+    /* lock addr mutex */
+
+    if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
+
+        rn = ngx_resolver_lookup_addr(r, ctx->addr);
+
+        if (rn) {
+            p = &rn->waiting;
+            w = rn->waiting;
+
+            while (w) {
+                if (w == ctx) {
+                    *p = w->next;
+
+                    goto done;
+                }
+
+                p = &w->next;
+                w = w->next;
+            }
+        }
+
+        addr = ntohl(ctx->addr);
+
+        ngx_log_error(NGX_LOG_ALERT, r->log, 0,
+                      "could not cancel %ud.%ud.%ud.%ud resolving",
+                      (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+                      (addr >> 8) & 0xff, addr & 0xff);
+    }
+
+done:
+
+    ngx_resolver_expire(r, &r->addr_rbtree, &r->addr_expire_queue);
+
+    /* unlock addr mutex */
+
+    /* lock alloc mutex */
+
+    if (ctx->event) {
+        ngx_resolver_free_locked(r, ctx->event);
+    }
+
+    ngx_resolver_free_locked(r, ctx);
+
+    /* unlock alloc mutex */
+}
+
+
+static void
+ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue)
+{
+    time_t                now;
+    ngx_uint_t            i;
+    ngx_queue_t          *q;
+    ngx_resolver_node_t  *rn;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver expire");
+
+    now = ngx_time();
+
+    for (i = 0; i < 2; i++) {
+        if (ngx_queue_empty(queue)) {
+            return;
+        }
+
+        q = ngx_queue_last(queue);
+
+        rn = ngx_queue_data(q, ngx_resolver_node_t, queue);
+
+        if (now <= rn->expire) {
+            return;
+        }
+
+        ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
+                       "resolver expire \"%*s\"", (size_t) rn->nlen, rn->name);
+
+        ngx_queue_remove(q);
+
+        ngx_rbtree_delete(tree, &rn->node);
+
+        ngx_resolver_free_node(r, rn);
+    }
+}
+
+
+static ngx_int_t
+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;
+
+    if (uc->connection == NULL) {
+        if (ngx_udp_connect(uc) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        uc->connection->data = r;
+        uc->connection->read->handler = ngx_resolver_read_response;
+        uc->connection->read->resolver = 1;
+    }
+
+    n = ngx_send(uc->connection, rn->query, rn->qlen);
+
+    if (n == -1) {
+        return NGX_ERROR;
+    }
+
+    if ((size_t) n != (size_t) rn->qlen) {
+        ngx_log_error(NGX_LOG_CRIT, uc->log, 0, "send() incomplete");
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_resolver_resend_handler(ngx_event_t *ev)
+{
+    time_t           timer, atimer, ntimer;
+    ngx_resolver_t  *r;
+
+    r = ev->data;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0,
+                   "resolver resend handler");
+
+    /* lock name mutex */
+
+    ntimer = ngx_resolver_resend(r, &r->name_rbtree, &r->name_resend_queue);
+
+    /* unlock name mutex */
+
+    /* lock addr mutex */
+
+    atimer = ngx_resolver_resend(r, &r->addr_rbtree, &r->addr_resend_queue);
+
+    /* unlock addr mutex */
+
+    if (ntimer == 0) {
+        timer = atimer;
+
+    } else if (atimer == 0) {
+        timer = ntimer;
+
+    } else {
+        timer = (atimer < ntimer) ? atimer : ntimer;
+    }
+
+    if (timer) {
+        ngx_add_timer(r->event, (ngx_msec_t) (timer * 1000));
+    }
+}
+
+
+static time_t
+ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue)
+{
+    time_t                now;
+    ngx_queue_t          *q;
+    ngx_resolver_node_t  *rn;
+
+    now = ngx_time();
+
+    for ( ;; ) {
+        if (ngx_queue_empty(queue)) {
+            return 0;
+        }
+
+        q = ngx_queue_last(queue);
+
+        rn = ngx_queue_data(q, ngx_resolver_node_t, queue);
+
+        if (now < rn->expire) {
+            return rn->expire - now;
+        }
+
+        ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,
+                       "resolver resend \"%*s\" %p",
+                       (size_t) rn->nlen, rn->name, rn->waiting);
+
+        ngx_queue_remove(q);
+
+        if (rn->waiting) {
+
+            if (ngx_resolver_send_query(r, rn) == NGX_OK) {
+
+                rn->expire = now + r->resend_timeout;
+
+                ngx_queue_insert_head(queue, &rn->queue);
+            }
+
+            continue;
+        }
+
+        ngx_rbtree_delete(tree, &rn->node);
+
+        ngx_resolver_free_node(r, rn);
+    }
+}
+
+
+static void
+ngx_resolver_read_response(ngx_event_t *rev)
+{
+    ssize_t            n;
+    ngx_connection_t  *c;
+    u_char             buf[NGX_RESOLVER_UDP_SIZE];
+
+    c = rev->data;
+
+    do {
+        n = ngx_udp_recv(c, buf, NGX_RESOLVER_UDP_SIZE);
+
+        if (n < 0) {
+            return;
+        }
+
+        ngx_resolver_process_response(c->data, buf, n);
+
+    } while (rev->ready);
+}
+
+
+static void
+ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, size_t n)
+{
+    char                  *err;
+    size_t                 len;
+    ngx_uint_t             i, ident, flags, code, nqs, nan, qtype, qclass;
+    ngx_resolver_qs_t     *qs;
+    ngx_resolver_query_t  *query;
+
+    if ((size_t) n < sizeof(ngx_resolver_query_t) + 1) {
+        goto short_response;
+    }
+
+    query = (ngx_resolver_query_t *) buf;
+
+    ident = (query->ident_hi << 8) + query->ident_lo;
+    flags = (query->flags_hi << 8) + query->flags_lo;
+    nqs = (query->nqs_hi << 8) + query->nqs_lo;
+    nan = (query->nan_hi << 8) + query->nan_lo;
+
+    ngx_log_debug6(NGX_LOG_DEBUG_CORE, r->log, 0,
+                   "resolver DNS response %ui fl:%04Xui %ui/%ui/%ui/%ui",
+                   ident, flags, nqs, nan,
+                   (query->nns_hi << 8) + query->nns_lo,
+                   (query->nar_hi << 8) + query->nar_lo);
+
+    if (!(flags & 0x8000)) {
+        ngx_log_error(r->log_level, r->log, 0,
+                      "invalid DNS response %ui fl:%04Xui", ident, flags);
+        return;
+    }
+
+    code = flags & 0x7f;
+
+    if (code == NGX_RESOLVE_FORMERR || code > NGX_RESOLVE_REFUSED) {
+        ngx_log_error(r->log_level, r->log, 0,
+                      "DNS error (%ui: %s), query id:%ui",
+                      code, ngx_resolver_strerror(code), ident);
+        return;
+    }
+
+    if (nqs != 1) {
+        err = "invalid number of questions in DNS response";
+        goto done;
+    }
+
+    i = sizeof(ngx_resolver_query_t);
+
+    while (i < (ngx_uint_t) n) {
+        if (buf[i] == '\0') {
+            goto found;
+        }
+
+        len = buf[i];
+        i += 1 + len;
+    }
+
+    goto short_response;
+
+found:
+
+    if (i++ == 0) {
+        err = "zero-length domain name in DNS response";
+        goto done;
+    }
+
+    if (i + sizeof(ngx_resolver_qs_t) + nan * (2 + sizeof(ngx_resolver_an_t))
+        > (ngx_uint_t) n)
+    {
+        goto short_response;
+    }
+
+    qs = (ngx_resolver_qs_t *) &buf[i];
+
+    qtype = (qs->type_hi << 8) + qs->type_lo;
+    qclass = (qs->class_hi << 8) + qs->class_lo;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
+                   "resolver DNS response qt:%ui cl:%ui", qtype, qclass);
+
+    if (qclass != 1) {
+        ngx_log_error(r->log_level, r->log, 0,
+                      "unknown query class %ui in DNS response", qclass);
+        return;
+    }
+
+    switch (qtype) {
+
+    case NGX_RESOLVE_A:
+
+        ngx_resolver_process_a(r, buf, n, ident, code, nan,
+                               i + sizeof(ngx_resolver_qs_t));
+
+        break;
+
+    case NGX_RESOLVE_PTR:
+
+        ngx_resolver_process_ptr(r, buf, n, ident, code, nan);
+
+        break;
+
+    default:
+        ngx_log_error(r->log_level, r->log, 0,
+                      "unknown query type %ui in DNS response", qtype);
+        return;
+    }
+
+    return;
+
+short_response:
+
+    err = "short dns response";
+
+done:
+
+    ngx_log_error(r->log_level, r->log, 0, err);
+
+    return;
+}
+
+
+static void
+ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,
+    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans)
+{
+    char                 *err;
+    u_char               *cname;
+    size_t                len;
+    uint32_t              hash;
+    in_addr_t             addr, *addrs;
+    ngx_str_t             name;
+    ngx_uint_t            qtype, qident, naddrs, a, i, n, start;
+    ngx_resolver_an_t    *an;
+    ngx_resolver_ctx_t   *ctx, *next;
+    ngx_resolver_node_t  *rn;
+
+    if (ngx_resolver_copy(r, &name, buf, &buf[12], &buf[last]) != NGX_OK) {
+        return;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name);
+
+    hash = ngx_crc32_short(name.data, name.len);
+
+    /* lock name mutex */
+
+    rn = ngx_resolver_lookup_name(r, &name, hash);
+
+    if (rn == NULL || rn->query == NULL) {
+        ngx_log_error(r->log_level, r->log, 0,
+                      "unexpected response for %V", &name);
+        goto failed;
+    }
+
+    qident = (rn->query[0] << 8) + rn->query[1];
+
+    if (ident != qident) {
+        ngx_log_error(r->log_level, r->log, 0,
+                      "wrong ident %ui response for %V, expect %ui",
+                      ident, &name, qident);
+        goto failed;
+    }
+
+    if (code == 0 && nan == 0) {
+        code = 3; /* NXDOMAIN */
+    }
+
+    if (code) {
+        next = rn->waiting;
+        rn->waiting = NULL;
+
+        ngx_queue_remove(&rn->queue);
+
+        ngx_rbtree_delete(&r->name_rbtree, &rn->node);
+
+        ngx_resolver_free_node(r, rn);
+
+        /* unlock name mutex */
+
+        while (next) {
+             ctx = next;
+             ctx->state = code;
+             next = ctx->next;
+
+             ctx->handler(ctx);
+        }
+
+        return;
+    }
+
+    i = ans;
+    naddrs = 0;
+    addr = 0;
+    addrs = NULL;
+    cname = NULL;
+    qtype = 0;
+
+    for (a = 0; a < nan; a++) {
+
+        start = i;
+
+        while (i < last) {
+
+            if (buf[i] & 0xc0) {
+                i += 2;
+                goto found;
+            }
+
+            if (buf[i] == 0) {
+                i++;
+                goto test_length;
+            }
+
+            i += 1 + buf[i];
+        }
+
+        goto short_response;
+
+    test_length:
+
+        if (i - start < 2) {
+            err = "invalid name in dns response";
+            goto invalid;
+        }
+
+    found:
+
+        if (i + sizeof(ngx_resolver_an_t) >= last) {
+            goto short_response;
+        }
+
+        an = (ngx_resolver_an_t *) &buf[i];
+
+        qtype = (an->type_hi << 8) + an->type_lo;
+        len = (an->len_hi << 8) + an->len_lo;
+
+        if (qtype == NGX_RESOLVE_A) {
+
+            i += sizeof(ngx_resolver_an_t);
+
+            if (i + len > last) {
+                goto short_response;
+            }
+
+            addr = htonl((buf[i] << 24) + (buf[i + 1] << 16)
+                         + (buf[i + 2] << 8) + (buf[i + 3]));
+
+            naddrs++;
+
+            i += len;
+
+        } else if (qtype == NGX_RESOLVE_CNAME) {
+            cname = &buf[i] + sizeof(ngx_resolver_an_t);
+            i += sizeof(ngx_resolver_an_t) + len;
+
+        } else if (qtype == NGX_RESOLVE_DNAME) {
+            i += sizeof(ngx_resolver_an_t) + len;
+
+        } else {
+            ngx_log_error(r->log_level, r->log, 0,
+                          "unexpected qtype %ui", qtype);
+        }
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
+                   "resolver naddrs:%ui cname:%p", naddrs, cname);
+
+    if (naddrs) {
+
+        if (naddrs == 1) {
+            rn->u.addr = addr;
+
+        } else {
+
+            addrs = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t));
+            if (addrs == NULL) {
+                return;
+            }
+
+            n = 0;
+            i = ans;
+
+            for (a = 0; a < nan; a++) {
+
+                for ( ;; ) {
+
+                    if (buf[i] & 0xc0) {
+                        i += 2;
+                        goto ok;
+                    }
+
+                    if (buf[i] == 0) {
+                        i++;
+                        goto ok;
+                    }
+
+                    i += 1 + buf[i];
+                }
+
+            ok:
+
+                an = (ngx_resolver_an_t *) &buf[i];
+
+                qtype = (an->type_hi << 8) + an->type_lo;
+                len = (an->len_hi << 8) + an->len_lo;
+
+                i += sizeof(ngx_resolver_an_t);
+
+                if (qtype == NGX_RESOLVE_A) {
+
+                    addrs[n++] = htonl((buf[i] << 24) + (buf[i + 1] << 16)
+                                       + (buf[i + 2] << 8) + (buf[i + 3]));
+
+                    if (n == naddrs) {
+                        break;
+                    }
+                }
+
+                i += len;
+            }
+
+            rn->u.addrs = addrs;
+
+            addrs = ngx_resolver_dup(r, rn->u.addrs,
+                                     naddrs * sizeof(in_addr_t));
+            if (addrs == NULL) {
+                return;
+            }
+        }
+
+        rn->naddrs = (u_short) naddrs;
+
+        ngx_queue_remove(&rn->queue);
+
+        rn->valid = ngx_time() + r->valid;
+        rn->expire = ngx_time() + r->expire;
+
+        ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);
+
+        next = rn->waiting;
+        rn->waiting = NULL;
+
+        /* unlock name mutex */
+
+        while (next) {
+             ctx = next;
+             ctx->state = NGX_OK;
+             ctx->naddrs = naddrs;
+             ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs;
+             ctx->addr = addr;
+             next = ctx->next;
+
+             ctx->handler(ctx);
+        }
+
+        if (naddrs) {
+            ngx_resolver_free(r, addrs);
+        }
+
+        return;
+
+    } else if (cname) {
+
+        /* CNAME only */
+
+        if (ngx_resolver_copy(r, &name, buf, cname, &buf[last]) != NGX_OK) {
+            return;
+        }
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,
+                       "resolver cname:\"%V\"", &name);
+
+        ngx_queue_remove(&rn->queue);
+
+        rn->cnlen = (u_short) name.len;
+        rn->u.cname = name.data;
+        rn->valid = ngx_time() + r->valid;
+        rn->expire = ngx_time() + r->expire;
+
+        ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);
+
+        ctx = rn->waiting;
+        rn->waiting = NULL;
+
+        if (ctx) {
+            ctx->name = name;
+
+            (void) ngx_resolve_name_locked(r, ctx);
+        }
+
+        return;
+    }
+
+    ngx_log_error(r->log_level, r->log, 0,
+               "no A or CNAME types in DNS responses, unknown query type: %ui",
+               qtype);
+    return;
+
+short_response:
+
+    err = "short dns response";
+
+invalid:
+
+    /* unlock name mutex */
+
+    ngx_log_error(r->log_level, r->log, 0, err);
+
+    return;
+
+failed:
+
+    /* unlock name mutex */
+
+    return;
+}
+
+
+static void
+ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,
+    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan)
+{
+    char                 *err;
+    size_t                len;
+    in_addr_t             addr;
+    ngx_int_t             digit;
+    ngx_str_t             name;
+    ngx_uint_t            i, mask, qtype, qclass, qident;
+    ngx_resolver_an_t    *an;
+    ngx_resolver_ctx_t   *ctx, *next;
+    ngx_resolver_node_t  *rn;
+
+    if (ngx_resolver_copy(r, NULL, buf, &buf[12], &buf[n]) != NGX_OK) {
+        goto invalid_in_addr_arpa;
+    }
+
+    addr = 0;
+    i = 12;
+
+    for (mask = 0; mask < 32; mask += 8) {
+        len = buf[i++];
+
+        digit = ngx_atoi(&buf[i], len);
+        if (digit == NGX_ERROR || digit > 255) {
+            goto invalid_in_addr_arpa;
+        }
+
+        addr += digit << mask;
+        i += len;
+    }
+
+    if (ngx_strcmp(&buf[i], "\7in-addr\4arpa") != 0) {
+        goto invalid_in_addr_arpa;
+    }
+
+    /* lock addr mutex */
+
+    rn = ngx_resolver_lookup_addr(r, addr);
+
+    if (rn == NULL || rn->query == NULL) {
+        ngx_log_error(r->log_level, r->log, 0,
+                      "unexpected response for %ud.%ud.%ud.%ud",
+                      (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+                      (addr >> 8) & 0xff, addr & 0xff);
+        goto failed;
+    }
+
+    qident = (rn->query[0] << 8) + rn->query[1];
+
+    if (ident != qident) {
+        ngx_log_error(r->log_level, r->log, 0,
+                    "wrong ident %ui response for %ud.%ud.%ud.%ud, expect %ui",
+                    ident, (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+                    (addr >> 8) & 0xff, addr & 0xff, qident);
+        goto failed;
+    }
+
+    if (code == 0 && nan == 0) {
+        code = 3; /* NXDOMAIN */
+    }
+
+    if (code) {
+        next = rn->waiting;
+        rn->waiting = NULL;
+
+        ngx_queue_remove(&rn->queue);
+
+        ngx_rbtree_delete(&r->addr_rbtree, &rn->node);
+
+        ngx_resolver_free_node(r, rn);
+
+        /* unlock addr mutex */
+
+        while (next) {
+             ctx = next;
+             ctx->state = code;
+             next = ctx->next;
+
+             ctx->handler(ctx);
+        }
+
+        return;
+    }
+
+    i += sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t);
+
+    if (i + 2 + sizeof(ngx_resolver_an_t) > (ngx_uint_t) n) {
+        goto short_response;
+    }
+
+    /* compression pointer to "XX.XX.XX.XX.in-addr.arpa */
+
+    if (buf[i] != 0xc0 || buf[i + 1] != 0x0c) {
+        err = "invalid in-addr.arpa name in DNS response";
+        goto invalid;
+    }
+
+    an = (ngx_resolver_an_t *) &buf[i + 2];
+
+    qtype = (an->type_hi << 8) + an->type_lo;
+    qclass = (an->class_hi << 8) + an->class_lo;
+    len = (an->len_hi << 8) + an->len_lo;
+
+    ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,
+                  "resolver qt:%ui cl:%ui len:%uz", qtype, qclass, len);
+
+    i += 2 + sizeof(ngx_resolver_an_t);
+
+    if (i + len > (ngx_uint_t) n) {
+        goto short_response;
+    }
+
+    len -= 2;
+
+    if (ngx_resolver_copy(r, &name, buf, &buf[i], &buf[n]) != NGX_OK) {
+        return;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver an:%V", &name);
+
+    if (len != (size_t) rn->nlen || ngx_strncmp(name.data, rn->name, len) != 0)
+    {
+        ngx_resolver_free(r, rn->name);
+        rn->name = name.data;
+
+        name.data = ngx_resolver_dup(r, rn->name, len);
+        if (name.data == NULL) {
+            goto failed;
+        }
+    }
+
+    ngx_queue_remove(&rn->queue);
+
+    rn->valid = ngx_time() + r->valid;
+    rn->expire = ngx_time() + r->expire;
+
+    ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue);
+
+    next = rn->waiting;
+    rn->waiting = NULL;
+
+    /* unlock addr mutex */
+
+    while (next) {
+         ctx = next;
+         ctx->state = NGX_OK;
+         ctx->name = name;
+         next = ctx->next;
+
+         ctx->handler(ctx);
+    }
+
+    ngx_resolver_free(r, name.data);
+
+    return;
+
+invalid_in_addr_arpa:
+
+    ngx_log_error(r->log_level, r->log, 0,
+                  "invalid in-addr.arpa name in DNS response");
+    return;
+
+short_response:
+
+    err = "short DNS response";
+
+invalid:
+
+    /* unlock addr mutex */
+
+    ngx_log_error(r->log_level, r->log, 0, err);
+
+    return;
+
+failed:
+
+    /* unlock addr mutex */
+
+    return;
+}
+
+
+static ngx_resolver_node_t *
+ngx_resolver_lookup_name(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash)
+{
+    ngx_int_t             rc;
+    size_t                len;
+    ngx_rbtree_node_t    *node, *sentinel;
+    ngx_resolver_node_t  *rn;
+
+    node = r->name_rbtree.root;
+    sentinel = r->name_rbtree.sentinel;
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        /* hash == node->key */
+
+        do {
+            rn = (ngx_resolver_node_t *) node;
+
+            len = (name->len > (size_t) rn->nlen) ? rn->nlen : name->len;
+
+            rc = ngx_strncmp(name->data, rn->name, len);
+
+            if (rc == 0) {
+                return rn;
+            }
+
+            node = (rc < 0) ? node->left : node->right;
+
+        } while (node != sentinel && hash == node->key);
+
+        break;
+    }
+
+    /* not found */
+
+    return NULL;
+}
+
+
+static ngx_resolver_node_t *
+ngx_resolver_lookup_addr(ngx_resolver_t *r, in_addr_t addr)
+{
+    ngx_rbtree_node_t  *node, *sentinel;
+
+    node = r->addr_rbtree.root;
+    sentinel = r->addr_rbtree.sentinel;
+
+    while (node != sentinel) {
+
+        if (addr < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (addr > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        /* addr == node->key */
+
+        return (ngx_resolver_node_t *) node;
+    }
+
+    /* not found */
+
+    return NULL;
+}
+
+
+static void
+ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    size_t                 len;
+    ngx_rbtree_node_t    **p;
+    ngx_resolver_node_t   *rn, *rn_temp;
+
+    for ( ;; ) {
+
+        if (node->key < temp->key) {
+
+            p = &temp->left;
+
+        } else if (node->key > temp->key) {
+
+            p = &temp->right;
+
+        } else { /* node->key == temp->key */
+
+            rn = (ngx_resolver_node_t *) node;
+            rn_temp = (ngx_resolver_node_t *) temp;
+
+            len = (rn->nlen > rn_temp->nlen) ? rn_temp->nlen : rn->nlen;
+
+            p = (ngx_strncmp(rn->name, rn_temp->name, len) < 0)
+                    ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}
+
+
+static ngx_int_t
+ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
+{
+    u_char                *p, *s;
+    size_t                 len;
+    ngx_uint_t             ident;
+    ngx_resolver_qs_t     *qs;
+    ngx_resolver_query_t  *query;
+
+    len = sizeof(ngx_resolver_query_t)
+          + 1 + ctx->name.len + 1 + sizeof(ngx_resolver_qs_t);
+
+    p = ngx_resolver_calloc(ctx->resolver, len);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    rn->qlen = (u_short) len;
+    rn->query = p;
+
+    query = (ngx_resolver_query_t *) p;
+
+    ident = ngx_random();
+
+    ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0,
+                   "resolve: \"%V\" %i", &ctx->name, ident & 0xffff);
+
+    query->ident_hi = (u_char) ((ident >> 8) & 0xff);
+    query->ident_lo = (u_char) (ident & 0xff);
+
+    /* recursion query */
+    query->flags_hi = 1; query->flags_lo = 0;
+
+    /* one question */
+    query->nqs_hi = 0; query->nqs_lo = 1;
+    query->nan_hi = 0; query->nan_lo = 0;
+    query->nns_hi = 0; query->nns_lo = 0;
+    query->nar_hi = 0; query->nar_lo = 0;
+
+    p += sizeof(ngx_resolver_query_t) + 1 + ctx->name.len + 1;
+
+    qs = (ngx_resolver_qs_t *) p;
+
+    /* query type */
+    qs->type_hi = 0; qs->type_lo = (u_char) ctx->type;
+
+    /* IP query class */
+    qs->class_hi = 0; qs->class_lo = 1;
+
+    /* convert "www.example.com" to "\3www\7example\3com\0" */
+
+    len = 0;
+    p--;
+    *p-- = '\0';
+
+    for (s = ctx->name.data + ctx->name.len - 1; s >= ctx->name.data; s--) {
+        if (*s != '.') {
+            *p = *s;
+            len++;
+
+        } else {
+            if (len == 0) {
+                return NGX_DECLINED;
+            }
+
+            *p = (u_char) len;
+            len = 0;
+        }
+
+        p--;
+    }
+
+    *p = (u_char) len;
+
+    return NGX_OK;
+}
+
+
+/* AF_INET only */
+
+static ngx_int_t
+ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
+{
+    u_char                *p, *d;
+    size_t                 len;
+    ngx_int_t              n;
+    ngx_uint_t             ident;
+    ngx_resolver_query_t  *query;
+
+    len = sizeof(ngx_resolver_query_t)
+          + sizeof(".255.255.255.255.in-addr.arpa.") - 1
+          + sizeof(ngx_resolver_qs_t);
+
+    p = ngx_resolver_calloc(ctx->resolver, len);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    rn->query = p;
+    query = (ngx_resolver_query_t *) p;
+
+    ident = ngx_random();
+
+    query->ident_hi = (u_char) ((ident >> 8) & 0xff);
+    query->ident_lo = (u_char) (ident & 0xff);
+
+    /* recursion query */
+    query->flags_hi = 1; query->flags_lo = 0;
+
+    /* one question */
+    query->nqs_hi = 0; query->nqs_lo = 1;
+    query->nan_hi = 0; query->nan_lo = 0;
+    query->nns_hi = 0; query->nns_lo = 0;
+    query->nar_hi = 0; query->nar_lo = 0;
+
+    p += sizeof(ngx_resolver_query_t);
+
+    for (n = 0; n < 32; n += 8){
+        d = ngx_sprintf(&p[1], "%ud", (ctx->addr >> n) & 0xff);
+        *p = (u_char) (d - &p[1]);
+        p = d;
+    }
+
+    /* query type "PTR", IP query class */
+    ngx_memcpy(p, "\7in-addr\4arpa\0\0\14\0\1", 18);
+
+    rn->qlen = (u_short)
+                  (p + sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t)
+                   - rn->query);
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, u_char *buf, u_char *src,
+    u_char *last)
+{
+    char        *err;
+    u_char      *p, *dst;
+    ssize_t      len;
+    ngx_uint_t   i, n;
+
+    p = src;
+    len = -1;
+
+    /*
+     * compression pointers allow to create endless loop, so we set limit;
+     * 128 pointers should be enough to store 255-byte name
+     */
+
+    for (i = 0; i < 128; i++) {
+        n = *p++;
+
+        if (n == 0) {
+            goto done;
+        }
+
+        if (n & 0xc0) {
+            n = (n & 0x3f << 8) + *p;
+            p = &buf[n];
+
+        } else {
+            len += 1 + n;
+            p = &p[n];
+        }
+
+        if (p >= last) {
+            err = "name is out of response";
+            goto invalid;
+        }
+    }
+
+    err = "compression pointers loop";
+
+invalid:
+
+    ngx_log_error(r->log_level, r->log, 0, err);
+
+    return NGX_ERROR;
+
+done:
+
+    if (name == NULL) {
+        return NGX_OK;
+    }
+
+    dst = ngx_resolver_alloc(r, len);
+    if (dst == NULL) {
+        return NGX_ERROR;
+    }
+
+    name->data = dst;
+
+    n = *src++;
+
+    for ( ;; ) {
+        if (n != 0xc0) {
+            ngx_memcpy(dst, src, n);
+            dst += n;
+            src += n;
+
+            n = *src++;
+
+            if (n != 0) {
+                *dst++ = '.';
+            }
+
+        } else {
+            n = (n & 0x3f << 8) + *src;
+            src = &buf[n];
+
+            n = *src++;
+        }
+
+        if (n == 0) {
+            name->len = dst - name->data;
+            return NGX_OK;
+        }
+    }
+}
+
+
+static void
+ngx_resolver_timeout_handler(ngx_event_t *ev)
+{
+    ngx_resolver_ctx_t  *ctx;
+
+    ctx = ev->data;
+
+    ctx->state = NGX_RESOLVE_TIMEDOUT;
+
+    ctx->handler(ctx);
+}
+
+
+static void
+ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn)
+{
+    /* lock alloc mutex */
+
+    if (rn->query) {
+        ngx_resolver_free_locked(r, rn->query);
+    }
+
+    if (rn->name) {
+        ngx_resolver_free_locked(r, rn->name);
+    }
+
+    if (rn->cnlen) {
+        ngx_resolver_free_locked(r, rn->u.cname);
+    }
+
+    if (rn->naddrs > 1) {
+        ngx_resolver_free_locked(r, rn->u.addrs);
+    }
+
+    ngx_resolver_free_locked(r, rn);
+
+    /* unlock alloc mutex */
+}
+
+
+static void *
+ngx_resolver_alloc(ngx_resolver_t *r, size_t size)
+{
+    u_char  *p;
+
+    /* lock alloc mutex */
+
+    p = ngx_alloc(size, r->log);
+
+    /* unlock alloc mutex */
+
+    return p;
+}
+
+
+static void *
+ngx_resolver_calloc(ngx_resolver_t *r, size_t size)
+{
+    u_char  *p;
+
+    p = ngx_resolver_alloc(r, size);
+
+    if (p) {
+        ngx_memzero(p, size);
+    }
+
+    return p;
+}
+
+
+static void
+ngx_resolver_free(ngx_resolver_t *r, void *p)
+{
+    /* lock alloc mutex */
+
+    ngx_free(p);
+
+    /* unlock alloc mutex */
+}
+
+
+static void
+ngx_resolver_free_locked(ngx_resolver_t *r, void *p)
+{
+    ngx_free(p);
+}
+
+
+static void *
+ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size)
+{
+    void  *dst;
+
+    dst = ngx_resolver_alloc(r, size);
+
+    if (dst == NULL) {
+        return dst;
+    }
+
+    ngx_memcpy(dst, src, size);
+
+    return dst;
+}
+
+
+char *
+ngx_resolver_strerror(ngx_int_t err)
+{
+    static char *errors[] = {
+        "Format error",     /* FORMERR */
+        "Server failure",   /* SERVFAIL */
+        "Host not found",   /* NXDOMAIN */
+        "Unimplemented",    /* NOTIMP */
+        "Operation refused" /* REFUSED */
+    };
+
+    if (err > 0 && err < 6) {
+        return errors[err - 1];
+    }
+
+    if (err == NGX_RESOLVE_TIMEDOUT) {
+        return "Operation timed out";
+    }
+
+    return "Unknown error";
+}
+
+
+ngx_int_t
+ngx_udp_connect(ngx_udp_connection_t *uc)
+{
+    int                rc;
+    ngx_int_t          event;
+    ngx_event_t       *rev, *wev;
+    ngx_socket_t       s;
+    ngx_connection_t  *c;
+
+    s = ngx_socket(AF_INET, SOCK_DGRAM, 0);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, uc->log, 0, "UDP socket %d", s);
+
+    if (s == -1) {
+        ngx_log_error(NGX_LOG_ALERT, uc->log, ngx_socket_errno,
+                      ngx_socket_n " failed");
+        return NGX_ERROR;
+    }
+
+    c = ngx_get_connection(s, uc->log);
+
+    if (c == NULL) {
+        if (ngx_close_socket(s) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, uc->log, ngx_socket_errno,
+                          ngx_close_socket_n "failed");
+        }
+
+        return NGX_ERROR;
+    }
+
+    if (ngx_nonblocking(s) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, uc->log, ngx_socket_errno,
+                      ngx_nonblocking_n " failed");
+
+        ngx_free_connection(c);
+
+        if (ngx_close_socket(s) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, uc->log, ngx_socket_errno,
+                          ngx_close_socket_n " failed");
+        }
+
+        return NGX_ERROR;
+    }
+
+    rev = c->read;
+    wev = c->write;
+
+    rev->log = uc->log;
+    wev->log = uc->log;
+
+    uc->connection = c;
+
+    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
+
+#if (NGX_THREADS)
+
+    /* TODO: lock event when call completion handler */
+
+    rev->lock = &c->lock;
+    wev->lock = &c->lock;
+    rev->own_lock = &c->lock;
+    wev->own_lock = &c->lock;
+
+#endif
+
+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, uc->log, 0,
+                   "connect to %V, fd:%d #%d", &uc->server, s, c->number);
+
+    rc = connect(s, uc->sockaddr, uc->socklen);
+
+    /* TODO: aio, iocp */
+
+    if (rc == -1) {
+        ngx_log_error(NGX_LOG_CRIT, uc->log, ngx_socket_errno,
+                      "connect() to %V failed", &uc->server);
+
+        return NGX_ERROR;
+    }
+
+    /* UDP sockets are always ready to write */
+    wev->ready = 1;
+
+    if (ngx_add_event) {
+
+        event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ?
+                    /* kqueue, epoll */                 NGX_CLEAR_EVENT:
+                    /* select, poll, /dev/poll */       NGX_LEVEL_EVENT;
+                    /* eventport event type has no meaning: oneshot only */
+
+        if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+    } else {
+        /* rtsig */
+
+        if (ngx_add_conn(c) == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+    }
+
+    return NGX_OK;
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_resolver.h
@@ -0,0 +1,148 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#ifndef _NGX_RESOLVER_H_INCLUDED_
+#define _NGX_RESOLVER_H_INCLUDED_
+
+
+#define NGX_RESOLVE_A         1
+#define NGX_RESOLVE_CNAME     5
+#define NGX_RESOLVE_PTR       12
+#define NGX_RESOLVE_MX        15
+#define NGX_RESOLVE_TXT       16
+#define NGX_RESOLVE_DNAME     39
+
+#define NGX_RESOLVE_FORMERR   1
+#define NGX_RESOLVE_SERVFAIL  2
+#define NGX_RESOLVE_NXDOMAIN  3
+#define NGX_RESOLVE_NOTIMP    4
+#define NGX_RESOLVE_REFUSED   5
+#define NGX_RESOLVE_TIMEDOUT  NGX_ETIMEDOUT
+
+
+#define NGX_NO_RESOLVER       (void *) -1
+
+#define NGX_RESOLVER_MAX_RECURSION    50
+
+
+typedef struct {
+    ngx_connection_t         *connection;
+    struct sockaddr          *sockaddr;
+    socklen_t                 socklen;
+    ngx_str_t                 server;
+    ngx_log_t                *log;
+} ngx_udp_connection_t;
+
+
+typedef struct ngx_resolver_ctx_s  ngx_resolver_ctx_t;
+
+typedef void (*ngx_resolver_handler_pt)(ngx_resolver_ctx_t *ctx);
+
+
+typedef struct {
+    ngx_rbtree_node_t         node;
+    ngx_queue_t               queue;
+
+    /* PTR: resolved name, A: name to resolve */
+    u_char                   *name;
+
+    u_short                   nlen;
+    u_short                   qlen;
+
+    u_char                   *query;
+
+    union {
+        in_addr_t             addr;
+        in_addr_t            *addrs;
+        u_char               *cname;
+    } u;
+
+    u_short                   naddrs;
+    u_short                   cnlen;
+
+    time_t                    expire;
+    time_t                    valid;
+
+    ngx_resolver_ctx_t       *waiting;
+} ngx_resolver_node_t;
+
+
+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;
+
+    ngx_log_t                *log;
+
+    /* ident must be after 3 pointers */
+    ngx_int_t                 ident;
+
+    ngx_rbtree_t              name_rbtree;
+    ngx_rbtree_node_t         name_sentinel;
+
+    ngx_rbtree_t              addr_rbtree;
+    ngx_rbtree_node_t         addr_sentinel;
+
+    ngx_queue_t               name_resend_queue;
+    ngx_queue_t               addr_resend_queue;
+
+    ngx_queue_t               name_expire_queue;
+    ngx_queue_t               addr_expire_queue;
+
+    time_t                    resend_timeout;
+    time_t                    expire;
+    time_t                    valid;
+
+    ngx_uint_t                log_level;
+} ngx_resolver_t;
+
+
+struct ngx_resolver_ctx_s {
+    ngx_resolver_ctx_t       *next;
+    ngx_resolver_t           *resolver;
+    ngx_udp_connection_t     *udp_connection;
+
+    /* ident must be after 3 pointers */
+    ngx_int_t                 ident;
+
+    ngx_int_t                 state;
+    ngx_int_t                 type;
+    ngx_str_t                 name;
+
+    ngx_uint_t                naddrs;
+    in_addr_t                *addrs;
+    in_addr_t                 addr;
+
+    /* TODO: DNS peers balancer ctx */
+
+    ngx_resolver_handler_pt   handler;
+    void                     *data;
+    ngx_msec_t                timeout;
+
+    ngx_uint_t                quick;  /* unsigned  quick:1; */
+    ngx_uint_t                recursion;
+    ngx_event_t              *event;
+};
+
+
+ngx_resolver_t *ngx_resolver_create(ngx_conf_t *cf, ngx_peer_addr_t *addr);
+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);
+void ngx_resolve_name_done(ngx_resolver_ctx_t *ctx);
+ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx);
+void ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx);
+char *ngx_resolver_strerror(ngx_int_t err);
+
+
+#endif /* _NGX_RESOLVER_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_sha1.h
@@ -0,0 +1,30 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_SHA1_H_INCLUDED_
+#define _NGX_SHA1_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (NGX_HAVE_OPENSSL_SHA1_H)
+#include <openssl/sha.h>
+#else
+#include <sha.h>
+#endif
+
+
+typedef SHA_CTX  ngx_sha1_t;
+
+
+#define ngx_sha1_init    SHA1_Init
+#define ngx_sha1_update  SHA1_Update
+#define ngx_sha1_final   SHA1_Final
+
+
+#endif /* _NGX_SHA1_H_INCLUDED_ */
--- a/src/core/ngx_slab.c
+++ b/src/core/ngx_slab.c
@@ -58,9 +58,22 @@
 
 
 #if (NGX_DEBUG_MALLOC)
-#define ngx_slab_junk(p, size)  ngx_memset(p, 0xD0, size)
+
+#define ngx_slab_junk(p, size)     ngx_memset(p, 0xD0, size)
+
 #else
+
+#if (NGX_FREEBSD)
+
+#define ngx_slab_junk(p, size)                                                \
+    if (ngx_freebsd_debug_malloc)  ngx_memset(p, 0xD0, size)
+
+#else
+
 #define ngx_slab_junk(p, size)
+
+#endif
+
 #endif
 
 static ngx_slab_page_t *ngx_slab_alloc_pages(ngx_slab_pool_t *pool,
@@ -111,10 +124,7 @@ ngx_slab_init(ngx_slab_pool_t *pool)
 
     p += n * sizeof(ngx_slab_page_t);
 
-    /* STUB: possible overflow on 64-bit platform */
-    pages = (ngx_uint_t) ((uint64_t) size * ngx_pagesize
-                          / (ngx_pagesize + sizeof(ngx_slab_page_t))
-                              / ngx_pagesize);
+    pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));
 
     ngx_memzero(p, pages * sizeof(ngx_slab_page_t));
 
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -8,6 +8,17 @@
 #include <ngx_core.h>
 
 
+void
+ngx_strlow(u_char *dst, u_char *src, size_t n)
+{
+    while (n--) {
+        *dst = ngx_tolower(*src);
+        dst++;
+        src++;
+    }
+}
+
+
 u_char *
 ngx_cpystrn(u_char *dst, u_char *src, size_t n)
 {
@@ -34,7 +45,7 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t 
 {
     u_char  *dst;
 
-    dst = ngx_palloc(pool, src->len);
+    dst = ngx_pnalloc(pool, src->len);
     if (dst == NULL) {
         return NULL;
     }
@@ -63,6 +74,7 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t 
  *    %V                        ngx_str_t *
  *    %v                        ngx_variable_value_t *
  *    %s                        null-terminated string
+ *    %*s                       length and string
  *    %Z                        '\0'
  *    %N                        '\n'
  *    %c                        char
@@ -112,7 +124,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c
                                      * but icc issues the warning
                                      */
     int                    d;
-    size_t                 len;
+    size_t                 len, slen;
     uint32_t               ui32;
     int64_t                i64;
     uint64_t               ui64;
@@ -146,6 +158,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c
             sign = 1;
             hexadecimal = 0;
             max_width = 0;
+            slen = (size_t) -1;
 
             p = temp + NGX_INT64_LEN;
 
@@ -179,6 +192,11 @@ ngx_vsnprintf(u_char *buf, size_t max, c
                     fmt++;
                     continue;
 
+                case '*':
+                    slen = va_arg(args, size_t);
+                    fmt++;
+                    continue;
+
                 default:
                     break;
                 }
@@ -214,9 +232,17 @@ ngx_vsnprintf(u_char *buf, size_t max, c
             case 's':
                 p = va_arg(args, u_char *);
 
-                while (*p && buf < last) {
-                    *buf++ = *p++;
+                if (slen == (size_t) -1) {
+                    while (*p && buf < last) {
+                        *buf++ = *p++;
+                    }
+
+                } else {
+                    len = (buf + slen < last) ? slen : (size_t) (last - buf);
+
+                    buf = ngx_cpymem(buf, p, len);
                 }
+
                 fmt++;
 
                 continue;
@@ -442,7 +468,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c
 
 
 /*
- * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII string only,
+ * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only,
  * and implement our own ngx_strcasecmp()/ngx_strncasecmp()
  * to avoid libc locale overhead.  Besides, we use the ngx_uint_t's
  * instead of the u_char's, because they are slightly faster.
@@ -503,6 +529,95 @@ ngx_strncasecmp(u_char *s1, u_char *s2, 
 }
 
 
+u_char *
+ngx_strnstr(u_char *s1, char *s2, size_t len)
+{
+    u_char  c1, c2;
+    size_t  n;
+
+    c2 = *(u_char *) s2++;
+
+    n = ngx_strlen(s2);
+
+    do {
+        do {
+            if (len-- == 0) {
+                return NULL;
+            }
+
+            c1 = *s1++;
+
+            if (c1 == 0) {
+                return NULL;
+            }
+
+        } while (c1 != c2);
+
+        if (n > len) {
+            return NULL;
+        }
+
+    } while (ngx_strncmp(s1, (u_char *) s2, n) != 0);
+
+    return --s1;
+}
+
+
+/*
+ * ngx_strstrn() and ngx_strcasestrn() are intended to search for static
+ * substring with known length in null-terminated string. The argument n
+ * must be length of the second substring - 1.
+ */
+
+u_char *
+ngx_strstrn(u_char *s1, char *s2, size_t n)
+{
+    u_char  c1, c2;
+
+    c2 = *(u_char *) s2++;
+
+    do {
+        do {
+            c1 = *s1++;
+
+            if (c1 == 0) {
+                return NULL;
+            }
+
+        } while (c1 != c2);
+
+    } while (ngx_strncmp(s1, (u_char *) s2, n) != 0);
+
+    return --s1;
+}
+
+
+u_char *
+ngx_strcasestrn(u_char *s1, char *s2, size_t n)
+{
+    ngx_uint_t  c1, c2;
+
+    c2 = (ngx_uint_t) *s2++;
+    c2  = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+
+    do {
+        do {
+            c1 = (ngx_uint_t) *s1++;
+
+            if (c1 == 0) {
+                return NULL;
+            }
+
+            c1  = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+
+        } while (c1 != c2);
+
+    } while (ngx_strncasecmp(s1, (u_char *) s2, n) != 0);
+
+    return --s1;
+}
+
+
 ngx_int_t
 ngx_rstrncmp(u_char *s1, u_char *s2, size_t n)
 {
@@ -727,18 +842,17 @@ ngx_hextoi(u_char *line, size_t n)
 }
 
 
-void
-ngx_md5_text(u_char *text, u_char *md5)
+u_char *
+ngx_hex_dump(u_char *dst, u_char *src, size_t len)
 {
-    int            i;
     static u_char  hex[] = "0123456789abcdef";
 
-    for (i = 0; i < 16; i++) {
-        *text++ = hex[md5[i] >> 4];
-        *text++ = hex[md5[i] & 0xf];
+    while (len--) {
+        *dst++ = hex[*src >> 4];
+        *dst++ = hex[*src++ & 0xf];
     }
 
-    *text = '\0';
+    return dst;
 }
 
 
@@ -849,16 +963,16 @@ ngx_decode_base64(ngx_str_t *dst, ngx_st
 
 
 /*
- * ngx_utf_decode() decodes two and more bytes UTF sequences only
+ * ngx_utf8_decode() decodes two and more bytes UTF sequences only
  * the return values:
  *    0x80 - 0x10ffff         valid character
- *    0x10ffff - 0xfffffffd   invalid sequence
+ *    0x110000 - 0xfffffffd   invalid sequence
  *    0xfffffffe              incomplete sequence
  *    0xffffffff              error
  */
 
 uint32_t
-ngx_utf_decode(u_char **p, size_t n)
+ngx_utf8_decode(u_char **p, size_t n)
 {
     size_t    len;
     uint32_t  u, i, valid;
@@ -915,31 +1029,26 @@ ngx_utf_decode(u_char **p, size_t n)
 
 
 size_t
-ngx_utf_length(u_char *p, size_t n)
+ngx_utf8_length(u_char *p, size_t n)
 {
-    u_char      c;
-    size_t      len;
-    ngx_uint_t  i;
+    u_char  c, *last;
+    size_t  len;
 
-    for (len = 0, i = 0; i < n; len++, i++) {
+    last = p + n;
 
-        c = p[i];
+    for (len = 0; p < last; len++) {
+
+        c = *p;
 
         if (c < 0x80) {
+            p++;
             continue;
         }
 
-        if (c >= 0xc0) {
-            for (c <<= 1; c & 0x80; c <<= 1) {
-                i++;
-            }
-
-            continue;
+        if (ngx_utf8_decode(&p, n) > 0x10ffff) {
+            /* invalid UTF-8 */
+            return n;
         }
-
-        /* invalid utf */
-
-        return n;
     }
 
     return len;
@@ -947,36 +1056,45 @@ ngx_utf_length(u_char *p, size_t n)
 
 
 u_char *
-ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n)
+ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len)
 {
-    u_char  c;
+    u_char  c, *next;
 
     if (n == 0) {
         return dst;
     }
 
-    for ( /* void */ ; --n; dst++, src++) {
+    while (--n) {
 
         c = *src;
         *dst = c;
 
         if (c < 0x80) {
-            if (*dst != '\0') {
+
+            if (c != '\0') {
+                dst++;
+                src++;
+                len--;
+
                 continue;
             }
 
             return dst;
         }
 
-        if (c >= 0xc0) {
-            for (c <<= 1; c & 0x80; c <<= 1) {
-               *++dst = *++src;
-            }
+        next = src;
 
-            continue;
+        if (ngx_utf8_decode(&next, len) > 0x10ffff) {
+            /* invalid UTF-8 */
+            break;
         }
 
-        /* invalid utf */
+        len--;
+
+        while (src < next) {
+            *++dst = *++src;
+            len--;
+        }
     }
 
     *dst = '\0';
@@ -1058,7 +1176,7 @@ ngx_escape_uri(u_char *dst, u_char *src,
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
 
                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
-        0x000000a5, /* 0000 0000 0000 0000  0000 0000 1010 0101 */
+        0x00000085, /* 0000 0000 0000 0000  0000 0000 1000 0101 */
 
                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
@@ -1154,7 +1272,9 @@ ngx_unescape_uri(u_char **dst, u_char **
 
         switch (state) {
         case sw_usual:
-            if (ch == '?' && type == NGX_UNESCAPE_URI) {
+            if (ch == '?'
+                && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT)))
+            {
                 *d++ = ch;
                 goto done;
             }
@@ -1197,7 +1317,7 @@ ngx_unescape_uri(u_char **dst, u_char **
             if (ch >= '0' && ch <= '9') {
                 ch = (u_char) ((decoded << 4) + ch - '0');
 
-                if (type == NGX_UNESCAPE_URI) {
+                if (type & NGX_UNESCAPE_REDIRECT) {
                     if (ch > '%' && ch < 0x7f) {
                         *d++ = ch;
                         break;
@@ -1217,7 +1337,17 @@ ngx_unescape_uri(u_char **dst, u_char **
             if (c >= 'a' && c <= 'f') {
                 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
 
-                if (type == NGX_UNESCAPE_URI) {
+                if (type & NGX_UNESCAPE_URI) {
+                    if (ch == '?') {
+                        *d++ = ch;
+                        goto done;
+                    }
+
+                    *d++ = ch;
+                    break;
+                }
+
+                if (type & NGX_UNESCAPE_REDIRECT) {
                     if (ch == '?') {
                         *d++ = ch;
                         goto done;
@@ -1250,30 +1380,97 @@ done:
 }
 
 
+uintptr_t
+ngx_escape_html(u_char *dst, u_char *src, size_t size)
+{
+    u_char      ch;
+    ngx_uint_t  i, len;
+
+    if (dst == NULL) {
+
+        len = 0;
+
+        for (i = 0; i < size; i++) {
+            switch (*src++) {
+
+            case '<':
+                len += sizeof("&lt;") - 2;
+                break;
+
+            case '>':
+                len += sizeof("&gt;") - 2;
+                break;
+
+            case '&':
+                len += sizeof("&amp;") - 2;
+                break;
+
+            default:
+                break;
+            }
+        }
+
+        return (uintptr_t) len;
+    }
+
+    for (i = 0; i < size; i++) {
+        ch = *src++;
+
+        switch (ch) {
+
+        case '<':
+            *dst++ = '&'; *dst++ = 'l'; *dst++ = 't'; *dst++ = ';';
+            break;
+
+        case '>':
+            *dst++ = '&'; *dst++ = 'g'; *dst++ = 't'; *dst++ = ';';
+            break;
+
+        case '&':
+            *dst++ = '&'; *dst++ = 'a'; *dst++ = 'm'; *dst++ = 'p';
+            *dst++ = ';';
+            break;
+
+        default:
+            *dst++ = ch;
+            break;
+        }
+    }
+
+    return (uintptr_t) dst;
+}
+
+
 /* ngx_sort() is implemented as insertion sort because we need stable sort */
 
 void
 ngx_sort(void *base, size_t n, size_t size,
-    int (*cmp)(const void *, const void *))
+    ngx_int_t (*cmp)(const void *, const void *))
 {
-    u_char  *p1, *p2;
-    u_char   buf[256];
+    u_char  *p1, *p2, *p;
+
+    p = ngx_alloc(size, ngx_cycle->log);
+    if (p == NULL) {
+        return;
+    }
 
     for (p1 = (u_char *) base + size;
          p1 < (u_char *) base + n * size;
          p1 += size)
     {
-        ngx_memcpy(buf, p1, size);
+        ngx_memcpy(p, p1, size);
 
         for (p2 = p1;
-             p2 > (u_char *) base && cmp(p2 - size, buf) > 0;
+             p2 > (u_char *) base && cmp(p2 - size, p) > 0;
              p2 -= size)
         {
             ngx_memcpy(p2, p2 - size, size);
         }
 
-        ngx_memcpy(p2, buf, size);
+        ngx_memcpy(p2, p, size);
     }
+
+    ngx_free(p);
 }
 
 
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -25,11 +25,12 @@ typedef struct {
 
 
 typedef struct {
-    unsigned    len:29;
+    unsigned    len:28;
 
     unsigned    valid:1;
-    unsigned    no_cachable:1;
+    unsigned    no_cacheable:1;
     unsigned    not_found:1;
+    unsigned    escape:1;
 
     u_char     *data;
 } ngx_variable_value_t;
@@ -42,6 +43,8 @@ typedef struct {
 #define ngx_tolower(c)      (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)
 #define ngx_toupper(c)      (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)
 
+void ngx_strlow(u_char *dst, u_char *src, size_t n);
+
 
 #define ngx_strncmp(s1, s2, n)  strncmp((const char *) s1, (const char *) s2, n)
 
@@ -114,7 +117,7 @@ ngx_copy(u_char *dst, u_char *src, size_
 
 
 /* msvc and icc7 compile memcmp() to the inline loop */
-#define ngx_memcmp                memcmp
+#define ngx_memcmp(s1, s2, n)  memcmp((const char *) s1, (const char *) s2, n)
 
 
 u_char *ngx_cpystrn(u_char *dst, u_char *src, size_t n);
@@ -126,6 +129,11 @@ u_char *ngx_vsnprintf(u_char *buf, size_
 ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2);
 ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n);
 
+u_char *ngx_strnstr(u_char *s1, char *s2, size_t n);
+
+u_char *ngx_strstrn(u_char *s1, char *s2, size_t n);
+u_char *ngx_strcasestrn(u_char *s1, char *s2, size_t n);
+
 ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n);
 ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n);
 ngx_int_t ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2);
@@ -136,7 +144,7 @@ off_t ngx_atoof(u_char *line, size_t n);
 time_t ngx_atotm(u_char *line, size_t n);
 ngx_int_t ngx_hextoi(u_char *line, size_t n);
 
-void ngx_md5_text(u_char *text, u_char *md5);
+u_char *ngx_hex_dump(u_char *dst, u_char *src, size_t len);
 
 
 #define ngx_base64_encoded_length(len)  (((len + 2) / 3) * 4)
@@ -145,27 +153,30 @@ void ngx_md5_text(u_char *text, u_char *
 void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src);
 ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src);
 
-uint32_t ngx_utf_decode(u_char **p, size_t n);
-size_t ngx_utf_length(u_char *p, size_t n);
-u_char *ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n);
+uint32_t ngx_utf8_decode(u_char **p, size_t n);
+size_t ngx_utf8_length(u_char *p, size_t n);
+u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len);
 
 
-#define NGX_ESCAPE_URI        0
-#define NGX_ESCAPE_ARGS       1
-#define NGX_ESCAPE_HTML       2
-#define NGX_ESCAPE_REFRESH    3
-#define NGX_ESCAPE_MEMCACHED  4
-#define NGX_ESCAPE_MAIL_AUTH  5
+#define NGX_ESCAPE_URI         0
+#define NGX_ESCAPE_ARGS        1
+#define NGX_ESCAPE_HTML        2
+#define NGX_ESCAPE_REFRESH     3
+#define NGX_ESCAPE_MEMCACHED   4
+#define NGX_ESCAPE_MAIL_AUTH   5
 
-#define NGX_UNESCAPE_URI      1
+#define NGX_UNESCAPE_URI       1
+#define NGX_UNESCAPE_REDIRECT  2
 
 uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size,
     ngx_uint_t type);
 void ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type);
+uintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size);
+
 
 
 void ngx_sort(void *base, size_t n, size_t size,
-    int (*cmp)(const void *, const void *));
+    ngx_int_t (*cmp)(const void *, const void *));
 #define ngx_qsort             qsort
 
 
--- a/src/core/ngx_times.c
+++ b/src/core/ngx_times.c
@@ -203,70 +203,84 @@ ngx_http_cookie_time(u_char *buf, time_t
 void
 ngx_gmtime(time_t t, ngx_tm_t *tp)
 {
-    ngx_int_t  sec, min, hour, mday, mon, year, wday, yday, days;
+    ngx_int_t   yday;
+    ngx_uint_t  n, sec, min, hour, mday, mon, year, wday, days, leap;
 
-    days = t / 86400;
+    /* the calculation is valid for positive time_t only */
+
+    n = (ngx_uint_t) t;
+
+    days = n / 86400;
 
     /* Jaunary 1, 1970 was Thursday */
+
     wday = (4 + days) % 7;
 
-    t %= 86400;
-    hour = t / 3600;
-    t %= 3600;
-    min = t / 60;
-    sec = t % 60;
+    n %= 86400;
+    hour = n / 3600;
+    n %= 3600;
+    min = n / 60;
+    sec = n % 60;
 
-    /* the algorithm based on Gauss's formula */
+    /*
+     * the algorithm based on Gauss' formula,
+     * see src/http/ngx_http_parse_time.c
+     */
 
+    /* days since March 1, 1 BC */
     days = days - (31 + 28) + 719527;
 
-    year = days * 400 / (365 * 400 + 100 - 4 + 1);
+    /*
+     * 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"
+     * becomes negative.
+     */
+
+    year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1);
+
     yday = days - (365 * year + year / 4 - year / 100 + year / 400);
 
-    mon = (yday + 31) * 12 / 367;
-    mday = yday - (mon * 367 / 12 - 31);
+    if (yday < 0) {
+        leap = (year % 4 == 0) && (year % 100 || (year % 400 == 0));
+        yday = 365 + leap + yday;
+        year--;
+    }
 
-    mon += 2;
+    /*
+     * The empirical formula that maps "yday" to month.
+     * There are at least 10 variants, some of them are:
+     *     mon = (yday + 31) * 15 / 459
+     *     mon = (yday + 31) * 17 / 520
+     *     mon = (yday + 31) * 20 / 612
+     */
+
+    mon = (yday + 31) * 10 / 306;
+
+    /* the Gauss' formula that evaluates days before the month */
+
+    mday = yday - (367 * mon / 12 - 30) + 1;
 
     if (yday >= 306) {
 
+        year++;
+        mon -= 10;
+
         /*
          * there is no "yday" in Win32 SYSTEMTIME
          *
          * yday -= 306;
          */
 
-        year++;
-        mon -= 12;
-
-        if (mday == 0) {
-            /* Jaunary 31 */
-            mon = 1;
-            mday = 31;
+    } else {
 
-        } else if (mon == 2) {
-
-            if ((year % 4 == 0) && (year % 100 || (year % 400 == 0))) {
-                if (mday > 29) {
-                    mon = 3;
-                    mday -= 29;
-                }
+        mon += 2;
 
-            } else if (mday > 28) {
-                mon = 3;
-                mday -= 28;
-            }
-        }
-/*
- *  there is no "yday" in Win32 SYSTEMTIME
- *
- *  } else {
- *      yday += 31 + 28;
- *
- *      if ((year % 4 == 0) && (year % 100 || (year % 400 == 0))) {
- *          yday++;
- *      }
- */
+        /*
+         * there is no "yday" in Win32 SYSTEMTIME
+         *
+         * yday += 31 + 28 + leap;
+         */
     }
 
     tp->ngx_tm_sec = (ngx_tm_sec_t) sec;
--- a/src/event/modules/ngx_aio_module.c
+++ b/src/event/modules/ngx_aio_module.c
@@ -28,6 +28,7 @@ static ngx_int_t ngx_aio_process_events(
 ngx_os_io_t ngx_os_aio = {
     ngx_aio_read,
     ngx_aio_read_chain,
+    NULL,
     ngx_aio_write,
     ngx_aio_write_chain,
     0
--- a/src/event/modules/ngx_epoll_module.c
+++ b/src/event/modules/ngx_epoll_module.c
@@ -143,15 +143,12 @@ ngx_module_t  ngx_epoll_module = {
 static ngx_int_t
 ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
 {
-    ngx_event_conf_t  *ecf;
     ngx_epoll_conf_t  *epcf;
 
-    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
-
     epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);
 
     if (ep == -1) {
-        ep = epoll_create(ecf->connections / 2);
+        ep = epoll_create(cycle->connection_n / 2);
 
         if (ep == -1) {
             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
--- a/src/event/modules/ngx_eventport_module.c
+++ b/src/event/modules/ngx_eventport_module.c
@@ -40,11 +40,15 @@ typedef struct  port_notify {
     void       *portnfy_user;   /* user defined */
 } port_notify_t;
 
+#if (__FreeBSD_version < 700005)
+
 typedef struct itimerspec {     /* definition per POSIX.4 */
     struct timespec it_interval;/* timer period */
     struct timespec it_value;   /* timer expiration */
 } itimerspec_t;
 
+#endif
+
 int port_create(void)
 {
     return -1;
@@ -106,7 +110,7 @@ static char *ngx_eventport_init_conf(ngx
 static int            ep = -1;
 static port_event_t  *event_list;
 static ngx_uint_t     nevents;
-static timer_t        event_timer = -1;
+static timer_t        event_timer = (timer_t) -1;
 
 static ngx_str_t      eventport_name = ngx_string("eventport");
 
@@ -237,13 +241,13 @@ ngx_eventport_init(ngx_cycle_t *cycle, n
 static void
 ngx_eventport_done(ngx_cycle_t *cycle)
 {
-    if (event_timer != -1) {
+    if (event_timer != (timer_t) -1) {
         if (timer_delete(event_timer) == -1) {
             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                           "timer_delete() failed");
         }
 
-        event_timer = -1;
+        event_timer = (timer_t) -1;
     }
 
     if (close(ep) == -1) {
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -446,7 +446,7 @@ ngx_kqueue_set_event(ngx_event_t *ev, ng
     || __FreeBSD_version >= 500018
                                  |NOTE_REVOKE
 #endif
-                                       ;
+                      ;
         kev->data = 0;
 
     } else {
--- a/src/event/modules/ngx_rtsig_module.c
+++ b/src/event/modules/ngx_rtsig_module.c
@@ -11,9 +11,14 @@
 
 #if (NGX_TEST_BUILD_RTSIG)
 
-#define F_SETSIG       10
+#ifdef  SIGRTMIN
+#define si_fd          _reason.__spare__.__spare2__[0]
+#else
 #define SIGRTMIN       33
 #define si_fd          __spare__[0]
+#endif
+
+#define F_SETSIG       10
 #define KERN_RTSIGNR   30
 #define KERN_RTSIGMAX  31
 
--- a/src/event/modules/ngx_select_module.c
+++ b/src/event/modules/ngx_select_module.c
@@ -254,9 +254,6 @@ ngx_select_process_events(ngx_cycle_t *c
     ngx_event_t        *ev, **queue;
     ngx_connection_t   *c;
     struct timeval      tv, *tp;
-#if !(NGX_WIN32)
-    ngx_uint_t          level;
-#endif
 
 #if !(NGX_WIN32)
 
@@ -348,6 +345,8 @@ ngx_select_process_events(ngx_cycle_t *c
 #else
 
     if (err) {
+        ngx_uint_t  level;
+
         if (err == NGX_EINTR) {
 
             if (ngx_event_timer_alarm) {
@@ -437,10 +436,10 @@ ngx_select_init_conf(ngx_cycle_t *cycle,
 
 #if !(NGX_WIN32)
 
-    if ((unsigned) ecf->connections > FD_SETSIZE) {
+    if (cycle->connection_n > FD_SETSIZE) {
         ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                       "the maximum number of files "
-                      "supported by select() is " ngx_value(FD_SETSIZE));
+                      "supported by select() is %ud", FD_SETSIZE);
         return NGX_CONF_ERROR;
     }
 
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -428,13 +428,9 @@ ngx_event_module_init(ngx_cycle_t *cycle
     void              ***cf;
     u_char              *shared;
     size_t               size, cl;
-    ngx_event_conf_t    *ecf;
+    ngx_shm_t            shm;
     ngx_core_conf_t     *ccf;
-    ngx_shm_t            shm;
-#if !(NGX_WIN32)
-    ngx_int_t            limit;
-    struct rlimit        rlmt;
-#endif
+    ngx_event_conf_t    *ecf;
 
     cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
 
@@ -456,6 +452,9 @@ ngx_event_module_init(ngx_cycle_t *cycle
     ngx_timer_resolution = ccf->timer_resolution;
 
 #if !(NGX_WIN32)
+    {
+    ngx_int_t      limit;
+    struct rlimit  rlmt;
 
     if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
@@ -475,7 +474,7 @@ ngx_event_module_init(ngx_cycle_t *cycle
                           ecf->connections, limit);
         }
     }
-
+    }
 #endif /* !(NGX_WIN32) */
 
 
@@ -573,13 +572,6 @@ ngx_event_process_init(ngx_cycle_t *cycl
     ngx_core_conf_t     *ccf;
     ngx_event_conf_t    *ecf;
     ngx_event_module_t  *module;
-#if (NGX_WIN32)
-    ngx_iocp_conf_t     *iocpcf;
-#else
-    struct rlimit        rlmt;
-    struct sigaction     sa;
-    struct itimerval     itv;
-#endif
 
     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
     ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
@@ -604,27 +596,30 @@ ngx_event_process_init(ngx_cycle_t *cycl
         return NGX_ERROR;
     }
 
-    cycle->connection_n = ecf->connections;
-
     for (m = 0; ngx_modules[m]; m++) {
         if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
             continue;
         }
 
-        if (ngx_modules[m]->ctx_index == ecf->use) {
-            module = ngx_modules[m]->ctx;
-            if (module->actions.init(cycle, ngx_timer_resolution) == NGX_ERROR)
-            {
-                /* fatal */
-                exit(2);
-            }
-            break;
+        if (ngx_modules[m]->ctx_index != ecf->use) {
+            continue;
         }
+
+        module = ngx_modules[m]->ctx;
+
+        if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
+            /* fatal */
+            exit(2);
+        }
+
+        break;
     }
 
 #if !(NGX_WIN32)
 
     if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
+        struct sigaction  sa;
+        struct itimerval  itv;
 
         ngx_memzero(&sa, sizeof(struct sigaction));
         sa.sa_handler = ngx_timer_signal_handler;
@@ -648,6 +643,7 @@ ngx_event_process_init(ngx_cycle_t *cycl
     }
 
     if (ngx_event_flags & NGX_USE_FD_EVENT) {
+        struct rlimit  rlmt;
 
         if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
@@ -666,15 +662,15 @@ ngx_event_process_init(ngx_cycle_t *cycl
 
 #endif
 
-    cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * ecf->connections,
-                                   cycle->log);
+    cycle->connections =
+        ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
     if (cycle->connections == NULL) {
         return NGX_ERROR;
     }
 
     c = cycle->connections;
 
-    cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * ecf->connections,
+    cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
                                    cycle->log);
     if (cycle->read_events == NULL) {
         return NGX_ERROR;
@@ -690,7 +686,7 @@ ngx_event_process_init(ngx_cycle_t *cycl
 #endif
     }
 
-    cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * ecf->connections,
+    cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
                                     cycle->log);
     if (cycle->write_events == NULL) {
         return NGX_ERROR;
@@ -724,7 +720,7 @@ ngx_event_process_init(ngx_cycle_t *cycl
     } while (i);
 
     cycle->free_connections = next;
-    cycle->free_connection_n = ecf->connections;
+    cycle->free_connection_n = cycle->connection_n;
 
     /* for each listening socket */
 
@@ -774,6 +770,8 @@ ngx_event_process_init(ngx_cycle_t *cycl
 #if (NGX_WIN32)
 
         if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
+            ngx_iocp_conf_t  *iocpcf;
+
             rev->handler = ngx_event_acceptex;
 
             if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {
@@ -945,7 +943,7 @@ ngx_event_connections(ngx_conf_t *cf, ng
     ngx_str_t  *value;
 
     if (ecf->connections != NGX_CONF_UNSET_UINT) {
-        return "is duplicate" ;
+        return "is duplicate";
     }
 
     if (ngx_strcmp(cmd->name.data, "connections") == 0) {
@@ -980,7 +978,7 @@ ngx_event_use(ngx_conf_t *cf, ngx_comman
     ngx_event_module_t   *module;
 
     if (ecf->use != NGX_CONF_UNSET_UINT) {
-        return "is duplicate" ;
+        return "is duplicate";
     }
 
     value = cf->args->elts;
@@ -1140,11 +1138,10 @@ ngx_event_init_conf(ngx_cycle_t *cycle, 
     ngx_uint_t           rtsig;
     ngx_core_conf_t     *ccf;
 #endif
-    ngx_int_t            i, connections;
+    ngx_int_t            i;
     ngx_module_t        *module;
     ngx_event_module_t  *event_module;
 
-    connections = NGX_CONF_UNSET_UINT;
     module = NULL;
 
 #if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
@@ -1153,11 +1150,9 @@ ngx_event_init_conf(ngx_cycle_t *cycle, 
 
     if (fd != -1) {
         close(fd);
-        connections = DEFAULT_CONNECTIONS;
         module = &ngx_epoll_module;
 
     } else if (ngx_errno != NGX_ENOSYS) {
-        connections = DEFAULT_CONNECTIONS;
         module = &ngx_epoll_module;
     }
 
@@ -1166,7 +1161,6 @@ ngx_event_init_conf(ngx_cycle_t *cycle, 
 #if (NGX_HAVE_RTSIG)
 
     if (module == NULL) {
-        connections = DEFAULT_CONNECTIONS;
         module = &ngx_rtsig_module;
         rtsig = 1;
 
@@ -1178,14 +1172,12 @@ ngx_event_init_conf(ngx_cycle_t *cycle, 
 
 #if (NGX_HAVE_DEVPOLL)
 
-    connections = DEFAULT_CONNECTIONS;
     module = &ngx_devpoll_module;
 
 #endif
 
 #if (NGX_HAVE_KQUEUE)
 
-    connections = DEFAULT_CONNECTIONS;
     module = &ngx_kqueue_module;
 
 #endif
@@ -1193,12 +1185,6 @@ ngx_event_init_conf(ngx_cycle_t *cycle, 
 #if (NGX_HAVE_SELECT)
 
     if (module == NULL) {
-
-#if (NGX_WIN32 || FD_SETSIZE >= DEFAULT_CONNECTIONS)
-        connections = DEFAULT_CONNECTIONS;
-#else
-        connections = FD_SETSIZE;
-#endif
         module = &ngx_select_module;
     }
 
@@ -1206,18 +1192,20 @@ ngx_event_init_conf(ngx_cycle_t *cycle, 
 
     if (module == NULL) {
         for (i = 0; ngx_modules[i]; i++) {
-            if (ngx_modules[i]->type == NGX_EVENT_MODULE) {
-                event_module = ngx_modules[i]->ctx;
+
+            if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
+                continue;
+            }
+
+            event_module = ngx_modules[i]->ctx;
 
-                if (ngx_strcmp(event_module->name->data, event_core_name.data)
-                    == 0)
-                {
-                    continue;
-                }
+            if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0)
+            {
+                continue;
+            }
 
-                module = ngx_modules[i];
-                break;
-            }
+            module = ngx_modules[i];
+            break;
         }
     }
 
@@ -1226,7 +1214,7 @@ ngx_event_init_conf(ngx_cycle_t *cycle, 
         return NGX_CONF_ERROR;
     }
 
-    ngx_conf_init_uint_value(ecf->connections, connections);
+    ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);
     cycle->connection_n = ecf->connections;
 
     ngx_conf_init_uint_value(ecf->use, module->ctx_index);
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -136,6 +136,7 @@ struct ngx_event_s {
 
     /* to test on worker exit */
     unsigned         channel:1;
+    unsigned         resolver:1;
 
 #if (NGX_THREADS)
 
@@ -429,6 +430,7 @@ extern ngx_os_io_t  ngx_io;
 
 #define ngx_recv             ngx_io.recv
 #define ngx_recv_chain       ngx_io.recv_chain
+#define ngx_udp_recv         ngx_io.udp_recv
 #define ngx_send             ngx_io.send
 #define ngx_send_chain       ngx_io.send_chain
 
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -201,7 +201,7 @@ ngx_event_accept(ngx_event_t *ev)
 #endif
 
         if (ls->addr_ntop) {
-            c->addr_text.data = ngx_palloc(c->pool, ls->addr_text_max_len);
+            c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
             if (c->addr_text.data == NULL) {
                 ngx_close_accepted_connection(c);
                 return;
--- a/src/event/ngx_event_busy_lock.c
+++ b/src/event/ngx_event_busy_lock.c
@@ -9,7 +9,7 @@
 #include <ngx_event.h>
 
 
-static ngx_int_t ngx_event_busy_lock_look_cachable(ngx_event_busy_lock_t *bl,
+static ngx_int_t ngx_event_busy_lock_look_cacheable(ngx_event_busy_lock_t *bl,
     ngx_event_busy_lock_ctx_t *ctx);
 static void ngx_event_busy_lock_handler(ngx_event_t *ev);
 static void ngx_event_busy_lock_posted_handler(ngx_event_t *ev);
@@ -65,14 +65,14 @@ ngx_event_busy_lock(ngx_event_busy_lock_
 
 
 ngx_int_t
-ngx_event_busy_lock_cachable(ngx_event_busy_lock_t *bl,
+ngx_event_busy_lock_cacheable(ngx_event_busy_lock_t *bl,
     ngx_event_busy_lock_ctx_t *ctx)
 {
     ngx_int_t  rc;
 
     ngx_mutex_lock(bl->mutex);
 
-    rc = ngx_event_busy_lock_look_cachable(bl, ctx);
+    rc = ngx_event_busy_lock_look_cacheable(bl, ctx);
 
     ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->event->log, 0,
                    "event busy lock: %d w:%d mw:%d",
@@ -201,14 +201,14 @@ ngx_event_busy_lock_cancel(ngx_event_bus
 
 
 static ngx_int_t
-ngx_event_busy_lock_look_cachable(ngx_event_busy_lock_t *bl,
+ngx_event_busy_lock_look_cacheable(ngx_event_busy_lock_t *bl,
     ngx_event_busy_lock_ctx_t *ctx)
 {
     ngx_int_t    free;
-    ngx_uint_t   i, bit, cachable, mask;
+    ngx_uint_t   i, bit, cacheable, mask;
 
     bit = 0;
-    cachable = 0;
+    cacheable = 0;
     free = -1;
 
 #if (NGX_SUPPRESS_WARN)
@@ -227,14 +227,14 @@ ngx_event_busy_lock_look_cachable(ngx_ev
                 ctx->slot = i;
                 return NGX_AGAIN;
             }
-            cachable++;
+            cacheable++;
 
         } else if (free == -1) {
             free = i;
         }
 
-        if (cachable == bl->cachable) {
-            if (free == -1 && cachable < bl->max_busy) {
+        if (cacheable == bl->cacheable) {
+            if (free == -1 && cacheable < bl->max_busy) {
                 free = i + 1;
             }
 
@@ -259,7 +259,7 @@ ngx_event_busy_lock_look_cachable(ngx_ev
     bl->md5_mask[free / 8] |= 1 << (free & 7);
     ctx->slot = free;
 
-    bl->cachable++;
+    bl->cacheable++;
     bl->busy++;
 
     return NGX_OK;
--- a/src/event/ngx_event_busy_lock.h
+++ b/src/event/ngx_event_busy_lock.h
@@ -34,7 +34,7 @@ struct ngx_event_busy_lock_ctx_s {
 typedef struct {
     u_char                     *md5_mask;
     char                       *md5;
-    ngx_uint_t                  cachable;
+    ngx_uint_t                  cacheable;
 
     ngx_uint_t                  busy;
     ngx_uint_t                  max_busy;
@@ -53,7 +53,7 @@ typedef struct {
 
 ngx_int_t ngx_event_busy_lock(ngx_event_busy_lock_t *bl,
     ngx_event_busy_lock_ctx_t *ctx);
-ngx_int_t ngx_event_busy_lock_cachable(ngx_event_busy_lock_t *bl,
+ngx_int_t ngx_event_busy_lock_cacheable(ngx_event_busy_lock_t *bl,
     ngx_event_busy_lock_ctx_t *ctx);
 void ngx_event_busy_unlock(ngx_event_busy_lock_t *bl,
     ngx_event_busy_lock_ctx_t *ctx);
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -139,12 +139,29 @@ ngx_event_connect_peer(ngx_peer_connecti
     if (rc == -1) {
         err = ngx_socket_errno;
 
-        /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */
 
-        if (err != NGX_EINPROGRESS && err != NGX_EAGAIN) {
+        if (err != NGX_EINPROGRESS
+#if (NGX_WIN32)
+            /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */
+            && err != NGX_EAGAIN
+#endif
+            )
+        {
+            if (err == NGX_ECONNREFUSED
+#if (NGX_LINUX)
+                /*
+                 * Linux returns EAGAIN instead of ECONNREFUSED
+                 * for unix sockets if listen queue is full
+                 */
+                || err == NGX_EAGAIN
+#endif
+                || err == NGX_ENETDOWN
+                || err == NGX_ENETUNREACH
+                || err == NGX_EHOSTDOWN
+                || err == NGX_EHOSTUNREACH)
+            {
+                level = NGX_LOG_ERR;
 
-            if (err == NGX_ECONNREFUSED || err == NGX_EHOSTUNREACH) {
-                level = NGX_LOG_ERR;
             } else {
                 level = NGX_LOG_CRIT;
             }
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -22,6 +22,7 @@ static void ngx_ssl_read_handler(ngx_eve
 static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
 static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
     ngx_err_t err, char *text);
+static void ngx_ssl_clear_error(ngx_log_t *log);
 
 static ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone,
     void *data);
@@ -181,11 +182,17 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_
     SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
 #endif
 
+    SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE);
 
     if (ngx_ssl_protocols[protocols >> 1] != 0) {
         SSL_CTX_set_options(ssl->ctx, ngx_ssl_protocols[protocols >> 1]);
     }
 
+    /*
+     * we need this option because in ngx_ssl_send_chain()
+     * we may switch to a buffered write and may copy leftover part of
+     * previously unbuffered data to our internal buffer
+     */
     SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 
     SSL_CTX_set_read_ahead(ssl->ctx, 1);
@@ -279,10 +286,11 @@ ngx_ssl_client_certificate(ngx_conf_t *c
 static int
 ngx_http_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)
 {
+#if (NGX_DEBUG)
     char              *subject, *issuer;
     int                err, depth;
     X509              *cert;
-    X509_NAME         *name;
+    X509_NAME         *sname, *iname;
     ngx_connection_t  *c;
     ngx_ssl_conn_t    *ssl_conn;
 
@@ -295,17 +303,26 @@ ngx_http_ssl_verify_callback(int ok, X50
     err = X509_STORE_CTX_get_error(x509_store);
     depth = X509_STORE_CTX_get_error_depth(x509_store);
 
-    name = X509_get_subject_name(cert);
-    subject = name ? X509_NAME_oneline(name, NULL, 0) : "(none)";
-
-    name = X509_get_issuer_name(cert);
-    issuer = name ? X509_NAME_oneline(name, NULL, 0) : "(none)";
+    sname = X509_get_subject_name(cert);
+    subject = sname ? X509_NAME_oneline(sname, NULL, 0) : "(none)";
+
+    iname = X509_get_issuer_name(cert);
+    issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)";
 
     ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    "verify:%d, error:%d, depth:%d, "
                    "subject:\"%s\",issuer: \"%s\"",
                    ok, err, depth, subject, issuer);
 
+    if (sname) {
+        OPENSSL_free(subject);
+    }
+
+    if (iname) {
+        OPENSSL_free(issuer);
+    }
+#endif
+
     return 1;
 }
 
@@ -336,6 +353,89 @@ ngx_ssl_generate_rsa512_key(ngx_ssl_t *s
 
 
 ngx_int_t
+ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
+{
+    DH   *dh;
+    BIO  *bio;
+
+    /*
+     * -----BEGIN DH PARAMETERS-----
+     * MIGHAoGBALu8LcrYRnSQfEP89YDpz9vZWKP1aLQtSwju1OsPs1BMbAMCducQgAxc
+     * y7qokiYUxb7spWWl/fHSh6K8BJvmd4Bg6RqSp1fjBI9osHb302zI8pul34HcLKcl
+     * 7OZicMyaUDXYzs7vnqAnSmOrHlj6/UmI0PZdFGdX2gcd8EXP4WubAgEC
+     * -----END DH PARAMETERS-----
+     */
+
+    static unsigned char dh1024_p[] = {
+        0xBB, 0xBC, 0x2D, 0xCA, 0xD8, 0x46, 0x74, 0x90, 0x7C, 0x43, 0xFC, 0xF5,
+        0x80, 0xE9, 0xCF, 0xDB, 0xD9, 0x58, 0xA3, 0xF5, 0x68, 0xB4, 0x2D, 0x4B,
+        0x08, 0xEE, 0xD4, 0xEB, 0x0F, 0xB3, 0x50, 0x4C, 0x6C, 0x03, 0x02, 0x76,
+        0xE7, 0x10, 0x80, 0x0C, 0x5C, 0xCB, 0xBA, 0xA8, 0x92, 0x26, 0x14, 0xC5,
+        0xBE, 0xEC, 0xA5, 0x65, 0xA5, 0xFD, 0xF1, 0xD2, 0x87, 0xA2, 0xBC, 0x04,
+        0x9B, 0xE6, 0x77, 0x80, 0x60, 0xE9, 0x1A, 0x92, 0xA7, 0x57, 0xE3, 0x04,
+        0x8F, 0x68, 0xB0, 0x76, 0xF7, 0xD3, 0x6C, 0xC8, 0xF2, 0x9B, 0xA5, 0xDF,
+        0x81, 0xDC, 0x2C, 0xA7, 0x25, 0xEC, 0xE6, 0x62, 0x70, 0xCC, 0x9A, 0x50,
+        0x35, 0xD8, 0xCE, 0xCE, 0xEF, 0x9E, 0xA0, 0x27, 0x4A, 0x63, 0xAB, 0x1E,
+        0x58, 0xFA, 0xFD, 0x49, 0x88, 0xD0, 0xF6, 0x5D, 0x14, 0x67, 0x57, 0xDA,
+        0x07, 0x1D, 0xF0, 0x45, 0xCF, 0xE1, 0x6B, 0x9B
+    };
+
+    static unsigned char dh1024_g[] = { 0x02 };
+
+
+    if (file->len == 0) {
+
+        dh = DH_new();
+        if (dh == NULL) {
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "DH_new() failed");
+            return NGX_ERROR;
+        }
+
+        dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
+        dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
+
+        if (dh->p == NULL || dh->g == NULL) {
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "BN_bin2bn() failed");
+            DH_free(dh);
+            return NGX_ERROR;
+        }
+
+        SSL_CTX_set_tmp_dh(ssl->ctx, dh);
+
+        DH_free(dh);
+
+        return NGX_OK;
+    }
+
+    if (ngx_conf_full_name(cf->cycle, file, 1) == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    bio = BIO_new_file((char *) file->data, "r");
+    if (bio == NULL) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "BIO_new_file(\"%s\") failed", file->data);
+        return NGX_ERROR;
+    }
+
+    dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+    if (dh == NULL) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "PEM_read_bio_DHparams(\"%s\") failed", file->data);
+        BIO_free(bio);
+        return NGX_ERROR;
+    }
+
+    SSL_CTX_set_tmp_dh(ssl->ctx, dh);
+
+    DH_free(dh);
+    BIO_free(bio);
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
 {
     ngx_ssl_connection_t  *sc;
@@ -345,14 +445,7 @@ ngx_ssl_create_connection(ngx_ssl_t *ssl
         return NGX_ERROR;
     }
 
-    if (flags & NGX_SSL_BUFFER) {
-        sc->buffer = 1;
-
-        sc->buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE);
-        if (sc->buf == NULL) {
-            return NGX_ERROR;
-        }
-    }
+    sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);
 
     sc->connection = SSL_new(ssl->ctx);
 
@@ -404,12 +497,17 @@ ngx_ssl_handshake(ngx_connection_t *c)
     int        n, sslerr;
     ngx_err_t  err;
 
+    ngx_ssl_clear_error(c->log);
+
     n = SSL_do_handshake(c->ssl->connection);
 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
 
     if (n == 1) {
 
+        c->read->ready = 0;
+        c->write->ready = 1;
+
         if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
             return NGX_ERROR;
         }
@@ -579,6 +677,11 @@ ngx_ssl_recv_chain(ngx_connection_t *c, 
         }
 
         if (bytes) {
+
+            if (n == 0 || n == NGX_ERROR) {
+                c->read->ready = 1;
+            }
+
             return bytes;
         }
 
@@ -605,6 +708,8 @@ ngx_ssl_recv(ngx_connection_t *c, u_char
 
     bytes = 0;
 
+    ngx_ssl_clear_error(c->log);
+
     /*
      * SSL_read() may return data in parts, so try to read
      * until SSL_read() would return no data
@@ -801,8 +906,28 @@ ngx_ssl_send_chain(ngx_connection_t *c, 
         limit = NGX_MAX_UINT32_VALUE - ngx_pagesize;
     }
 
-
     buf = c->ssl->buf;
+
+    if (buf == NULL) {
+        buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE);
+        if (buf == NULL) {
+            return NGX_CHAIN_ERROR;
+        }
+
+        c->ssl->buf = buf;
+    }
+
+    if (buf->start == NULL) {
+        buf->start = ngx_palloc(c->pool, NGX_SSL_BUFSIZE);
+        if (buf->start == NULL) {
+            return NGX_CHAIN_ERROR;
+        }
+
+        buf->pos = buf->start;
+        buf->last = buf->start;
+        buf->end = buf->start + NGX_SSL_BUFSIZE;
+    }
+
     send = 0;
     flush = (in == NULL) ? 1 : 0;
 
@@ -895,6 +1020,8 @@ ngx_ssl_write(ngx_connection_t *c, u_cha
     int        n, sslerr;
     ngx_err_t  err;
 
+    ngx_ssl_clear_error(c->log);
+
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %d", size);
 
     n = SSL_write(c->ssl->connection, data, size);
@@ -975,12 +1102,22 @@ ngx_ssl_read_handler(ngx_event_t *rev)
 }
 
 
+void
+ngx_ssl_free_buffer(ngx_connection_t *c)
+{
+    if (c->ssl->buf && c->ssl->buf->start) {
+        if (ngx_pfree(c->pool, c->ssl->buf->start) == NGX_OK) {
+            c->ssl->buf->start = NULL;
+        }
+    }
+}
+
+
 ngx_int_t
 ngx_ssl_shutdown(ngx_connection_t *c)
 {
-    int         n, sslerr, mode;
-    ngx_err_t   err;
-    ngx_uint_t  again;
+    int        n, sslerr, mode;
+    ngx_err_t  err;
 
     if (c->timedout) {
         mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN;
@@ -999,40 +1136,31 @@ ngx_ssl_shutdown(ngx_connection_t *c)
 
     SSL_set_shutdown(c->ssl->connection, mode);
 
-    again = 0;
+    ngx_ssl_clear_error(c->log);
+
+    n = SSL_shutdown(c->ssl->connection);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n);
+
     sslerr = 0;
 
-    for ( ;; ) {
-        n = SSL_shutdown(c->ssl->connection);
-
-        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n);
-
-        if (n == 1 || (n == 0 && c->timedout)) {
-            SSL_free(c->ssl->connection);
-            c->ssl = NULL;
-
-            return NGX_OK;
-        }
-
-        if (n == 0) {
-            again = 1;
-            break;
-        }
-
-        break;
-    }
-
-    if (!again) {
+    /* SSL_shutdown() never returns -1, on error it returns 0 */
+
+    if (n != 1 && ERR_peek_error()) {
         sslerr = SSL_get_error(c->ssl->connection, n);
 
         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                        "SSL_get_error: %d", sslerr);
     }
 
-    if (again
-        || sslerr == SSL_ERROR_WANT_READ
-        || sslerr == SSL_ERROR_WANT_WRITE)
-    {
+    if (n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN) {
+        SSL_free(c->ssl->connection);
+        c->ssl = NULL;
+
+        return NGX_OK;
+    }
+
+    if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) {
         c->read->handler = ngx_ssl_shutdown_handler;
         c->write->handler = ngx_ssl_shutdown_handler;
 
@@ -1044,7 +1172,7 @@ ngx_ssl_shutdown(ngx_connection_t *c)
             return NGX_ERROR;
         }
 
-        if (again || sslerr == SSL_ERROR_WANT_READ) {
+        if (sslerr == SSL_ERROR_WANT_READ) {
             ngx_add_timer(c->read, 30000);
         }
 
@@ -1089,6 +1217,7 @@ static void
 ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
     char *text)
 {
+    int         n;
     ngx_uint_t  level;
 
     level = NGX_LOG_CRIT;
@@ -1102,6 +1231,9 @@ ngx_ssl_connection_error(ngx_connection_
             || err == NGX_ETIMEDOUT
 #endif
             || err == NGX_ECONNREFUSED
+            || err == NGX_ENETDOWN
+            || err == NGX_ENETUNREACH
+            || err == NGX_EHOSTDOWN
             || err == NGX_EHOSTUNREACH)
         {
             switch (c->log_error) {
@@ -1119,18 +1251,69 @@ ngx_ssl_connection_error(ngx_connection_
                 break;
             }
         }
+
+    } else if (sslerr == SSL_ERROR_SSL) {
+
+        n = ERR_GET_REASON(ERR_peek_error());
+
+            /* handshake failures */
+        if (n == SSL_R_DIGEST_CHECK_FAILED
+            || n == SSL_R_NO_SHARED_CIPHER
+            || n == SSL_R_UNEXPECTED_MESSAGE
+            || n == SSL_R_WRONG_VERSION_NUMBER
+            || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC
+            || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
+            || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE
+            || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC
+            || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE
+            || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE
+            || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE
+            || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN
+            || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER
+            || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA)
+        {
+            switch (c->log_error) {
+
+            case NGX_ERROR_IGNORE_ECONNRESET:
+            case NGX_ERROR_INFO:
+                level = NGX_LOG_INFO;
+                break;
+
+            case NGX_ERROR_ERR:
+                level = NGX_LOG_ERR;
+                break;
+
+            default:
+                break;
+            }
+        }
     }
 
     ngx_ssl_error(level, c->log, err, text);
 }
 
 
+static void
+ngx_ssl_clear_error(ngx_log_t *log)
+{
+    while (ERR_peek_error()) {
+        ngx_ssl_error(NGX_LOG_ALERT, log, 0, "ignoring stale global SSL error");
+    }
+
+    ERR_clear_error();
+}
+
+
 void ngx_cdecl
 ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
 {
-    u_long   n;
-    va_list  args;
-    u_char   errstr[NGX_MAX_CONF_ERRSTR], *p, *last;
+    u_long    n;
+    va_list   args;
+    u_char   *p, *last;
+    u_char    errstr[NGX_MAX_CONF_ERRSTR];
 
     last = errstr + NGX_MAX_CONF_ERRSTR;
 
@@ -1140,7 +1323,7 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_
 
     p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p);
 
-    while (p < last) {
+    for ( ;; ) {
 
         n = ERR_get_error();
 
@@ -1148,6 +1331,10 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_
             break;
         }
 
+        if (p >= last) {
+            continue;
+        }
+
         *p++ = ' ';
 
         ERR_error_string_n(n, (char *) p, last - p);
@@ -1167,6 +1354,34 @@ ngx_ssl_session_cache(ngx_ssl_t *ssl, ng
 {
     long  cache_mode;
 
+    if (builtin_session_cache == NGX_SSL_NO_SCACHE) {
+        SSL_CTX_set_session_cache_mode(ssl->ctx, SSL_SESS_CACHE_OFF);
+        return NGX_OK;
+    }
+
+    if (builtin_session_cache == NGX_SSL_NONE_SCACHE) {
+
+        /*
+         * If the server explicitly says that it does not support
+         * session reuse (see SSL_SESS_CACHE_OFF above), then
+         * Outlook Express fails to upload a sent email to
+         * the Sent Items folder on the IMAP server via a separate IMAP
+         * connection in the background. Therefore we have a special
+         * mode (SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL_STORE)
+         * where the server pretends that it supports session reuse,
+         * but it does not actually store any session.
+         */
+
+        SSL_CTX_set_session_cache_mode(ssl->ctx,
+                                       SSL_SESS_CACHE_SERVER
+                                       |SSL_SESS_CACHE_NO_AUTO_CLEAR
+                                       |SSL_SESS_CACHE_NO_INTERNAL_STORE);
+
+        SSL_CTX_sess_set_cache_size(ssl->ctx, 1);
+
+        return NGX_OK;
+    }
+
     cache_mode = SSL_SESS_CACHE_SERVER;
 
     if (shm_zone && builtin_session_cache == NGX_SSL_NO_BUILTIN_SCACHE) {
@@ -1210,7 +1425,6 @@ static ngx_int_t
 ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)
 {
     ngx_slab_pool_t          *shpool;
-    ngx_rbtree_node_t        *sentinel;
     ngx_ssl_session_cache_t  *cache;
 
     if (data) {
@@ -1225,27 +1439,10 @@ ngx_ssl_session_cache_init(ngx_shm_zone_
         return NGX_ERROR;
     }
 
-    cache->session_cache_head.prev = NULL;
-    cache->session_cache_head.next = &cache->session_cache_tail;
-
-    cache->session_cache_tail.prev = &cache->session_cache_head;
-    cache->session_cache_tail.next = NULL;
-
-    cache->session_rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));
-    if (cache->session_rbtree == NULL) {
-        return NGX_ERROR;
-    }
-
-    sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));
-    if (sentinel == NULL) {
-        return NGX_ERROR;
-    }
-
-    ngx_rbtree_sentinel_init(sentinel);
-
-    cache->session_rbtree->root = sentinel;
-    cache->session_rbtree->sentinel = sentinel;
-    cache->session_rbtree->insert = ngx_ssl_session_rbtree_insert_value;
+    ngx_rbtree_init(&cache->session_rbtree, &cache->sentinel,
+                    ngx_ssl_session_rbtree_insert_value);
+
+    ngx_queue_init(&cache->expire_queue);
 
     shm_zone->data = cache;
 
@@ -1277,7 +1474,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_
     u_char                   *p, *id, *cached_sess;
     uint32_t                  hash;
     SSL_CTX                  *ssl_ctx;
-    ngx_time_t               *tp;
     ngx_shm_zone_t           *shm_zone;
     ngx_connection_t         *c;
     ngx_slab_pool_t          *shpool;
@@ -1353,22 +1549,17 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_
                    "http ssl new session: %08XD:%d:%d",
                    hash, sess->session_id_length, len);
 
-    tp = ngx_timeofday();
-
     sess_id->node.key = hash;
     sess_id->node.data = (u_char) sess->session_id_length;
     sess_id->id = id;
     sess_id->len = len;
     sess_id->session = cached_sess;
 
-    sess_id->expire = tp->sec + SSL_CTX_get_timeout(ssl_ctx);
-
-    sess_id->next = cache->session_cache_head.next;
-    sess_id->next->prev = sess_id;
-    sess_id->prev = &cache->session_cache_head;
-    cache->session_cache_head.next = sess_id;
-
-    ngx_rbtree_insert(cache->session_rbtree, &sess_id->node);
+    sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx);
+
+    ngx_queue_insert_head(&cache->expire_queue, &sess_id->queue);
+
+    ngx_rbtree_insert(&cache->session_rbtree, &sess_id->node);
 
     ngx_shmtx_unlock(&shpool->mutex);
 
@@ -1403,7 +1594,6 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_
     u_char                   *p;
     uint32_t                  hash;
     ngx_int_t                 rc;
-    ngx_time_t               *tp;
     ngx_shm_zone_t           *shm_zone;
     ngx_slab_pool_t          *shpool;
     ngx_connection_t         *c;
@@ -1426,18 +1616,14 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_
 
     cache = shm_zone->data;
 
-    if (cache->session_rbtree == NULL) {
-        return NULL;
-    }
-
     sess = NULL;
 
     shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
 
     ngx_shmtx_lock(&shpool->mutex);
 
-    node = cache->session_rbtree->root;
-    sentinel = cache->session_rbtree->sentinel;
+    node = cache->session_rbtree.root;
+    sentinel = cache->session_rbtree.sentinel;
 
     while (node != sentinel) {
 
@@ -1460,9 +1646,7 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_
                               (size_t) len, (size_t) node->data);
             if (rc == 0) {
 
-                tp = ngx_timeofday();
-
-                if (sess_id->expire > tp->sec) {
+                if (sess_id->expire > ngx_time()) {
                     ngx_memcpy(buf, sess_id->session, sess_id->len);
 
                     ngx_shmtx_unlock(&shpool->mutex);
@@ -1473,10 +1657,9 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_
                     return sess;
                 }
 
-                sess_id->next->prev = sess_id->prev;
-                sess_id->prev->next = sess_id->next;
-
-                ngx_rbtree_delete(cache->session_rbtree, node);
+                ngx_queue_remove(&sess_id->queue);
+
+                ngx_rbtree_delete(&cache->session_rbtree, node);
 
                 ngx_slab_free_locked(shpool, sess_id->session);
 #if (NGX_PTR_SIZE == 4)
@@ -1504,6 +1687,15 @@ done:
 }
 
 
+void
+ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
+{
+     SSL_CTX_remove_session(ssl, sess);
+
+     ngx_ssl_remove_session(ssl, sess);
+}
+
+
 static void
 ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
 {
@@ -1519,6 +1711,10 @@ ngx_ssl_remove_session(SSL_CTX *ssl, ngx
 
     shm_zone = SSL_CTX_get_ex_data(ssl, ngx_ssl_session_cache_index);
 
+    if (shm_zone == NULL) {
+        return;
+    }
+
     cache = shm_zone->data;
 
     id = sess->session_id;
@@ -1533,8 +1729,8 @@ ngx_ssl_remove_session(SSL_CTX *ssl, ngx
 
     ngx_shmtx_lock(&shpool->mutex);
 
-    node = cache->session_rbtree->root;
-    sentinel = cache->session_rbtree->sentinel;
+    node = cache->session_rbtree.root;
+    sentinel = cache->session_rbtree.sentinel;
 
     while (node != sentinel) {
 
@@ -1556,10 +1752,10 @@ ngx_ssl_remove_session(SSL_CTX *ssl, ngx
             rc = ngx_memn2cmp(id, sess_id->id, len, (size_t) node->data);
 
             if (rc == 0) {
-                sess_id->next->prev = sess_id->prev;
-                sess_id->prev->next = sess_id->next;
-
-                ngx_rbtree_delete(cache->session_rbtree, node);
+
+                ngx_queue_remove(&sess_id->queue);
+
+                ngx_rbtree_delete(&cache->session_rbtree, node);
 
                 ngx_slab_free_locked(shpool, sess_id->session);
 #if (NGX_PTR_SIZE == 4)
@@ -1587,31 +1783,33 @@ static void
 ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,
     ngx_slab_pool_t *shpool, ngx_uint_t n)
 {
-    ngx_time_t         *tp;
+    time_t              now;
+    ngx_queue_t        *q;
     ngx_ssl_sess_id_t  *sess_id;
 
-    tp = ngx_timeofday();
+    now = ngx_time();
 
     while (n < 3) {
 
-        sess_id = cache->session_cache_tail.prev;
-
-        if (sess_id == &cache->session_cache_head) {
+        if (ngx_queue_empty(&cache->expire_queue)) {
             return;
         }
 
-        if (n++ != 0 && sess_id->expire > tp->sec) {
+        q = ngx_queue_last(&cache->expire_queue);
+
+        sess_id = ngx_queue_data(q, ngx_ssl_sess_id_t, queue);
+
+        if (n++ != 0 && sess_id->expire > now) {
             return;
         }
 
-        sess_id->next->prev = sess_id->prev;
-        sess_id->prev->next = sess_id->next;
-
-        ngx_rbtree_delete(cache->session_rbtree, &sess_id->node);
+        ngx_queue_remove(q);
 
         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
                        "expire session: %08Xi", sess_id->node.key);
 
+        ngx_rbtree_delete(&cache->session_rbtree, &sess_id->node);
+
         ngx_slab_free_locked(shpool, sess_id->session);
 #if (NGX_PTR_SIZE == 4)
         ngx_slab_free_locked(shpool, sess_id->id);
@@ -1625,56 +1823,37 @@ static void
 ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
 {
-    ngx_ssl_sess_id_t  *sess_id, *sess_id_temp;
+    ngx_rbtree_node_t  **p;
+    ngx_ssl_sess_id_t   *sess_id, *sess_id_temp;
 
     for ( ;; ) {
 
         if (node->key < temp->key) {
 
-            if (temp->left == sentinel) {
-                temp->left = node;
-                break;
-            }
-
-            temp = temp->left;
+            p = &temp->left;
 
         } else if (node->key > temp->key) {
 
-            if (temp->right == sentinel) {
-                temp->right = node;
-                break;
-            }
-
-            temp = temp->right;
+            p = &temp->right;
 
         } else { /* node->key == temp->key */
 
             sess_id = (ngx_ssl_sess_id_t *) node;
             sess_id_temp = (ngx_ssl_sess_id_t *) temp;
 
-            if (ngx_memn2cmp(sess_id->id, sess_id_temp->id,
-                             (size_t) node->data, (size_t) temp->data)
-                < 0)
-            {
-                if (temp->left == sentinel) {
-                    temp->left = node;
-                    break;
-                }
-
-                temp = temp->left;
-
-            } else {
-
-                if (temp->right == sentinel) {
-                    temp->right = node;
-                    break;
-                }
-
-                temp = temp->right;
-            }
+            p = (ngx_memn2cmp(sess_id->id, sess_id_temp->id,
+                              (size_t) node->data, (size_t) temp->data)
+                 < 0) ? &temp->left : &temp->right;
         }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
     }
 
+    *p = node;
     node->parent = temp;
     node->left = sentinel;
     node->right = sentinel;
@@ -1708,6 +1887,100 @@ ngx_ssl_get_cipher_name(ngx_connection_t
 
 
 ngx_int_t
+ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+    size_t   len;
+    BIO     *bio;
+    X509    *cert;
+
+    s->len = 0;
+
+    cert = SSL_get_peer_certificate(c->ssl->connection);
+    if (cert == NULL) {
+        return NGX_OK;
+    }
+
+    bio = BIO_new(BIO_s_mem());
+    if (bio == NULL) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed");
+        X509_free(cert);
+        return NGX_ERROR;
+    }
+
+    if (PEM_write_bio_X509(bio, cert) == 0) {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "PEM_write_bio_X509() failed");
+        goto failed;
+    }
+
+    len = BIO_pending(bio);
+    s->len = len;
+
+    s->data = ngx_pnalloc(pool, len);
+    if (s->data == NULL) {
+        goto failed;
+    }
+
+    BIO_read(bio, s->data, len);
+
+    BIO_free(bio);
+    X509_free(cert);
+
+    return NGX_OK;
+
+failed:
+
+    BIO_free(bio);
+    X509_free(cert);
+
+    return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+    u_char      *p;
+    size_t       len;
+    ngx_uint_t   i;
+    ngx_str_t    cert;
+
+    if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (cert.len == 0) {
+        s->len = 0;
+        return NGX_OK;
+    }
+
+    len = cert.len - 1;
+
+    for (i = 0; i < cert.len - 1; i++) {
+        if (cert.data[i] == LF) {
+            len++;
+        }
+    }
+
+    s->len = len;
+    s->data = ngx_pnalloc(pool, len);
+    if (s->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    p = s->data;
+
+    for (i = 0; i < len; i++) {
+        *p++ = cert.data[i];
+        if (cert.data[i] == LF) {
+            *p++ = '\t';
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
 {
     char       *p;
@@ -1724,6 +1997,7 @@ ngx_ssl_get_subject_dn(ngx_connection_t 
 
     name = X509_get_subject_name(cert);
     if (name == NULL) {
+        X509_free(cert);
         return NGX_ERROR;
     }
 
@@ -1732,15 +2006,17 @@ ngx_ssl_get_subject_dn(ngx_connection_t 
     for (len = 0; p[len]; len++) { /* void */ }
 
     s->len = len;
-    s->data = ngx_palloc(pool, len);
+    s->data = ngx_pnalloc(pool, len);
     if (s->data == NULL) {
         OPENSSL_free(p);
+        X509_free(cert);
         return NGX_ERROR;
     }
 
     ngx_memcpy(s->data, p, len);
 
     OPENSSL_free(p);
+    X509_free(cert);
 
     return NGX_OK;
 }
@@ -1763,6 +2039,7 @@ ngx_ssl_get_issuer_dn(ngx_connection_t *
 
     name = X509_get_issuer_name(cert);
     if (name == NULL) {
+        X509_free(cert);
         return NGX_ERROR;
     }
 
@@ -1771,15 +2048,17 @@ ngx_ssl_get_issuer_dn(ngx_connection_t *
     for (len = 0; p[len]; len++) { /* void */ }
 
     s->len = len;
-    s->data = ngx_palloc(pool, len);
+    s->data = ngx_pnalloc(pool, len);
     if (s->data == NULL) {
         OPENSSL_free(p);
+        X509_free(cert);
         return NGX_ERROR;
     }
 
     ngx_memcpy(s->data, p, len);
 
     OPENSSL_free(p);
+    X509_free(cert);
 
     return NGX_OK;
 }
@@ -1801,6 +2080,7 @@ ngx_ssl_get_serial_number(ngx_connection
 
     bio = BIO_new(BIO_s_mem());
     if (bio == NULL) {
+        X509_free(cert);
         return NGX_ERROR;
     }
 
@@ -1808,14 +2088,16 @@ ngx_ssl_get_serial_number(ngx_connection
     len = BIO_pending(bio);
 
     s->len = len;
-    s->data = ngx_palloc(pool, len);
+    s->data = ngx_pnalloc(pool, len);
     if (s->data == NULL) {
         BIO_free(bio);
+        X509_free(cert);
         return NGX_ERROR;
     }
 
     BIO_read(bio, s->data, len);
     BIO_free(bio);
+    X509_free(cert);
 
     return NGX_OK;
 }
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -51,11 +51,13 @@ typedef struct {
 } ngx_ssl_connection_t;
 
 
-#define NGX_SSL_DFLT_BUILTIN_SCACHE  -2
-#define NGX_SSL_NO_BUILTIN_SCACHE    -3
+#define NGX_SSL_NO_SCACHE            -2
+#define NGX_SSL_NONE_SCACHE          -3
+#define NGX_SSL_NO_BUILTIN_SCACHE    -4
+#define NGX_SSL_DFLT_BUILTIN_SCACHE  -5
 
 
-#define NGX_SSL_MAX_SESSION_SIZE (4096)
+#define NGX_SSL_MAX_SESSION_SIZE  4096
 
 typedef struct ngx_ssl_sess_id_s  ngx_ssl_sess_id_t;
 
@@ -64,8 +66,7 @@ struct ngx_ssl_sess_id_s {
     u_char                     *id;
     size_t                      len;
     u_char                     *session;
-    ngx_ssl_sess_id_t          *prev;
-    ngx_ssl_sess_id_t          *next;
+    ngx_queue_t                 queue;
     time_t                      expire;
 #if (NGX_PTR_SIZE == 8)
     void                       *stub;
@@ -75,9 +76,9 @@ struct ngx_ssl_sess_id_s {
 
 
 typedef struct {
-    ngx_rbtree_t               *session_rbtree;
-    ngx_ssl_sess_id_t           session_cache_head;
-    ngx_ssl_sess_id_t           session_cache_tail;
+    ngx_rbtree_t                session_rbtree;
+    ngx_rbtree_node_t           sentinel;
+    ngx_queue_t                 expire_queue;
 } ngx_ssl_session_cache_t;
 
 
@@ -100,11 +101,13 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t
 ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_str_t *cert, ngx_int_t depth);
 ngx_int_t ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl);
+ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
 ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
     ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout);
 ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
     ngx_uint_t flags);
 
+void ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);
 ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session);
 #define ngx_ssl_get_session(c)      SSL_get1_session(c->ssl->connection)
 #define ngx_ssl_free_session        SSL_SESSION_free
@@ -118,6 +121,10 @@ ngx_int_t ngx_ssl_get_protocol(ngx_conne
     ngx_str_t *s);
 ngx_int_t ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
+ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
+ngx_int_t ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
 ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
 ngx_int_t ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool,
@@ -132,6 +139,7 @@ ssize_t ngx_ssl_write(ngx_connection_t *
 ssize_t ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl);
 ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,
     off_t limit);
+void ngx_ssl_free_buffer(ngx_connection_t *c);
 ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c);
 void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
     char *fmt, ...);
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -21,7 +21,7 @@ static ngx_int_t ngx_event_pipe_drain_ch
 
 
 ngx_int_t
-ngx_event_pipe(ngx_event_pipe_t *p, int do_write)
+ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write)
 {
     u_int         flags;
     ngx_event_t  *rev, *wev;
@@ -192,7 +192,7 @@ ngx_event_pipe_read_upstream(ngx_event_p
                 chain->buf = b;
                 chain->next = NULL;
 
-            } else if (!p->cachable
+            } else if (!p->cacheable
                        && p->downstream->data == p->output_ctx
                        && p->downstream->write->ready
                        && !p->downstream->write->delayed)
@@ -209,7 +209,7 @@ ngx_event_pipe_read_upstream(ngx_event_p
 
                 break;
 
-            } else if (p->cachable
+            } else if (p->cacheable
                        || p->temp_file->offset < p->max_temp_file_size)
             {
 
@@ -406,7 +406,7 @@ ngx_event_pipe_read_upstream(ngx_event_p
         }
     }
 
-    if (p->cachable && p->in) {
+    if (p->cacheable && p->in) {
         if (ngx_event_pipe_write_chain_to_temp_file(p) == NGX_ABORT) {
             return NGX_ABORT;
         }
@@ -421,6 +421,7 @@ ngx_event_pipe_write_to_downstream(ngx_e
 {
     u_char            *prev;
     size_t             bsize;
+    ngx_int_t          rc;
     ngx_uint_t         flush, prev_last_shadow;
     ngx_chain_t       *out, **ll, *cl;
     ngx_connection_t  *downstream;
@@ -451,7 +452,13 @@ ngx_event_pipe_write_to_downstream(ngx_e
                     cl->buf->recycled = 0;
                 }
 
-                if (p->output_filter(p->output_ctx, p->out) == NGX_ERROR) {
+                rc = p->output_filter(p->output_ctx, p->out);
+
+                if (downstream->destroyed) {
+                    return NGX_ABORT;
+                }
+
+                if (rc == NGX_ERROR) {
                     p->downstream_error = 1;
                     return ngx_event_pipe_drain_chains(p);
                 }
@@ -467,12 +474,13 @@ ngx_event_pipe_write_to_downstream(ngx_e
                     cl->buf->recycled = 0;
                 }
 
-                if (p->output_filter(p->output_ctx, p->in) == NGX_ERROR) {
+                rc = p->output_filter(p->output_ctx, p->in);
 
-                    if (downstream->destroyed) {
-                        return NGX_ABORT;
-                    }
+                if (downstream->destroyed) {
+                    return NGX_ABORT;
+                }
 
+                if (rc == NGX_ERROR) {
                     p->downstream_error = 1;
                     return ngx_event_pipe_drain_chains(p);
                 }
@@ -542,7 +550,7 @@ ngx_event_pipe_write_to_downstream(ngx_e
 
                 ngx_event_pipe_free_shadow_raw_buf(&p->free_raw_bufs, cl->buf);
 
-            } else if (!p->cachable && p->in) {
+            } else if (!p->cacheable && p->in) {
                 cl = p->in;
 
                 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0,
@@ -602,7 +610,13 @@ ngx_event_pipe_write_to_downstream(ngx_e
             break;
         }
 
-        if (p->output_filter(p->output_ctx, out) == NGX_ERROR) {
+        rc = p->output_filter(p->output_ctx, out);
+
+        if (downstream->destroyed) {
+            return NGX_ABORT;
+        }
+
+        if (rc == NGX_ERROR) {
             p->downstream_error = 1;
             return ngx_event_pipe_drain_chains(p);
         }
@@ -612,7 +626,7 @@ ngx_event_pipe_write_to_downstream(ngx_e
         for (cl = p->free; cl; cl = cl->next) {
 
             if (cl->buf->temp_file) {
-                if (p->cachable || !p->cyclic_temp_file) {
+                if (p->cacheable || !p->cyclic_temp_file) {
                     continue;
                 }
 
@@ -659,7 +673,7 @@ ngx_event_pipe_write_chain_to_temp_file(
         out = p->in;
     }
 
-    if (!p->cachable) {
+    if (!p->cacheable) {
 
         size = 0;
         cl = out;
@@ -866,7 +880,7 @@ ngx_event_pipe_free_shadow_raw_buf(ngx_c
 
     ll = free;
 
-    for (cl = *free ; cl; cl = cl->next) {
+    for (cl = *free; cl; cl = cl->next) {
         if (cl->buf == s) {
             *ll = cl->next;
             break;
--- a/src/event/ngx_event_pipe.h
+++ b/src/event/ngx_event_pipe.h
@@ -47,7 +47,7 @@ struct ngx_event_pipe_s {
     void                             *output_ctx;
 
     unsigned           read:1;
-    unsigned           cachable:1;
+    unsigned           cacheable:1;
     unsigned           single_buf:1;
     unsigned           free_bufs:1;
     unsigned           upstream_done:1;
@@ -86,7 +86,7 @@ struct ngx_event_pipe_s {
 };
 
 
-ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, int do_write);
+ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write);
 ngx_int_t ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf);
 ngx_int_t ngx_event_pipe_add_free_buf(ngx_event_pipe_t *p, ngx_buf_t *b);
 
--- a/src/event/ngx_event_timer.c
+++ b/src/event/ngx_event_timer.c
@@ -26,9 +26,8 @@ static ngx_rbtree_node_t          ngx_ev
 ngx_int_t
 ngx_event_timer_init(ngx_log_t *log)
 {
-    ngx_event_timer_rbtree.root = &ngx_event_timer_sentinel;
-    ngx_event_timer_rbtree.sentinel = &ngx_event_timer_sentinel;
-    ngx_event_timer_rbtree.insert = ngx_rbtree_insert_timer_value;
+    ngx_rbtree_init(&ngx_event_timer_rbtree, &ngx_event_timer_sentinel,
+                    ngx_rbtree_insert_timer_value);
 
 #if (NGX_THREADS)
 
--- a/src/event/ngx_event_timer.h
+++ b/src/event/ngx_event_timer.h
@@ -65,9 +65,9 @@ ngx_event_add_timer(ngx_event_t *ev, ngx
     if (ev->timer_set) {
 
         /*
-         * Use the previous timer value if a difference between them is less
-         * then NGX_TIMER_LAZY_DELAY milliseconds.  It allows to minimize
-         * the rbtree operations for the fast connections.
+         * Use a previous timer value if difference between it and a new
+         * value is less than NGX_TIMER_LAZY_DELAY milliseconds: this allows
+         * to minimize the rbtree operations for fast connections.
          */
 
         diff = (ngx_msec_int_t) (key - ev->timer.key);
--- a/src/http/modules/ngx_http_access_module.c
+++ b/src/http/modules/ngx_http_access_module.c
@@ -98,7 +98,7 @@ ngx_http_access_handler(ngx_http_request
     alcf = ngx_http_get_module_loc_conf(r, ngx_http_access_module);
 
     if (alcf->rules == NULL) {
-        return NGX_OK;
+        return NGX_DECLINED;
     }
 
     /* AF_INET only */
@@ -116,7 +116,7 @@ ngx_http_access_handler(ngx_http_request
             if (rule[i].deny) {
                 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-                if (!clcf->satisfy_any) {
+                if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                                   "access forbidden by rule");
                 }
@@ -128,7 +128,7 @@ ngx_http_access_handler(ngx_http_request
         }
     }
 
-    return NGX_OK;
+    return NGX_DECLINED;
 }
 
 
--- a/src/http/modules/ngx_http_auth_basic_module.c
+++ b/src/http/modules/ngx_http_auth_basic_module.c
@@ -113,7 +113,7 @@ ngx_http_auth_basic_handler(ngx_http_req
     alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module);
 
     if (alcf->realm.len == 0 || alcf->user_file.len == 0) {
-        return NGX_OK;
+        return NGX_DECLINED;
     }
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_auth_basic_module);
@@ -232,7 +232,7 @@ ngx_http_auth_basic_handler(ngx_http_req
 
     if (state == sw_passwd) {
         pwd.len = i - passwd;
-        pwd.data = ngx_palloc(r->pool, pwd.len + 1);
+        pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);
         if (pwd.data == NULL) {
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
@@ -400,7 +400,7 @@ ngx_http_auth_basic(ngx_conf_t *cf, void
 
     len = sizeof("Basic realm=\"") - 1 + realm->len + 1;
 
-    basic = ngx_palloc(cf->pool, len);
+    basic = ngx_pnalloc(cf->pool, len);
     if (basic == NULL) {
         return NGX_CONF_ERROR;
     }
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -135,7 +135,7 @@ ngx_http_autoindex_handler(ngx_http_requ
 {
     u_char                         *last, *filename, scale;
     off_t                           length;
-    size_t                          len, copy, allocated, root;
+    size_t                          len, utf_len, allocated, root;
     ngx_tm_t                        tm;
     ngx_err_t                       err;
     ngx_buf_t                      *b;
@@ -181,7 +181,10 @@ ngx_http_autoindex_handler(ngx_http_requ
     }
 
     allocated = path.len;
-    path.len = last - path.data - 1;
+    path.len = last - path.data;
+    if (path.len > 1) {
+        path.len--;
+    }
     path.data[path.len] = '\0';
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -236,6 +239,11 @@ ngx_http_autoindex_handler(ngx_http_requ
     rc = ngx_http_send_header(r);
 
     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+        if (ngx_close_dir(&dir) == NGX_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+                          ngx_close_dir_n " \"%V\" failed", &path);
+        }
+
         return rc;
     }
 
@@ -274,7 +282,7 @@ ngx_http_autoindex_handler(ngx_http_requ
                 allocated = path.len + 1 + len + 1
                                      + NGX_HTTP_AUTOINDEX_PREALLOCATE;
 
-                filename = ngx_palloc(pool, allocated);
+                filename = ngx_pnalloc(pool, allocated);
                 if (filename == NULL) {
                     return ngx_http_autoindex_error(r, &dir, &path);
                 }
@@ -310,7 +318,7 @@ ngx_http_autoindex_handler(ngx_http_requ
 
         entry->name.len = len;
 
-        entry->name.data = ngx_palloc(pool, len + 1);
+        entry->name.data = ngx_pnalloc(pool, len + 1);
         if (entry->name.data == NULL) {
             return ngx_http_autoindex_error(r, &dir, &path);
         }
@@ -321,7 +329,7 @@ ngx_http_autoindex_handler(ngx_http_requ
                                            NGX_ESCAPE_HTML);
 
         if (r->utf8) {
-            entry->utf_len = ngx_utf_length(entry->name.data, entry->name.len);
+            entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len);
         } else {
             entry->utf_len = len;
         }
@@ -404,15 +412,16 @@ ngx_http_autoindex_handler(ngx_http_requ
 
         len = entry[i].utf_len;
 
-        if (entry[i].name.len - len) {
+        if (entry[i].name.len != len) {
             if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
-                copy = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;
+                utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;
 
             } else {
-                copy = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
+                utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
             }
 
-            b->last = ngx_utf_cpystrn(b->last, entry[i].name.data, copy);
+            b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,
+                                       utf_len, entry[i].name.len + 1);
             last = b->last;
 
         } else {
--- a/src/http/modules/ngx_http_browser_module.c
+++ b/src/http/modules/ngx_http_browser_module.c
@@ -397,7 +397,7 @@ ngx_http_browser_add_variable(ngx_conf_t
 
     for (var = ngx_http_browsers; var->name.len; var++) {
 
-        v = ngx_http_add_variable(cf, &var->name, NGX_HTTP_VAR_CHANGABLE);
+        v = ngx_http_add_variable(cf, &var->name, NGX_HTTP_VAR_CHANGEABLE);
         if (v == NULL) {
             return NGX_ERROR;
         }
@@ -673,7 +673,7 @@ ngx_http_modern_browser_value(ngx_conf_t
 
     bcf->modern_browser_value->len = value[1].len;
     bcf->modern_browser_value->valid = 1;
-    bcf->modern_browser_value->no_cachable = 0;
+    bcf->modern_browser_value->no_cacheable = 0;
     bcf->modern_browser_value->not_found = 0;
     bcf->modern_browser_value->data = value[1].data;
 
@@ -698,7 +698,7 @@ ngx_http_ancient_browser_value(ngx_conf_
 
     bcf->ancient_browser_value->len = value[1].len;
     bcf->ancient_browser_value->valid = 1;
-    bcf->ancient_browser_value->no_cachable = 0;
+    bcf->ancient_browser_value->no_cacheable = 0;
     bcf->ancient_browser_value->not_found = 0;
     bcf->ancient_browser_value->data = value[1].data;
 
--- a/src/http/modules/ngx_http_charset_filter_module.c
+++ b/src/http/modules/ngx_http_charset_filter_module.c
@@ -204,6 +204,12 @@ ngx_http_charset_header_filter(ngx_http_
 
     if (r == r->main) {
 
+        if (r->headers_out.content_encoding
+            && r->headers_out.content_encoding->value.len)
+        {
+            return ngx_http_next_header_filter(r);
+        }
+
         if (r->headers_out.content_type.len == 0) {
             return ngx_http_next_header_filter(r);
         }
@@ -366,8 +372,8 @@ ngx_http_charset_header_filter(ngx_http_
 no_charset_map:
 
     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                  "no \"charset_map\" between the charsets "
-                  "\"%V\" and \"%V\"", from, to);
+                  "no \"charset_map\" between the charsets \"%V\" and \"%V\"",
+                  from, to);
 
     return ngx_http_next_header_filter(r);
 }
@@ -561,25 +567,33 @@ ngx_http_charset_body_filter(ngx_http_re
 static ngx_uint_t
 ngx_http_charset_recode(ngx_buf_t *b, u_char *table)
 {
-    u_char  *p;
-
-    for (p = b->pos; p < b->last; p++) {
+    u_char  *p, *last;
 
-        if (*p == table[*p]) {
-            continue;
-        }
+    last = b->last;
 
-        while (p < b->last) {
-            *p = table[*p];
-            p++;
+    for (p = b->pos; p < last; p++) {
+
+        if (*p != table[*p]) {
+            goto recode;
         }
-
-        b->in_file = 0;
-
-        return 1;
     }
 
     return 0;
+
+recode:
+
+    do {
+        if (*p != table[*p]) {
+            *p = table[*p];
+        }
+
+        p++;
+
+    } while (p < last);
+
+    b->in_file = 0;
+
+    return 1;
 }
 
 
@@ -628,7 +642,7 @@ ngx_http_charset_recode_from_utf8(ngx_po
                 size = buf->last - src;
 
                 saved = src;
-                n = ngx_utf_decode(&saved, size);
+                n = ngx_utf8_decode(&saved, size);
 
                 if (n == 0xfffffffe) {
                     /* incomplete UTF-8 symbol */
@@ -696,7 +710,7 @@ ngx_http_charset_recode_from_utf8(ngx_po
     }
 
     saved = ctx->saved;
-    n = ngx_utf_decode(&saved, i);
+    n = ngx_utf8_decode(&saved, i);
 
     c = '\0';
 
@@ -804,7 +818,7 @@ recode:
 
         len = buf->last - src;
 
-        n = ngx_utf_decode(&src, len);
+        n = ngx_utf8_decode(&src, len);
 
         if (n < 0x10000) {
 
@@ -1256,7 +1270,7 @@ ngx_http_charset_map(ngx_conf_t *cf, ngx
 
         p = &table->src2dst[src * NGX_UTF_LEN] + 1;
 
-        n = ngx_utf_decode(&p, i);
+        n = ngx_utf8_decode(&p, i);
 
         if (n > 0xffff) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -1462,6 +1476,12 @@ ngx_http_charset_merge_loc_conf(ngx_conf
         return NGX_CONF_OK;
     }
 
+    if (conf->source_charset >= NGX_HTTP_CHARSET_VAR
+        || conf->charset >= NGX_HTTP_CHARSET_VAR)
+    {
+        return NGX_CONF_OK;
+    }
+
     mcf = ngx_http_conf_get_module_main_conf(cf,
                                              ngx_http_charset_filter_module);
     recode = mcf->recodes.elts;
@@ -1519,9 +1539,8 @@ ngx_http_charset_postconfiguration(ngx_c
         }
 
         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                      " no \"charset_map\" between the charsets "
-                      "\"%V\" and \"%V\"",
-                      &charset[c].name, &charset[recode[i].dst].name);
+                   "no \"charset_map\" between the charsets \"%V\" and \"%V\"",
+                   &charset[c].name, &charset[recode[i].dst].name);
         return NGX_ERROR;
 
     next:
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -21,8 +21,9 @@
 
 typedef struct {
     ngx_uint_t  methods;
+    ngx_uint_t  access;
+    ngx_uint_t  min_delete_depth;
     ngx_flag_t  create_full_put_path;
-    ngx_uint_t  access;
 } ngx_http_dav_loc_conf_t;
 
 
@@ -37,10 +38,11 @@ static ngx_int_t ngx_http_dav_handler(ng
 static void ngx_http_dav_put_handler(ngx_http_request_t *r);
 
 static ngx_int_t ngx_http_dav_delete_handler(ngx_http_request_t *r);
-static ngx_int_t ngx_http_dav_no_init(void *ctx, void *prev);
-static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path);
+static ngx_int_t ngx_http_dav_delete_path(ngx_http_request_t *r,
+    ngx_str_t *path, ngx_uint_t dir);
 static ngx_int_t ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
 static ngx_int_t ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path);
+static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path);
 
 static ngx_int_t ngx_http_dav_mkcol_handler(ngx_http_request_t *r,
     ngx_http_dav_loc_conf_t *dlcf);
@@ -49,10 +51,11 @@ static ngx_int_t ngx_http_dav_copy_move_
 static ngx_int_t ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
 static ngx_int_t ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx,
     ngx_str_t *path);
-static ngx_int_t ngx_http_dav_copy_file(ngx_tree_ctx_t *ctx, ngx_str_t *path);
+static ngx_int_t ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx,
+    ngx_str_t *path);
+static ngx_int_t ngx_http_dav_copy_file(ngx_tree_ctx_t *ctx, u_char *from,
+    u_char *to);
 
-static ngx_int_t ngx_http_dav_delete_path(ngx_http_request_t *r,
-    ngx_str_t *path, ngx_uint_t dir);
 static ngx_int_t ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt);
 static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,
     ngx_int_t not_found, char *failed, u_char *path);
@@ -90,6 +93,13 @@ static ngx_command_t  ngx_http_dav_comma
       offsetof(ngx_http_dav_loc_conf_t, create_full_put_path),
       NULL },
 
+    { ngx_string("min_delete_depth"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_num_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_dav_loc_conf_t, min_delete_depth),
+      NULL },
+
     { ngx_string("dav_access"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
       ngx_conf_set_access_slot,
@@ -154,7 +164,9 @@ ngx_http_dav_handler(ngx_http_request_t 
     case NGX_HTTP_PUT:
 
         if (r->uri.data[r->uri.len - 1] == '/') {
-            return NGX_HTTP_BAD_REQUEST;
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "can not PUT to a collection");
+            return NGX_HTTP_CONFLICT;
         }
 
         r->request_body_in_file_only = 1;
@@ -195,14 +207,12 @@ ngx_http_dav_handler(ngx_http_request_t 
 static void
 ngx_http_dav_put_handler(ngx_http_request_t *r)
 {
-    char                     *failed;
-    u_char                   *name;
     size_t                    root;
     time_t                    date;
-    ngx_err_t                 err;
     ngx_str_t                *temp, path;
-    ngx_uint_t                status, not_found;
+    ngx_uint_t                status;
     ngx_file_info_t           fi;
+    ngx_ext_rename_file_t     ext;
     ngx_http_dav_loc_conf_t  *dlcf;
 
     ngx_http_map_uri_to_path(r, &path, &root, 0);
@@ -235,94 +245,27 @@ ngx_http_dav_put_handler(ngx_http_reques
 
     dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
 
-#if !(NGX_WIN32)
-
-    if (ngx_change_file_access(temp->data, dlcf->access) == NGX_FILE_ERROR) {
-        err = ngx_errno;
-        not_found = NGX_HTTP_INTERNAL_SERVER_ERROR;
-        failed = ngx_change_file_access_n;
-        name = temp->data;
-
-        goto failed;
-    }
-
-#endif
+    ext.access = dlcf->access;
+    ext.time = -1;
+    ext.create_path = dlcf->create_full_put_path;
+    ext.delete_file = 1;
+    ext.log = r->connection->log;
 
     if (r->headers_in.date) {
         date = ngx_http_parse_time(r->headers_in.date->value.data,
                                    r->headers_in.date->value.len);
 
         if (date != NGX_ERROR) {
-            if (ngx_set_file_time(temp->data,
-                                  r->request_body->temp_file->file.fd, date)
-                != NGX_OK)
-            {
-                err = ngx_errno;
-                not_found = NGX_HTTP_INTERNAL_SERVER_ERROR;
-                failed = ngx_set_file_time_n;
-                name = temp->data;
-
-                goto failed;
-            }
+            ext.time = date;
+            ext.fd = r->request_body->temp_file->file.fd;
         }
     }
 
-    not_found = NGX_HTTP_CONFLICT;
-    failed = ngx_rename_file_n;
-    name = path.data;
-
-    if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) {
-        goto ok;
-    }
-
-    err = ngx_errno;
-
-    if (err == NGX_ENOENT) {
-
-        if (dlcf->create_full_put_path) {
-            err = ngx_create_full_path(path.data, ngx_dir_access(dlcf->access));
-
-            if (err == 0) {
-                if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) {
-                    goto ok;
-                }
-
-                err = ngx_errno;
-            }
-        }
+    if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) {
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
     }
 
-#if (NGX_WIN32)
-
-    if (err == NGX_EEXIST) {
-        if (ngx_win32_rename_file(temp, &path, r->pool) != NGX_ERROR) {
-
-            if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) {
-                goto ok;
-            }
-        }
-
-        err = ngx_errno;
-    }
-
-#endif
-
-failed:
-
-    if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
-                      ngx_delete_file_n " \"%s\" failed",
-                      temp->data);
-    }
-
-    ngx_http_finalize_request(r,
-                              ngx_http_dav_error(r->connection->log, err,
-                                                 not_found, failed, name));
-
-    return;
-
-ok:
-
     if (status == NGX_HTTP_CREATED) {
         if (ngx_http_dav_location(r, path.data) != NGX_OK) {
             ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
@@ -343,43 +286,67 @@ ok:
 static ngx_int_t
 ngx_http_dav_delete_handler(ngx_http_request_t *r)
 {
-    size_t           root;
-    ngx_int_t        rc, depth;
-    ngx_uint_t       dir;
-    ngx_str_t        path;
-    ngx_file_info_t  fi;
+    size_t                    root;
+    ngx_err_t                 err;
+    ngx_int_t                 rc, depth;
+    ngx_uint_t                i, d, dir;
+    ngx_str_t                 path;
+    ngx_file_info_t           fi;
+    ngx_http_dav_loc_conf_t  *dlcf;
 
     if (r->headers_in.content_length_n > 0) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "DELETE with body is unsupported");
         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
     }
 
-    rc = ngx_http_discard_request_body(r);
+    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
+
+    if (dlcf->min_delete_depth) {
+        d = 0;
 
-    if (rc != NGX_OK) {
-        return rc;
+        for (i = 0; i < r->uri.len; /* void */) {
+            if (r->uri.data[i++] == '/') {
+                if (++d >= dlcf->min_delete_depth && i < r->uri.len) {
+                    goto ok;
+                }
+            }
+        }
+
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "insufficient URI depth:%i to DELETE", d);
+        return NGX_HTTP_CONFLICT;
     }
 
+ok:
+
     ngx_http_map_uri_to_path(r, &path, &root, 0);
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http delete filename: \"%s\"", path.data);
 
     if (ngx_file_info(path.data, &fi) == -1) {
-        return ngx_http_dav_error(r->connection->log, ngx_errno,
-                                  NGX_HTTP_NOT_FOUND, ngx_file_info_n,
-                                  path.data);
+        err = ngx_errno;
+
+        rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;
+
+        return ngx_http_dav_error(r->connection->log, err,
+                                  rc, ngx_file_info_n, path.data);
     }
 
     if (ngx_is_dir(&fi)) {
 
         if (r->uri.data[r->uri.len - 1] != '/') {
-            /* TODO: 301 */
-            return NGX_HTTP_BAD_REQUEST;
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
+                          "DELETE \"%s\" failed", path.data);
+            return NGX_HTTP_CONFLICT;
         }
 
         depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
 
         if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "\"Depth\" header must be infinity");
             return NGX_HTTP_BAD_REQUEST;
         }
 
@@ -389,9 +356,16 @@ ngx_http_dav_delete_handler(ngx_http_req
 
     } else {
 
+        /*
+         * we do not need to test (r->uri.data[r->uri.len - 1] == '/')
+         * because ngx_file_info("/file/") returned NGX_ENOTDIR above
+         */
+
         depth = ngx_http_dav_depth(r, 0);
 
         if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "\"Depth\" header must be 0 or infinity");
             return NGX_HTTP_BAD_REQUEST;
         }
 
@@ -409,16 +383,45 @@ ngx_http_dav_delete_handler(ngx_http_req
 
 
 static ngx_int_t
-ngx_http_dav_no_init(void *ctx, void *prev)
+ngx_http_dav_delete_path(ngx_http_request_t *r, ngx_str_t *path, ngx_uint_t dir)
 {
-    return NGX_OK;
-}
+    char            *failed;
+    ngx_tree_ctx_t   tree;
+
+    if (dir) {
+
+        tree.init_handler = NULL;
+        tree.file_handler = ngx_http_dav_delete_file;
+        tree.pre_tree_handler = ngx_http_dav_noop;
+        tree.post_tree_handler = ngx_http_dav_delete_dir;
+        tree.spec_handler = ngx_http_dav_delete_file;
+        tree.data = NULL;
+        tree.alloc = 0;
+        tree.log = r->connection->log;
+
+        /* TODO: 207 */
 
+        if (ngx_walk_tree(&tree, path) != NGX_OK) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
 
-static ngx_int_t
-ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
-{
-    return NGX_OK;
+        if (ngx_delete_dir(path->data) != NGX_FILE_ERROR) {
+            return NGX_OK;
+        }
+
+        failed = ngx_delete_dir_n;
+
+    } else {
+
+        if (ngx_delete_file(path->data) != NGX_FILE_ERROR) {
+            return NGX_OK;
+        }
+
+        failed = ngx_delete_file_n;
+    }
+
+    return ngx_http_dav_error(r->connection->log, ngx_errno,
+                              NGX_HTTP_NOT_FOUND, failed, path->data);
 }
 
 
@@ -459,23 +462,34 @@ ngx_http_dav_delete_file(ngx_tree_ctx_t 
 
 
 static ngx_int_t
+ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_dav_mkcol_handler(ngx_http_request_t *r, ngx_http_dav_loc_conf_t *dlcf)
 {
+    u_char    *p;
     size_t     root;
-    ngx_int_t  rc;
     ngx_str_t  path;
 
     if (r->headers_in.content_length_n > 0) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "MKCOL with body is unsupported");
         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
     }
 
-    rc = ngx_http_discard_request_body(r);
-
-    if (rc != NGX_OK) {
-        return rc;
+    if (r->uri.data[r->uri.len - 1] != '/') {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "MKCOL can create a collection only");
+        return NGX_HTTP_CONFLICT;
     }
 
-    ngx_http_map_uri_to_path(r, &path, &root, 0);
+    p = ngx_http_map_uri_to_path(r, &path, &root, 0);
+
+    *(p - 1) = '\0';
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http mkcol path: \"%s\"", path.data);
@@ -491,8 +505,7 @@ ngx_http_dav_mkcol_handler(ngx_http_requ
     }
 
     return ngx_http_dav_error(r->connection->log, ngx_errno,
-                              NGX_HTTP_BAD_REQUEST, ngx_create_dir_n,
-                              path.data);
+                              NGX_HTTP_CONFLICT, ngx_create_dir_n, path.data);
 }
 
 
@@ -500,15 +513,16 @@ static ngx_int_t
 ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
 {
     u_char                   *p, *host, *last, ch;
-    size_t                    root;
+    size_t                    len, root;
     ngx_err_t                 err;
     ngx_int_t                 rc, depth;
-    ngx_uint_t                overwrite, slash;
+    ngx_uint_t                overwrite, slash, dir;
     ngx_str_t                 path, uri;
     ngx_tree_ctx_t            tree;
     ngx_file_info_t           fi;
     ngx_table_elt_t          *dest, *over;
     ngx_http_dav_copy_ctx_t   copy;
+    ngx_http_dav_loc_conf_t  *dlcf;
 
     if (r->headers_in.content_length_n > 0) {
         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
@@ -522,8 +536,12 @@ ngx_http_dav_copy_move_handler(ngx_http_
         return NGX_HTTP_BAD_REQUEST;
     }
 
-    if (dest->value.len < sizeof("http://") - 1 + r->server_name.len + 1) {
-        goto invalid_destination;
+    len = r->headers_in.server.len;
+
+    if (len == 0) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "client sent no \"Host\" header");
+        return NGX_HTTP_BAD_REQUEST;
     }
 
 #if (NGX_HTTP_SSL)
@@ -549,18 +567,17 @@ ngx_http_dav_copy_move_handler(ngx_http_
         host = dest->value.data + sizeof("http://") - 1;
     }
 
-    if (ngx_strncmp(host, r->server_name.data, r->server_name.len) != 0) {
+    if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "Destination URI \"%V\" is handled by "
+                      "\"Destination\" URI \"%V\" is handled by "
                       "different repository than the source URI",
                       &dest->value);
-
         return NGX_HTTP_BAD_REQUEST;
     }
 
     last = dest->value.data + dest->value.len;
 
-    for (p = host + r->server_name.len; p < last; p++) {
+    for (p = host + len; p < last; p++) {
         if (*p == '/') {
             goto destination_done;
         }
@@ -571,15 +588,36 @@ invalid_destination:
     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   "client sent invalid \"Destination\" header: \"%V\"",
                   &dest->value);
-
     return NGX_HTTP_BAD_REQUEST;
 
 destination_done:
 
-    depth = ngx_http_dav_depth(r, 0);
+    if ((r->uri.data[r->uri.len - 1] == '/' && *(last - 1) != '/')
+        || (r->uri.data[r->uri.len - 1] != '/' && *(last - 1) == '/'))
+    {
+         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                       "both URI \"%V\" and \"Destination\" URI \"%V\" "
+                       "should be either collections or non-collections",
+                       &r->uri, &dest->value);
+         return NGX_HTTP_CONFLICT;
+    }
+
+    depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
 
-    if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
-        return NGX_HTTP_BAD_REQUEST;
+    if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
+
+        if (r->method == NGX_HTTP_COPY) {
+            if (depth != 0) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "\"Depth\" header must be 0 or infinity");
+                return NGX_HTTP_BAD_REQUEST;
+            }
+
+        } else {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "\"Depth\" header must be infinity");
+            return NGX_HTTP_BAD_REQUEST;
+        }
     }
 
     over = r->headers_in.overwrite;
@@ -603,7 +641,6 @@ destination_done:
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "client sent invalid \"Overwrite\" header: \"%V\"",
                       &over->value);
-
         return NGX_HTTP_BAD_REQUEST;
     }
 
@@ -611,12 +648,6 @@ destination_done:
 
 overwrite_done:
 
-    rc = ngx_http_discard_request_body(r);
-
-    if (rc != NGX_OK) {
-        return rc;
-    }
-
     ngx_http_map_uri_to_path(r, &path, &root, 0);
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -656,26 +687,27 @@ overwrite_done:
 
         /* destination does not exist */
 
+        overwrite = 0;
+        dir = 0;
+
     } else {
 
         /* destination exists */
 
         if (ngx_is_dir(&fi) && !slash) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "\"%V\" could not be %Ved to collection \"%V\"",
+                          &r->uri, &r->method_name, &dest->value);
             return NGX_HTTP_CONFLICT;
         }
 
         if (!overwrite) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EEXIST,
+                          "\"%s\" could not be created", copy.path.data);
             return NGX_HTTP_PRECONDITION_FAILED;
         }
 
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "http delete: \"%s\"", copy.path.data);
-
-        rc = ngx_http_dav_delete_path(r, &copy.path, ngx_is_dir(&fi));
-
-        if (rc != NGX_OK) {
-            return rc;
-        }
+        dir = ngx_is_dir(&fi);
     }
 
     if (ngx_file_info(path.data, &fi) == -1) {
@@ -684,14 +716,28 @@ overwrite_done:
                                   path.data);
     }
 
-
     if (ngx_is_dir(&fi)) {
 
         if (r->uri.data[r->uri.len - 1] != '/') {
-            /* TODO: 301 */
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "\"%V\" is collection", &r->uri);
             return NGX_HTTP_BAD_REQUEST;
         }
 
+        if (overwrite) {
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http delete: \"%s\"", copy.path.data);
+
+            rc = ngx_http_dav_delete_path(r, &copy.path, dir);
+
+            if (rc != NGX_OK) {
+                return rc;
+            }
+        }
+    }
+
+    if (ngx_is_dir(&fi)) {
+
         path.len -= 2;  /* omit "/\0" */
 
         if (r->method == NGX_HTTP_MOVE) {
@@ -710,8 +756,8 @@ overwrite_done:
 
         copy.len = path.len;
 
-        tree.init_handler = ngx_http_dav_no_init;
-        tree.file_handler = ngx_http_dav_copy_file;
+        tree.init_handler = NULL;
+        tree.file_handler = ngx_http_dav_copy_tree_file;
         tree.pre_tree_handler = ngx_http_dav_copy_dir;
         tree.post_tree_handler = ngx_http_dav_copy_dir_time;
         tree.spec_handler = ngx_http_dav_noop;
@@ -734,21 +780,21 @@ overwrite_done:
 
     } else {
 
-        if (dest->value.data[dest->value.len - 1] == '/') {
-            return NGX_HTTP_BAD_REQUEST;
-        }
-
         if (r->method == NGX_HTTP_MOVE) {
             if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {
                 return NGX_HTTP_NO_CONTENT;
             }
         }
 
-        tree.data = &copy;
+        dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
+
+        tree.size = ngx_file_size(&fi);
+        tree.mtime = ngx_file_mtime(&fi);
+        tree.access = dlcf->access;
         tree.log = r->connection->log;
 
-        if (ngx_http_dav_copy_file(&tree, &path) != NGX_FILE_ERROR) {
-
+        if (ngx_http_dav_copy_file(&tree, path.data, copy.path.data) == NGX_OK)
+        {
             if (r->method == NGX_HTTP_MOVE) {
                 rc = ngx_http_dav_delete_path(r, &path, 0);
 
@@ -807,9 +853,6 @@ ngx_http_dav_copy_dir_time(ngx_tree_ctx_
     u_char                   *p, *dir;
     size_t                    len;
     ngx_http_dav_copy_ctx_t  *copy;
-#if (WIN32)
-    ngx_fd_t                  fd;
-#endif
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
                    "http copy dir time: \"%s\"", path->data);
@@ -829,7 +872,9 @@ ngx_http_dav_copy_dir_time(ngx_tree_ctx_
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
                    "http copy dir time to: \"%s\"", dir);
 
-#if (WIN32)
+#if (NGX_WIN32)
+    {
+    ngx_fd_t  fd;
 
     fd = ngx_open_file(dir, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);
 
@@ -847,6 +892,7 @@ ngx_http_dav_copy_dir_time(ngx_tree_ctx_
         ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
                       ngx_close_file_n " \"%s\" failed", dir);
     }
+    }
 
 failed:
 
@@ -866,15 +912,11 @@ failed:
 
 
 static ngx_int_t
-ngx_http_dav_copy_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
 {
     u_char                   *p, *file;
     size_t                    len;
-    off_t                     size;
-    ssize_t                   n;
-    ngx_fd_t                  fd, copy_fd;
     ngx_http_dav_copy_ctx_t  *copy;
-    u_char                    buf[NGX_HTTP_DAV_COPY_BLOCK];
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
                    "http copy file: \"%s\"", path->data);
@@ -894,21 +936,39 @@ ngx_http_dav_copy_file(ngx_tree_ctx_t *c
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
                    "http copy file to: \"%s\"", file);
 
-    fd = ngx_open_file(path->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+    (void) ngx_http_dav_copy_file(ctx, path->data, file);
+
+    ngx_free(file);
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_copy_file(ngx_tree_ctx_t *ctx, u_char *from, u_char *to)
+{
+    off_t       size;
+    ssize_t     n;
+    ngx_fd_t    fd, cfd;
+    ngx_int_t   rc;
+    u_char      buf[NGX_HTTP_DAV_COPY_BLOCK];
+
+    fd = ngx_open_file(from, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
 
     if (fd == NGX_INVALID_FILE) {
         (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n,
-                                  path->data);
-        goto failed;
+                                  from);
+        return NGX_ERROR;
     }
 
-    copy_fd = ngx_open_file(file, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN,
-                            ctx->access);
+    cfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN,
+                        ctx->access);
 
-    if (copy_fd == NGX_INVALID_FILE) {
-        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n,
-                                  file);
-        goto copy_failed;
+    rc = NGX_ERROR;
+
+    if (cfd == NGX_INVALID_FILE) {
+        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, to);
+        goto failed;
     }
 
     for (size = ctx->size; size > 0; size -= n) {
@@ -917,81 +977,39 @@ ngx_http_dav_copy_file(ngx_tree_ctx_t *c
 
         if (n == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
-                          ngx_read_fd_n " \"%s\" failed", path->data);
-            break;
+                          ngx_read_fd_n " \"%s\" failed", from);
+            goto failed;
         }
 
-        if (ngx_write_fd(copy_fd, buf, n) == NGX_FILE_ERROR) {
+        if (ngx_write_fd(cfd, buf, n) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
-                          ngx_write_fd_n " \"%s\" failed", file);
+                          ngx_write_fd_n " \"%s\" failed", to);
+            goto failed;
         }
     }
 
-    if (ngx_set_file_time(file, copy_fd, ctx->mtime) != NGX_OK) {
+    if (ngx_set_file_time(to, cfd, ctx->mtime) != NGX_OK) {
         ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
-                      ngx_set_file_time_n " \"%s\" failed", file);
+                      ngx_set_file_time_n " \"%s\" failed", to);
+        goto failed;
     }
 
-    if (ngx_close_file(copy_fd) == NGX_FILE_ERROR) {
+    if (ngx_close_file(cfd) == NGX_FILE_ERROR) {
         ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
-                      ngx_close_file_n " \"%s\" failed", file);
+                      ngx_close_file_n " \"%s\" failed", to);
+        goto failed;
     }
 
-copy_failed:
-
-    if (ngx_close_file(fd) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
-                      ngx_close_file_n " \"%s\" failed", path->data);
-    }
+    rc = NGX_OK;
 
 failed:
 
-    ngx_free(file);
-
-    return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_http_dav_delete_path(ngx_http_request_t *r, ngx_str_t *path, ngx_uint_t dir)
-{
-    char            *failed;
-    ngx_tree_ctx_t   tree;
-
-    if (dir) {
-
-        tree.init_handler = ngx_http_dav_no_init;
-        tree.file_handler = ngx_http_dav_delete_file;
-        tree.pre_tree_handler = ngx_http_dav_noop;
-        tree.post_tree_handler = ngx_http_dav_delete_dir;
-        tree.spec_handler = ngx_http_dav_delete_file;
-        tree.data = NULL;
-        tree.alloc = 0;
-        tree.log = r->connection->log;
-
-        /* todo: 207 */
-
-        if (ngx_walk_tree(&tree, path) != NGX_OK) {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
-        }
-
-        if (ngx_delete_dir(path->data) != NGX_FILE_ERROR) {
-            return NGX_OK;
-        }
-
-        failed = ngx_delete_dir_n;
-
-    } else {
-
-        if (ngx_delete_file(path->data) != NGX_FILE_ERROR) {
-            return NGX_OK;
-        }
-
-        failed = ngx_delete_file_n;
+    if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
+                      ngx_close_file_n " \"%s\" failed", from);
     }
 
-    return ngx_http_dav_error(r->connection->log, ngx_errno,
-                              NGX_HTTP_NOT_FOUND, failed, path->data);
+    return rc;
 }
 
 
@@ -1084,7 +1102,7 @@ ngx_http_dav_location(ngx_http_request_t
         location = path + clcf->root.len;
 
     } else {
-        location = ngx_palloc(r->pool, r->uri.len);
+        location = ngx_pnalloc(r->pool, r->uri.len);
         if (location == NULL) {
             return NGX_ERROR;
         }
@@ -1120,8 +1138,9 @@ ngx_http_dav_create_loc_conf(ngx_conf_t 
      *     conf->methods = 0;
      */
 
+    conf->min_delete_depth = NGX_CONF_UNSET_UINT;
+    conf->access = NGX_CONF_UNSET_UINT;
     conf->create_full_put_path = NGX_CONF_UNSET;
-    conf->access = NGX_CONF_UNSET_UINT;
 
     return conf;
 }
@@ -1136,11 +1155,14 @@ ngx_http_dav_merge_loc_conf(ngx_conf_t *
     ngx_conf_merge_bitmask_value(conf->methods, prev->methods,
                          (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF));
 
-    ngx_conf_merge_value(conf->create_full_put_path, prev->create_full_put_path,
-                         0);
+    ngx_conf_merge_uint_value(conf->min_delete_depth,
+                         prev->min_delete_depth, 0);
 
     ngx_conf_merge_uint_value(conf->access, prev->access, 0600);
 
+    ngx_conf_merge_value(conf->create_full_put_path,
+                         prev->create_full_put_path, 0);
+
     return NGX_CONF_OK;
 }
 
--- a/src/http/modules/ngx_http_empty_gif_module.c
+++ b/src/http/modules/ngx_http_empty_gif_module.c
@@ -127,6 +127,8 @@ ngx_http_empty_gif_handler(ngx_http_requ
 
     if (r->method == NGX_HTTP_HEAD) {
         r->headers_out.status = NGX_HTTP_OK;
+        r->headers_out.content_length_n = sizeof(ngx_empty_gif);
+        r->headers_out.last_modified_time = 23349600;
 
         return ngx_http_send_header(r);
     }
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -135,44 +135,9 @@ static char *ngx_http_fastcgi_upstream_f
     ngx_command_t *cmd, void *conf);
 
 
-static ngx_http_fastcgi_request_start_t  ngx_http_fastcgi_request_start = {
-    { 1,                                               /* version */
-      NGX_HTTP_FASTCGI_BEGIN_REQUEST,                  /* type */
-      0,                                               /* request_id_hi */
-      1,                                               /* request_id_lo */
-      0,                                               /* content_length_hi */
-      sizeof(ngx_http_fastcgi_begin_request_t),        /* content_length_lo */
-      0,                                               /* padding_length */
-      0 },                                             /* reserved */
-
-    { 0,                                               /* role_hi */
-      NGX_HTTP_FASTCGI_RESPONDER,                      /* role_lo */
-      0, /* NGX_HTTP_FASTCGI_KEEP_CONN */              /* flags */
-      { 0, 0, 0, 0, 0 } },                             /* reserved[5] */
-
-    { 1,                                               /* version */
-      NGX_HTTP_FASTCGI_PARAMS,                         /* type */
-      0,                                               /* request_id_hi */
-      1 },                                             /* request_id_lo */
-
-};
-
-
-static ngx_str_t  ngx_http_fastcgi_script_name =
-    ngx_string("fastcgi_script_name");
-
-
 static ngx_conf_post_t  ngx_http_fastcgi_lowat_post =
     { ngx_http_fastcgi_lowat_check };
 
-static ngx_conf_deprecated_t  ngx_conf_deprecated_fastcgi_header_buffer_size = {
-    ngx_conf_deprecated, "fastcgi_header_buffer_size", "fastcgi_buffer_size"
-};
-
-static ngx_conf_deprecated_t  ngx_conf_deprecated_fastcgi_redirect_errors = {
-    ngx_conf_deprecated, "fastcgi_redirect_errors", "fastcgi_intercept_errors"
-};
-
 
 static ngx_conf_bitmask_t  ngx_http_fastcgi_next_upstream_masks[] = {
     { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
@@ -251,13 +216,6 @@ static ngx_command_t  ngx_http_fastcgi_c
       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size),
       NULL },
 
-    { ngx_string("fastcgi_header_buffer_size"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_size_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size),
-      &ngx_conf_deprecated_fastcgi_header_buffer_size },
-
     { ngx_string("fastcgi_pass_request_headers"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -279,13 +237,6 @@ static ngx_command_t  ngx_http_fastcgi_c
       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors),
       NULL },
 
-    { ngx_string("fastcgi_redirect_errors"),
-      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.intercept_errors),
-      &ngx_conf_deprecated_fastcgi_redirect_errors },
-
     { ngx_string("fastcgi_read_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_msec_slot,
@@ -412,12 +363,40 @@ ngx_module_t  ngx_http_fastcgi_module = 
 };
 
 
+static ngx_http_fastcgi_request_start_t  ngx_http_fastcgi_request_start = {
+    { 1,                                               /* version */
+      NGX_HTTP_FASTCGI_BEGIN_REQUEST,                  /* type */
+      0,                                               /* request_id_hi */
+      1,                                               /* request_id_lo */
+      0,                                               /* content_length_hi */
+      sizeof(ngx_http_fastcgi_begin_request_t),        /* content_length_lo */
+      0,                                               /* padding_length */
+      0 },                                             /* reserved */
+
+    { 0,                                               /* role_hi */
+      NGX_HTTP_FASTCGI_RESPONDER,                      /* role_lo */
+      0, /* NGX_HTTP_FASTCGI_KEEP_CONN */              /* flags */
+      { 0, 0, 0, 0, 0 } },                             /* reserved[5] */
+
+    { 1,                                               /* version */
+      NGX_HTTP_FASTCGI_PARAMS,                         /* type */
+      0,                                               /* request_id_hi */
+      1 },                                             /* request_id_lo */
+
+};
+
+
+static ngx_str_t  ngx_http_fastcgi_script_name =
+    ngx_string("fastcgi_script_name");
+
+
 static ngx_str_t  ngx_http_fastcgi_hide_headers[] = {
     ngx_string("Status"),
     ngx_string("X-Accel-Expires"),
     ngx_string("X-Accel-Redirect"),
     ngx_string("X-Accel-Limit-Rate"),
-    ngx_string("X-Accel-Buffer"),
+    ngx_string("X-Accel-Buffering"),
+    ngx_string("X-Accel-Charset"),
     ngx_null_string
 };
 
@@ -432,7 +411,7 @@ ngx_http_fastcgi_handler(ngx_http_reques
     if (r->subrequest_in_memory) {
         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                       "ngx_http_fastcgi_module does not support "
-                      "subrequest in memeory");
+                      "subrequest in memory");
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
@@ -443,6 +422,8 @@ ngx_http_fastcgi_handler(ngx_http_reques
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
+    u->schema = flcf->upstream.schema;
+
     u->peer.log = r->connection->log;
     u->peer.log_error = NGX_ERROR_ERR;
 #if (NGX_THREADS)
@@ -505,7 +486,7 @@ ngx_http_fastcgi_create_request(ngx_http
     if (flcf->params_len) {
         ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
 
-        ngx_http_script_flush_no_cachable_variables(r, flcf->flushes);
+        ngx_http_script_flush_no_cacheable_variables(r, flcf->flushes);
         le.flushed = 1;
 
         le.ip = flcf->params_len->elts;
@@ -551,7 +532,7 @@ ngx_http_fastcgi_create_request(ngx_http
 
     if (len > 65535) {
         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                      "fastcgi: the request record is too big");
+                      "fastcgi request record is too big: %uz", len);
         return NGX_ERROR;
     }
 
@@ -636,6 +617,11 @@ ngx_http_fastcgi_create_request(ngx_http
                 code((ngx_http_script_engine_t *) &e);
             }
             e.ip += sizeof(uintptr_t);
+
+            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "fastcgi param: \"%*s: %*s\"",
+                           key_len, e.pos - (key_len + val_len),
+                           val_len, e.pos - val_len);
         }
 
         b->last = e.pos;
@@ -885,7 +871,7 @@ ngx_http_fastcgi_process_header(ngx_http
     if (f == NULL) {
         f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
         if (f == NULL) {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            return NGX_ERROR;
         }
 
         ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
@@ -993,7 +979,7 @@ ngx_http_fastcgi_process_header(ngx_http
 
                     for (i = 0; i < flcf->catch_stderr->nelts; i++) {
                         if (ngx_strstr(line.data, pattern[i].data)) {
-                            return NGX_HTTP_BAD_GATEWAY;
+                            return NGX_HTTP_UPSTREAM_INVALID_HEADER;
                         }
                     }
                 }
@@ -1061,7 +1047,7 @@ ngx_http_fastcgi_process_header(ngx_http
 
                 h = ngx_list_push(&u->headers_in.headers);
                 if (h == NULL) {
-                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                    return NGX_ERROR;
                 }
 
                 if (f->split_parts && f->split_parts->nelts) {
@@ -1073,9 +1059,9 @@ ngx_http_fastcgi_process_header(ngx_http
                         size += part[i].end - part[i].start;
                     }
 
-                    p = ngx_palloc(r->pool, size);
+                    p = ngx_pnalloc(r->pool, size);
                     if (p == NULL) {
-                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                        return NGX_ERROR;
                     }
 
                     buf.pos = p;
@@ -1101,9 +1087,9 @@ ngx_http_fastcgi_process_header(ngx_http
                     h->value.data = r->header_start;
                     h->value.data[h->value.len] = '\0';
 
-                    h->lowcase_key = ngx_palloc(r->pool, h->key.len);
+                    h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
                     if (h->lowcase_key == NULL) {
-                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                        return NGX_ERROR;
                     }
 
                 } else {
@@ -1111,11 +1097,11 @@ ngx_http_fastcgi_process_header(ngx_http
                     h->key.len = r->header_name_end - r->header_name_start;
                     h->value.len = r->header_end - r->header_start;
 
-                    h->key.data = ngx_palloc(r->pool,
-                                             h->key.len + 1 + h->value.len + 1
-                                             + h->key.len);
+                    h->key.data = ngx_pnalloc(r->pool,
+                                              h->key.len + 1 + h->value.len + 1
+                                              + h->key.len);
                     if (h->key.data == NULL) {
-                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                        return NGX_ERROR;
                     }
 
                     h->value.data = h->key.data + h->key.len + 1;
@@ -1134,16 +1120,14 @@ ngx_http_fastcgi_process_header(ngx_http
                     ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
 
                 } else {
-                    for (i = 0; i < h->key.len; i++) {
-                        h->lowcase_key[i] = ngx_tolower(h->key.data[i]);
-                    }
+                    ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
                 }
 
                 hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
                                    h->lowcase_key, h->key.len);
 
                 if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
-                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                    return NGX_ERROR;
                 }
 
                 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -1172,12 +1156,22 @@ ngx_http_fastcgi_process_header(ngx_http
                     status = ngx_atoi(status_line->data, 3);
 
                     if (status == NGX_ERROR) {
-                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                      "upstream sent invalid status \"%V\"",
+                                      status_line);
+                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;
                     }
 
                     u->headers_in.status_n = status;
                     u->headers_in.status_line = *status_line;
 
+                } else if (u->headers_in.location) {
+                    u->headers_in.status_n = 302;
+                    u->headers_in.status_line.len =
+                                           sizeof("302 Moved Temporarily") - 1;
+                    u->headers_in.status_line.data =
+                                           (u_char *) "302 Moved Temporarily"; 
+
                 } else {
                     u->headers_in.status_n = 200;
                     u->headers_in.status_line.len = sizeof("200 OK") - 1;
@@ -1186,8 +1180,8 @@ ngx_http_fastcgi_process_header(ngx_http
 
                 u->state->status = u->headers_in.status_n;
 #if 0
-                if (u->cachable) {
-                    u->cachable = ngx_http_upstream_is_cachable(r);
+                if (u->cacheable) {
+                    u->cacheable = ngx_http_upstream_is_cacheable(r);
                 }
 #endif
 
@@ -1221,7 +1215,7 @@ ngx_http_fastcgi_process_header(ngx_http
         }
 
         if (rc == NGX_OK) {
-            return NGX_AGAIN;
+            continue;
         }
 
         /* rc == NGX_AGAIN */
@@ -1233,7 +1227,7 @@ ngx_http_fastcgi_process_header(ngx_http
             f->split_parts = ngx_array_create(r->pool, 1,
                                         sizeof(ngx_http_fastcgi_split_part_t));
             if (f->split_parts == NULL) {
-                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                return NGX_ERROR;
             }
         }
 
@@ -1610,7 +1604,7 @@ ngx_http_fastcgi_add_variables(ngx_conf_
     ngx_http_variable_t  *var;
 
     var = ngx_http_add_variable(cf, &ngx_http_fastcgi_script_name,
-                                NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHABLE);
+                                NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE);
     if (var == NULL) {
         return NGX_ERROR;
     }
@@ -1638,8 +1632,6 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
      *     conf->upstream.next_upstream = 0;
      *     conf->upstream.temp_path = NULL;
      *     conf->upstream.hide_headers_hash = { NULL, 0 };
-     *     conf->upstream.hide_headers = NULL;
-     *     conf->upstream.pass_headers = NULL;
      *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
@@ -1669,6 +1661,9 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
     conf->upstream.pass_request_headers = NGX_CONF_UNSET;
     conf->upstream.pass_request_body = NGX_CONF_UNSET;
 
+    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
+    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
+
     conf->upstream.intercept_errors = NGX_CONF_UNSET;
 
     /* "fastcgi_cyclic_temp_file" is disabled */
@@ -1689,11 +1684,8 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
     u_char                       *p;
     size_t                        size;
     uintptr_t                    *code;
-    ngx_str_t                    *header;
-    ngx_uint_t                    i, j;
-    ngx_array_t                   hide_headers;
+    ngx_uint_t                    i;
     ngx_keyval_t                 *src;
-    ngx_hash_key_t               *hk;
     ngx_hash_init_t               hash;
     ngx_http_script_compile_t     sc;
     ngx_http_script_copy_code_t  *copy;
@@ -1855,108 +1847,19 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
 
     ngx_conf_merge_str_value(conf->index, prev->index, "");
 
-    if (conf->upstream.hide_headers == NULL
-        && conf->upstream.pass_headers == NULL)
-    {
-        conf->upstream.hide_headers = prev->upstream.hide_headers;
-        conf->upstream.pass_headers = prev->upstream.pass_headers;
-        conf->upstream.hide_headers_hash = prev->upstream.hide_headers_hash;
-
-        if (conf->upstream.hide_headers_hash.buckets) {
-            goto peers;
-        }
-
-    } else {
-        if (conf->upstream.hide_headers == NULL) {
-            conf->upstream.hide_headers = prev->upstream.hide_headers;
-        }
-
-        if (conf->upstream.pass_headers == NULL) {
-            conf->upstream.pass_headers = prev->upstream.pass_headers;
-        }
-    }
-
-    if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
+    hash.max_size = 512;
+    hash.bucket_size = ngx_align(64, ngx_cacheline_size);
+    hash.name = "fastcgi_hide_headers_hash";
+
+    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
+                                            &prev->upstream,
+                                            ngx_http_fastcgi_hide_headers,
+                                            &hash)
         != NGX_OK)
     {
         return NGX_CONF_ERROR;
     }
 
-    for (header = ngx_http_fastcgi_hide_headers; header->len; header++) {
-        hk = ngx_array_push(&hide_headers);
-        if (hk == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        hk->key = *header;
-        hk->key_hash = ngx_hash_key_lc(header->data, header->len);
-        hk->value = (void *) 1;
-    }
-
-    if (conf->upstream.hide_headers) {
-
-        header = conf->upstream.hide_headers->elts;
-
-        for (i = 0; i < conf->upstream.hide_headers->nelts; i++) {
-
-            hk = hide_headers.elts;
-
-            for (j = 0; j < hide_headers.nelts; j++) {
-                if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) {
-                    goto exist;
-                }
-            }
-
-            hk = ngx_array_push(&hide_headers);
-            if (hk == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            hk->key = header[i];
-            hk->key_hash = ngx_hash_key_lc(header[i].data, header[i].len);
-            hk->value = (void *) 1;
-
-        exist:
-
-            continue;
-        }
-    }
-
-    if (conf->upstream.pass_headers) {
-
-        hk = hide_headers.elts;
-        header = conf->upstream.pass_headers->elts;
-
-        for (i = 0; i < conf->upstream.pass_headers->nelts; i++) {
-
-            for (j = 0; j < hide_headers.nelts; j++) {
-
-                if (hk[j].key.data == NULL) {
-                    continue;
-                }
-
-                if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) {
-                    hk[j].key.data = NULL;
-                    break;
-                }
-            }
-        }
-    }
-
-    hash.hash = &conf->upstream.hide_headers_hash;
-    hash.key = ngx_hash_key_lc;
-    hash.max_size = 512;
-    hash.bucket_size = ngx_align(64, ngx_cacheline_size);
-    hash.name = "fastcgi_hide_headers_hash";
-    hash.pool = cf->pool;
-    hash.temp_pool = NULL;
-
-    if (ngx_hash_init(&hash, hide_headers.elts, hide_headers.nelts) != NGX_OK) {
-        return NGX_CONF_ERROR;
-    }
-
-peers:
-
     if (conf->upstream.upstream == NULL) {
         conf->upstream.upstream = prev->upstream.upstream;
         conf->upstream.schema = prev->upstream.schema;
@@ -2104,7 +2007,7 @@ ngx_http_fastcgi_script_name_variable(ng
 
     if (r->uri.len) {
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
 
         flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
@@ -2117,7 +2020,7 @@ ngx_http_fastcgi_script_name_variable(ng
 
         v->len = r->uri.len + flcf->index.len;
 
-        v->data = ngx_palloc(r->pool, v->len);
+        v->data = ngx_pnalloc(r->pool, v->len);
         if (v->data == NULL) {
             return NGX_ERROR;
         }
@@ -2128,7 +2031,7 @@ ngx_http_fastcgi_script_name_variable(ng
     } else {
         v->len = 0;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = NULL;
 
@@ -2171,8 +2074,6 @@ ngx_http_fastcgi_pass(ngx_conf_t *cf, ng
 
     clcf->handler = ngx_http_fastcgi_handler;
 
-    lcf->upstream.location = clcf->name;
-
     if (clcf->name.data[clcf->name.len - 1] == '/') {
         clcf->auto_redirect = 1;
     }
@@ -2215,7 +2116,7 @@ ngx_http_fastcgi_store(ngx_conf_t *cf, n
     sc.source = &value[1];
     sc.lengths = &flcf->upstream.store_lengths;
     sc.values = &flcf->upstream.store_values;
-    sc.variables = ngx_http_script_variables_count(&value[1]);;
+    sc.variables = ngx_http_script_variables_count(&value[1]);
     sc.complete_lengths = 1;
     sc.complete_values = 1;
 
--- a/src/http/modules/ngx_http_flv_module.c
+++ b/src/http/modules/ngx_http_flv_module.c
@@ -60,10 +60,9 @@ ngx_module_t  ngx_http_flv_module = {
 static ngx_int_t
 ngx_http_flv_handler(ngx_http_request_t *r)
 {
-    u_char                    *p, *last;
+    u_char                    *p, *n, *last;
     off_t                      start, len;
     size_t                     root;
-    ngx_fd_t                   fd;
     ngx_int_t                  rc;
     ngx_uint_t                 level, i;
     ngx_str_t                  path;
@@ -106,15 +105,17 @@ ngx_http_flv_handler(ngx_http_request_t 
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    of.test_dir = 0;
-    of.retest = clcf->open_file_cache_retest;
+    ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+    of.directio = clcf->directio;
+    of.valid = clcf->open_file_cache_valid;
+    of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
     of.events = clcf->open_file_cache_events;
-    
-    rc = ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool);
 
-    if (rc == NGX_ERROR) {
-
+    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+        != NGX_OK)
+    {
         switch (of.err) {
 
         case 0:
@@ -149,11 +150,9 @@ ngx_http_flv_handler(ngx_http_request_t 
         return rc;
     }
 
-    fd = of.fd;
-
     if (!of.is_file) {
 
-        if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+        if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                           ngx_close_file_n " \"%s\" failed", path.data);
         }
@@ -161,17 +160,25 @@ ngx_http_flv_handler(ngx_http_request_t 
         return NGX_DECLINED;
     }
 
+    r->root_tested = 1;
+
     start = 0;
     len = of.size;
     i = 1;
 
     if (r->args.len) {
-        p = (u_char *) ngx_strstr(r->args.data, "start=");
+        p = (u_char *) ngx_strnstr(r->args.data, "start=", r->args.len);
 
         if (p) {
             p += 6;
 
-            start = ngx_atoof(p, r->args.len - (p - r->args.data));
+            for (n = p; n < r->args.data + r->args.len; n++) {
+                if (*n == '&') {
+                    break;
+                }
+            }
+
+            start = ngx_atoof(p, n - p);
 
             if (start == NGX_ERROR || start >= len) {
                 start = 0;
@@ -206,9 +213,6 @@ ngx_http_flv_handler(ngx_http_request_t 
 
         out[0].buf = b;
         out[0].next = &out[1];
-
-    } else {
-        r->allow_ranges = 1;
     }
 
 
@@ -222,6 +226,8 @@ ngx_http_flv_handler(ngx_http_request_t 
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
+    r->allow_ranges = 1;
+
     rc = ngx_http_send_header(r);
 
     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
@@ -235,7 +241,7 @@ ngx_http_flv_handler(ngx_http_request_t 
     b->last_buf = 1;
     b->last_in_chain = 1;
 
-    b->file->fd = fd;
+    b->file->fd = of.fd;
     b->file->name = path;
     b->file->log = log;
 
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -116,7 +116,7 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
         name.data++;
     }
 
-    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGABLE);
+    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
     if (var == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -257,7 +257,7 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
         }
 
         var->valid = 1;
-        var->no_cachable = 0;
+        var->no_cacheable = 0;
         var->not_found = 0;
 
         v = ngx_array_push(&ctx->values);
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -19,9 +19,6 @@ typedef struct {
 
     ngx_bufs_t           bufs;
 
-    ngx_uint_t           http_version;
-    ngx_uint_t           proxied;
-
     ngx_int_t            level;
     size_t               wbits;
     size_t               memlevel;
@@ -29,17 +26,6 @@ typedef struct {
 } ngx_http_gzip_conf_t;
 
 
-#define NGX_HTTP_GZIP_PROXIED_OFF       0x0002
-#define NGX_HTTP_GZIP_PROXIED_EXPIRED   0x0004
-#define NGX_HTTP_GZIP_PROXIED_NO_CACHE  0x0008
-#define NGX_HTTP_GZIP_PROXIED_NO_STORE  0x0010
-#define NGX_HTTP_GZIP_PROXIED_PRIVATE   0x0020
-#define NGX_HTTP_GZIP_PROXIED_NO_LM     0x0040
-#define NGX_HTTP_GZIP_PROXIED_NO_ETAG   0x0080
-#define NGX_HTTP_GZIP_PROXIED_AUTH      0x0100
-#define NGX_HTTP_GZIP_PROXIED_ANY       0x0200
-
-
 typedef struct {
     ngx_chain_t         *in;
     ngx_chain_t         *free;
@@ -69,8 +55,6 @@ typedef struct {
 } ngx_http_gzip_ctx_t;
 
 
-static ngx_int_t ngx_http_gzip_proxied(ngx_http_request_t *r,
-    ngx_http_gzip_conf_t *conf);
 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
     u_int size);
 static void ngx_http_gzip_filter_free(void *opaque, void *address);
@@ -98,27 +82,6 @@ static ngx_conf_post_handler_pt  ngx_htt
 static ngx_conf_post_handler_pt  ngx_http_gzip_hash_p = ngx_http_gzip_hash;
 
 
-static ngx_conf_enum_t  ngx_http_gzip_http_version[] = {
-    { ngx_string("1.0"), NGX_HTTP_VERSION_10 },
-    { ngx_string("1.1"), NGX_HTTP_VERSION_11 },
-    { ngx_null_string, 0 }
-};
-
-
-static ngx_conf_bitmask_t  ngx_http_gzip_proxied_mask[] = {
-    { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF },
-    { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED },
-    { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE },
-    { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE },
-    { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE },
-    { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM },
-    { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG },
-    { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH },
-    { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY },
-    { ngx_null_string, 0 }
-};
-
-
 static ngx_command_t  ngx_http_gzip_filter_commands[] = {
 
     { ngx_string("gzip"),
@@ -171,20 +134,6 @@ static ngx_command_t  ngx_http_gzip_filt
       offsetof(ngx_http_gzip_conf_t, no_buffer),
       NULL },
 
-    { ngx_string("gzip_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_gzip_conf_t, http_version),
-      &ngx_http_gzip_http_version },
-
-    { ngx_string("gzip_proxied"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
-      ngx_conf_set_bitmask_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_gzip_conf_t, proxied),
-      &ngx_http_gzip_proxied_mask },
-
     { ngx_string("gzip_min_length"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_size_slot,
@@ -247,10 +196,6 @@ struct gztrailer {
 
 
 static ngx_str_t  ngx_http_gzip_ratio = ngx_string("gzip_ratio");
-static ngx_str_t  ngx_http_gzip_no_cache = ngx_string("no-cache");
-static ngx_str_t  ngx_http_gzip_no_store = ngx_string("no-store");
-static ngx_str_t  ngx_http_gzip_private = ngx_string("private");
-
 
 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
@@ -261,6 +206,7 @@ ngx_http_gzip_header_filter(ngx_http_req
 {
     ngx_str_t             *type;
     ngx_uint_t             i;
+    ngx_table_elt_t       *h;
     ngx_http_gzip_ctx_t   *ctx;
     ngx_http_gzip_conf_t  *conf;
 
@@ -271,21 +217,16 @@ ngx_http_gzip_header_filter(ngx_http_req
             && r->headers_out.status != NGX_HTTP_FORBIDDEN
             && r->headers_out.status != NGX_HTTP_NOT_FOUND)
         || r->header_only
-        || r != r->main
-        || r->http_version < conf->http_version
         || r->headers_out.content_type.len == 0
         || (r->headers_out.content_encoding
             && r->headers_out.content_encoding->value.len)
-        || r->headers_in.accept_encoding == NULL
         || (r->headers_out.content_length_n != -1
             && r->headers_out.content_length_n < conf->min_length)
-        || ngx_strstr(r->headers_in.accept_encoding->value.data, "gzip") == NULL
-       )
+        || ngx_http_gzip_ok(r) != NGX_OK)
     {
         return ngx_http_next_header_filter(r);
     }
 
-
     type = conf->types->elts;
     for (i = 0; i < conf->types->nelts; i++) {
         if (r->headers_out.content_type.len >= type[i].len
@@ -298,32 +239,8 @@ ngx_http_gzip_header_filter(ngx_http_req
 
     return ngx_http_next_header_filter(r);
 
-
 found:
 
-    if (r->headers_in.via) {
-        if (conf->proxied & NGX_HTTP_GZIP_PROXIED_OFF) {
-            return ngx_http_next_header_filter(r);
-        }
-
-        if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_ANY)
-            && ngx_http_gzip_proxied(r, conf) == NGX_DECLINED)
-        {
-            return ngx_http_next_header_filter(r);
-        }
-    }
-
-
-    /*
-     * if the URL (without the "http://" prefix) is longer than 253 bytes
-     * then MSIE 4.x can not handle the compressed stream - it waits too long,
-     * hangs up or crashes
-     */
-
-    if (r->headers_in.msie4 && r->unparsed_uri.len > 200) {
-        return ngx_http_next_header_filter(r);
-    }
-
     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t));
     if (ctx == NULL) {
         return NGX_ERROR;
@@ -331,19 +248,20 @@ found:
 
     ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
 
-
     ctx->request = r;
 
-    r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers);
-    if (r->headers_out.content_encoding == NULL) {
+    h = ngx_list_push(&r->headers_out.headers);
+    if (h == NULL) {
         return NGX_ERROR;
     }
 
-    r->headers_out.content_encoding->hash = 1;
-    r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1;
-    r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding";
-    r->headers_out.content_encoding->value.len = sizeof("gzip") - 1;
-    r->headers_out.content_encoding->value.data = (u_char *) "gzip";
+    h->hash = 1;
+    h->key.len = sizeof("Content-Encoding") - 1;
+    h->key.data = (u_char *) "Content-Encoding";
+    h->value.len = sizeof("gzip") - 1;
+    h->value.data = (u_char *) "gzip";
+
+    r->headers_out.content_encoding = h;
 
     ctx->length = r->headers_out.content_length_n;
 
@@ -357,89 +275,6 @@ found:
 
 
 static ngx_int_t
-ngx_http_gzip_proxied(ngx_http_request_t *r, ngx_http_gzip_conf_t *conf)
-{
-    time_t  date, expires;
-
-    if (r->headers_in.authorization
-        && (conf->proxied & NGX_HTTP_GZIP_PROXIED_AUTH))
-    {
-        return NGX_OK;
-    }
-
-    if (r->headers_out.expires) {
-
-        if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_EXPIRED)) {
-            return NGX_DECLINED;
-        }
-
-        expires = ngx_http_parse_time(r->headers_out.expires->value.data,
-                                      r->headers_out.expires->value.len);
-        if (expires == NGX_ERROR) {
-            return NGX_DECLINED;
-        }
-
-        if (r->headers_out.date) {
-            date = ngx_http_parse_time(r->headers_out.date->value.data,
-                                       r->headers_out.date->value.len);
-            if (date == NGX_ERROR) {
-                return NGX_DECLINED;
-            }
-
-        } else {
-            date = ngx_time();
-        }
-
-        if (expires < date) {
-            return NGX_OK;
-        }
-
-        return NGX_DECLINED;
-    }
-
-    if (r->headers_out.cache_control.elts) {
-
-        if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_CACHE)
-            && ngx_http_parse_multi_header_lines(&r->headers_out.cache_control,
-                   &ngx_http_gzip_no_cache, NULL) >= 0)
-        {
-            return NGX_OK;
-        }
-
-        if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_STORE)
-            && ngx_http_parse_multi_header_lines(&r->headers_out.cache_control,
-                   &ngx_http_gzip_no_store, NULL) >= 0)
-        {
-            return NGX_OK;
-        }
-
-        if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_PRIVATE)
-            && ngx_http_parse_multi_header_lines(&r->headers_out.cache_control,
-                   &ngx_http_gzip_private, NULL) >= 0)
-        {
-            return NGX_OK;
-        }
-
-        return NGX_DECLINED;
-    }
-
-    if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_LM)
-        && r->headers_out.last_modified)
-    {
-        return NGX_DECLINED;
-    }
-
-    if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_ETAG)
-        && r->headers_out.etag)
-    {
-        return NGX_DECLINED;
-    }
-
-    return NGX_OK;
-}
-
-
-static ngx_int_t
 ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
     int                    rc, wbits, memlevel;
@@ -811,12 +646,15 @@ ngx_http_gzip_body_filter(ngx_http_reque
             }
         }
 
-        if (last == NGX_AGAIN && !ctx->done) {
-            return NGX_AGAIN;
-        }
+        if (ctx->out == NULL) {
 
-        if (ctx->out == NULL && ctx->busy == NULL) {
-            return NGX_OK;
+            if (last == NGX_AGAIN) {
+                return NGX_AGAIN;
+            }
+
+            if (ctx->busy == NULL) {
+                return NGX_OK;
+            }
         }
 
         last = ngx_http_next_body_filter(r, ctx->out);
@@ -938,7 +776,7 @@ ngx_http_gzip_ratio_variable(ngx_http_re
     ngx_http_gzip_ctx_t  *ctx;
 
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
@@ -948,7 +786,7 @@ ngx_http_gzip_ratio_variable(ngx_http_re
         return NGX_OK;
     }
 
-    v->data = ngx_palloc(r->pool, NGX_INT32_LEN + 3);
+    v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN + 3);
     if (v->data == NULL) {
         return NGX_ERROR;
     }
@@ -988,15 +826,12 @@ ngx_http_gzip_create_conf(ngx_conf_t *cf
      * set by ngx_pcalloc():
      *
      *     conf->bufs.num = 0;
-     *     conf->proxied = 0;
      *     conf->types = NULL;
      */
 
     conf->enable = NGX_CONF_UNSET;
     conf->no_buffer = NGX_CONF_UNSET;
 
-    conf->http_version = NGX_CONF_UNSET_UINT;
-
     conf->level = NGX_CONF_UNSET;
     conf->wbits = (size_t) NGX_CONF_UNSET;
     conf->memlevel = (size_t) NGX_CONF_UNSET;
@@ -1018,11 +853,6 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf,
 
     ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize);
 
-    ngx_conf_merge_uint_value(conf->http_version, prev->http_version,
-                              NGX_HTTP_VERSION_11);
-    ngx_conf_merge_bitmask_value(conf->proxied, prev->proxied,
-                              (NGX_CONF_BITMASK_SET|NGX_HTTP_GZIP_PROXIED_OFF));
-
     ngx_conf_merge_value(conf->level, prev->level, 1);
     ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
     ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,
@@ -1105,7 +935,7 @@ ngx_http_gzip_types(ngx_conf_t *cf, ngx_
 
         type->len = value[i].len;
 
-        type->data = ngx_palloc(cf->pool, type->len + 1);
+        type->data = ngx_pnalloc(cf->pool, type->len + 1);
         if (type->data == NULL) {
             return NGX_CONF_ERROR;
         }
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_gzip_static_module.c
@@ -0,0 +1,290 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    ngx_flag_t  enable;
+} ngx_http_gzip_static_conf_t;
+
+
+static ngx_int_t ngx_http_gzip_static_handler(ngx_http_request_t *r);
+static void *ngx_http_gzip_static_create_conf(ngx_conf_t *cf);
+static char *ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent,
+    void *child);
+static ngx_int_t ngx_http_gzip_static_init(ngx_conf_t *cf);
+
+
+static ngx_command_t  ngx_http_gzip_static_commands[] = {
+
+    { ngx_string("gzip_static"),
+      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_gzip_static_conf_t, enable),
+      NULL },
+
+      ngx_null_command
+};
+
+
+ngx_http_module_t  ngx_http_gzip_static_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    ngx_http_gzip_static_init,             /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_gzip_static_create_conf,      /* create location configuration */
+    ngx_http_gzip_static_merge_conf        /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_gzip_static_module = {
+    NGX_MODULE_V1,
+    &ngx_http_gzip_static_module_ctx,      /* module context */
+    ngx_http_gzip_static_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_gzip_static_handler(ngx_http_request_t *r)
+{
+    u_char                       *p;
+    size_t                        root;
+    ngx_str_t                     path;
+    ngx_int_t                     rc;
+    ngx_uint_t                    level;
+    ngx_log_t                    *log;
+    ngx_buf_t                    *b;
+    ngx_chain_t                   out;
+    ngx_table_elt_t              *h;
+    ngx_open_file_info_t          of;
+    ngx_http_core_loc_conf_t     *clcf;
+    ngx_http_gzip_static_conf_t  *gzcf;
+
+    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
+        return NGX_DECLINED;
+    }
+
+    if (r->uri.data[r->uri.len - 1] == '/') {
+        return NGX_DECLINED;
+    }
+
+    /* TODO: Win32 */
+    if (r->zero_in_uri) {
+        return NGX_DECLINED;
+    }
+
+    gzcf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_static_module);
+
+    if (!gzcf->enable || ngx_http_gzip_ok(r) != NGX_OK) {
+        return NGX_DECLINED;
+    }
+
+    log = r->connection->log;
+
+    p = ngx_http_map_uri_to_path(r, &path, &root, sizeof(".gz") - 1);
+    if (p == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    *p++ = '.';
+    *p++ = 'g';
+    *p++ = 'z';
+    *p = '\0';
+
+    path.len = p - path.data;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
+                   "http filename: \"%s\"", path.data);
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+    of.directio = clcf->directio;
+    of.valid = clcf->open_file_cache_valid;
+    of.min_uses = clcf->open_file_cache_min_uses;
+    of.errors = clcf->open_file_cache_errors;
+    of.events = clcf->open_file_cache_events;
+
+    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+        != NGX_OK)
+    {
+        switch (of.err) {
+
+        case 0:
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+
+        case NGX_ENOENT:
+        case NGX_ENOTDIR:
+        case NGX_ENAMETOOLONG:
+
+            return NGX_DECLINED;
+
+        case NGX_EACCES:
+
+            level = NGX_LOG_ERR;
+            break;
+
+        default:
+
+            level = NGX_LOG_CRIT;
+            break;
+        }
+
+        ngx_log_error(level, log, of.err,
+                      ngx_open_file_n " \"%s\" failed", path.data);
+
+        return NGX_DECLINED;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
+
+    if (of.is_dir) {
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
+        return NGX_DECLINED;
+    }
+
+#if !(NGX_WIN32) /* the not regular files are probably Unix specific */
+
+    if (!of.is_file) {
+        ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
+                      "\"%s\" is not a regular file", path.data);
+
+        return NGX_HTTP_NOT_FOUND;
+    }
+
+#endif
+
+    r->root_tested = 1;
+
+    rc = ngx_http_discard_request_body(r);
+
+    if (rc != NGX_OK) {
+        return rc;
+    }
+
+    log->action = "sending response to client";
+
+    r->headers_out.status = NGX_HTTP_OK;
+    r->headers_out.content_length_n = of.size;
+    r->headers_out.last_modified_time = of.mtime;
+
+    if (ngx_http_set_content_type(r) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    h = ngx_list_push(&r->headers_out.headers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    h->hash = 1;
+    h->key.len = sizeof("Content-Encoding") - 1;
+    h->key.data = (u_char *) "Content-Encoding";
+    h->value.len = sizeof("gzip") - 1;
+    h->value.data = (u_char *) "gzip";
+
+    r->headers_out.content_encoding = h;
+
+    /* we need to allocate all before the header would be sent */
+
+    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+    if (b == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
+    if (b->file == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    rc = ngx_http_send_header(r);
+
+    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+        return rc;
+    }
+
+    b->file_pos = 0;
+    b->file_last = of.size;
+
+    b->in_file = b->file_last ? 1 : 0;
+    b->last_buf = 1;
+    b->last_in_chain = 1;
+
+    b->file->fd = of.fd;
+    b->file->name = path;
+    b->file->log = log;
+
+    out.buf = b;
+    out.next = NULL;
+
+    return ngx_http_output_filter(r, &out);
+}
+
+
+static void *
+ngx_http_gzip_static_create_conf(ngx_conf_t *cf)
+{
+    ngx_http_gzip_static_conf_t  *conf;
+
+    conf = ngx_palloc(cf->pool, sizeof(ngx_http_gzip_static_conf_t));
+    if (conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    conf->enable = NGX_CONF_UNSET;
+
+    return conf;
+}
+
+
+static char *
+ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_gzip_static_conf_t *prev = parent;
+    ngx_http_gzip_static_conf_t *conf = child;
+
+    ngx_conf_merge_value(conf->enable, prev->enable, 0);
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_static_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_CONTENT_PHASE].handlers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    *h = ngx_http_gzip_static_handler;
+
+    return NGX_OK;
+}
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -31,18 +31,20 @@ struct ngx_http_header_val_s {
 };
 
 
+#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
+
+
 typedef struct {
-    time_t                   expires;
+    ngx_uint_t               expires;
+    time_t                   expires_time;
     ngx_array_t             *headers;
 } ngx_http_headers_conf_t;
 
 
-#define NGX_HTTP_EXPIRES_UNSET   -2147483647
-#define NGX_HTTP_EXPIRES_OFF     -2147483646
-#define NGX_HTTP_EXPIRES_EPOCH   -2147483645
-#define NGX_HTTP_EXPIRES_MAX     -2147483644
-
-
 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,
@@ -76,7 +78,7 @@ static ngx_command_t  ngx_http_headers_f
 
     { ngx_string("expires"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
-                        |NGX_CONF_TAKE1,
+                        |NGX_CONF_TAKE12,
       ngx_http_headers_expires,
       NGX_HTTP_LOC_CONF_OFFSET,
       0,
@@ -185,6 +187,7 @@ static ngx_int_t
 ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
 {
     size_t            len;
+    time_t            since;
     ngx_uint_t        i;
     ngx_table_elt_t  *expires, *cc, **ccp;
 
@@ -261,12 +264,12 @@ ngx_http_set_expires(ngx_http_request_t 
         return NGX_OK;
     }
 
-    expires->value.data = ngx_palloc(r->pool, len);
+    expires->value.data = ngx_pnalloc(r->pool, len);
     if (expires->value.data == NULL) {
         return NGX_ERROR;
     }
 
-    if (conf->expires == 0) {
+    if (conf->expires_time == 0) {
         ngx_memcpy(expires->value.data, ngx_cached_http_time.data,
                    ngx_cached_http_time.len + 1);
 
@@ -276,22 +279,32 @@ ngx_http_set_expires(ngx_http_request_t 
         return NGX_OK;
     }
 
-    ngx_http_time(expires->value.data, ngx_time() + conf->expires);
+    if (conf->expires == NGX_HTTP_EXPIRES_ACCESS
+        || r->headers_out.last_modified_time == -1)
+    {
+        since = ngx_time();
 
-    if (conf->expires < 0) {
+    } else {
+        since = r->headers_out.last_modified_time;
+    }
+
+    ngx_http_time(expires->value.data, since + conf->expires_time);
+
+    if (conf->expires_time < 0) {
         cc->value.len = sizeof("no-cache") - 1;
         cc->value.data = (u_char *) "no-cache";
 
         return NGX_OK;
     }
 
-    cc->value.data = ngx_palloc(r->pool,
-                                sizeof("max-age=") + NGX_TIME_T_LEN + 1);
+    cc->value.data = ngx_pnalloc(r->pool,
+                                 sizeof("max-age=") + NGX_TIME_T_LEN + 1);
     if (cc->value.data == NULL) {
         return NGX_ERROR;
     }
 
-    cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", conf->expires)
+    cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T",
+                                since + conf->expires_time - ngx_time())
                     - cc->value.data;
 
     return NGX_OK;
@@ -369,7 +382,14 @@ ngx_http_set_last_modified(ngx_http_requ
         old = NULL;
     }
 
+    r->headers_out.last_modified_time = -1;
+
     if (old == NULL || *old == NULL) {
+
+        if (value->len == 0) {
+            return NGX_OK;
+        }
+
         h = ngx_list_push(&r->headers_out.headers);
         if (h == NULL) {
             return NGX_ERROR;
@@ -377,14 +397,17 @@ ngx_http_set_last_modified(ngx_http_requ
 
     } else {
         h = *old;
+
+        if (value->len == 0) {
+            h->hash = 0;
+            return NGX_OK;
+        }
     }
 
     h->hash = hv->value.hash;
     h->key = hv->value.key;
     h->value = *value;
 
-    r->headers_out.last_modified_time = -1;
-
     return NGX_OK;
 }
 
@@ -403,9 +426,10 @@ ngx_http_headers_create_conf(ngx_conf_t 
      * set by ngx_pcalloc():
      *
      *     conf->headers = NULL;
+     *     conf->expires_time = 0;
      */
 
-    conf->expires = NGX_HTTP_EXPIRES_UNSET;
+    conf->expires = NGX_CONF_UNSET_UINT;
 
     return conf;
 }
@@ -417,9 +441,13 @@ ngx_http_headers_merge_conf(ngx_conf_t *
     ngx_http_headers_conf_t *prev = parent;
     ngx_http_headers_conf_t *conf = child;
 
-    if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
-        conf->expires = (prev->expires == NGX_HTTP_EXPIRES_UNSET) ?
-                            NGX_HTTP_EXPIRES_OFF : prev->expires;
+    if (conf->expires == NGX_CONF_UNSET_UINT) {
+        conf->expires = prev->expires;
+        conf->expires_time = prev->expires_time;
+
+        if (conf->expires == NGX_CONF_UNSET_UINT) {
+            conf->expires = NGX_HTTP_EXPIRES_OFF;
+        }
     }
 
     if (conf->headers == NULL) {
@@ -445,56 +473,73 @@ ngx_http_headers_expires(ngx_conf_t *cf,
 {
     ngx_http_headers_conf_t *hcf = conf;
 
-    ngx_uint_t   minus;
+    ngx_uint_t   minus, n;
     ngx_str_t   *value;
 
-    if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) {
+    if (hcf->expires != NGX_CONF_UNSET_UINT) {
         return "is duplicate";
     }
 
     value = cf->args->elts;
 
-    if (ngx_strcmp(value[1].data, "epoch") == 0) {
-        hcf->expires = NGX_HTTP_EXPIRES_EPOCH;
-        return NGX_CONF_OK;
-    }
+    if (cf->args->nelts == 2) {
+
+        if (ngx_strcmp(value[1].data, "epoch") == 0) {
+            hcf->expires = NGX_HTTP_EXPIRES_EPOCH;
+            return NGX_CONF_OK;
+        }
+
+        if (ngx_strcmp(value[1].data, "max") == 0) {
+            hcf->expires = NGX_HTTP_EXPIRES_MAX;
+            return NGX_CONF_OK;
+        }
 
-    if (ngx_strcmp(value[1].data, "max") == 0) {
-        hcf->expires = NGX_HTTP_EXPIRES_MAX;
-        return NGX_CONF_OK;
+        if (ngx_strcmp(value[1].data, "off") == 0) {
+            hcf->expires = NGX_HTTP_EXPIRES_OFF;
+            return NGX_CONF_OK;
+        }
+
+        hcf->expires = NGX_HTTP_EXPIRES_ACCESS;
+
+        n = 1;
+
+    } else { /* cf->args->nelts == 3 */
+
+        if (ngx_strcmp(value[1].data, "modified") != 0) {
+            return "invalid value";
+        }
+
+        hcf->expires = NGX_HTTP_EXPIRES_MODIFIED;
+
+        n = 2;
     }
 
-    if (ngx_strcmp(value[1].data, "off") == 0) {
-        hcf->expires = NGX_HTTP_EXPIRES_OFF;
-        return NGX_CONF_OK;
-    }
-
-    if (value[1].data[0] == '+') {
-        value[1].data++;
-        value[1].len--;
+    if (value[n].data[0] == '+') {
+        value[n].data++;
+        value[n].len--;
         minus = 0;
 
-    } else if (value[1].data[0] == '-') {
-        value[1].data++;
-        value[1].len--;
+    } else if (value[n].data[0] == '-') {
+        value[n].data++;
+        value[n].len--;
         minus = 1;
 
     } else {
         minus = 0;
     }
 
-    hcf->expires = ngx_parse_time(&value[1], 1);
+    hcf->expires_time = ngx_parse_time(&value[n], 1);
 
-    if (hcf->expires == NGX_ERROR) {
+    if (hcf->expires_time == NGX_ERROR) {
         return "invalid value";
     }
 
-    if (hcf->expires == NGX_PARSE_LARGE_TIME) {
+    if (hcf->expires_time == NGX_PARSE_LARGE_TIME) {
         return "value must be less than 68 years";
     }
 
     if (minus) {
-        hcf->expires = - hcf->expires;
+        hcf->expires_time = - hcf->expires_time;
     }
 
     return NGX_CONF_OK;
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -22,25 +22,13 @@ typedef struct {
 } ngx_http_index_loc_conf_t;
 
 
-typedef struct {
-    ngx_uint_t               current;
-
-    ngx_str_t                path;
-    ngx_str_t                index;
-
-    size_t                   root;
-
-    ngx_uint_t               tested;     /* unsigned  tested:1 */
-} ngx_http_index_ctx_t;
-
-
 #define NGX_HTTP_DEFAULT_INDEX   "index.html"
 
 
 static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r,
-    ngx_http_core_loc_conf_t *clcf, ngx_http_index_ctx_t *ctx);
-static ngx_int_t ngx_http_index_error(ngx_http_request_t *r,
-    ngx_http_index_ctx_t *ctx, ngx_err_t err);
+    ngx_http_core_loc_conf_t *clcf, u_char *path, u_char *last);
+static ngx_int_t ngx_http_index_error(ngx_http_request_t *r, u_char *file,
+    ngx_err_t err);
 
 static ngx_int_t ngx_http_index_init(ngx_conf_t *cf);
 static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf);
@@ -107,14 +95,13 @@ ngx_module_t  ngx_http_index_module = {
 static ngx_int_t
 ngx_http_index_handler(ngx_http_request_t *r)
 {
-    u_char                       *last;
-    size_t                        len;
+    u_char                       *p, *name;
+    size_t                        len, nlen, root, allocated;
     ngx_int_t                     rc;
     ngx_str_t                     path, uri;
     ngx_log_t                    *log;
-    ngx_uint_t                    i;
+    ngx_uint_t                    i, dir_tested;
     ngx_http_index_t             *index;
-    ngx_http_index_ctx_t         *ctx;
     ngx_open_file_info_t          of;
     ngx_http_script_code_pt       code;
     ngx_http_script_engine_t      e;
@@ -140,24 +127,14 @@ ngx_http_index_handler(ngx_http_request_
     ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    /*
-     * we use context because the handler supports an async file opening,
-     * and may be called several times
-     */
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_index_module);
-    if (ctx == NULL) {
-
-        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_index_ctx_t));
-        if (ctx == NULL) {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
-        }
-
-        ngx_http_set_ctx(r, ctx, ngx_http_index_module);
-    }
+    allocated = 0;
+    root = 0;
+    dir_tested = 0;
+    name = NULL;
+    path.data = NULL;
 
     index = ilcf->indices->elts;
-    for (i = ctx->current; i < ilcf->indices->nelts; i++) {
+    for (i = 0; i < ilcf->indices->nelts; i++) {
 
         if (index[i].lengths == NULL) {
 
@@ -166,7 +143,7 @@ ngx_http_index_handler(ngx_http_request_
             }
 
             len = ilcf->max_index_len;
-            ctx->index.len = index[i].name.len;
+            nlen = index[i].name.len;
 
         } else {
             ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
@@ -184,45 +161,44 @@ ngx_http_index_handler(ngx_http_request_
                 len += lcode(&e);
             }
 
-            ctx->index.len = len;
+            nlen = len;
 
             /* 16 bytes are preallocation */
 
             len += 16;
         }
 
-        if (len > (size_t) (ctx->path.data + ctx->path.len - ctx->index.data)) {
+        if (len > (size_t) (path.data + allocated - name)) {
 
-            last = ngx_http_map_uri_to_path(r, &ctx->path, &ctx->root, len);
-            if (last == NULL) {
+            name = ngx_http_map_uri_to_path(r, &path, &root, len);
+            if (name == NULL) {
                 return NGX_ERROR;
             }
 
-            ctx->index.data = last;
+            allocated = path.len;
         }
 
-        path.data = ctx->path.data;
-
         if (index[i].values == NULL) {
 
             /* index[i].name.len includes the terminating '\0' */
 
-            ngx_memcpy(ctx->index.data, index[i].name.data, index[i].name.len);
+            ngx_memcpy(name, index[i].name.data, index[i].name.len);
 
-            path.len = (ctx->index.data + index[i].name.len - 1) - path.data;
+            path.len = (name + index[i].name.len - 1) - path.data;
 
         } else {
             e.ip = index[i].values->elts;
-            e.pos = ctx->index.data;
+            e.pos = name;
 
             while (*(uintptr_t *) e.ip) {
                 code = *(ngx_http_script_code_pt *) e.ip;
                 code((ngx_http_script_engine_t *) &e);
             }
 
-            if (*ctx->index.data == '/') {
-                ctx->index.len--;
-                return ngx_http_internal_redirect(r, &ctx->index, &r->args);
+            if (*name == '/') {
+                uri.len = nlen - 1;
+                uri.data = name;
+                return ngx_http_internal_redirect(r, &uri, &r->args);
             }
 
             path.len = e.pos - path.data;
@@ -230,47 +206,38 @@ ngx_http_index_handler(ngx_http_request_
             *e.pos++ = '\0';
         }
 
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
-                       "open index \"%V\"", &path);
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "open index \"%V\"", &path);
+
+        ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
-        of.test_dir = 0;
-        of.retest = clcf->open_file_cache_retest;
+        of.directio = clcf->directio;
+        of.valid = clcf->open_file_cache_valid;
+        of.min_uses = clcf->open_file_cache_min_uses;
         of.errors = clcf->open_file_cache_errors;
         of.events = clcf->open_file_cache_events;
 
-        rc = ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool);
-
-#if 0
-        if (rc == NGX_AGAIN) {
-            ctx->current = i;
-            return NGX_AGAIN;
-        }
-#endif
-
-        if (rc == NGX_ERROR) {
-
+        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+            != NGX_OK)
+        {
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, of.err,
-                           ngx_open_file_n " \"%s\" failed", ctx->path.data);
+                           ngx_open_file_n " \"%s\" failed", path.data);
 
             if (of.err == 0) {
                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
             }
 
-            if (of.err == NGX_ENOTDIR) {
-                return ngx_http_index_error(r, ctx, of.err);
-
-            } else if (of.err == NGX_EACCES) {
-                return ngx_http_index_error(r, ctx, of.err);
+            if (of.err == NGX_ENOTDIR || of.err == NGX_EACCES) {
+                return ngx_http_index_error(r, path.data, of.err);
             }
 
-            if (!ctx->tested) {
-                rc = ngx_http_index_test_dir(r, clcf, ctx);
+            if (!dir_tested) {
+                rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);
 
                 if (rc != NGX_OK) {
                     return rc;
                 }
 
-                ctx->tested = 1;
+                dir_tested = 1;
             }
 
             if (of.err == NGX_ENOENT) {
@@ -278,24 +245,24 @@ ngx_http_index_handler(ngx_http_request_
             }
 
             ngx_log_error(NGX_LOG_ERR, log, of.err,
-                          ngx_open_file_n " \"%s\" failed", ctx->path.data);
+                          ngx_open_file_n " \"%s\" failed", path.data);
 
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
-        uri.len = r->uri.len + ctx->index.len - 1;
+        uri.len = r->uri.len + nlen - 1;
 
         if (!clcf->alias) {
-            uri.data = ctx->path.data + ctx->root;
+            uri.data = path.data + root;
 
         } else {
-            uri.data = ngx_palloc(r->pool, uri.len);
+            uri.data = ngx_pnalloc(r->pool, uri.len);
             if (uri.data == NULL) {
                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
             }
 
-            last = ngx_copy(uri.data, r->uri.data, r->uri.len);
-            ngx_memcpy(last, ctx->index.data, ctx->index.len - 1);
+            p = ngx_copy(uri.data, r->uri.data, r->uri.len);
+            ngx_memcpy(p, name, nlen - 1);
         }
 
         return ngx_http_internal_redirect(r, &uri, &r->args);
@@ -307,70 +274,73 @@ ngx_http_index_handler(ngx_http_request_
 
 static ngx_int_t
 ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
-    ngx_http_index_ctx_t *ctx)
+    u_char *path, u_char *last)
 {
     u_char                c;
-    ngx_str_t             path;
-    ngx_uint_t            i;
+    ngx_str_t             dir;
     ngx_open_file_info_t  of;
 
-    c = *(ctx->index.data - 1);
-    i = (c == '/') ? 1 : 0;
-    *(ctx->index.data - i) = '\0';
+    c = *last;
+    if (c != '/' || path == last) {
+        /* "alias" without trailing slash */
+        c = *(++last);
+    }
+    *last = '\0';
 
-    path.len = (ctx->index.data - i) - ctx->path.data;
-    path.data = ctx->path.data;
+    dir.len = last - path;
+    dir.data = path;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http index check dir: \"%V\"", &path);
+                   "http index check dir: \"%V\"", &dir);
+
+    ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
     of.test_dir = 1;
-    of.retest = clcf->open_file_cache_retest;
+    of.valid = clcf->open_file_cache_valid;
     of.errors = clcf->open_file_cache_errors;
 
-    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+    if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)
         != NGX_OK)
     {
         if (of.err) {
 
             if (of.err == NGX_ENOENT) {
-                *(ctx->index.data - i) = c;
-                return ngx_http_index_error(r, ctx, of.err);
+                *last = c;
+                return ngx_http_index_error(r, dir.data, NGX_ENOENT);
             }
 
             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
-                          ngx_open_file_n " \"%s\" failed", path.data);
+                          ngx_open_file_n " \"%s\" failed", dir.data);
         }
 
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    *(ctx->index.data - i) = c;
+    *last = c;
 
     if (of.is_dir) {
         return NGX_OK;
     }
 
     ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                  "\"%s\" is not a directory", path.data);
+                  "\"%s\" is not a directory", dir.data);
 
     return NGX_HTTP_INTERNAL_SERVER_ERROR;
 }
 
 
 static ngx_int_t
-ngx_http_index_error(ngx_http_request_t *r, ngx_http_index_ctx_t *ctx,
-    ngx_err_t err)
+ngx_http_index_error(ngx_http_request_t *r, u_char *file, ngx_err_t err)
 {
     if (err == NGX_EACCES) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
-                      "\"%s\" is forbidden", ctx->path.data);
+                      "\"%s\" is forbidden", file);
 
         return NGX_HTTP_FORBIDDEN;
     }
 
     ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
-                  "\"%s\" is not found", ctx->path.data);
+                  "\"%s\" is not found", file);
 
     return NGX_HTTP_NOT_FOUND;
 }
@@ -472,11 +442,11 @@ ngx_http_index_set_index(ngx_conf_t *cf,
     value = cf->args->elts;
 
     for (i = 1; i < cf->args->nelts; i++) {
+
         if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                                "only the last index in \"index\" directive "
-                               "may be absolute");
-            return NGX_CONF_ERROR;
+                               "should be absolute");
         }
 
         if (value[i].len == 0) {
@@ -503,6 +473,10 @@ ngx_http_index_set_index(ngx_conf_t *cf,
                 ilcf->max_index_len = index->name.len;
             }
 
+            if (index->name.data[0] == '/') {
+                continue;
+            }
+
             /* include the terminating '\0' to the length to use ngx_copy() */
             index->name.len++;
 
--- a/src/http/modules/ngx_http_limit_zone_module.c
+++ b/src/http/modules/ngx_http_limit_zone_module.c
@@ -239,54 +239,36 @@ 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_http_limit_zone_node_t  *lzn, *lznt;
+    ngx_rbtree_node_t           **p;
+    ngx_http_limit_zone_node_t   *lzn, *lznt;
 
     for ( ;; ) {
 
         if (node->key < temp->key) {
 
-            if (temp->left == sentinel) {
-                temp->left = node;
-                break;
-            }
-
-            temp = temp->left;
+            p = &temp->left;
 
         } else if (node->key > temp->key) {
 
-            if (temp->right == sentinel) {
-                temp->right = node;
-                break;
-            }
-
-            temp = temp->right;
+            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;
 
-            if (ngx_memn2cmp(lzn->data, lznt->data, lzn->len, lznt->len) < 0) {
-
-                if (temp->left == sentinel) {
-                    temp->left = node;
-                    break;
-                }
-
-                temp = temp->left;
+            p = (ngx_memn2cmp(lzn->data, lznt->data, lzn->len, lznt->len) < 0)
+                ? &temp->left : &temp->right;
+        }
 
-            } else {
+        if (*p == sentinel) {
+            break;
+        }
 
-                if (temp->right == sentinel) {
-                    temp->right = node;
-                    break;
-                }
-
-                temp = temp->right;
-            }
-        }
+        temp = *p;
     }
 
+    *p = node;
     node->parent = temp;
     node->left = sentinel;
     node->right = sentinel;
@@ -362,11 +344,8 @@ ngx_http_limit_zone_init_zone(ngx_shm_zo
         return NGX_ERROR;
     }
 
-    ngx_rbtree_sentinel_init(sentinel);
-
-    ctx->rbtree->root = sentinel;
-    ctx->rbtree->sentinel = sentinel;
-    ctx->rbtree->insert = ngx_http_limit_zone_rbtree_insert_value;
+    ngx_rbtree_init(ctx->rbtree, sentinel,
+                    ngx_http_limit_zone_rbtree_insert_value);
 
     return NGX_OK;
 }
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -40,7 +40,14 @@ typedef struct {
 
 
 typedef struct {
+    ngx_array_t                *lengths;
+    ngx_array_t                *values;
+} ngx_http_log_script_t;
+
+
+typedef struct {
     ngx_open_file_t            *file;
+    ngx_http_log_script_t      *script;
     time_t                      disk_full_time;
     time_t                      error_log_time;
     ngx_array_t                *ops;        /* array of ngx_http_log_op_t */
@@ -49,6 +56,11 @@ typedef struct {
 
 typedef struct {
     ngx_array_t                *logs;       /* array of ngx_http_log_t */
+
+    ngx_open_file_cache_t      *open_file_cache;
+    time_t                      open_file_cache_valid;
+    ngx_uint_t                  open_file_cache_min_uses;
+
     ngx_uint_t                  off;        /* unsigned  off:1 */
 } ngx_http_log_loc_conf_t;
 
@@ -62,6 +74,8 @@ typedef struct {
 
 static void ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log,
     u_char *buf, size_t len);
+static ssize_t ngx_http_log_script_write(ngx_http_request_t *r,
+    ngx_http_log_script_t *script, u_char **name, u_char *buf, size_t len);
 
 static u_char *ngx_http_log_connection(ngx_http_request_t *r, u_char *buf,
     ngx_http_log_op_t *op);
@@ -88,6 +102,7 @@ static size_t ngx_http_log_variable_getl
     uintptr_t data);
 static u_char *ngx_http_log_variable(ngx_http_request_t *r, u_char *buf,
     ngx_http_log_op_t *op);
+static uintptr_t ngx_http_log_escape(u_char *dst, u_char *src, size_t size);
 
 
 static void *ngx_http_log_create_main_conf(ngx_conf_t *cf);
@@ -100,6 +115,8 @@ static char *ngx_http_log_set_format(ngx
     void *conf);
 static char *ngx_http_log_compile_format(ngx_conf_t *cf,
     ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s);
+static char *ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 static ngx_int_t ngx_http_log_init(ngx_conf_t *cf);
 
 
@@ -114,12 +131,19 @@ static ngx_command_t  ngx_http_log_comma
 
     { ngx_string("access_log"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
-                        |NGX_CONF_TAKE123,
+                        |NGX_HTTP_LMT_CONF|NGX_CONF_TAKE123,
       ngx_http_log_set_log,
       NGX_HTTP_LOC_CONF_OFFSET,
       0,
       NULL },
 
+    { ngx_string("open_log_file_cache"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
+      ngx_http_log_open_file_cache,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
       ngx_null_command
 };
 
@@ -234,7 +258,7 @@ ngx_http_log_handler(ngx_http_request_t 
 
         file = log[l].file;
 
-        if (file->buffer) {
+        if (file && file->buffer) {
 
             if (len > (size_t) (file->last - file->pos)) {
 
@@ -260,7 +284,7 @@ ngx_http_log_handler(ngx_http_request_t 
             }
         }
 
-        line = ngx_palloc(r->pool, len);
+        line = ngx_pnalloc(r->pool, len);
         if (line == NULL) {
             return NGX_ERROR;
         }
@@ -284,11 +308,19 @@ static void
 ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log, u_char *buf,
     size_t len)
 {
-    time_t     now;
-    ssize_t    n;
-    ngx_err_t  err;
+    u_char     *name;
+    time_t      now;
+    ssize_t     n;
+    ngx_err_t   err;
 
-    n = ngx_write_fd(log->file->fd, buf, len);
+    if (log->script == NULL) {
+        name = log->file->name.data;
+        n = ngx_write_fd(log->file->fd, buf, len);
+
+    } else {
+        name = NULL;
+        n = ngx_http_log_script_write(r, log->script, &name, buf, len);
+    }
 
     if (n == (ssize_t) len) {
         return;
@@ -305,8 +337,7 @@ ngx_http_log_write(ngx_http_request_t *r
 
         if (now - log->error_log_time > 59) {
             ngx_log_error(NGX_LOG_ALERT, r->connection->log, err,
-                          ngx_write_fd_n " to \"%V\" failed",
-                          &log->file->name);
+                          ngx_write_fd_n " to \"%s\" failed", name);
 
             log->error_log_time = now;
         }
@@ -316,14 +347,109 @@ ngx_http_log_write(ngx_http_request_t *r
 
     if (now - log->error_log_time > 59) {
         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                      ngx_write_fd_n " to \"%V\" was incomplete: %z of %uz",
-                      &log->file->name, n, len);
+                      ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
+                      name, n, len);
 
         log->error_log_time = now;
     }
 }
 
 
+static ssize_t
+ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script,
+    u_char **name, u_char *buf, size_t len)
+{
+    size_t                     root;
+    ssize_t                    n;
+    ngx_str_t                  log, path;
+    ngx_open_file_info_t       of;
+    ngx_http_log_loc_conf_t   *llcf;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    if (!r->root_tested) {
+
+        /* test root directory existance */
+
+        if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
+            /* simulate successfull logging */
+            return len;
+        }
+
+        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;
+        of.min_uses = clcf->open_file_cache_min_uses;
+        of.errors = clcf->open_file_cache_errors;
+        of.events = clcf->open_file_cache_events;
+
+        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+            != NGX_OK)
+        {
+            if (of.err == 0) {
+                /* simulate successfull logging */
+                return len;
+            }
+
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err,
+                          "testing \"%s\" existence failed", path.data);
+
+            /* simulate successfull logging */
+            return len;
+        }
+
+        if (!of.is_dir) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ENOTDIR,
+                          "testing \"%s\" existence failed", path.data);
+
+            /* simulate successfull logging */
+            return len;
+        }
+    }
+
+    if (ngx_http_script_run(r, &log, script->lengths->elts, 1,
+                            script->values->elts)
+        == NULL)
+    {
+        /* simulate successfull logging */
+        return len;
+    }
+
+    log.data[log.len - 1] = '\0';
+    *name = log.data;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http log \"%s\"", log.data);
+
+    llcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);
+
+    ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+    of.log = 1;
+    of.valid = llcf->open_file_cache_valid;
+    of.min_uses = llcf->open_file_cache_min_uses;
+
+    if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool)
+        != NGX_OK)
+    {
+        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+                      ngx_open_file_n " \"%s\" failed", log.data);
+        /* simulate successfull logging */
+        return len;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http log #%d", of.fd);
+
+    n = ngx_write_fd(of.fd, buf, len);
+
+    return n;
+}
+
+
 static u_char *
 ngx_http_log_copy_short(ngx_http_request_t *r, u_char *buf,
     ngx_http_log_op_t *op)
@@ -400,7 +526,8 @@ ngx_http_log_request_time(ngx_http_reque
 
     tp = ngx_timeofday();
 
-    ms = (tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec);
+    ms = (ngx_msec_int_t)
+             ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));
     ms = (ms >= 0) ? ms : 0;
 
     return ngx_sprintf(buf, "%T.%03M", ms / 1000, ms % 1000);
@@ -477,6 +604,7 @@ ngx_http_log_variable_compile(ngx_conf_t
 static size_t
 ngx_http_log_variable_getlen(ngx_http_request_t *r, uintptr_t data)
 {
+    uintptr_t                   len;
     ngx_http_variable_value_t  *value;
 
     value = ngx_http_get_indexed_variable(r, data);
@@ -485,7 +613,11 @@ ngx_http_log_variable_getlen(ngx_http_re
         return 1;
     }
 
-    return value->len;
+    len = ngx_http_log_escape(NULL, value->data, value->len);
+
+    value->escape = len ? 1 : 0;
+
+    return value->len + len * 3;
 }
 
 
@@ -501,7 +633,70 @@ ngx_http_log_variable(ngx_http_request_t
         return buf + 1;
     }
 
-    return ngx_cpymem(buf, value->data, value->len);
+    if (value->escape == 0) {
+        return ngx_cpymem(buf, value->data, value->len);
+
+    } else {
+        return (u_char *) ngx_http_log_escape(buf, value->data, value->len);
+    }
+}
+
+
+static uintptr_t
+ngx_http_log_escape(u_char *dst, u_char *src, size_t size)
+{
+    ngx_uint_t      i, n;
+    static u_char   hex[] = "0123456789ABCDEF";
+
+    static uint32_t   escape[] = {
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+
+                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
+        0x00000004, /* 0000 0000 0000 0000  0000 0000 0000 0100 */
+
+                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
+        0x10000000, /* 0001 0000 0000 0000  0000 0000 0000 0000 */
+
+                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
+        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+
+        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+    };
+
+
+    if (dst == NULL) {
+
+        /* find the number of the characters to be escaped */
+
+        n  = 0;
+
+        for (i = 0; i < size; i++) {
+            if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
+                n++;
+            }
+            src++;
+        }
+
+        return (uintptr_t) n;
+    }
+
+    for (i = 0; i < size; i++) {
+        if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
+            *dst++ = '\\';
+            *dst++ = 'x';
+            *dst++ = hex[*src >> 4];
+            *dst++ = hex[*src & 0xf];
+            src++;
+
+        } else {
+            *dst++ = *src++;
+        }
+    }
+
+    return (uintptr_t) dst;
 }
 
 
@@ -550,6 +745,8 @@ ngx_http_log_create_loc_conf(ngx_conf_t 
         return NGX_CONF_ERROR;
     }
 
+    conf->open_file_cache = NGX_CONF_UNSET_PTR;
+
     return conf;
 }
 
@@ -564,11 +761,23 @@ ngx_http_log_merge_loc_conf(ngx_conf_t *
     ngx_http_log_fmt_t        *fmt;
     ngx_http_log_main_conf_t  *lmcf;
 
+    if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
+
+        conf->open_file_cache = prev->open_file_cache;
+        conf->open_file_cache_valid = prev->open_file_cache_valid;
+        conf->open_file_cache_min_uses = prev->open_file_cache_min_uses;
+
+        if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
+            conf->open_file_cache = NULL;
+        }
+    }
+
     if (conf->logs || conf->off) {
         return NGX_CONF_OK;
     }
 
-    *conf = *prev;
+    conf->logs = prev->logs;
+    conf->off = prev->off;
 
     if (conf->logs || conf->off) {
         return NGX_CONF_OK;
@@ -589,6 +798,7 @@ ngx_http_log_merge_loc_conf(ngx_conf_t *
         return NGX_CONF_ERROR;
     }
 
+    log->script = NULL;
     log->disk_full_time = 0;
     log->error_log_time = 0;
 
@@ -608,12 +818,13 @@ ngx_http_log_set_log(ngx_conf_t *cf, ngx
 {
     ngx_http_log_loc_conf_t *llcf = conf;
 
-    ssize_t                    buf;
-    ngx_uint_t                 i;
-    ngx_str_t                 *value, name;
-    ngx_http_log_t            *log;
-    ngx_http_log_fmt_t        *fmt;
-    ngx_http_log_main_conf_t  *lmcf;
+    ssize_t                     buf;
+    ngx_uint_t                  i, n;
+    ngx_str_t                  *value, name;
+    ngx_http_log_t             *log;
+    ngx_http_log_fmt_t         *fmt;
+    ngx_http_log_main_conf_t   *lmcf;
+    ngx_http_script_compile_t   sc;
 
     value = cf->args->elts;
 
@@ -636,14 +847,41 @@ ngx_http_log_set_log(ngx_conf_t *cf, ngx
         return NGX_CONF_ERROR;
     }
 
-    log->file = ngx_conf_open_file(cf->cycle, &value[1]);
-    if (log->file == NULL) {
-        return NGX_CONF_ERROR;
+    ngx_memzero(log, sizeof(ngx_http_log_t));
+
+    n = ngx_http_script_variables_count(&value[1]);
+
+    if (n == 0) {
+        log->file = ngx_conf_open_file(cf->cycle, &value[1]);
+        if (log->file == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+    } else {
+        if (ngx_conf_full_name(cf->cycle, &value[1], 0) == NGX_ERROR) {
+            return NGX_CONF_ERROR;
+        }
+
+        log->script = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_script_t));
+        if (log->script == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+        sc.cf = cf;
+        sc.source = &value[1];
+        sc.lengths = &log->script->lengths;
+        sc.values = &log->script->values;
+        sc.variables = n;
+        sc.complete_lengths = 1;
+        sc.complete_values = 1;
+
+        if (ngx_http_script_compile(&sc) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
     }
 
-    log->disk_full_time = 0;
-    log->error_log_time = 0;
-
     if (cf->args->nelts >= 3) {
         name = value[2];
 
@@ -680,6 +918,12 @@ buffer:
             return NGX_CONF_ERROR;
         }
 
+        if (log->script) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "buffered logs can not have variables in name");
+            return NGX_CONF_ERROR;
+        }
+
         name.len = value[3].len - 7;
         name.data = value[3].data + 7;
 
@@ -727,7 +971,10 @@ ngx_http_log_set_format(ngx_conf_t *cf, 
         if (fmt[i].name.len == value[1].len
             && ngx_strcmp(fmt[i].name.data, value[1].data) == 0)
         {
-            return "duplicate \"log_format\" name";
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "duplicate \"log_format\" name \"%V\"",
+                               &value[1]);
+            return NGX_CONF_ERROR;
         }
     }
 
@@ -897,7 +1144,7 @@ ngx_http_log_compile_format(ngx_conf_t *
                 } else {
                     op->run = ngx_http_log_copy_long;
 
-                    p = ngx_palloc(cf->pool, len);
+                    p = ngx_pnalloc(cf->pool, len);
                     if (p == NULL) {
                         return NGX_CONF_ERROR;
                     }
@@ -919,6 +1166,114 @@ invalid:
 }
 
 
+static char *
+ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_log_loc_conf_t *llcf = conf;
+
+    time_t       inactive, valid;
+    ngx_str_t   *value, s;
+    ngx_int_t    max, min_uses;
+    ngx_uint_t   i;
+
+    if (llcf->open_file_cache != NGX_CONF_UNSET_PTR) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    max = 0;
+    inactive = 10;
+    valid = 60;
+    min_uses = 1;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
+
+            max = ngx_atoi(value[i].data + 4, value[i].len - 4);
+            if (max == NGX_ERROR) {
+                goto failed;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
+
+            s.len = value[i].len - 9;
+            s.data = value[i].data + 9;
+
+            inactive = ngx_parse_time(&s, 1);
+            if (inactive < 0) {
+                goto failed;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "min_uses=", 9) == 0) {
+
+            min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9);
+            if (min_uses == NGX_ERROR) {
+                goto failed;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
+
+            s.len = value[i].len - 6;
+            s.data = value[i].data + 6;
+
+            valid = ngx_parse_time(&s, 1);
+            if (valid < 0) {
+                goto failed;
+            }
+
+            continue;
+        }
+
+        if (ngx_strcmp(value[i].data, "off") == 0) {
+
+            llcf->open_file_cache = NULL;
+
+            continue;
+        }
+
+    failed:
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid \"open_log_file_cache\" parameter \"%V\"",
+                           &value[i]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (llcf->open_file_cache == NULL) {
+        return NGX_CONF_OK;
+    }
+
+    if (max == 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                        "\"open_log_file_cache\" must have \"max\" parameter");
+        return NGX_CONF_ERROR;
+    }
+
+    llcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);
+
+    if (llcf->open_file_cache) {
+
+        llcf->open_file_cache_valid = valid;
+        llcf->open_file_cache_min_uses = min_uses;
+
+        return NGX_CONF_OK;
+    }
+
+    return NGX_CONF_ERROR;
+}
+
+
 static ngx_int_t
 ngx_http_log_init(ngx_conf_t *cf)
 {
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -106,7 +106,7 @@ ngx_http_map_variable(ngx_http_request_t
 
     size_t                      len;
     u_char                     *name;
-    ngx_uint_t                  key, i;
+    ngx_uint_t                  key;
     ngx_http_variable_value_t  *vv, *value;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -130,16 +130,12 @@ ngx_http_map_variable(ngx_http_request_t
         return NGX_OK;
     }
 
-    name = ngx_palloc(r->pool, len);
+    name = ngx_pnalloc(r->pool, len);
     if (name == NULL) {
         return NGX_ERROR;
     }
 
-    key = 0;
-    for (i = 0; i < len; i++) {
-        name[i] = ngx_tolower(vv->data[i]);
-        key = ngx_hash(key, name[i]);
-    }
+    key = ngx_hash_strlow(name, vv->data, len);
 
     value = ngx_hash_find_combined(&map->hash, key, name, len);
 
@@ -221,7 +217,7 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_c
     name.len--;
     name.data++;
 
-    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGABLE);
+    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
     if (var == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -430,7 +426,7 @@ ngx_http_map(ngx_conf_t *cf, ngx_command
     }
 
     var->valid = 1;
-    var->no_cachable = 0;
+    var->no_cacheable = 0;
     var->not_found = 0;
 
     vp = ngx_array_push(&ctx->values_hash[key]);
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -58,7 +58,7 @@ static ngx_conf_bitmask_t  ngx_http_memc
 static ngx_command_t  ngx_http_memcached_commands[] = {
 
     { ngx_string("memcached_pass"),
-      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
       ngx_http_memcached_pass,
       NGX_HTTP_LOC_CONF_OFFSET,
       0,
@@ -184,6 +184,8 @@ ngx_http_memcached_handler(ngx_http_requ
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
+    u->schema = mlcf->upstream.schema;
+
     u->peer.log = r->connection->log;
     u->peer.log_error = NGX_ERROR_ERR;
 #if (NGX_THREADS)
@@ -371,6 +373,7 @@ found:
         }
 
         u->headers_in.status_n = 200;
+        u->state->status = 200;
         u->buffer.pos = p + 1;
 
         return NGX_OK;
@@ -381,6 +384,7 @@ found:
                       "key: \"%V\" was not found by memcached", &ctx->key);
 
         u->headers_in.status_n = 404;
+        u->state->status = 404;
 
         return NGX_OK;
     }
@@ -425,16 +429,16 @@ ngx_http_memcached_filter(void *data, ss
     if (u->length == ctx->rest) {
 
         if (ngx_strncmp(b->last,
-                        ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END
-                                                 - ctx->rest,
-                        bytes) != 0)
+                   ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,
+                   ctx->rest)
+            != 0)
         {
             ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
                           "memcached sent invalid trailer");
         }
 
-        u->length -= bytes;
-        ctx->rest -= bytes;
+        u->length = 0;
+        ctx->rest = 0;
 
         return NGX_OK;
     }
@@ -453,28 +457,29 @@ ngx_http_memcached_filter(void *data, ss
 
     *ll = cl;
 
-    cl->buf->pos = b->last;
+    last = b->last;
+    cl->buf->pos = last;
     b->last += bytes;
     cl->buf->last = b->last;
+    cl->buf->tag = u->output.tag;
 
     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
                    "memcached filter bytes:%z size:%z length:%z rest:%z",
                    bytes, b->last - b->pos, u->length, ctx->rest);
 
-    if (b->last - b->pos <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) {
+    if (bytes <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) {
         u->length -= bytes;
         return NGX_OK;
     }
 
-
-    last = b->pos + u->length - NGX_HTTP_MEMCACHED_END;
+    last += u->length - NGX_HTTP_MEMCACHED_END;
 
     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");
     }
 
-    ctx->rest = u->length - (b->last - b->pos);
+    ctx->rest -= b->last - last;
     b->last = last;
     cl->buf->last = last;
     u->length = ctx->rest;
@@ -520,8 +525,6 @@ ngx_http_memcached_create_loc_conf(ngx_c
      *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
-     *
-     *     conf->index = 0;
      */
 
     conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
@@ -544,6 +547,8 @@ ngx_http_memcached_create_loc_conf(ngx_c
     conf->upstream.pass_request_headers = 0;
     conf->upstream.pass_request_body = 0;
 
+    conf->index = NGX_CONF_UNSET;
+
     return conf;
 }
 
@@ -578,6 +583,15 @@ ngx_http_memcached_merge_loc_conf(ngx_co
                                        |NGX_HTTP_UPSTREAM_FT_OFF;
     }
 
+    if (conf->upstream.upstream == NULL) {
+        conf->upstream.upstream = prev->upstream.upstream;
+        conf->upstream.schema = prev->upstream.schema;
+    }
+
+    if (conf->index == NGX_CONF_UNSET) {
+        conf->index = prev->index;
+    }
+
     return NGX_CONF_OK;
 }
 
@@ -614,8 +628,6 @@ ngx_http_memcached_pass(ngx_conf_t *cf, 
 
     clcf->handler = ngx_http_memcached_handler;
 
-    lcf->upstream.location = clcf->name;
-
     if (clcf->name.data[clcf->name.len - 1] == '/') {
         clcf->auto_redirect = 1;
     }
--- a/src/http/modules/ngx_http_not_modified_filter_module.c
+++ b/src/http/modules/ngx_http_not_modified_filter_module.c
@@ -70,7 +70,7 @@ ngx_int_t ngx_http_not_modified_header_f
      * I think that the equality of the dates is correcter
      */
 
-    if (ims != NGX_ERROR && ims == r->headers_out.last_modified_time) {
+    if (ims == r->headers_out.last_modified_time) {
         r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
         r->headers_out.content_type.len = 0;
         ngx_http_clear_content_length(r);
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -33,6 +33,13 @@ struct ngx_http_proxy_redirect_s {
 
 
 typedef struct {
+    ngx_str_t                      host_header;
+    ngx_str_t                      port;
+    ngx_str_t                      uri;
+} ngx_http_proxy_vars_t;
+
+
+typedef struct {
     ngx_http_upstream_conf_t       upstream;
 
     ngx_array_t                   *flushes;
@@ -45,13 +52,18 @@ typedef struct {
     ngx_array_t                   *headers_source;
     ngx_array_t                   *headers_names;
 
+    ngx_array_t                   *proxy_lengths;
+    ngx_array_t                   *proxy_values;
+
     ngx_array_t                   *redirects;
 
     ngx_str_t                      body_source;
 
     ngx_str_t                      method;
-    ngx_str_t                      host_header;
-    ngx_str_t                      port;
+    ngx_str_t                      location;
+    ngx_str_t                      url;
+
+    ngx_http_proxy_vars_t          vars;
 
     ngx_flag_t                     redirect;
 
@@ -66,6 +78,8 @@ typedef struct {
     u_char                        *status_start;
     u_char                        *status_end;
 
+    ngx_http_proxy_vars_t          vars;
+
     size_t                         internal_body_length;
 } ngx_http_proxy_ctx_t;
 
@@ -73,11 +87,13 @@ typedef struct {
 #define NGX_HTTP_PROXY_PARSE_NO_HEADER  20
 
 
+static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r,
+    ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf);
 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_parse_status_line(ngx_http_request_t *r,
-    ngx_http_proxy_ctx_t *p);
+    ngx_http_proxy_ctx_t *ctx);
 static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r);
 static void ngx_http_proxy_abort_request(ngx_http_request_t *r);
 static void ngx_http_proxy_finalize_request(ngx_http_request_t *r,
@@ -115,18 +131,17 @@ static char *ngx_http_proxy_upstream_max
 static char *ngx_http_proxy_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
     ngx_command_t *cmd, void *conf);
 
+#if (NGX_HTTP_SSL)
+static ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf,
+    ngx_http_proxy_loc_conf_t *plcf);
+#endif
+static ngx_int_t ngx_http_proxy_set_vars(ngx_pool_t *pool, ngx_url_t *u,
+    ngx_http_proxy_vars_t *v);
+
 
 static ngx_conf_post_t  ngx_http_proxy_lowat_post =
     { ngx_http_proxy_lowat_check };
 
-static ngx_conf_deprecated_t  ngx_conf_deprecated_proxy_header_buffer_size = {
-    ngx_conf_deprecated, "proxy_header_buffer_size", "proxy_buffer_size"
-};
-
-static ngx_conf_deprecated_t  ngx_conf_deprecated_proxy_redirect_errors = {
-    ngx_conf_deprecated, "proxy_redirect_errors", "proxy_intercept_errors"
-};
-
 
 static ngx_conf_bitmask_t  ngx_http_proxy_next_upstream_masks[] = {
     { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
@@ -212,13 +227,6 @@ static ngx_command_t  ngx_http_proxy_com
       offsetof(ngx_http_proxy_loc_conf_t, upstream.intercept_errors),
       NULL },
 
-    { ngx_string("proxy_redirect_errors"),
-      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.intercept_errors),
-      &ngx_conf_deprecated_proxy_redirect_errors },
-
     { ngx_string("proxy_set_header"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
       ngx_conf_set_keyval_slot,
@@ -275,13 +283,6 @@ static ngx_command_t  ngx_http_proxy_com
       offsetof(ngx_http_proxy_loc_conf_t, upstream.buffer_size),
       NULL },
 
-    { ngx_string("proxy_header_buffer_size"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_size_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_proxy_loc_conf_t, upstream.buffer_size),
-      &ngx_conf_deprecated_proxy_header_buffer_size },
-
     { ngx_string("proxy_read_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_msec_slot,
@@ -401,6 +402,7 @@ static ngx_keyval_t  ngx_http_proxy_head
     { ngx_string("Host"), ngx_string("$proxy_host") },
     { ngx_string("Connection"), ngx_string("close") },
     { ngx_string("Keep-Alive"), ngx_string("") },
+    { ngx_string("Expect"), ngx_string("") },
     { ngx_null_string, ngx_null_string }
 };
 
@@ -412,7 +414,8 @@ static ngx_str_t  ngx_http_proxy_hide_he
     ngx_string("X-Accel-Expires"),
     ngx_string("X-Accel-Redirect"),
     ngx_string("X-Accel-Limit-Rate"),
-    ngx_string("X-Accel-Buffer"),
+    ngx_string("X-Accel-Buffering"),
+    ngx_string("X-Accel-Charset"),
     ngx_null_string
 };
 
@@ -420,10 +423,10 @@ static ngx_str_t  ngx_http_proxy_hide_he
 static ngx_http_variable_t  ngx_http_proxy_vars[] = {
 
     { ngx_string("proxy_host"), NULL, ngx_http_proxy_host_variable, 0,
-      NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOHASH, 0 },
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
 
     { ngx_string("proxy_port"), NULL, ngx_http_proxy_port_variable, 0,
-      NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOHASH, 0 },
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
 
     { ngx_string("proxy_add_x_forwarded_for"), NULL,
       ngx_http_proxy_add_x_forwarded_for_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
@@ -444,15 +447,38 @@ ngx_http_proxy_handler(ngx_http_request_
 {
     ngx_int_t                   rc;
     ngx_http_upstream_t        *u;
+    ngx_http_proxy_ctx_t       *ctx;
     ngx_http_proxy_loc_conf_t  *plcf;
 
-    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
-
     u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
     if (u == NULL) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
+    r->upstream = u;
+
+    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t));
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_http_set_ctx(r, ctx, ngx_http_proxy_module);
+
+    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
+
+    if (plcf->proxy_lengths == 0) {
+        ctx->vars = plcf->vars;
+        u->schema = plcf->upstream.schema;
+#if (NGX_HTTP_SSL)
+        u->ssl = (plcf->upstream.ssl != NULL);
+#endif
+
+    } else {
+        if (ngx_http_proxy_eval(r, ctx, plcf) != NGX_OK) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+
     u->peer.log = r->connection->log;
     u->peer.log_error = NGX_ERROR_ERR;
 #if (NGX_THREADS)
@@ -484,8 +510,6 @@ ngx_http_proxy_handler(ngx_http_request_
 
     u->accel = 1;
 
-    r->upstream = u;
-
     rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
 
     if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
@@ -497,6 +521,82 @@ ngx_http_proxy_handler(ngx_http_request_
 
 
 static ngx_int_t
+ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
+    ngx_http_proxy_loc_conf_t *plcf)
+{
+    size_t     add;
+    u_short    port;
+    ngx_str_t  proxy;
+    ngx_url_t  u;
+
+    if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0,
+                            plcf->proxy_values->elts)
+        == NULL)
+    {
+        return NGX_ERROR;
+    }
+
+    if (ngx_strncasecmp(proxy.data, (u_char *) "http://", 7) == 0) {
+
+        add = 7;
+        port = 80;
+
+#if (NGX_HTTP_SSL)
+
+    } else if (ngx_strncasecmp(proxy.data, (u_char *) "https://", 8) == 0) {
+
+        add = 8;
+        port = 443;
+        r->upstream->ssl = 1;
+
+#endif
+
+    } else {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "invalid URL prefix in \"%V\"", &proxy);
+        return NGX_ERROR;
+    }
+
+    r->upstream->schema.len = add;
+    r->upstream->schema.data = proxy.data;
+
+    ngx_memzero(&u, sizeof(ngx_url_t));
+
+    u.url.len = proxy.len - add;
+    u.url.data = proxy.data + add;
+    u.default_port = port;
+    u.uri_part = 1;
+    u.no_resolve = 1;
+
+    if (ngx_parse_url(r->pool, &u) != NGX_OK) {
+        if (u.err) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "%s in upstream \"%V\"", u.err, &u.url);
+        }
+
+        return NGX_ERROR;
+    }
+
+    if (ngx_http_proxy_set_vars(r->pool, &u, &ctx->vars) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    r->upstream->resolved = ngx_pcalloc(r->pool,
+                                        sizeof(ngx_http_upstream_resolved_t));
+    if (r->upstream->resolved == NULL) {
+        return NGX_ERROR;
+    }
+
+    r->upstream->resolved->host = u.host;
+    r->upstream->resolved->port = (in_port_t) (u.no_port ? u.default_port:
+                                                           u.port);
+    r->upstream->resolved->default_port = u.default_port;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_proxy_create_request(ngx_http_request_t *r)
 {
     size_t                        len, loc_len, body_len;
@@ -508,7 +608,7 @@ ngx_http_proxy_create_request(ngx_http_r
     ngx_list_part_t              *part;
     ngx_table_elt_t              *header;
     ngx_http_upstream_t          *u;
-    ngx_http_proxy_ctx_t         *p;
+    ngx_http_proxy_ctx_t         *ctx;
     ngx_http_script_code_pt       code;
     ngx_http_script_engine_t      e, le;
     ngx_http_proxy_loc_conf_t    *plcf;
@@ -518,15 +618,6 @@ ngx_http_proxy_create_request(ngx_http_r
 
     plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
 
-    p = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t));
-    if (p == NULL) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-    ngx_http_set_ctx(r, p, ngx_http_proxy_module);
-
-    len = sizeof(ngx_http_proxy_version) - 1 + sizeof(CRLF) - 1;
-
     if (u->method.len) {
         /* HEAD was changed to GET to cache response */
         method = u->method;
@@ -540,28 +631,36 @@ ngx_http_proxy_create_request(ngx_http_r
         method.len++;
     }
 
-    len += method.len + u->conf->uri.len;
+    len = method.len + sizeof(ngx_http_proxy_version) - 1 + sizeof(CRLF) - 1;
 
     escape = 0;
-
-    loc_len = (r->valid_location && u->conf->uri.len) ? u->conf->location.len:
-                                                        0;
-
-    if (u->conf->uri.len == 0 && r->valid_unparsed_uri && r == r->main) {
+    loc_len = 0;
+    unparsed_uri = 0;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+    if (plcf->proxy_lengths) {
+        len += ctx->vars.uri.len;
+
+    } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main)
+    {
         unparsed_uri = 1;
         len += r->unparsed_uri.len;
 
     } else {
-        unparsed_uri = 0;
+        loc_len = (r->valid_location && ctx->vars.uri.len) ?
+                      plcf->location.len : 0;
+
         if (r->quoted_uri || r->internal) {
             escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
                                         r->uri.len - loc_len, NGX_ESCAPE_URI);
         }
 
-        len += r->uri.len - loc_len + escape + sizeof("?") - 1 + r->args.len;
+        len += ctx->vars.uri.len + r->uri.len - loc_len + escape
+               + sizeof("?") - 1 + r->args.len;
     }
 
-    ngx_http_script_flush_no_cachable_variables(r, plcf->flushes);
+    ngx_http_script_flush_no_cacheable_variables(r, plcf->flushes);
 
     if (plcf->body_set_len) {
         le.ip = plcf->body_set_len->elts;
@@ -574,7 +673,7 @@ ngx_http_proxy_create_request(ngx_http_r
             body_len += lcode(&le);
         }
 
-        p->internal_body_length = body_len;
+        ctx->internal_body_length = body_len;
         len += body_len;
     }
 
@@ -638,12 +737,15 @@ ngx_http_proxy_create_request(ngx_http_r
 
     u->uri.data = b->last;
 
-    if (unparsed_uri) {
+    if (plcf->proxy_lengths) {
+        b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
+
+    } else if (unparsed_uri) {
         b->last = ngx_copy(b->last, r->unparsed_uri.data, r->unparsed_uri.len);
 
     } else {
         if (r->valid_location) {
-            b->last = ngx_copy(b->last, u->conf->uri.data, u->conf->uri.len);
+            b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
         }
 
         if (escape) {
@@ -759,16 +861,9 @@ ngx_http_proxy_create_request(ngx_http_r
         b->last = e.pos;
     }
 
-#if (NGX_DEBUG)
-    {
-    ngx_str_t  s;
-
-    s.len = b->last - b->pos;
-    s.data = b->pos;
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http proxy header:\n\"%V\"", &s);
-    }
-#endif
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http proxy header:\n\"%*s\"",
+                   (size_t) (b->last - b->pos), b->pos);
 
     if (plcf->body_set == NULL && plcf->upstream.pass_request_body) {
 
@@ -809,18 +904,18 @@ ngx_http_proxy_create_request(ngx_http_r
 static ngx_int_t
 ngx_http_proxy_reinit_request(ngx_http_request_t *r)
 {
-    ngx_http_proxy_ctx_t  *p;
-
-    p = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
-
-    if (p == NULL) {
+    ngx_http_proxy_ctx_t  *ctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+    if (ctx == NULL) {
         return NGX_OK;
     }
 
-    p->status = 0;
-    p->status_count = 0;
-    p->status_start = NULL;
-    p->status_end = NULL;
+    ctx->status = 0;
+    ctx->status_count = 0;
+    ctx->status_start = NULL;
+    ctx->status_end = NULL;
 
     r->upstream->process_header = ngx_http_proxy_process_status_line;
 
@@ -833,15 +928,15 @@ ngx_http_proxy_process_status_line(ngx_h
 {
     ngx_int_t              rc;
     ngx_http_upstream_t   *u;
-    ngx_http_proxy_ctx_t  *p;
-
-    p = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
-
-    if (p == NULL) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    ngx_http_proxy_ctx_t  *ctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+    if (ctx == NULL) {
+        return NGX_ERROR;
     }
 
-    rc = ngx_http_proxy_parse_status_line(r, p);
+    rc = ngx_http_proxy_parse_status_line(r, ctx);
 
     if (rc == NGX_AGAIN) {
         return rc;
@@ -860,22 +955,23 @@ ngx_http_proxy_process_status_line(ngx_h
 #endif
 
         r->http_version = NGX_HTTP_VERSION_9;
-        p->status = NGX_HTTP_OK;
+        u->headers_in.status_n = NGX_HTTP_OK;
+        u->state->status = NGX_HTTP_OK;
 
         return NGX_OK;
     }
 
-    u->headers_in.status_n = p->status;
-    u->state->status = p->status;
-
-    u->headers_in.status_line.len = p->status_end - p->status_start;
-    u->headers_in.status_line.data = ngx_palloc(r->pool,
-                                                u->headers_in.status_line.len);
+    u->headers_in.status_n = ctx->status;
+    u->state->status = ctx->status;
+
+    u->headers_in.status_line.len = ctx->status_end - ctx->status_start;
+    u->headers_in.status_line.data = ngx_pnalloc(r->pool,
+                                                 u->headers_in.status_line.len);
     if (u->headers_in.status_line.data == NULL) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        return NGX_ERROR;
     }
 
-    ngx_memcpy(u->headers_in.status_line.data, p->status_start,
+    ngx_memcpy(u->headers_in.status_line.data, ctx->status_start,
                u->headers_in.status_line.len);
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -889,10 +985,11 @@ ngx_http_proxy_process_status_line(ngx_h
 
 
 static ngx_int_t
-ngx_http_proxy_parse_status_line(ngx_http_request_t *r, ngx_http_proxy_ctx_t *p)
+ngx_http_proxy_parse_status_line(ngx_http_request_t *r,
+    ngx_http_proxy_ctx_t *ctx)
 {
     u_char                ch;
-    u_char               *pos;
+    u_char               *p;
     ngx_http_upstream_t  *u;
     enum  {
         sw_start = 0,
@@ -914,8 +1011,8 @@ ngx_http_proxy_parse_status_line(ngx_htt
 
     state = r->state;
 
-    for (pos = u->buffer.pos; pos < u->buffer.last; pos++) {
-        ch = *pos;
+    for (p = u->buffer.pos; p < u->buffer.last; p++) {
+        ch = *p;
 
         switch (state) {
 
@@ -1024,11 +1121,11 @@ ngx_http_proxy_parse_status_line(ngx_htt
                 return NGX_HTTP_PROXY_PARSE_NO_HEADER;
             }
 
-            p->status = p->status * 10 + ch - '0';
-
-            if (++p->status_count == 3) {
+            ctx->status = ctx->status * 10 + ch - '0';
+
+            if (++ctx->status_count == 3) {
                 state = sw_space_after_status;
-                p->status_start = pos - 2;
+                ctx->status_start = p - 2;
             }
 
             break;
@@ -1066,7 +1163,7 @@ ngx_http_proxy_parse_status_line(ngx_htt
 
         /* end of status line */
         case sw_almost_done:
-            p->status_end = pos - 1;
+            ctx->status_end = p - 1;
             switch (ch) {
             case LF:
                 goto done;
@@ -1076,17 +1173,17 @@ ngx_http_proxy_parse_status_line(ngx_htt
         }
     }
 
-    u->buffer.pos = pos;
+    u->buffer.pos = p;
     r->state = state;
 
     return NGX_AGAIN;
 
 done:
 
-    u->buffer.pos = pos + 1;
-
-    if (p->status_end == NULL) {
-        p->status_end = pos;
+    u->buffer.pos = p + 1;
+
+    if (ctx->status_end == NULL) {
+        ctx->status_end = p;
     }
 
     r->state = sw_start;
@@ -1099,14 +1196,13 @@ static ngx_int_t
 ngx_http_proxy_process_header(ngx_http_request_t *r)
 {
     ngx_int_t                       rc;
-    ngx_uint_t                      i;
     ngx_table_elt_t                *h;
     ngx_http_upstream_header_t     *hh;
     ngx_http_upstream_main_conf_t  *umcf;
 
     umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
 
-    for ( ;;  ) {
+    for ( ;; ) {
 
         rc = ngx_http_parse_header_line(r, &r->upstream->buffer);
 
@@ -1116,7 +1212,7 @@ ngx_http_proxy_process_header(ngx_http_r
 
             h = ngx_list_push(&r->upstream->headers_in.headers);
             if (h == NULL) {
-                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                return NGX_ERROR;
             }
 
             h->hash = r->header_hash;
@@ -1124,10 +1220,10 @@ ngx_http_proxy_process_header(ngx_http_r
             h->key.len = r->header_name_end - r->header_name_start;
             h->value.len = r->header_end - r->header_start;
 
-            h->key.data = ngx_palloc(r->pool,
+            h->key.data = ngx_pnalloc(r->pool,
                                h->key.len + 1 + h->value.len + 1 + h->key.len);
             if (h->key.data == NULL) {
-                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                return NGX_ERROR;
             }
 
             h->value.data = h->key.data + h->key.len + 1;
@@ -1140,16 +1236,14 @@ ngx_http_proxy_process_header(ngx_http_r
                 ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
 
             } else {
-                for (i = 0; i < h->key.len; i++) {
-                    h->lowcase_key[i] = ngx_tolower(h->key.data[i]);
-                }
+                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
             }
 
             hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
                                h->lowcase_key, h->key.len);
 
             if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
-                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                return NGX_ERROR;
             }
 
             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -1174,7 +1268,7 @@ ngx_http_proxy_process_header(ngx_http_r
             if (r->upstream->headers_in.server == NULL) {
                 h = ngx_list_push(&r->upstream->headers_in.headers);
                 if (h == NULL) {
-                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                    return NGX_ERROR;
                 }
 
                 h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(
@@ -1190,7 +1284,7 @@ ngx_http_proxy_process_header(ngx_http_r
             if (r->upstream->headers_in.date == NULL) {
                 h = ngx_list_push(&r->upstream->headers_in.headers);
                 if (h == NULL) {
-                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                    return NGX_ERROR;
                 }
 
                 h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e');
@@ -1243,15 +1337,20 @@ static ngx_int_t
 ngx_http_proxy_host_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
-    ngx_http_proxy_loc_conf_t  *plcf;
-
-    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
-
-    v->len = plcf->host_header.len;
+    ngx_http_proxy_ctx_t  *ctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+    if (ctx == NULL) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->len = ctx->vars.host_header.len;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
-    v->data = plcf->host_header.data;
+    v->data = ctx->vars.host_header.data;
 
     return NGX_OK;
 }
@@ -1261,15 +1360,20 @@ static ngx_int_t
 ngx_http_proxy_port_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
-    ngx_http_proxy_loc_conf_t  *plcf;
-
-    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
-
-    v->len = plcf->port.len;
+    ngx_http_proxy_ctx_t  *ctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+    if (ctx == NULL) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->len = ctx->vars.port.len;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
-    v->data = plcf->port.data;
+    v->data = ctx->vars.port.data;
 
     return NGX_OK;
 }
@@ -1282,7 +1386,7 @@ ngx_http_proxy_add_x_forwarded_for_varia
     u_char  *p;
 
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
 
     if (r->headers_in.x_forwarded_for == NULL) {
@@ -1294,7 +1398,7 @@ ngx_http_proxy_add_x_forwarded_for_varia
     v->len = r->headers_in.x_forwarded_for->value.len
              + sizeof(", ") - 1 + r->connection->addr_text.len;
 
-    p = ngx_palloc(r->pool, v->len);
+    p = ngx_pnalloc(r->pool, v->len);
     if (p == NULL) {
         return NGX_ERROR;
     }
@@ -1316,26 +1420,26 @@ static ngx_int_t
 ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
-    ngx_http_proxy_ctx_t  *p;
-
-    p = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
-
-    if (p == NULL) {
+    ngx_http_proxy_ctx_t  *ctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+    if (ctx == NULL) {
         v->not_found = 1;
         return NGX_OK;
     }
 
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
 
-    v->data = ngx_palloc(r->connection->pool, NGX_SIZE_T_LEN);
+    v->data = ngx_pnalloc(r->connection->pool, NGX_SIZE_T_LEN);
 
     if (v->data == NULL) {
         return NGX_ERROR;
     }
 
-    v->len = ngx_sprintf(v->data, "%uz", p->internal_body_length) - v->data;
+    v->len = ngx_sprintf(v->data, "%uz", ctx->internal_body_length) - v->data;
 
     return NGX_OK;
 }
@@ -1386,7 +1490,7 @@ ngx_http_proxy_rewrite_redirect_text(ngx
 
     len = prefix + pr->replacement.text.len + h->value.len - pr->redirect.len;
 
-    data = ngx_palloc(r->pool, len);
+    data = ngx_pnalloc(r->pool, len);
     if (data == NULL) {
         return NGX_ERROR;
     }
@@ -1438,7 +1542,7 @@ ngx_http_proxy_rewrite_redirect_vars(ngx
         len += lcode(&e);
     }
 
-    data = ngx_palloc(r->pool, len);
+    data = ngx_pnalloc(r->pool, len);
     if (data == NULL) {
         return NGX_ERROR;
     }
@@ -1501,8 +1605,6 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
      *     conf->upstream.next_upstream = 0;
      *     conf->upstream.temp_path = NULL;
      *     conf->upstream.hide_headers_hash = { NULL, 0 };
-     *     conf->upstream.hide_headers = NULL;
-     *     conf->upstream.pass_headers = NULL;
      *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
@@ -1539,6 +1641,9 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
     conf->upstream.pass_request_headers = NGX_CONF_UNSET;
     conf->upstream.pass_request_body = NGX_CONF_UNSET;
 
+    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
+    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
+
     conf->upstream.intercept_errors = NGX_CONF_UNSET;
 
     /* "proxy_cyclic_temp_file" is disabled */
@@ -1563,9 +1668,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     u_char                       *p;
     size_t                        size;
     uintptr_t                    *code;
-    ngx_str_t                    *header;
-    ngx_uint_t                    i, j;
-    ngx_array_t                   hide_headers;
+    ngx_uint_t                    i;
     ngx_keyval_t                 *src, *s, *h;
     ngx_hash_key_t               *hk;
     ngx_hash_init_t               hash;
@@ -1739,7 +1842,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
             conf->redirects = prev->redirects;
         }
 
-        if (conf->redirects == NULL && conf->upstream.url.data) {
+        if (conf->redirects == NULL && conf->url.data) {
 
             conf->redirects = ngx_array_create(cf->pool, 1,
                                             sizeof(ngx_http_proxy_redirect_t));
@@ -1753,10 +1856,10 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
             }
 
             pr->handler = ngx_http_proxy_rewrite_redirect_text;
-            pr->redirect = conf->upstream.url;
-
-            if (conf->upstream.uri.len) {
-                pr->replacement.text = conf->upstream.location;
+            pr->redirect = conf->url;
+
+            if (conf->vars.uri.len) {
+                pr->replacement.text = conf->location;
 
             } else {
                 pr->replacement.text.len = 0;
@@ -1765,6 +1868,12 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
         }
     }
 
+    /* STUB */
+    if (prev->proxy_lengths) {
+        conf->proxy_lengths = prev->proxy_lengths;
+        conf->proxy_values = prev->proxy_values;
+    }
+
     ngx_conf_merge_uint_value(conf->headers_hash_max_size,
                               prev->headers_hash_max_size, 512);
 
@@ -1774,112 +1883,22 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     conf->headers_hash_bucket_size = ngx_align(conf->headers_hash_bucket_size,
                                                ngx_cacheline_size);
 
-    if (conf->upstream.hide_headers == NULL
-        && conf->upstream.pass_headers == NULL)
-    {
-        conf->upstream.hide_headers = prev->upstream.hide_headers;
-        conf->upstream.pass_headers = prev->upstream.pass_headers;
-        conf->upstream.hide_headers_hash = prev->upstream.hide_headers_hash;
-
-        if (conf->upstream.hide_headers_hash.buckets) {
-            goto peers;
-        }
-
-    } else {
-        if (conf->upstream.hide_headers == NULL) {
-            conf->upstream.hide_headers = prev->upstream.hide_headers;
-        }
-
-        if (conf->upstream.pass_headers == NULL) {
-            conf->upstream.pass_headers = prev->upstream.pass_headers;
-        }
-    }
-
-    if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
+    hash.max_size = conf->headers_hash_max_size;
+    hash.bucket_size = conf->headers_hash_bucket_size;
+    hash.name = "proxy_headers_hash";
+
+    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
+                                            &prev->upstream,
+                                            ngx_http_proxy_hide_headers, &hash)
         != NGX_OK)
     {
         return NGX_CONF_ERROR;
     }
 
-    for (header = ngx_http_proxy_hide_headers; header->len; header++) {
-        hk = ngx_array_push(&hide_headers);
-        if (hk == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        hk->key = *header;
-        hk->key_hash = ngx_hash_key_lc(header->data, header->len);
-        hk->value = (void *) 1;
-    }
-
-    if (conf->upstream.hide_headers) {
-
-        header = conf->upstream.hide_headers->elts;
-
-        for (i = 0; i < conf->upstream.hide_headers->nelts; i++) {
-
-            hk = hide_headers.elts;
-
-            for (j = 0; j < hide_headers.nelts; j++) {
-                if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) {
-                    goto exist;
-                }
-            }
-
-            hk = ngx_array_push(&hide_headers);
-            if (hk == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            hk->key = header[i];
-            hk->key_hash = ngx_hash_key_lc(header[i].data, header[i].len);
-            hk->value = (void *) 1;
-
-        exist:
-
-            continue;
-        }
-    }
-
-    if (conf->upstream.pass_headers) {
-
-        hk = hide_headers.elts;
-        header = conf->upstream.pass_headers->elts;
-
-        for (i = 0; i < conf->upstream.pass_headers->nelts; i++) {
-            for (j = 0; j < hide_headers.nelts; j++) {
-
-                if (hk[j].key.data == NULL) {
-                    continue;
-                }
-
-                if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) {
-                    hk[j].key.data = NULL;
-                    break;
-                }
-            }
-        }
-    }
-
-    hash.hash = &conf->upstream.hide_headers_hash;
-    hash.key = ngx_hash_key_lc;
-    hash.max_size = conf->headers_hash_max_size;
-    hash.bucket_size = conf->headers_hash_bucket_size;
-    hash.name = "proxy_headers_hash";
-    hash.pool = cf->pool;
-    hash.temp_pool = NULL;
-
-    if (ngx_hash_init(&hash, hide_headers.elts, hide_headers.nelts) != NGX_OK) {
-        return NGX_CONF_ERROR;
-    }
-
-peers:
-
     if (conf->upstream.upstream == NULL) {
         conf->upstream.upstream = prev->upstream.upstream;
 
-        conf->host_header = prev->host_header;
-        conf->port = prev->port;
+        conf->vars = prev->vars;
         conf->upstream.schema = prev->upstream.schema;
     }
 
@@ -2155,24 +2174,53 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
 {
     ngx_http_proxy_loc_conf_t *plcf = conf;
 
-    u_char                    *p;
-    size_t                     add;
-    u_short                    port;
-    ngx_str_t                 *value, *url;
-    ngx_url_t                  u;
-    ngx_http_core_loc_conf_t  *clcf;
-#if (NGX_HTTP_SSL)
-    ngx_pool_cleanup_t        *cln;
-#endif
+    size_t                      add;
+    u_short                     port;
+    ngx_str_t                  *value, *url;
+    ngx_url_t                   u;
+    ngx_uint_t                  n;
+    ngx_http_core_loc_conf_t   *clcf;
+    ngx_http_script_compile_t   sc;
 
     if (plcf->upstream.schema.len) {
         return "is duplicate";
     }
 
+    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+
     value = cf->args->elts;
 
     url = &value[1];
 
+    n = ngx_http_script_variables_count(url);
+
+    if (n) {
+
+        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+        sc.cf = cf;
+        sc.source = url;
+        sc.lengths = &plcf->proxy_lengths;
+        sc.values = &plcf->proxy_values;
+        sc.variables = n;
+        sc.complete_lengths = 1;
+        sc.complete_values = 1;
+
+        if (ngx_http_script_compile(&sc) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+#if (NGX_HTTP_SSL)
+        if (ngx_http_proxy_set_ssl(cf, plcf) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+#endif
+
+        clcf->handler = ngx_http_proxy_handler;
+
+        return NGX_CONF_OK;
+    }
+
     if (ngx_strncasecmp(url->data, (u_char *) "http://", 7) == 0) {
         add = 7;
         port = 80;
@@ -2180,32 +2228,12 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
     } else if (ngx_strncasecmp(url->data, (u_char *) "https://", 8) == 0) {
 
 #if (NGX_HTTP_SSL)
+        if (ngx_http_proxy_set_ssl(cf, plcf) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
 
         add = 8;
         port = 443;
-
-        plcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
-        if (plcf->upstream.ssl == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        plcf->upstream.ssl->log = cf->log;
-
-        if (ngx_ssl_create(plcf->upstream.ssl,
-                           NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1, NULL)
-            != NGX_OK)
-        {
-            return NGX_CONF_ERROR;
-        }
-
-        cln = ngx_pool_cleanup_add(cf->pool, 0);
-        if (cln == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        cln->handler = ngx_ssl_cleanup_ctx;
-        cln->data = plcf->upstream.ssl;
-
 #else
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "https protocol requires SSL support");
@@ -2230,59 +2258,23 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
         return NGX_CONF_ERROR;
     }
 
-    if (!u.unix_socket) {
-        if (u.no_port || u.port == port) {
-            plcf->host_header = u.host;
-
-            if (port == 80) {
-                plcf->port.len = sizeof("80") - 1;
-                plcf->port.data = (u_char *) "80";
-
-            } else {
-                plcf->port.len = sizeof("443") - 1;
-                plcf->port.data = (u_char *) "443";
-            }
-
-        } else {
-            p = ngx_palloc(cf->pool, u.host.len + sizeof(":65536") - 1);
-            if (p == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            plcf->host_header.len = ngx_sprintf(p, "%V:%d", &u.host, u.port)
-                                        - p;
-            plcf->host_header.data = p;
-
-            plcf->port.len = plcf->host_header.len -  u.host.len - 1;
-            plcf->port.data = p + u.host.len + 1;
-        }
-
-
-    } else {
-        plcf->host_header.len = sizeof("localhost") - 1;
-        plcf->host_header.data = (u_char *) "localhost";
-        plcf->port.len = 0;
-        plcf->port.data = (u_char *) "";
+    if (ngx_http_proxy_set_vars(cf->pool, &u, &plcf->vars) != NGX_OK) {
+        return NGX_CONF_ERROR;
     }
 
-    plcf->upstream.uri = u.uri;
-
     plcf->upstream.schema.len = add;
     plcf->upstream.schema.data = url->data;
-
-    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+    plcf->location = clcf->name;
 
     clcf->handler = ngx_http_proxy_handler;
 
-    plcf->upstream.location = clcf->name;
-
     if (clcf->named
 #if (NGX_PCRE)
         || clcf->regex
 #endif
         || clcf->noname)
     {
-        if (plcf->upstream.uri.len) {
+        if (plcf->vars.uri.len) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "\"proxy_pass\" may not have URI part in "
                                "location given by regular expression, "
@@ -2292,10 +2284,10 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
             return NGX_CONF_ERROR;
         }
 
-        plcf->upstream.location.len = 0;
+        plcf->location.len = 0;
     }
 
-    plcf->upstream.url = *url;
+    plcf->url = *url;
 
     if (clcf->name.data[clcf->name.len - 1] == '/') {
         clcf->auto_redirect = 1;
@@ -2341,7 +2333,7 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, 
     }
 
     if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "default") == 0) {
-        if (plcf->upstream.url.data == NULL) {
+        if (plcf->url.data == NULL) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "\"proxy_rewrite_location default\" must go "
                                "after the \"proxy_pass\" directive");
@@ -2349,10 +2341,10 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, 
         }
 
         pr->handler = ngx_http_proxy_rewrite_redirect_text;
-        pr->redirect = plcf->upstream.url;
-
-        if (plcf->upstream.uri.len) {
-            pr->replacement.text = plcf->upstream.location;
+        pr->redirect = plcf->url;
+
+        if (plcf->vars.uri.len) {
+            pr->replacement.text = plcf->location;
 
         } else {
             pr->replacement.text.len = 0;
@@ -2429,7 +2421,7 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx
     sc.source = &value[1];
     sc.lengths = &plcf->upstream.store_lengths;
     sc.values = &plcf->upstream.store_values;
-    sc.variables = ngx_http_script_variables_count(&value[1]);;
+    sc.variables = ngx_http_script_variables_count(&value[1]);
     sc.complete_lengths = 1;
     sc.complete_values = 1;
 
@@ -2494,3 +2486,74 @@ ngx_http_proxy_upstream_fail_timeout_uns
 
     return NGX_CONF_ERROR;
 }
+
+
+#if (NGX_HTTP_SSL)
+
+static ngx_int_t
+ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)
+{
+    ngx_pool_cleanup_t  *cln;
+
+    plcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
+    if (plcf->upstream.ssl == NULL) {
+        return NGX_ERROR;
+    }
+
+    plcf->upstream.ssl->log = cf->log;
+
+    if (ngx_ssl_create(plcf->upstream.ssl,
+                       NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1, NULL)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        return NGX_ERROR;
+    }
+
+    cln->handler = ngx_ssl_cleanup_ctx;
+    cln->data = plcf->upstream.ssl;
+
+    return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_proxy_set_vars(ngx_pool_t *pool, ngx_url_t *u,
+    ngx_http_proxy_vars_t *v)
+{
+    if (!u->unix_socket) {
+        if (u->no_port || u->port == u->default_port) {
+            v->host_header = u->host;
+
+            if (u->default_port == 80) {
+                v->port.len = sizeof("80") - 1;
+                v->port.data = (u_char *) "80";
+
+            } else {
+                v->port.len = sizeof("443") - 1;
+                v->port.data = (u_char *) "443";
+            }
+
+        } else {
+            v->host_header.len = u->host.len + 1 + u->port_text.len;
+            v->host_header.data = u->host.data;
+            v->port = u->port_text;
+        }
+
+    } else {
+        v->host_header.len = sizeof("localhost") - 1;
+        v->host_header.data = (u_char *) "localhost";
+        v->port.len = 0;
+        v->port.data = (u_char *) "";
+    }
+
+    v->uri = u->uri;
+
+    return NGX_OK;
+}
--- a/src/http/modules/ngx_http_range_filter_module.c
+++ b/src/http/modules/ngx_http_range_filter_module.c
@@ -58,6 +58,20 @@ typedef struct {
 } ngx_http_range_filter_ctx_t;
 
 
+ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx);
+static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx);
+static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx);
+static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r);
+static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
+static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
+static ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
+
 static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf);
 static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf);
 
@@ -131,14 +145,8 @@ static ngx_http_output_body_filter_pt   
 static ngx_int_t
 ngx_http_range_header_filter(ngx_http_request_t *r)
 {
-    u_char                       *p;
-    size_t                        len;
-    off_t                         start, end;
+    time_t                        if_range;
     ngx_int_t                     rc;
-    ngx_uint_t                    suffix, i;
-    ngx_atomic_uint_t             boundary;
-    ngx_table_elt_t              *content_range;
-    ngx_http_range_t             *range;
     ngx_http_range_filter_ctx_t  *ctx;
 
     if (r->http_version < NGX_HTTP_VERSION_10
@@ -156,18 +164,21 @@ ngx_http_range_header_filter(ngx_http_re
                            (u_char *) "bytes=", 6)
            != 0)
     {
-        r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
-        if (r->headers_out.accept_ranges == NULL) {
-            return NGX_ERROR;
-        }
+        goto next_filter;
+    }
+
+    if (r->headers_in.if_range && r->headers_out.last_modified_time != -1) {
+
+        if_range = ngx_http_parse_time(r->headers_in.if_range->value.data,
+                                       r->headers_in.if_range->value.len);
 
-        r->headers_out.accept_ranges->hash = 1;
-        r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1;
-        r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges";
-        r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1;
-        r->headers_out.accept_ranges->value.data = (u_char *) "bytes";
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http ir:%d lm:%d",
+                       if_range, r->headers_out.last_modified_time);
 
-        return ngx_http_next_header_filter(r);
+        if (if_range != r->headers_out.last_modified_time) {
+            goto next_filter;
+        }
     }
 
     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
@@ -181,8 +192,54 @@ ngx_http_range_header_filter(ngx_http_re
         return NGX_ERROR;
     }
 
-    rc = 0;
-    range = NULL;
+    rc = ngx_http_range_parse(r, ctx);
+
+    if (rc == NGX_OK) {
+
+        ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
+
+        r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
+
+        if (ctx->ranges.nelts == 1) {
+            return ngx_http_range_singlepart_header(r, ctx);
+        }
+
+        return ngx_http_range_multipart_header(r, ctx);
+    }
+
+    if (rc == NGX_HTTP_RANGE_NOT_SATISFIABLE) {
+        return ngx_http_range_not_satisfiable(r);
+    }
+
+    /* rc == NGX_ERROR */
+
+    return rc;
+
+next_filter:
+
+    r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
+    if (r->headers_out.accept_ranges == NULL) {
+        return NGX_ERROR;
+    }
+
+    r->headers_out.accept_ranges->hash = 1;
+    r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1;
+    r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges";
+    r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1;
+    r->headers_out.accept_ranges->value.data = (u_char *) "bytes";
+
+    return ngx_http_next_header_filter(r);
+}
+
+
+ngx_int_t
+ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx)
+{
+    u_char            *p;
+    off_t              start, end;
+    ngx_uint_t         suffix;
+    ngx_http_range_t  *range;
+
     p = r->headers_in.range->value.data + 6;
 
     for ( ;; ) {
@@ -194,8 +251,7 @@ ngx_http_range_header_filter(ngx_http_re
 
         if (*p != '-') {
             if (*p < '0' || *p > '9') {
-                rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-                break;
+                return NGX_HTTP_RANGE_NOT_SATISFIABLE;
             }
 
             while (*p >= '0' && *p <= '9') {
@@ -205,13 +261,11 @@ ngx_http_range_header_filter(ngx_http_re
             while (*p == ' ') { p++; }
 
             if (*p++ != '-') {
-                rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-                break;
+                return NGX_HTTP_RANGE_NOT_SATISFIABLE;
             }
 
             if (start >= r->headers_out.content_length_n) {
-                rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-                break;
+                return NGX_HTTP_RANGE_NOT_SATISFIABLE;
             }
 
             while (*p == ' ') { p++; }
@@ -226,7 +280,7 @@ ngx_http_range_header_filter(ngx_http_re
                 range->end = r->headers_out.content_length_n;
 
                 if (*p++ != ',') {
-                    break;
+                    return NGX_OK;
                 }
 
                 continue;
@@ -238,8 +292,7 @@ ngx_http_range_header_filter(ngx_http_re
         }
 
         if (*p < '0' || *p > '9') {
-            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-            break;
+            return NGX_HTTP_RANGE_NOT_SATISFIABLE;
         }
 
         while (*p >= '0' && *p <= '9') {
@@ -249,8 +302,7 @@ ngx_http_range_header_filter(ngx_http_re
         while (*p == ' ') { p++; }
 
         if (*p != ',' && *p != '\0') {
-            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-            break;
+            return NGX_HTTP_RANGE_NOT_SATISFIABLE;
         }
 
         if (suffix) {
@@ -259,8 +311,7 @@ ngx_http_range_header_filter(ngx_http_re
         }
 
         if (start > end) {
-            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-            break;
+            return NGX_HTTP_RANGE_NOT_SATISFIABLE;
         }
 
         range = ngx_array_push(&ctx->ranges);
@@ -282,86 +333,65 @@ ngx_http_range_header_filter(ngx_http_re
         }
 
         if (*p++ != ',') {
-            break;
+            return NGX_OK;
         }
     }
+}
+
+
+static ngx_int_t
+ngx_http_range_singlepart_header(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx)
+{
+    ngx_table_elt_t   *content_range;
+    ngx_http_range_t  *range;
+
+    content_range = ngx_list_push(&r->headers_out.headers);
+    if (content_range == NULL) {
+        return NGX_ERROR;
+    }
 
-    if (rc) {
-
-        /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */
-
-        r->headers_out.status = rc;
-
-        content_range = ngx_list_push(&r->headers_out.headers);
-        if (content_range == NULL) {
-            return NGX_ERROR;
-        }
-
-        r->headers_out.content_range = content_range;
+    r->headers_out.content_range = content_range;
 
-        content_range->hash = 1;
-        content_range->key.len = sizeof("Content-Range") - 1;
-        content_range->key.data = (u_char *) "Content-Range";
+    content_range->hash = 1;
+    content_range->key.len = sizeof("Content-Range") - 1;
+    content_range->key.data = (u_char *) "Content-Range";
 
-        content_range->value.data = ngx_palloc(r->pool,
-                                       sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
-        if (content_range->value.data == NULL) {
-            return NGX_ERROR;
-        }
-
-        content_range->value.len = ngx_sprintf(content_range->value.data,
-                                               "bytes */%O",
-                                               r->headers_out.content_length_n)
-                                   - content_range->value.data;
-
-        ngx_http_clear_content_length(r);
-
-        return rc;
+    content_range->value.data = ngx_pnalloc(r->pool,
+                                    sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
+    if (content_range->value.data == NULL) {
+        return NGX_ERROR;
     }
 
-    ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
-
-    r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
-
-    if (ctx->ranges.nelts == 1) {
+    /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
 
-        content_range = ngx_list_push(&r->headers_out.headers);
-        if (content_range == NULL) {
-            return NGX_ERROR;
-        }
-
-        r->headers_out.content_range = content_range;
-
-        content_range->hash = 1;
-        content_range->key.len = sizeof("Content-Range") - 1;
-        content_range->key.data = (u_char *) "Content-Range";
+    range = ctx->ranges.elts;
 
-        content_range->value.data =
-               ngx_palloc(r->pool, sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
-        if (content_range->value.data == NULL) {
-            return NGX_ERROR;
-        }
-
-        /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
+    content_range->value.len = ngx_sprintf(content_range->value.data,
+                                           "bytes %O-%O/%O",
+                                           range->start, range->end - 1,
+                                           r->headers_out.content_length_n)
+                               - content_range->value.data;
 
-        content_range->value.len = ngx_sprintf(content_range->value.data,
-                                               "bytes %O-%O/%O",
-                                               range->start, range->end - 1,
-                                               r->headers_out.content_length_n)
-                                   - content_range->value.data;
+    r->headers_out.content_length_n = range->end - range->start;
 
-        r->headers_out.content_length_n = range->end - range->start;
-
-        if (r->headers_out.content_length) {
-            r->headers_out.content_length->hash = 0;
-            r->headers_out.content_length = NULL;
-        }
-
-        return ngx_http_next_header_filter(r);
+    if (r->headers_out.content_length) {
+        r->headers_out.content_length->hash = 0;
+        r->headers_out.content_length = NULL;
     }
 
+    return ngx_http_next_header_filter(r);
+}
 
-    /* TODO: what if no content_type ?? */
+
+static ngx_int_t
+ngx_http_range_multipart_header(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx)
+{
+    size_t              len;
+    ngx_uint_t          i;
+    ngx_http_range_t   *range;
+    ngx_atomic_uint_t   boundary;
 
     len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
           + sizeof(CRLF "Content-Type: ") - 1
@@ -372,7 +402,7 @@ ngx_http_range_header_filter(ngx_http_re
         len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
     }
 
-    ctx->boundary_header.data = ngx_palloc(r->pool, len);
+    ctx->boundary_header.data = ngx_pnalloc(r->pool, len);
     if (ctx->boundary_header.data == NULL) {
         return NGX_ERROR;
     }
@@ -399,7 +429,7 @@ ngx_http_range_header_filter(ngx_http_re
 
         r->headers_out.charset.len = 0;
 
-    } else {
+    } else if (r->headers_out.content_type.len) {
         ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
                                            CRLF "--%0muA" CRLF
                                            "Content-Type: %V" CRLF
@@ -407,12 +437,19 @@ ngx_http_range_header_filter(ngx_http_re
                                            boundary,
                                            &r->headers_out.content_type)
                                    - ctx->boundary_header.data;
+
+    } else {
+        ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
+                                           CRLF "--%0muA" CRLF
+                                           "Content-Range: bytes ",
+                                           boundary)
+                                   - ctx->boundary_header.data;
     }
 
     r->headers_out.content_type.data =
-        ngx_palloc(r->pool,
-                   sizeof("Content-Type: multipart/byteranges; boundary=") - 1
-                   + NGX_ATOMIC_T_LEN);
+        ngx_pnalloc(r->pool,
+                    sizeof("Content-Type: multipart/byteranges; boundary=") - 1
+                    + NGX_ATOMIC_T_LEN);
 
     if (r->headers_out.content_type.data == NULL) {
         return NGX_ERROR;
@@ -437,7 +474,7 @@ ngx_http_range_header_filter(ngx_http_re
         /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */
 
         range[i].content_range.data =
-                                ngx_palloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4);
+                               ngx_pnalloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4);
 
         if (range[i].content_range.data == NULL) {
             return NGX_ERROR;
@@ -465,13 +502,43 @@ ngx_http_range_header_filter(ngx_http_re
 
 
 static ngx_int_t
+ngx_http_range_not_satisfiable(ngx_http_request_t *r)
+{
+    ngx_table_elt_t  *content_range;
+
+    r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
+
+    content_range = ngx_list_push(&r->headers_out.headers);
+    if (content_range == NULL) {
+        return NGX_ERROR;
+    }
+
+    r->headers_out.content_range = content_range;
+
+    content_range->hash = 1;
+    content_range->key.len = sizeof("Content-Range") - 1;
+    content_range->key.data = (u_char *) "Content-Range";
+
+    content_range->value.data = ngx_pnalloc(r->pool,
+                                       sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
+    if (content_range->value.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    content_range->value.len = ngx_sprintf(content_range->value.data,
+                                           "bytes */%O",
+                                           r->headers_out.content_length_n)
+                               - content_range->value.data;
+
+    ngx_http_clear_content_length(r);
+
+    return NGX_HTTP_RANGE_NOT_SATISFIABLE;
+}
+
+
+static ngx_int_t
 ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    off_t                         start, last;
-    ngx_buf_t                    *b, *buf;
-    ngx_uint_t                    i;
-    ngx_chain_t                  *out, *hcl, *rcl, *dcl, **ll;
-    ngx_http_range_t             *range;
     ngx_http_range_filter_ctx_t  *ctx;
 
     if (in == NULL) {
@@ -484,17 +551,40 @@ ngx_http_range_body_filter(ngx_http_requ
         return ngx_http_next_body_filter(r, in);
     }
 
-    buf = in->buf;
+    if (ctx->ranges.nelts == 1) {
+        return ngx_http_range_singlepart_body(r, ctx, in);
+    }
+
+    /*
+     * multipart ranges are supported only if whole body is in a single buffer
+     */
 
     if (ngx_buf_special(in->buf)) {
         return ngx_http_next_body_filter(r, in);
     }
 
+    if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    return ngx_http_range_multipart_body(r, ctx, in);
+}
+
+
+static ngx_int_t
+ngx_http_range_test_overlapped(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
+{
+    off_t              start, last;
+    ngx_buf_t         *buf;
+    ngx_uint_t         i;
+    ngx_http_range_t  *range;
+
     if (ctx->offset) {
         goto overlapped;
     }
 
-    range = ctx->ranges.elts;
+    buf = in->buf;
 
     if (!buf->last_buf) {
 
@@ -507,6 +597,7 @@ ngx_http_range_body_filter(ngx_http_requ
             last = buf->last - buf->start + ctx->offset;
         }
 
+        range = ctx->ranges.elts;
         for (i = 0; i < ctx->ranges.nelts; i++) {
             if (start > range[i].start || last < range[i].end) {
                  goto overlapped;
@@ -514,29 +605,111 @@ ngx_http_range_body_filter(ngx_http_requ
         }
     }
 
-    /*
-     * the optimized version for the responses
-     * that are passed in the single buffer
-     */
-
     ctx->offset = ngx_buf_size(buf);
 
-    if (ctx->ranges.nelts == 1) {
+    return NGX_OK;
+
+overlapped:
+
+    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                  "range in overlapped buffers");
+
+    return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_range_singlepart_body(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
+{
+    off_t              start, last;
+    ngx_buf_t         *buf;
+    ngx_chain_t       *out, *cl, **ll;
+    ngx_http_range_t  *range;
+
+    out = NULL;
+    ll = &out;
+    range = ctx->ranges.elts;
 
-        if (buf->in_file) {
-            buf->file_pos = range->start;
-            buf->file_last = range->end;
+    for (cl = in; cl; cl = cl->next) {
+
+        buf = cl->buf;
+
+        start = ctx->offset;
+        last = ctx->offset + ngx_buf_size(buf);
+
+        ctx->offset = last;
+
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http range body buf: %O-%O", start, last);
+
+        if (ngx_buf_special(buf)) {
+            *ll = cl;
+            ll = &cl->next;
+            continue;
+        }
+
+        if (range->end <= start || range->start >= last) {
+
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http range body skip");
+
+            buf->pos = buf->last;
+            continue;
         }
 
-        if (ngx_buf_in_memory(buf)) {
-            buf->pos = buf->start + (size_t) range->start;
-            buf->last = buf->start + (size_t) range->end;
+        if (range->start > start) {
+
+            if (buf->in_file) {
+                buf->file_pos += range->start - start;
+            }
+
+            if (ngx_buf_in_memory(buf)) {
+                buf->pos += (size_t) (range->start - start);
+            }
         }
 
-        return ngx_http_next_body_filter(r, in);
+        if (range->end <= last) {
+
+            if (buf->in_file) {
+                buf->file_last -= last - range->end;
+            }
+
+            if (ngx_buf_in_memory(buf)) {
+                buf->last -= (size_t) (last - range->end);
+            }
+
+            buf->last_buf = 1;
+            *ll = cl;
+            cl->next = NULL;
+
+            break;
+        }
+
+        *ll = cl;
+        ll = &cl->next;
     }
 
+    if (out == NULL) {
+        return NGX_OK;
+    }
+
+    return ngx_http_next_body_filter(r, out);
+}
+
+
+static ngx_int_t
+ngx_http_range_multipart_body(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
+{
+    ngx_buf_t         *b, *buf;
+    ngx_uint_t         i;
+    ngx_chain_t       *out, *hcl, *rcl, *dcl, **ll;
+    ngx_http_range_t  *range;
+
     ll = &out;
+    buf = in->buf;
+    range = ctx->ranges.elts;
 
     for (i = 0; i < ctx->ranges.nelts; i++) {
 
@@ -630,8 +803,8 @@ ngx_http_range_body_filter(ngx_http_requ
     b->temporary = 1;
     b->last_buf = 1;
 
-    b->pos = ngx_palloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
-                                 + sizeof("--" CRLF) - 1);
+    b->pos = ngx_pnalloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
+                                  + sizeof("--" CRLF) - 1);
     if (b->pos == NULL) {
         return NGX_ERROR;
     }
@@ -652,13 +825,6 @@ ngx_http_range_body_filter(ngx_http_requ
     *ll = hcl;
 
     return ngx_http_next_body_filter(r, out);
-
-overlapped:
-
-    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                  "range in overlapped buffers");
-
-    return NGX_ERROR;
 }
 
 
--- a/src/http/modules/ngx_http_realip_module.c
+++ b/src/http/modules/ngx_http_realip_module.c
@@ -163,7 +163,7 @@ ngx_http_realip_handler(ngx_http_request
                 return NGX_DECLINED;
             }
 
-            p = ngx_palloc(r->connection->pool, len);
+            p = ngx_pnalloc(r->connection->pool, len);
             if (p == NULL) {
                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
             }
--- a/src/http/modules/ngx_http_referer_module.c
+++ b/src/http/modules/ngx_http_referer_module.c
@@ -11,14 +11,7 @@
 
 #define NGX_HTTP_REFERER_NO_URI_PART  ((void *) 4)
 
-#if (NGX_PCRE)
-
-typedef struct {
-    ngx_regex_t             *regex;
-    ngx_str_t                name;
-} ngx_http_referer_regex_t;
-
-#else
+#if !(NGX_PCRE)
 
 #define ngx_regex_t          void
 
@@ -106,11 +99,6 @@ ngx_http_referer_variable(ngx_http_reque
     ngx_uint_t                 i, key;
     ngx_http_referer_conf_t   *rlcf;
     u_char                     buf[256];
-#if (NGX_PCRE)
-    ngx_int_t                  n;
-    ngx_str_t                  referer;
-    ngx_http_referer_regex_t  *regex;
-#endif
 
     rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
 
@@ -173,31 +161,23 @@ ngx_http_referer_variable(ngx_http_reque
 #if (NGX_PCRE)
 
     if (rlcf->regex) {
+        ngx_int_t  rc;
+        ngx_str_t  referer;
 
         referer.len = len - 7;
         referer.data = ref;
 
-        regex = rlcf->regex->elts;
-
-        for (i = 0; i < rlcf->regex->nelts; i++) {
-            n = ngx_regex_exec(regex[i].regex, &referer, NULL, 0);
-
-            if (n == NGX_REGEX_NO_MATCHED) {
-                continue;
-            }
+        rc = ngx_regex_exec_array(rlcf->regex, &referer, r->connection->log);
 
-            if (n < 0) {
-                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                              ngx_regex_exec_n
-                              " failed: %d on \"%V\" using \"%V\"",
-                              n, &referer, &regex[i].name);
-                return NGX_ERROR;
-            }
-
-            /* match */
-
+        if (rc == NGX_OK) {
             goto valid;
         }
+
+        if (rc == NGX_ERROR) {
+            return rc;
+        }
+
+        /* NGX_DECLINED */
     }
 
 #endif
@@ -244,6 +224,10 @@ ngx_http_referer_create_conf(ngx_conf_t 
         return NGX_CONF_ERROR;
     }
 
+#if (NGX_PCRE)
+    conf->regex = NGX_CONF_UNSET_PTR;
+#endif
+
     conf->no_referer = NGX_CONF_UNSET;
     conf->blocked_referer = NGX_CONF_UNSET;
 
@@ -262,6 +246,9 @@ ngx_http_referer_merge_conf(ngx_conf_t *
     if (conf->keys == NULL) {
         conf->hash = prev->hash;
 
+#if (NGX_PCRE)
+        ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
+#endif
         ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);
         ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);
 
@@ -337,6 +324,10 @@ ngx_http_referer_merge_conf(ngx_conf_t *
         conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
     }
 
+#if (NGX_PCRE)
+    ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
+#endif
+
     if (conf->no_referer == NGX_CONF_UNSET) {
         conf->no_referer = 0;
     }
@@ -367,7 +358,7 @@ ngx_http_valid_referers(ngx_conf_t *cf, 
     name.data = (u_char *) "invalid_referer";
 
     var = ngx_http_add_variable(cf, &name,
-                                NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOHASH);
+                                NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOHASH);
     if (var == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -511,26 +502,25 @@ ngx_http_add_regex_referer(ngx_conf_t *c
     ngx_str_t *name, ngx_regex_t *regex)
 {
 #if (NGX_PCRE)
-    ngx_str_t                  err;
-    ngx_http_referer_regex_t  *rr;
-    u_char                     errstr[NGX_MAX_CONF_ERRSTR];
+    ngx_str_t         err;
+    ngx_regex_elt_t  *re;
+    u_char            errstr[NGX_MAX_CONF_ERRSTR];
 
-    if (rlcf->regex == NULL) {
-        rlcf->regex = ngx_array_create(cf->pool, 2,
-                                       sizeof(ngx_http_referer_regex_t));
+    if (rlcf->regex == NGX_CONF_UNSET_PTR) {
+        rlcf->regex = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));
         if (rlcf->regex == NULL) {
             return NGX_CONF_ERROR;
         }
     }
 
-    rr = ngx_array_push(rlcf->regex);
-    if (rr == NULL) {
+    re = ngx_array_push(rlcf->regex);
+    if (re == NULL) {
         return NGX_CONF_ERROR;
     }
 
     if (regex) {
-        rr->regex = regex;
-        rr->name = *name;
+        re->regex = regex;
+        re->name = name->data;
 
         return NGX_CONF_OK;
     }
@@ -541,14 +531,14 @@ ngx_http_add_regex_referer(ngx_conf_t *c
     name->len--;
     name->data++;
 
-    rr->regex = ngx_regex_compile(name, NGX_REGEX_CASELESS, cf->pool, &err);
+    re->regex = ngx_regex_compile(name, NGX_REGEX_CASELESS, cf->pool, &err);
 
-    if (rr->regex == NULL) {
+    if (re->regex == NULL) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
         return NGX_CONF_ERROR;
     }
 
-    rr->name = *name;
+    re->name = name->data;
 
     return NGX_CONF_OK;
 
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -357,6 +357,12 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com
         last = 1;
     }
 
+    if (ngx_strncmp(value[2].data, "https://", sizeof("https://") - 1) == 0) {
+        regex->status = NGX_HTTP_MOVED_TEMPORARILY;
+        regex->redirect = 1;
+        last = 1;
+    }
+
     if (cf->args->nelts == 4) {
         if (ngx_strcmp(value[3].data, "last") == 0) {
             last = 1;
@@ -524,7 +530,7 @@ ngx_http_rewrite_if(ngx_conf_t *cf, ngx_
     ngx_conf_t                    save;
     ngx_http_module_t            *module;
     ngx_http_conf_ctx_t          *ctx, *pctx;
-    ngx_http_core_loc_conf_t     *clcf, *pclcf, **clcfp;
+    ngx_http_core_loc_conf_t     *clcf, *pclcf;
     ngx_http_script_if_code_t    *if_code;
     ngx_http_rewrite_loc_conf_t  *nlcf;
 
@@ -567,21 +573,10 @@ ngx_http_rewrite_if(ngx_conf_t *cf, ngx_
     clcf->name = pclcf->name;
     clcf->noname = 1;
 
-    if (pclcf->locations == NULL) {
-        pclcf->locations = ngx_array_create(cf->pool, 2, sizeof(void *));
-        if (pclcf->locations == NULL) {
-            return NGX_CONF_ERROR;
-        }
-    }
-
-    clcfp = ngx_array_push(pclcf->locations);
-    if (clcfp == NULL) {
+    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
-    *clcfp = clcf;
-
-
     if (ngx_http_rewrite_if_condition(cf, lcf) != NGX_CONF_OK) {
         return NGX_CONF_ERROR;
     }
@@ -924,7 +919,7 @@ ngx_http_rewrite_set(ngx_conf_t *cf, ngx
     value[1].len--;
     value[1].data++;
 
-    v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGABLE);
+    v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
     if (v == NULL) {
         return NGX_CONF_ERROR;
     }
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -100,7 +100,7 @@ static ngx_int_t ngx_http_ssi_endblock(n
     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
 
 static ngx_int_t ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
-    ngx_http_variable_value_t *v,  uintptr_t gmt);
+    ngx_http_variable_value_t *v, uintptr_t gmt);
 
 static char *ngx_http_ssi_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 
@@ -212,6 +212,7 @@ static ngx_str_t ngx_http_ssi_null_strin
 
 #define  NGX_HTTP_SSI_ECHO_VAR         0
 #define  NGX_HTTP_SSI_ECHO_DEFAULT     1
+#define  NGX_HTTP_SSI_ECHO_ENCODING    2
 
 #define  NGX_HTTP_SSI_CONFIG_ERRMSG    0
 #define  NGX_HTTP_SSI_CONFIG_TIMEFMT   1
@@ -237,6 +238,7 @@ static ngx_http_ssi_param_t  ngx_http_ss
 static ngx_http_ssi_param_t  ngx_http_ssi_echo_params[] = {
     { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1, 0 },
     { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0, 0 },
+    { ngx_string("encoding"), NGX_HTTP_SSI_ECHO_ENCODING, 0, 0 },
     { ngx_null_string, 0, 0, 0 }
 };
 
@@ -301,10 +303,10 @@ static ngx_http_ssi_command_t  ngx_http_
 static ngx_http_variable_t  ngx_http_ssi_vars[] = {
 
     { ngx_string("date_local"), NULL, ngx_http_ssi_date_gmt_local_variable, 0,
-      NGX_HTTP_VAR_NOCACHABLE, 0 },
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("date_gmt"), NULL, ngx_http_ssi_date_gmt_local_variable, 1,
-      NGX_HTTP_VAR_NOCACHABLE, 0 },
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
@@ -355,6 +357,7 @@ found:
     ctx->value_len = slcf->value_len;
     ctx->last_out = &ctx->out;
 
+    ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
     ctx->output = 1;
 
     ctx->params.elts = ctx->params_array;
@@ -555,8 +558,9 @@ ngx_http_ssi_body_filter(ngx_http_reques
                     if (b->in_file) {
                         if (slcf->min_file_chunk < (size_t) (b->last - b->pos))
                         {
-                            b->file_last = b->file_pos + (b->last - b->start);
-                            b->file_pos += b->pos - b->start;
+                            b->file_last = b->file_pos
+                                                   + (b->last - ctx->buf->pos);
+                            b->file_pos += b->pos - ctx->buf->pos;
 
                         } else {
                             b->in_file = 0;
@@ -1148,8 +1152,8 @@ ngx_http_ssi_parse(ngx_http_request_t *r
 
             default:
                 ctx->command.len = 1;
-                ctx->command.data = ngx_palloc(r->pool,
-                                               NGX_HTTP_SSI_COMMAND_LEN);
+                ctx->command.data = ngx_pnalloc(r->pool,
+                                                NGX_HTTP_SSI_COMMAND_LEN);
                 if (ctx->command.data == NULL) {
                     return NGX_ERROR;
                 }
@@ -1215,8 +1219,8 @@ ngx_http_ssi_parse(ngx_http_request_t *r
                 }
 
                 ctx->param->key.len = 1;
-                ctx->param->key.data = ngx_palloc(r->pool,
-                                                  NGX_HTTP_SSI_PARAM_LEN);
+                ctx->param->key.data = ngx_pnalloc(r->pool,
+                                                   NGX_HTTP_SSI_PARAM_LEN);
                 if (ctx->param->key.data == NULL) {
                     return NGX_ERROR;
                 }
@@ -1226,8 +1230,8 @@ ngx_http_ssi_parse(ngx_http_request_t *r
                 ctx->param->value.len = 0;
 
                 if (ctx->value_buf == NULL) {
-                    ctx->param->value.data = ngx_palloc(r->pool,
-                                                        ctx->value_len);
+                    ctx->param->value.data = ngx_pnalloc(r->pool,
+                                                         ctx->value_len);
                     if (ctx->param->value.data == NULL) {
                         return NGX_ERROR;
                     }
@@ -1405,7 +1409,7 @@ ngx_http_ssi_parse(ngx_http_request_t *r
         case ssi_postparam_state:
 
             if (ctx->param->value.len + 1 < ctx->value_len / 2) {
-                value = ngx_palloc(r->pool, ctx->param->value.len + 1);
+                value = ngx_pnalloc(r->pool, ctx->param->value.len + 1);
                 if (value == NULL) {
                     return NGX_ERROR;
                 }
@@ -1601,7 +1605,7 @@ ngx_http_ssi_evaluate_string(ngx_http_re
     size_t                     *size, len, prefix, part_len;
     ngx_str_t                   var, *val;
     ngx_int_t                   key;
-    ngx_uint_t                  i, j, n, bracket, quoted;
+    ngx_uint_t                  i, n, bracket, quoted;
     ngx_array_t                 lengths, values;
     ngx_http_variable_value_t  *vv;
 
@@ -1623,7 +1627,7 @@ ngx_http_ssi_evaluate_string(ngx_http_re
             if (prefix) {
                 len = prefix + text->len;
 
-                data = ngx_palloc(r->pool, len);
+                data = ngx_pnalloc(r->pool, len);
                 if (data == NULL) {
                     return NGX_ERROR;
                 }
@@ -1634,7 +1638,7 @@ ngx_http_ssi_evaluate_string(ngx_http_re
 
         quoted = 0;
 
-        for (i = 0 ; i < text->len; i++) {
+        for (i = 0; i < text->len; i++) {
             ch = text->data[i];
 
             if (!quoted) {
@@ -1727,12 +1731,7 @@ ngx_http_ssi_evaluate_string(ngx_http_re
                 goto invalid_variable;
             }
 
-            key = 0;
-
-            for (j = 0; j < var.len; j++) {
-                var.data[j] = ngx_tolower(var.data[j]);
-                key = ngx_hash(key, var.data[j]);
-            }
+            key = ngx_hash_strlow(var.data, var.data, var.len);
 
             val = ngx_http_ssi_get_variable(r, &var, key);
 
@@ -1826,7 +1825,7 @@ ngx_http_ssi_evaluate_string(ngx_http_re
         }
     }
 
-    p = ngx_palloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0));
+    p = ngx_pnalloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0));
     if (p == NULL) {
         return NGX_ERROR;
     }
@@ -1855,6 +1854,8 @@ static ngx_int_t
 ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
     ngx_str_t **params)
 {
+    u_char                      *dst, *src;
+    size_t                       len;
     ngx_int_t                    rc, key;
     ngx_str_t                   *uri, *file, *wait, *set, *stub, args;
     ngx_buf_t                   *b;
@@ -1924,13 +1925,25 @@ ngx_http_ssi_include(ngx_http_request_t 
         return rc;
     }
 
+    dst = uri->data;
+    src = uri->data;
+
+    ngx_unescape_uri(&dst, &src, uri->len, NGX_UNESCAPE_URI);
+
+    len = (uri->data + uri->len) - src;
+    if (len) {
+        dst = ngx_copy(dst, src, len);
+    }
+
+    uri->len = dst - uri->data;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "ssi include: \"%V\"", uri);
+
     args.len = 0;
     args.data = NULL;
     flags = 0;
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "ssi include: \"%V\"", uri);
-
     if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
         return NGX_HTTP_SSI_ERROR;
     }
@@ -1966,6 +1979,7 @@ ngx_http_ssi_include(ngx_http_request_t 
 
         if (bl[i].count++) {
 
+            out = NULL;
             ll = &out;
 
             for (tl = bl[i].bufs; tl; tl = tl->next) {
@@ -2006,12 +2020,7 @@ ngx_http_ssi_include(ngx_http_request_t 
     }
 
     if (set) {
-        key = 0;
-
-        for (i = 0; i < set->len; i++) {
-            set->data[i] = ngx_tolower(set->data[i]);
-            key = ngx_hash(key, set->data[i]);
-        }
+        key = ngx_hash_strlow(set->data, set->data, set->len);
 
         psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
         if (psr == NULL) {
@@ -2119,10 +2128,11 @@ static ngx_int_t
 ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
     ngx_str_t **params)
 {
+    u_char                     *p;
+    uintptr_t                   len;
     ngx_int_t                   key;
-    ngx_uint_t                  i;
     ngx_buf_t                  *b;
-    ngx_str_t                  *var, *value, text;
+    ngx_str_t                  *var, *value, *enc, text;
     ngx_chain_t                *cl;
     ngx_http_variable_value_t  *vv;
 
@@ -2131,12 +2141,7 @@ ngx_http_ssi_echo(ngx_http_request_t *r,
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "ssi echo \"%V\"", var);
 
-    key = 0;
-
-    for (i = 0; i < var->len; i++) {
-        var->data[i] = ngx_tolower(var->data[i]);
-        key = ngx_hash(key, var->data[i]);
-    }
+    key = ngx_hash_strlow(var->data, var->data, var->len);
 
     value = ngx_http_ssi_get_variable(r, var, key);
 
@@ -2170,6 +2175,69 @@ ngx_http_ssi_echo(ngx_http_request_t *r,
         }
     }
 
+    enc = params[NGX_HTTP_SSI_ECHO_ENCODING];
+
+    if (enc) {
+        if (enc->len == 4 && ngx_strncmp(enc->data, "none", 4) == 0) {
+
+            ctx->encoding = NGX_HTTP_SSI_NO_ENCODING;
+
+        } else if (enc->len == 3 && ngx_strncmp(enc->data, "url", 3) == 0) {
+
+            ctx->encoding = NGX_HTTP_SSI_URL_ENCODING;
+
+        } else if (enc->len == 6 && ngx_strncmp(enc->data, "entity", 6) == 0) {
+
+            ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
+
+        } else {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "unknown encoding \"%V\" in the \"echo\" command",
+                          enc);
+        }
+    }
+
+    switch (ctx->encoding) {
+
+    case NGX_HTTP_SSI_NO_ENCODING:
+        break;
+
+    case NGX_HTTP_SSI_URL_ENCODING:
+        len = 2 * ngx_escape_uri(NULL, value->data, value->len,
+                                 NGX_ESCAPE_HTML);
+
+        if (len) {
+            p = ngx_pnalloc(r->pool, value->len + len);
+            if (p == NULL) {
+                return NGX_HTTP_SSI_ERROR;
+            }
+
+            (void) ngx_escape_uri(p, value->data, value->len, NGX_ESCAPE_HTML);
+
+            value->len += len;
+            value->data = p;
+        }
+
+        break;
+
+    case NGX_HTTP_SSI_ENTITY_ENCODING:
+        len = ngx_escape_html(NULL, value->data, value->len);
+
+        if (len) {
+            p = ngx_pnalloc(r->pool, value->len + len);
+            if (p == NULL) {
+                return NGX_HTTP_SSI_ERROR;
+            }
+
+            (void) ngx_escape_html(p, value->data, value->len);
+
+            value->len += len;
+            value->data = p;
+        }
+
+        break;
+    }
+
     b = ngx_calloc_buf(r->pool);
     if (b == NULL) {
         return NGX_HTTP_SSI_ERROR;
@@ -2203,7 +2271,7 @@ ngx_http_ssi_config(ngx_http_request_t *
 
     if (value) {
         ctx->timefmt.len = value->len;
-        ctx->timefmt.data = ngx_palloc(r->pool, value->len + 1);
+        ctx->timefmt.data = ngx_pnalloc(r->pool, value->len + 1);
         if (ctx->timefmt.data == NULL) {
             return NGX_HTTP_SSI_ERROR;
         }
@@ -2226,7 +2294,6 @@ ngx_http_ssi_set(ngx_http_request_t *r, 
     ngx_str_t **params)
 {
     ngx_int_t            key, rc;
-    ngx_uint_t           i;
     ngx_str_t           *name, *value, *vv;
     ngx_http_ssi_var_t  *var;
     ngx_http_ssi_ctx_t  *mctx;
@@ -2253,12 +2320,7 @@ ngx_http_ssi_set(ngx_http_request_t *r, 
         return rc;
     }
 
-    key = 0;
-
-    for (i = 0; i < name->len; i++) {
-        name->data[i] = ngx_tolower(name->data[i]);
-        key = ngx_hash(key, name->data[i]);
-    }
+    key = ngx_hash_strlow(name->data, name->data, name->len);
 
     vv = ngx_http_ssi_get_variable(r, name, key);
 
@@ -2291,11 +2353,6 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
     ngx_str_t    *expr, left, right;
     ngx_int_t     rc;
     ngx_uint_t    negative, noregex, flags;
-#if (NGX_PCRE)
-    ngx_str_t     err;
-    ngx_regex_t  *regex;
-    u_char        errstr[NGX_MAX_CONF_ERRSTR];
-#endif
 
     if (ctx->command.len == 2) {
         if (ctx->conditional) {
@@ -2429,6 +2486,10 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
 
     } else {
 #if (NGX_PCRE)
+        ngx_str_t     err;
+        ngx_regex_t  *regex;
+        u_char        errstr[NGX_MAX_CONF_ERRSTR];
+
         err.len = NGX_MAX_CONF_ERRSTR;
         err.data = errstr;
 
@@ -2565,7 +2626,7 @@ ngx_http_ssi_endblock(ngx_http_request_t
 
 static ngx_int_t
 ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
-    ngx_http_variable_value_t *v,  uintptr_t gmt)
+    ngx_http_variable_value_t *v, uintptr_t gmt)
 {
     ngx_http_ssi_ctx_t  *ctx;
     ngx_time_t          *tp;
@@ -2573,17 +2634,18 @@ ngx_http_ssi_date_gmt_local_variable(ngx
     char                 buf[NGX_HTTP_SSI_DATE_LEN];
 
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
 
     tp = ngx_timeofday();
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
 
-    if (ctx->timefmt.len == sizeof("%s") - 1
-        && ctx->timefmt.data[0] == '%' && ctx->timefmt.data[1] == 's')
+    if (ctx == NULL
+        || (ctx->timefmt.len == sizeof("%s") - 1
+            && ctx->timefmt.data[0] == '%' && ctx->timefmt.data[1] == 's'))
     {
-        v->data = ngx_palloc(r->pool, NGX_TIME_T_LEN);
+        v->data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);
         if (v->data == NULL) {
             return NGX_ERROR;
         }
@@ -2606,7 +2668,7 @@ ngx_http_ssi_date_gmt_local_variable(ngx
         return NGX_ERROR;
     }
 
-    v->data = ngx_palloc(r->pool, v->len);
+    v->data = ngx_pnalloc(r->pool, v->len);
     if (v->data == NULL) {
         return NGX_ERROR;
     }
@@ -2655,7 +2717,7 @@ ngx_http_ssi_types(ngx_conf_t *cf, ngx_c
 
         type->len = value[i].len;
 
-        type->data = ngx_palloc(cf->pool, type->len + 1);
+        type->data = ngx_pnalloc(cf->pool, type->len + 1);
         if (type->data == NULL) {
             return NGX_CONF_ERROR;
         }
--- a/src/http/modules/ngx_http_ssi_filter_module.h
+++ b/src/http/modules/ngx_http_ssi_filter_module.h
@@ -13,15 +13,20 @@
 #include <ngx_http.h>
 
 
-#define NGX_HTTP_SSI_MAX_PARAMS     16
+#define NGX_HTTP_SSI_MAX_PARAMS       16
 
-#define NGX_HTTP_SSI_COMMAND_LEN    32
-#define NGX_HTTP_SSI_PARAM_LEN      32
-#define NGX_HTTP_SSI_PARAMS_N       4
+#define NGX_HTTP_SSI_COMMAND_LEN      32
+#define NGX_HTTP_SSI_PARAM_LEN        32
+#define NGX_HTTP_SSI_PARAMS_N         4
 
 
-#define NGX_HTTP_SSI_COND_IF        1
-#define NGX_HTTP_SSI_COND_ELSE      2
+#define NGX_HTTP_SSI_COND_IF          1
+#define NGX_HTTP_SSI_COND_ELSE        2
+
+
+#define NGX_HTTP_SSI_NO_ENCODING      0
+#define NGX_HTTP_SSI_URL_ENCODING     1
+#define NGX_HTTP_SSI_ENTITY_ENCODING  2
 
 
 typedef struct {
@@ -60,6 +65,7 @@ typedef struct {
     ngx_array_t              *blocks;
 
     unsigned                  conditional:2;
+    unsigned                  encoding:2;
     unsigned                  block:1;
     unsigned                  output:1;
     unsigned                  output_chosen:1;
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -13,9 +13,9 @@ typedef ngx_int_t (*ngx_ssl_variable_han
     ngx_pool_t *pool, ngx_str_t *s);
 
 
-#define NGX_DEFLAUT_CERTIFICATE      "cert.pem"
-#define NGX_DEFLAUT_CERTIFICATE_KEY  "cert.pem"
-#define NGX_DEFLAUT_CIPHERS  "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
+#define NGX_DEFAULT_CERTIFICATE      "cert.pem"
+#define NGX_DEFAULT_CERTIFICATE_KEY  "cert.pem"
+#define NGX_DEFAULT_CIPHERS  "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
 
 
 static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r,
@@ -49,6 +49,14 @@ static ngx_conf_bitmask_t  ngx_http_ssl_
 };
 
 
+static ngx_conf_enum_t  ngx_http_ssl_verify[] = {
+    { ngx_string("off"), 0 },
+    { ngx_string("on"), 1 },
+    { ngx_string("ask"), 2 },
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_command_t  ngx_http_ssl_commands[] = {
 
     { ngx_string("ssl"),
@@ -72,6 +80,13 @@ static ngx_command_t  ngx_http_ssl_comma
       offsetof(ngx_http_ssl_srv_conf_t, certificate_key),
       NULL },
 
+    { ngx_string("ssl_dhparam"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_ssl_srv_conf_t, dhparam),
+      NULL },
+
     { ngx_string("ssl_protocols"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
       ngx_conf_set_bitmask_slot,
@@ -88,10 +103,10 @@ static ngx_command_t  ngx_http_ssl_comma
 
     { ngx_string("ssl_verify_client"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
-      ngx_conf_set_flag_slot,
+      ngx_conf_set_enum_slot,
       NGX_HTTP_SRV_CONF_OFFSET,
       offsetof(ngx_http_ssl_srv_conf_t, verify),
-      NULL },
+      &ngx_http_ssl_verify },
 
     { ngx_string("ssl_verify_depth"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
@@ -170,19 +185,26 @@ ngx_module_t  ngx_http_ssl_module = {
 static ngx_http_variable_t  ngx_http_ssl_vars[] = {
 
     { ngx_string("ssl_protocol"), NULL, ngx_http_ssl_static_variable,
-      (uintptr_t) ngx_ssl_get_protocol, NGX_HTTP_VAR_CHANGABLE, 0 },
+      (uintptr_t) ngx_ssl_get_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
     { ngx_string("ssl_cipher"), NULL, ngx_http_ssl_static_variable,
-      (uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGABLE, 0 },
+      (uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_cert"), NULL, ngx_http_ssl_variable,
+      (uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_raw_cert"), NULL, ngx_http_ssl_variable,
+      (uintptr_t) ngx_ssl_get_raw_certificate,
+      NGX_HTTP_VAR_CHANGEABLE, 0 },
 
     { ngx_string("ssl_client_s_dn"), NULL, ngx_http_ssl_variable,
-      (uintptr_t) ngx_ssl_get_subject_dn, NGX_HTTP_VAR_CHANGABLE, 0 },
+      (uintptr_t) ngx_ssl_get_subject_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
     { ngx_string("ssl_client_i_dn"), NULL, ngx_http_ssl_variable,
-      (uintptr_t) ngx_ssl_get_issuer_dn, NGX_HTTP_VAR_CHANGABLE, 0 },
+      (uintptr_t) ngx_ssl_get_issuer_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
     { ngx_string("ssl_client_serial"), NULL, ngx_http_ssl_variable,
-      (uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGABLE, 0 },
+      (uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
@@ -210,7 +232,7 @@ ngx_http_ssl_static_variable(ngx_http_re
 
         v->len = len;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
 
         return NGX_OK;
@@ -241,7 +263,7 @@ ngx_http_ssl_variable(ngx_http_request_t
 
         if (v->len) {
             v->valid = 1;
-            v->no_cachable = 0;
+            v->no_cacheable = 0;
             v->not_found = 0;
 
             return NGX_OK;
@@ -287,21 +309,19 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t 
      * set by ngx_pcalloc():
      *
      *     sscf->protocols = 0;
-     *     sscf->certificate.len = 0;
-     *     sscf->certificate.data = NULL;
-     *     sscf->certificate_key.len = 0;
-     *     sscf->certificate_key.data = NULL;
-     *     sscf->client_certificate.len = 0;
-     *     sscf->client_certificate.data = NULL;
+     *     sscf->certificate = { 0, NULL };
+     *     sscf->certificate_key = { 0, NULL };
+     *     sscf->dhparam = { 0, NULL };
+     *     sscf->client_certificate = { 0, NULL };
      *     sscf->ciphers.len = 0;
      *     sscf->ciphers.data = NULL;
      *     sscf->shm_zone = NULL;
      */
 
     sscf->enable = NGX_CONF_UNSET;
+    sscf->prefer_server_ciphers = NGX_CONF_UNSET;
     sscf->verify = NGX_CONF_UNSET;
     sscf->verify_depth = NGX_CONF_UNSET;
-    sscf->prefer_server_ciphers = NGX_CONF_UNSET;
     sscf->builtin_session_cache = NGX_CONF_UNSET;
     sscf->session_timeout = NGX_CONF_UNSET;
 
@@ -333,19 +353,21 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
                          (NGX_CONF_BITMASK_SET
                           |NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1));
 
-    ngx_conf_merge_value(conf->verify, prev->verify, 0);
-    ngx_conf_merge_value(conf->verify_depth, prev->verify_depth, 1);
+    ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
+    ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
 
     ngx_conf_merge_str_value(conf->certificate, prev->certificate,
-                         NGX_DEFLAUT_CERTIFICATE);
+                         NGX_DEFAULT_CERTIFICATE);
 
     ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key,
-                         NGX_DEFLAUT_CERTIFICATE_KEY);
+                         NGX_DEFAULT_CERTIFICATE_KEY);
+
+    ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
 
     ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
                          "");
 
-    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFLAUT_CIPHERS);
+    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
 
     conf->ssl.log = cf->log;
@@ -392,6 +414,13 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
     }
 
     if (conf->verify) {
+
+        if (conf->client_certificate.len == 0) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "no ssl_client_certificate for ssl_client_verify");
+            return NGX_CONF_ERROR;
+        }
+
         if (ngx_ssl_client_certificate(cf, &conf->ssl,
                                        &conf->client_certificate,
                                        conf->verify_depth)
@@ -414,9 +443,12 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
         return NGX_CONF_ERROR;
     }
 
+    if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
     ngx_conf_merge_value(conf->builtin_session_cache,
-                         prev->builtin_session_cache,
-                         NGX_SSL_DFLT_BUILTIN_SCACHE);
+                         prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);
 
     if (conf->shm_zone == NULL) {
         conf->shm_zone = prev->shm_zone;
@@ -448,6 +480,16 @@ ngx_http_ssl_session_cache(ngx_conf_t *c
 
     for (i = 1; i < cf->args->nelts; i++) {
 
+        if (ngx_strcmp(value[i].data, "off") == 0) {
+            sscf->builtin_session_cache = NGX_SSL_NO_SCACHE;
+            continue;
+        }
+
+        if (ngx_strcmp(value[i].data, "none") == 0) {
+            sscf->builtin_session_cache = NGX_SSL_NONE_SCACHE;
+            continue;
+        }
+
         if (ngx_strcmp(value[i].data, "builtin") == 0) {
             sscf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
             continue;
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -22,8 +22,8 @@ typedef struct {
 
     ngx_uint_t                      protocols;
 
-    ngx_int_t                       verify;
-    ngx_int_t                       verify_depth;
+    ngx_uint_t                      verify;
+    ngx_uint_t                      verify_depth;
 
     ssize_t                         builtin_session_cache;
 
@@ -31,6 +31,7 @@ typedef struct {
 
     ngx_str_t                       certificate;
     ngx_str_t                       certificate_key;
+    ngx_str_t                       dhparam;
     ngx_str_t                       client_certificate;
 
     ngx_str_t                       ciphers;
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -48,8 +48,7 @@ static ngx_int_t
 ngx_http_static_handler(ngx_http_request_t *r)
 {
     u_char                    *last, *location;
-    size_t                     root;
-    ngx_fd_t                   fd;
+    size_t                     root, len;
     ngx_str_t                  path;
     ngx_int_t                  rc;
     ngx_uint_t                 level;
@@ -97,15 +96,17 @@ ngx_http_static_handler(ngx_http_request
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    of.test_dir = 0;
-    of.retest = clcf->open_file_cache_retest;
+    ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+    of.directio = clcf->directio;
+    of.valid = clcf->open_file_cache_valid;
+    of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
     of.events = clcf->open_file_cache_events;
 
-    rc = ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool);
-
-    if (rc == NGX_ERROR) {
-
+    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+        != NGX_OK)
+    {
         switch (of.err) {
 
         case 0:
@@ -140,9 +141,9 @@ ngx_http_static_handler(ngx_http_request
         return rc;
     }
 
-    fd = of.fd;
+    r->root_tested = 1;
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", fd);
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
 
     if (of.is_dir) {
 
@@ -153,26 +154,39 @@ ngx_http_static_handler(ngx_http_request
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
-        if (!clcf->alias && clcf->root_lengths == NULL) {
+        len = r->uri.len + 1;
+
+        if (!clcf->alias && clcf->root_lengths == NULL && r->args.len == 0) {
             location = path.data + clcf->root.len;
 
+            *last = '/';
+
         } else {
-            location = ngx_palloc(r->pool, r->uri.len + 1);
+            if (r->args.len) {
+                len += r->args.len + 1;
+            }
+
+            location = ngx_pnalloc(r->pool, len);
             if (location == NULL) {
                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
             }
 
             last = ngx_copy(location, r->uri.data, r->uri.len);
-        }
+
+            *last = '/';
 
-        *last = '/';
+            if (r->args.len) {
+                *++last = '?';
+                ngx_memcpy(++last, r->args.data, r->args.len);
+            }
+        }
 
         /*
          * we do not need to set the r->headers_out.location->hash and
          * r->headers_out.location->key fields
          */
 
-        r->headers_out.location->value.len = r->uri.len + 1;
+        r->headers_out.location->value.len = len;
         r->headers_out.location->value.data = location;
 
         return NGX_HTTP_MOVED_PERMANENTLY;
@@ -230,7 +244,7 @@ ngx_http_static_handler(ngx_http_request
     b->last_buf = (r == r->main) ? 1: 0;
     b->last_in_chain = 1;
 
-    b->file->fd = fd;
+    b->file->fd = of.fd;
     b->file->name = path;
     b->file->log = log;
 
--- a/src/http/modules/ngx_http_stub_status_module.c
+++ b/src/http/modules/ngx_http_stub_status_module.c
@@ -129,7 +129,7 @@ static ngx_int_t ngx_http_status_handler
         return rc;
     }
 
-    return ngx_http_output_filter(r, &out);;
+    return ngx_http_output_filter(r, &out);
 }
 
 
--- a/src/http/modules/ngx_http_sub_filter_module.c
+++ b/src/http/modules/ngx_http_sub_filter_module.c
@@ -322,8 +322,8 @@ ngx_http_sub_body_filter(ngx_http_reques
                 b->recycled = 0;
 
                 if (b->in_file) {
-                    b->file_last = b->file_pos + (b->last - b->start);
-                    b->file_pos += b->pos - b->start;
+                    b->file_last = b->file_pos + (b->last - ctx->buf->pos);
+                    b->file_pos += b->pos - ctx->buf->pos;
                 }
 
                 cl->next = NULL;
@@ -369,9 +369,14 @@ ngx_http_sub_body_filter(ngx_http_reques
                 }
             }
 
-            b->memory = 1;
-            b->pos = ctx->sub.data;
-            b->last = ctx->sub.data + ctx->sub.len;
+            if (ctx->sub.len) {
+                b->memory = 1;
+                b->pos = ctx->sub.data;
+                b->last = ctx->sub.data + ctx->sub.len;
+
+            } else {
+                b->sync = 1;
+            }
 
             cl->buf = b;
             cl->next = NULL;
@@ -557,6 +562,7 @@ ngx_http_sub_parse(ngx_http_request_t *r
                 ch = ngx_tolower(ch);
             }
 
+            ctx->state = state;
             ctx->pos = p;
             ctx->looked = looked;
             ctx->copy_end = p;
@@ -578,9 +584,13 @@ ngx_http_sub_parse(ngx_http_request_t *r
             looked++;
 
             if (looked == ctx->match.len) {
+                if ((size_t) (p - ctx->pos) < looked) {
+                    ctx->saved = 0;
+                }
+
                 ctx->state = sub_start_state;
                 ctx->pos = p + 1;
-                ctx->looked = looked;
+                ctx->looked = 0;
                 ctx->copy_end = copy_end;
 
                 if (ctx->copy_start == NULL && copy_end) {
@@ -622,7 +632,6 @@ ngx_http_sub_filter(ngx_conf_t *cf, ngx_
 
     ngx_str_t                  *value;
     ngx_int_t                   n;
-    ngx_uint_t                  i;
     ngx_http_script_compile_t   sc;
 
     if (slcf->match.len) {
@@ -631,11 +640,9 @@ ngx_http_sub_filter(ngx_conf_t *cf, ngx_
 
     value = cf->args->elts;
 
-    slcf->match = value[1];
+    ngx_strlow(value[1].data, value[1].data, value[1].len);
 
-    for (i = 0; i < value[1].len; i++) {
-        value[1].data[i] = ngx_tolower(value[1].data[i]);
-    }
+    slcf->match = value[1];
 
     n = ngx_http_script_variables_count(&value[2]);
 
@@ -700,7 +707,7 @@ ngx_http_sub_types(ngx_conf_t *cf, ngx_c
 
         type->len = value[i].len;
 
-        type->data = ngx_palloc(cf->pool, type->len + 1);
+        type->data = ngx_pnalloc(cf->pool, type->len + 1);
         if (type->data == NULL) {
             return NGX_CONF_ERROR;
         }
--- a/src/http/modules/ngx_http_userid_filter_module.c
+++ b/src/http/modules/ngx_http_userid_filter_module.c
@@ -41,15 +41,14 @@ typedef struct {
 } ngx_http_userid_ctx_t;
 
 
-static void ngx_http_userid_get_uid(ngx_http_request_t *r,
-    ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
+static ngx_http_userid_ctx_t *ngx_http_userid_get_uid(ngx_http_request_t *r,
+    ngx_http_userid_conf_t *conf);
+static ngx_int_t ngx_http_userid_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, ngx_str_t *name, uint32_t *uid);
 static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r,
     ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
 
 static ngx_int_t ngx_http_userid_add_variables(ngx_conf_t *cf);
-static ngx_int_t ngx_http_userid_variable(ngx_http_request_t *r,
-    ngx_http_variable_value_t *v, uintptr_t data);
-
 static ngx_int_t ngx_http_userid_init(ngx_conf_t *cf);
 static void *ngx_http_userid_create_conf(ngx_conf_t *cf);
 static char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent,
@@ -61,9 +60,11 @@ static char *ngx_http_userid_expires(ngx
 static char *ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data);
 static char *ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static ngx_int_t ngx_http_userid_init_worker(ngx_cycle_t *cycle);
 
 
 
+static uint32_t  start_value;
 static uint32_t  sequencer_v1 = 1;
 static uint32_t  sequencer_v2 = 0x03030302;
 
@@ -173,7 +174,7 @@ ngx_module_t  ngx_http_userid_filter_mod
     NGX_HTTP_MODULE,                       /* module type */
     NULL,                                  /* init master */
     NULL,                                  /* init module */
-    NULL,                                  /* init process */
+    ngx_http_userid_init_worker,           /* init process */
     NULL,                                  /* init thread */
     NULL,                                  /* exit thread */
     NULL,                                  /* exit process */
@@ -189,7 +190,6 @@ static ngx_str_t  ngx_http_userid_set = 
 static ngx_int_t
 ngx_http_userid_filter(ngx_http_request_t *r)
 {
-    ngx_int_t                rc;
     ngx_http_userid_ctx_t   *ctx;
     ngx_http_userid_conf_t  *conf;
 
@@ -199,25 +199,18 @@ ngx_http_userid_filter(ngx_http_request_
 
     conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
 
-    if (conf->enable == NGX_HTTP_USERID_OFF) {
+    if (conf->enable <= NGX_HTTP_USERID_LOG) {
         return ngx_http_next_header_filter(r);
     }
 
+    ctx = ngx_http_userid_get_uid(r, conf);
 
-    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_userid_ctx_t));
     if (ctx == NULL) {
         return NGX_ERROR;
     }
 
-    ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module);
-
-    ngx_http_userid_get_uid(r, ctx, conf);
+    if (ctx->uid_got[3] != 0) {
 
-    if (conf->enable == NGX_HTTP_USERID_LOG) {
-        return ngx_http_next_header_filter(r);
-    }
-
-    if (ctx->uid_got[3] != 0) {
         if (conf->mark == '\0') {
             return ngx_http_next_header_filter(r);
 
@@ -231,28 +224,95 @@ ngx_http_userid_filter(ngx_http_request_
         }
     }
 
-    rc = ngx_http_userid_set_uid(r, ctx, conf);
+    /* ctx->status == NGX_DECLINED */
 
-    if (rc != NGX_OK) {
-        return rc;
+    if (ngx_http_userid_set_uid(r, ctx, conf) == NGX_OK) {
+        return ngx_http_next_header_filter(r);
     }
 
-    return ngx_http_next_header_filter(r);
+    return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_userid_got_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_http_userid_ctx_t   *ctx;
+    ngx_http_userid_conf_t  *conf;
+
+    conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);
+
+    if (conf->enable == NGX_HTTP_USERID_OFF) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    ctx = ngx_http_userid_get_uid(r, conf);
+
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (ctx->uid_got[3] != 0) {
+        return ngx_http_userid_variable(r, v, &conf->name, ctx->uid_got);
+    }
+
+    /* ctx->status == NGX_DECLINED */
+
+    v->not_found = 1;
+
+    return NGX_OK;
 }
 
 
-static void
-ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
-    ngx_http_userid_conf_t *conf)
+static ngx_int_t
+ngx_http_userid_set_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
 {
-    ngx_int_t          n;
-    ngx_str_t          src, dst;
-    ngx_table_elt_t  **cookies;
+    ngx_http_userid_ctx_t   *ctx;
+    ngx_http_userid_conf_t  *conf;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
+
+    if (ctx == NULL || ctx->uid_set[3] == 0) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
+
+    return ngx_http_userid_variable(r, v, &conf->name, ctx->uid_set);
+}
+
+
+static ngx_http_userid_ctx_t *
+ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_conf_t *conf)
+{
+    ngx_int_t                n;
+    ngx_str_t                src, dst;
+    ngx_table_elt_t        **cookies;
+    ngx_http_userid_ctx_t   *ctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
+
+    if (ctx) {
+        return ctx;
+    }
+
+    if (ctx == NULL) {
+        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_userid_ctx_t));
+        if (ctx == NULL) {
+            return NULL;
+        }
+
+        ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module);
+    }
 
     n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &conf->name,
                                           &ctx->cookie);
     if (n == NGX_DECLINED) {
-        return;
+        return ctx;
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -263,7 +323,7 @@ ngx_http_userid_get_uid(ngx_http_request
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "client sent too short userid cookie \"%V\"",
                       &cookies[n]->value);
-        return;
+        return ctx;
     }
 
     src = ctx->cookie;
@@ -284,13 +344,15 @@ ngx_http_userid_get_uid(ngx_http_request
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "client sent invalid userid cookie \"%V\"",
                       &cookies[n]->value);
-        return;
+        return ctx;
     }
 
     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "uid: %08XD%08XD%08XD%08XD",
                    ctx->uid_got[0], ctx->uid_got[1],
                    ctx->uid_got[2], ctx->uid_got[3]);
+
+    return ctx;
 }
 
 
@@ -298,13 +360,10 @@ static ngx_int_t
 ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
     ngx_http_userid_conf_t *conf)
 {
-    u_char              *cookie, *p;
-    size_t               len;
-    socklen_t            slen;
-    struct sockaddr_in   sin;
-    ngx_str_t            src, dst;
-    ngx_table_elt_t     *set_cookie, *p3p;
-
+    u_char           *cookie, *p;
+    size_t            len;
+    ngx_str_t         src, dst;
+    ngx_table_elt_t  *set_cookie, *p3p;
     /*
      * TODO: in the threaded mode the sequencers should be in TLS and their
      * ranges should be divided between threads
@@ -318,25 +377,15 @@ ngx_http_userid_set_uid(ngx_http_request
             } else {
                 ctx->uid_set[0] = conf->service;
             }
-            ctx->uid_set[1] = ngx_time();
-            ctx->uid_set[2] = ngx_pid;
+            ctx->uid_set[1] = (uint32_t) ngx_time();
+            ctx->uid_set[2] = start_value;
             ctx->uid_set[3] = sequencer_v1;
             sequencer_v1 += 0x100;
 
         } else {
             if (conf->service == NGX_CONF_UNSET) {
-                if (r->in_addr == 0) {
-                    slen = sizeof(struct sockaddr_in);
-                    if (getsockname(r->connection->fd,
-                                    (struct sockaddr *) &sin, &slen)
-                        == -1)
-                    {
-                        ngx_connection_error(r->connection, ngx_socket_errno,
-                                             "getsockname() failed");
-                        return NGX_ERROR;
-                    }
-
-                    r->in_addr = sin.sin_addr.s_addr;
+                if (ngx_http_server_addr(r, NULL) != NGX_OK) {
+                    return NGX_ERROR;
                 }
 
                 ctx->uid_set[0] = htonl(r->in_addr);
@@ -345,8 +394,8 @@ ngx_http_userid_set_uid(ngx_http_request
                 ctx->uid_set[0] = htonl(conf->service);
             }
 
-            ctx->uid_set[1] = htonl(ngx_time());
-            ctx->uid_set[2] = htonl(ngx_pid);
+            ctx->uid_set[1] = htonl((uint32_t) ngx_time());
+            ctx->uid_set[2] = htonl(start_value);
             ctx->uid_set[3] = htonl(sequencer_v2);
             sequencer_v2 += 0x100;
             if (sequencer_v2 < 0x03030302) {
@@ -371,7 +420,7 @@ ngx_http_userid_set_uid(ngx_http_request
         len += conf->domain.len;
     }
 
-    cookie = ngx_palloc(r->pool, len);
+    cookie = ngx_pnalloc(r->pool, len);
     if (cookie == NULL) {
         return NGX_ERROR;
     }
@@ -443,6 +492,27 @@ ngx_http_userid_set_uid(ngx_http_request
 
 
 static ngx_int_t
+ngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    ngx_str_t *name, uint32_t *uid)
+{
+    v->len = name->len + sizeof("=00001111222233334444555566667777") - 1;
+    v->data = ngx_pnalloc(r->pool, v->len);
+    if (v->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+
+    ngx_sprintf(v->data, "%V=%08XD%08XD%08XD%08XD",
+                name, uid[0], uid[1], uid[2], uid[3]);
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_userid_add_variables(ngx_conf_t *cf)
 {
     ngx_http_variable_t  *var;
@@ -452,52 +522,14 @@ ngx_http_userid_add_variables(ngx_conf_t
         return NGX_ERROR;
     }
 
-    var->get_handler = ngx_http_userid_variable;
-    var->data = offsetof(ngx_http_userid_ctx_t, uid_got);
+    var->get_handler = ngx_http_userid_got_variable;
 
     var = ngx_http_add_variable(cf, &ngx_http_userid_set, NGX_HTTP_VAR_NOHASH);
     if (var == NULL) {
         return NGX_ERROR;
     }
 
-    var->get_handler = ngx_http_userid_variable;
-    var->data = offsetof(ngx_http_userid_ctx_t, uid_set);
-
-    return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
-    uintptr_t data)
-{
-    uint32_t                *uid;
-    ngx_http_userid_ctx_t   *ctx;
-    ngx_http_userid_conf_t  *conf;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
-
-    uid = (uint32_t *) ((char *) ctx + data);
-
-    if (ctx == NULL || uid[3] == 0) {
-        v->not_found = 1;
-        return NGX_OK;
-    }
-
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
-
-    v->len = conf->name.len + sizeof("=00001111222233334444555566667777") - 1;
-    v->data = ngx_palloc(r->pool, v->len);
-    if (v->data == NULL) {
-        return NGX_ERROR;
-    }
-
-    v->valid = 1;
-    v->no_cachable = 0;
-    v->not_found = 0;
-
-    ngx_sprintf(v->data, "%V=%08XD%08XD%08XD%08XD",
-                &conf->name, uid[0], uid[1], uid[2], uid[3]);
+    var->get_handler = ngx_http_userid_set_variable;
 
     return NGX_OK;
 }
@@ -588,7 +620,7 @@ ngx_http_userid_domain(ngx_conf_t *cf, v
         return NGX_CONF_OK;
     }
 
-    new = ngx_palloc(cf->pool, sizeof("; domain=") - 1 + domain->len);
+    new = ngx_pnalloc(cf->pool, sizeof("; domain=") - 1 + domain->len);
     if (new == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -610,7 +642,7 @@ ngx_http_userid_path(ngx_conf_t *cf, voi
 
     u_char  *p, *new;
 
-    new = ngx_palloc(cf->pool, sizeof("; path=") - 1 + path->len);
+    new = ngx_pnalloc(cf->pool, sizeof("; path=") - 1 + path->len);
     if (new == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -706,3 +738,18 @@ ngx_http_userid_mark(ngx_conf_t *cf, ngx
 
     return NGX_CONF_OK;
 }
+
+
+static ngx_int_t
+ngx_http_userid_init_worker(ngx_cycle_t *cycle)
+{
+    struct timeval  tp;
+
+    ngx_gettimeofday(&tp);
+
+    /* use the most significant usec part that fits to 16 bits */
+    start_value = ((tp.tv_usec / 20) << 16) | ngx_pid;
+
+    return NGX_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -0,0 +1,1109 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+
+
+#ifndef NGX_HTTP_XSLT_REUSE_DTD
+#define NGX_HTTP_XSLT_REUSE_DTD  1
+#endif
+
+
+typedef struct {
+    ngx_array_t         *lengths;
+    ngx_array_t         *values;
+} 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_hash;
+    ngx_array_t         *keys;
+} ngx_http_xslt_filter_conf_t;
+
+
+typedef struct {
+    xmlDocPtr            doc;
+    xmlParserCtxtPtr     ctxt;
+    xmlSAXHandler       *sax;
+    ngx_http_request_t  *request;
+    ngx_array_t          params;
+    unsigned             done:1;
+    unsigned             html:1;
+} ngx_http_xslt_filter_ctx_t;
+
+
+static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,
+    ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
+static ngx_int_t ngx_http_xslt_filter_internal_error(ngx_http_request_t *r);
+static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r,
+    ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
+
+
+static void ngx_http_xslt_sax_start_document(void *data);
+static void ngx_http_xslt_sax_end_document(void *data);
+static void ngx_http_xslt_sax_internal_subset(void *data, const xmlChar *name,
+    const xmlChar *externalId, const xmlChar *systemId);
+static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
+    const xmlChar *externalId, const xmlChar *systemId);
+static void ngx_http_xslt_sax_entity_decl(void *data, const xmlChar *name,
+    int type, const xmlChar *publicId, const xmlChar *systemId,
+    xmlChar *content);
+static void ngx_http_xslt_sax_attribute_decl(void *data, const xmlChar *elem,
+    const xmlChar *fullname, int type, int def, const xmlChar *defaultValue,
+    xmlEnumerationPtr tree);
+static void ngx_http_xslt_sax_element_decl(void *data, const xmlChar * name,
+    int type, xmlElementContentPtr content);
+static void ngx_http_xslt_sax_notation_decl(void *data, const xmlChar *name,
+    const xmlChar *publicId, const xmlChar *systemId);
+static void ngx_http_xslt_sax_unparsed_entity_decl(void *data,
+    const xmlChar *name, const xmlChar *publicId, const xmlChar *systemId,
+    const xmlChar *notationName);
+static void ngx_http_xslt_sax_start_element(void *data,
+    const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI,
+    int nb_namespaces, const xmlChar **namespaces, int nb_attributes,
+    int nb_defaulted, const xmlChar **attributes);
+static void ngx_http_xslt_sax_end_element(void *data,
+    const xmlChar * localname ATTRIBUTE_UNUSED,
+    const xmlChar * prefix ATTRIBUTE_UNUSED,
+    const xmlChar * URI ATTRIBUTE_UNUSED);
+static void ngx_http_xslt_sax_characters(void *data, const xmlChar *p, int len);
+static void ngx_http_xslt_sax_cdata_block(void *data, const xmlChar *p,
+    int len);
+static xmlEntityPtr ngx_http_xslt_sax_get_entity(void *data,
+    const xmlChar *name);
+static xmlEntityPtr ngx_http_xslt_sax_get_parameter_entity(void *data,
+    const xmlChar *name);
+static xmlParserInputPtr ngx_http_xslt_sax_resolve_entity(void *data,
+    const xmlChar *publicId, const xmlChar *systemId);
+static void ngx_http_xslt_sax_reference(void *data, const xmlChar *name);
+static void ngx_http_xslt_sax_comment(void *data, const xmlChar *value);
+static void ngx_http_xslt_sax_processing_instruction(void *data,
+    const xmlChar *target, const xmlChar *pidata);
+static int ngx_http_xslt_sax_is_standalone(void *data);
+static int ngx_http_xslt_sax_has_internal_subset(void *data);
+static int ngx_http_xslt_sax_has_external_subset(void *data);
+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);
+static void ngx_http_xslt_cleanup(void *data);
+
+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 void ngx_http_xslt_cleanup_stylesheet(void *data);
+static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf);
+static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent,
+    void *child);
+static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf);
+
+
+ngx_str_t  ngx_http_xslt_default_types[] = {
+    ngx_string("text/xml"),
+    ngx_null_string
+};
+
+
+static ngx_command_t  ngx_http_xslt_filter_commands[] = {
+
+    { ngx_string("xml_entities"),
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
+      ngx_http_xslt_entities,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("xslt_stylesheet"),
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_1MORE,
+      ngx_http_xslt_stylesheet,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("xslt_types"),
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_1MORE,
+      ngx_http_types_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_xslt_filter_conf_t, keys),
+      &ngx_http_xslt_default_types[0] },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_xslt_filter_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    ngx_http_xslt_filter_init,             /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_xslt_filter_create_conf,      /* create location configuration */
+    ngx_http_xslt_filter_merge_conf        /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_xslt_filter_module = {
+    NGX_MODULE_V1,
+    &ngx_http_xslt_filter_module_ctx,      /* module context */
+    ngx_http_xslt_filter_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_http_output_header_filter_pt  ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_xslt_header_filter(ngx_http_request_t *r)
+{
+    ngx_http_xslt_filter_ctx_t   *ctx;
+    ngx_http_xslt_filter_conf_t  *conf;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "xslt filter header");
+
+    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
+        return ngx_http_next_header_filter(r);
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
+
+    if (conf->sheets.nelts == 0
+        || ngx_http_test_content_type(r, &conf->types_hash) == NULL)
+    {
+        return ngx_http_next_header_filter(r);
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
+
+    if (ctx) {
+        return ngx_http_next_header_filter(r);
+    }
+
+    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);
+
+    r->main_filter_need_in_memory = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    ngx_chain_t                 *cl;
+    ngx_http_xslt_filter_ctx_t  *ctx;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "xslt filter body");
+
+    if (in == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
+
+    if (ctx == NULL || ctx->done) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    for (cl = in; cl; cl = cl->next) {
+
+        if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {
+
+            if (ctx->ctxt->myDoc){
+
+#if (NGX_HTTP_XSLT_REUSE_DTD)
+                ctx->ctxt->myDoc->extSubset = NULL;
+#endif
+                xmlFreeDoc(ctx->ctxt->myDoc);
+            }
+
+            xmlFreeParserCtxt(ctx->ctxt);
+
+            return ngx_http_xslt_send(r, ctx, NULL);
+        }
+
+        if (cl->buf->last_buf) {
+
+            ctx->doc = ctx->ctxt->myDoc;
+
+#if (NGX_HTTP_XSLT_REUSE_DTD)
+            ctx->doc->extSubset = NULL;
+#endif
+
+            xmlFreeParserCtxt(ctx->ctxt);
+
+            if (ctx->ctxt->wellFormed) {
+                return ngx_http_xslt_send(r, ctx,
+                                       ngx_http_xslt_apply_stylesheet(r, ctx));
+            }
+
+            xmlFreeDoc(ctx->doc);
+
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "not well formed XML document");
+
+            return ngx_http_xslt_send(r, ctx, NULL);
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
+    ngx_buf_t *b)
+{
+    ngx_int_t            rc;
+    ngx_chain_t          out;
+    ngx_pool_cleanup_t  *cln;
+
+    ctx->done = 1;
+
+    if (b == NULL) {
+        return ngx_http_xslt_filter_internal_error(r);
+    }
+
+    cln = ngx_pool_cleanup_add(r->pool, 0);
+
+    if (cln == NULL) {
+        ngx_free(b->pos);
+        return ngx_http_special_response_handler(r,
+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
+    }
+
+    if (ctx->html) {
+        r->headers_out.content_type_len = sizeof("text/html") - 1;
+        r->headers_out.content_type.len = sizeof("text/html") - 1;
+        r->headers_out.content_type.data = (u_char *) "text/html";
+    }
+
+    r->headers_out.content_length_n = b->last - b->pos;
+
+    if (r->headers_out.content_length) {
+        r->headers_out.content_length->hash = 0;
+        r->headers_out.content_length = NULL;
+    }
+
+    r->allow_ranges = 1;
+
+    rc = ngx_http_next_header_filter(r);
+
+    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+        ngx_free(b->pos);
+        return rc;
+    }
+
+    cln->handler = ngx_http_xslt_cleanup;
+    cln->data = b->pos;
+
+    out.buf = b;
+    out.next = NULL;
+
+    return ngx_http_next_body_filter(r, &out);
+}
+
+
+static ngx_int_t
+ngx_http_xslt_filter_internal_error(ngx_http_request_t *r)
+{
+    ngx_int_t  rc;
+
+    /* clear the modules contexts */
+    ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
+
+    rc = ngx_http_special_response_handler(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+    /* NGX_ERROR resets any pending data */
+
+    return (rc == NGX_OK) ? NGX_ERROR : rc;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
+    ngx_buf_t *b)
+{
+    int                err;
+    xmlSAXHandler     *sax;
+    xmlParserCtxtPtr   ctxt;
+
+    if (ctx->ctxt == NULL) {
+
+        ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
+        if (ctxt == NULL) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "xmlCreatePushParserCtxt() failed");
+            return NGX_ERROR;
+        }
+
+        ctx->sax = ngx_palloc(r->pool, sizeof(xmlSAXHandler));
+        if (ctx->sax == NULL) {
+            return NGX_ERROR;
+        }
+
+        sax = ctxt->sax;
+
+        ngx_memcpy(ctx->sax, sax, sizeof(xmlSAXHandler));
+
+        sax->startDocument = ngx_http_xslt_sax_start_document;
+        sax->endDocument = ngx_http_xslt_sax_end_document;
+
+        sax->internalSubset = ngx_http_xslt_sax_internal_subset;
+        sax->externalSubset = ngx_http_xslt_sax_external_subset;
+        sax->entityDecl = ngx_http_xslt_sax_entity_decl;
+        sax->attributeDecl = ngx_http_xslt_sax_attribute_decl;
+        sax->elementDecl = ngx_http_xslt_sax_element_decl;
+        sax->notationDecl = ngx_http_xslt_sax_notation_decl;
+        sax->unparsedEntityDecl = ngx_http_xslt_sax_unparsed_entity_decl;
+        sax->setDocumentLocator = NULL;
+
+        sax->startElementNs = ngx_http_xslt_sax_start_element;
+        sax->endElementNs = ngx_http_xslt_sax_end_element;
+
+        sax->characters = ngx_http_xslt_sax_characters;
+        sax->ignorableWhitespace  = ngx_http_xslt_sax_characters;
+        sax->cdataBlock = ngx_http_xslt_sax_cdata_block;
+        sax->getEntity = ngx_http_xslt_sax_get_entity;
+        sax->resolveEntity = ngx_http_xslt_sax_resolve_entity;
+        sax->getParameterEntity = ngx_http_xslt_sax_get_parameter_entity;
+        sax->reference = ngx_http_xslt_sax_reference;
+        sax->comment = ngx_http_xslt_sax_comment;
+        sax->processingInstruction = ngx_http_xslt_sax_processing_instruction;
+
+        sax->isStandalone = ngx_http_xslt_sax_is_standalone;
+        sax->hasInternalSubset = ngx_http_xslt_sax_has_internal_subset;
+        sax->hasExternalSubset = ngx_http_xslt_sax_has_external_subset;
+
+        sax->warning = NULL;
+        sax->error = ngx_http_xslt_sax_error;
+        sax->fatalError = ngx_http_xslt_sax_error;
+
+        ctxt->userData = ctx;
+
+        ctxt->replaceEntities = 1;
+        ctxt->loadsubset = 1;
+
+        ctx->ctxt = ctxt;
+        ctx->request = r;
+    }
+
+    err = xmlParseChunk(ctx->ctxt, (char *) b->pos,
+                        (int) (b->last - b->pos), b->last_buf);
+
+    if (err == 0) {
+        b->pos = b->last;
+        return NGX_OK;
+    }
+
+    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                  "xmlParseChunk() failed, error:%d", err);
+
+    return NGX_ERROR;
+}
+
+
+static void
+ngx_http_xslt_sax_start_document(void *data)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->startDocument(ctx->ctxt);
+}
+
+
+static void
+ngx_http_xslt_sax_end_document(void *data)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->endDocument(ctx->ctxt);
+}
+
+
+static void
+ngx_http_xslt_sax_internal_subset(void *data, const xmlChar *name,
+    const xmlChar *externalId, const xmlChar *systemId)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->internalSubset(ctx->ctxt, name, externalId, systemId);
+}
+
+
+static void
+ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
+    const xmlChar *externalId, const xmlChar *systemId)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    xmlDocPtr                     doc;
+    xmlDtdPtr                     dtd;
+    ngx_http_request_t           *r;
+    ngx_http_xslt_filter_conf_t  *conf;
+
+    r = ctx->request;
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
+
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "xslt filter extSubset: \"%s\" \"%s\" \"%s\"",
+                   name ? name : (xmlChar *) "",
+                   externalId ? externalId : (xmlChar *) "",
+                   systemId ? systemId : (xmlChar *) "");
+
+    doc = ctx->ctxt->myDoc;
+
+#if (NGX_HTTP_XSLT_REUSE_DTD)
+
+    dtd = conf->dtd;
+
+#else
+
+    dtd = xmlCopyDtd(conf->dtd);
+    if (dtd == NULL) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "xmlCopyDtd() failed");
+        return;
+    }
+
+    dtd->name = xmlStrdup(name);
+
+    if (doc->children == NULL) {
+        xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
+
+    } else {
+        xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);
+    }
+
+#endif
+
+    doc->extSubset = dtd;
+}
+
+
+static void
+ngx_http_xslt_sax_entity_decl(void *data, const xmlChar *name, int type,
+    const xmlChar *publicId, const xmlChar *systemId, xmlChar *content)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->entityDecl(ctx->ctxt, name, type, publicId, systemId, content);
+}
+
+
+static void
+ngx_http_xslt_sax_attribute_decl(void *data, const xmlChar *elem,
+    const xmlChar *fullname, int type, int def, const xmlChar *defaultValue,
+    xmlEnumerationPtr tree)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->attributeDecl(ctx->ctxt, elem, fullname, type, def, defaultValue,
+                            tree);
+}
+
+
+static void
+ngx_http_xslt_sax_element_decl(void *data, const xmlChar * name, int type,
+    xmlElementContentPtr content)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->elementDecl(ctx->ctxt, name, type, content);
+}
+
+
+static void
+ngx_http_xslt_sax_notation_decl(void *data, const xmlChar *name,
+    const xmlChar *publicId, const xmlChar *systemId)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->notationDecl(ctx->ctxt, name, publicId, systemId);
+}
+
+
+static void
+ngx_http_xslt_sax_unparsed_entity_decl(void *data, const xmlChar *name,
+    const xmlChar *publicId, const xmlChar *systemId,
+    const xmlChar *notationName)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->unparsedEntityDecl(ctx->ctxt, name, publicId, systemId,
+                                 notationName);
+}
+
+
+static void
+ngx_http_xslt_sax_start_element(void *data, const xmlChar *localname,
+    const xmlChar *prefix, const xmlChar *URI, int nb_namespaces,
+    const xmlChar **namespaces, int nb_attributes, int nb_defaulted,
+    const xmlChar **attributes)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->startElementNs(ctx->ctxt, localname, prefix, URI, nb_namespaces,
+        namespaces, nb_attributes, nb_defaulted, attributes);
+}
+
+
+static void
+ngx_http_xslt_sax_end_element(void *data,
+    const xmlChar * localname ATTRIBUTE_UNUSED,
+    const xmlChar * prefix ATTRIBUTE_UNUSED,
+    const xmlChar * URI ATTRIBUTE_UNUSED)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->endElementNs(ctx->ctxt, localname, prefix, URI);
+}
+
+
+static void
+ngx_http_xslt_sax_characters(void *data, const xmlChar *p, int len)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->characters(ctx->ctxt, p, len);
+}
+
+
+static void
+ngx_http_xslt_sax_cdata_block(void *data, const xmlChar *p, int len)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->cdataBlock(ctx->ctxt, p, len);
+}
+
+
+static xmlEntityPtr
+ngx_http_xslt_sax_get_entity(void *data, const xmlChar *name)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->getEntity(ctx->ctxt, name);
+}
+
+
+static xmlEntityPtr
+ngx_http_xslt_sax_get_parameter_entity(void *data, const xmlChar *name)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->getParameterEntity(ctx->ctxt, name);
+}
+
+
+static xmlParserInputPtr
+ngx_http_xslt_sax_resolve_entity(void *data, const xmlChar *publicId,
+    const xmlChar *systemId)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->resolveEntity(ctx->ctxt, publicId, systemId);
+}
+
+
+static void
+ngx_http_xslt_sax_reference(void *data, const xmlChar *name)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->reference(ctx->ctxt, name);
+}
+
+
+static void
+ngx_http_xslt_sax_comment(void *data, const xmlChar *value)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->comment(ctx->ctxt, value);
+}
+
+
+static void
+ngx_http_xslt_sax_processing_instruction(void *data, const xmlChar *target,
+    const xmlChar *pidata)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->processingInstruction(ctx->ctxt, target, pidata);
+}
+
+
+static int
+ngx_http_xslt_sax_is_standalone(void *data)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->isStandalone(ctx->ctxt);
+}
+
+
+static int
+ngx_http_xslt_sax_has_internal_subset(void *data)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->hasInternalSubset(ctx->ctxt);
+}
+
+
+static int
+ngx_http_xslt_sax_has_external_subset(void *data)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->hasExternalSubset(ctx->ctxt);
+}
+
+
+static void ngx_cdecl
+ngx_http_xslt_sax_error(void *data, const char *msg, ...)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    size_t    n;
+    va_list   args;
+    u_char    buf[NGX_MAX_ERROR_STR];
+
+    buf[0] = '\0';
+
+    va_start(args, msg);
+    n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);
+    va_end(args);
+
+    while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
+
+    ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
+                  "libxml2 error: \"%*s\"", n, buf);
+}
+
+
+static ngx_buf_t *
+ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
+    ngx_http_xslt_filter_ctx_t *ctx)
+{
+    int                           len, rc;
+    ngx_buf_t                    *b;
+    ngx_uint_t                    i;
+    xmlChar                      *buf;
+    xmlDocPtr                     doc, res;
+    ngx_http_xslt_sheet_t        *sheet;
+    ngx_http_xslt_filter_conf_t  *conf;
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
+    sheet = conf->sheets.elts;
+    doc = ctx->doc;
+
+    /* preallocate array for 4 params */
+
+    if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))
+        != NGX_OK)
+    {
+        xmlFreeDoc(doc);
+        return NULL;
+    }
+
+    for (i = 0; i < conf->sheets.nelts; i++) {
+
+        if (ngx_http_xslt_params(r, ctx, &sheet[i].params) != NGX_OK) {
+            xmlFreeDoc(doc);
+            return NULL;
+        }
+
+        res = xsltApplyStylesheet(sheet[i].stylesheet, doc, ctx->params.elts);
+
+        xmlFreeDoc(doc);
+
+        if (res == NULL) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "xsltApplyStylesheet() failed");
+            return NULL;
+        }
+
+        doc = res;
+
+        /* reset array elements */
+        ctx->params.nelts = 0;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "xslt filter doc type: %d", doc->type);
+
+    ctx->html = (doc->type == XML_HTML_DOCUMENT_NODE) ? 1 : 0;
+
+    rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);
+
+    xmlFreeDoc(doc);
+
+    if (rc != 0) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "xsltSaveResultToString() failed");
+        return NULL;
+    }
+
+    if (len == 0) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "xsltSaveResultToString() returned zero-length result");
+        return NULL;
+    }
+
+    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+    if (b == NULL) {
+        ngx_free(buf);
+        return NULL;
+    }
+
+    b->pos = buf;
+    b->last = buf + len;
+    b->memory = 1;
+    b->last_buf = 1;
+
+    return b;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
+    ngx_array_t *params)
+{
+    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_script_run(r, &string, param[i].lengths->elts, 1,
+                                param[i].values->elts)
+            == NULL)
+        {
+            return NGX_ERROR;
+        }
+
+        last = string.data + string.len - 1;
+        *last = '\0';
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "xslt filter param: \"%s\"", string.data);
+
+        p = string.data;
+
+        while (p && *p) {
+
+            value = p;
+            p = (u_char *) ngx_strchr(p, '=');
+            if (p == NULL) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                "invalid libxslt parameter \"%s\"", value);
+                return NGX_ERROR;
+            }
+            *p++ = '\0';
+
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "xslt filter param name: \"%s\"", value);
+
+            s = ngx_array_push(&ctx->params);
+            if (s == NULL) {
+                return NGX_ERROR;
+            }
+
+            *s = value;
+
+            value = p;
+            p = (u_char *) ngx_strchr(p, ':');
+
+            if (p) {
+                len = p - value;
+                *p++ = '\0';
+
+            } else {
+                len = last - value;
+            }
+
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "xslt filter param value: \"%s\"", value);
+
+            dst = value;
+            src = value;
+
+            ngx_unescape_uri(&dst, &src, len, 0);
+
+            *dst = '\0';
+
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "xslt filter param unescaped: \"%s\"", value);
+
+            s = ngx_array_push(&ctx->params);
+            if (s == NULL) {
+                return NGX_ERROR;
+            }
+
+            *s = value;
+        }
+    }
+
+    s = ngx_array_push(&ctx->params);
+    if (s == NULL) {
+        return NGX_ERROR;
+    }
+
+    *s = NULL;
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_http_xslt_cleanup(void *data)
+{
+    ngx_free(data);
+}
+
+
+static char *
+ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_xslt_filter_conf_t *xlcf = conf;
+
+    ngx_str_t  *value;
+
+    if (xlcf->dtd) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);
+
+    if (xlcf->dtd == NULL) {
+        ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+
+static char *
+ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_xslt_filter_conf_t *xlcf = conf;
+
+    ngx_str_t                  *value;
+    ngx_uint_t                  i, n;
+    ngx_pool_cleanup_t         *cln;
+    ngx_http_xslt_sheet_t      *sheet;
+    ngx_http_xslt_param_t      *param;
+    ngx_http_script_compile_t   sc;
+
+    value = cf->args->elts;
+
+    if (xlcf->sheets.elts == NULL) {
+        if (ngx_array_init(&xlcf->sheets, cf->pool, 1,
+                           sizeof(ngx_http_xslt_sheet_t))
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    sheet = ngx_array_push(&xlcf->sheets);
+    if (sheet == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));
+
+    if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    sheet->stylesheet = xsltParseStylesheetFile(value[1].data);
+    if (sheet->stylesheet == NULL) {
+        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+                           "xsltParseStylesheetFile(\"%s\") failed",
+                           value[1].data);
+        return NGX_CONF_ERROR;
+    }
+
+    cln->handler = ngx_http_xslt_cleanup_stylesheet;
+    cln->data = sheet->stylesheet;
+
+    n = cf->args->nelts;
+
+    if (n == 2) {
+        return NGX_CONF_OK;
+    }
+
+    if (ngx_array_init(&sheet->params, cf->pool, n - 2,
+                       sizeof(ngx_http_xslt_param_t))
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    for (i = 2; i < n; i++) {
+
+        param = ngx_array_push(&sheet->params);
+        if (param == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        param->lengths = NULL;
+        param->values = NULL;
+
+        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+        sc.cf = cf;
+        sc.source = &value[i];
+        sc.lengths = &param->lengths;
+        sc.values = &param->values;
+        sc.variables = ngx_http_script_variables_count(&value[i]);
+        sc.complete_lengths = 1;
+        sc.complete_values = 1;
+
+        if (ngx_http_script_compile(&sc) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static void
+ngx_http_xslt_cleanup_stylesheet(void *data)
+{
+    xsltStylesheetPtr  stylesheet = data;
+
+    xsltFreeStylesheet(stylesheet);
+}
+
+
+
+static void *
+ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
+{
+    ngx_http_xslt_filter_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_conf_t));
+    if (conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     conf->dtd
+     *     conf->sheets
+     */
+
+    return conf;
+}
+
+
+static char *
+ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_xslt_filter_conf_t *prev = parent;
+    ngx_http_xslt_filter_conf_t *conf = child;
+
+    if (conf->dtd == NULL) {
+        conf->dtd = prev->dtd;
+    }
+
+    if (conf->sheets.nelts == 0) {
+        conf->sheets = prev->sheets;
+    }
+
+    if (ngx_http_merge_types(cf, conf->keys, &conf->types_hash, prev->keys,
+                             &prev->types_hash, ngx_http_xslt_default_types)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_filter_init(ngx_conf_t *cf)
+{
+    xmlInitParser();
+
+    ngx_http_next_header_filter = ngx_http_top_header_filter;
+    ngx_http_top_header_filter = ngx_http_xslt_header_filter;
+
+    ngx_http_next_body_filter = ngx_http_top_body_filter;
+    ngx_http_top_body_filter = ngx_http_xslt_body_filter;
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
+{
+    xsltCleanupGlobals();
+    xmlCleanupParser();
+}
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.6.12';
+our $VERSION = '0.7.8';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -13,19 +13,16 @@
 
 #include "XSUB.h"
 
+
 #define ngx_http_perl_set_request(r)                                          \
     r = INT2PTR(ngx_http_request_t *, SvIV((SV *) SvRV(ST(0))))
 
 
-#define ngx_http_perl_set_targ(p, len, z)                                     \
+#define ngx_http_perl_set_targ(p, len)                                        \
                                                                               \
-    sv_upgrade(TARG, SVt_PV);                                                 \
+    SvUPGRADE(TARG, SVt_PV);                                                  \
     SvPOK_on(TARG);                                                           \
-    SvPV_set(TARG, (char *) p);                                               \
-    SvLEN_set(TARG, len + z);                                                 \
-    SvCUR_set(TARG, len);                                                     \
-    SvFAKE_on(TARG);                                                          \
-    SvREADONLY_on(TARG);                                                      \
+    sv_setpvn(TARG, (char *) p, len)
 
 
 static ngx_int_t
@@ -42,18 +39,25 @@ ngx_http_perl_sv2str(pTHX_ ngx_http_requ
 
     s->len = len;
 
-    if (SvREADONLY(sv)) {
+    if (SvREADONLY(sv) && SvPOK(sv)) {
         s->data = p;
+
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "perl sv2str: %08XD \"%V\"", sv->sv_flags, s);
+
         return NGX_OK;
     }
 
-    s->data = ngx_palloc(r->pool, len);
+    s->data = ngx_pnalloc(r->pool, len);
     if (s->data == NULL) {
         return NGX_ERROR;
     }
 
     ngx_memcpy(s->data, p, len);
 
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "perl sv2str: %08XD \"%V\"", sv->sv_flags, s);
+
     return NGX_OK;
 }
 
@@ -165,7 +169,7 @@ uri(r)
     ngx_http_request_t  *r;
 
     ngx_http_perl_set_request(r);
-    ngx_http_perl_set_targ(r->uri.data, r->uri.len, 0);
+    ngx_http_perl_set_targ(r->uri.data, r->uri.len);
 
     ST(0) = TARG;
 
@@ -178,7 +182,7 @@ args(r)
     ngx_http_request_t  *r;
 
     ngx_http_perl_set_request(r);
-    ngx_http_perl_set_targ(r->args.data, r->args.len, 0);
+    ngx_http_perl_set_targ(r->args.data, r->args.len);
 
     ST(0) = TARG;
 
@@ -191,7 +195,7 @@ request_method(r)
     ngx_http_request_t  *r;
 
     ngx_http_perl_set_request(r);
-    ngx_http_perl_set_targ(r->method_name.data, r->method_name.len, 0);
+    ngx_http_perl_set_targ(r->method_name.data, r->method_name.len);
 
     ST(0) = TARG;
 
@@ -205,7 +209,7 @@ remote_addr(r)
 
     ngx_http_perl_set_request(r);
     ngx_http_perl_set_targ(r->connection->addr_text.data,
-                           r->connection->addr_text.len, 1);
+                           r->connection->addr_text.len);
 
     ST(0) = TARG;
 
@@ -238,16 +242,12 @@ header_in(r, key)
 
     /* look up hashed headers */
 
-    lowcase_key = ngx_palloc(r->pool, len);
+    lowcase_key = ngx_pnalloc(r->pool, len);
     if (lowcase_key == NULL) {
         XSRETURN_UNDEF;
     }
 
-    hash = 0;
-    for (i = 0; i < len; i++) {
-        lowcase_key[i] = ngx_tolower(p[i]);
-        hash = ngx_hash(hash, lowcase_key[i]);
-    }
+    hash = ngx_hash_strlow(lowcase_key, p, len);
 
     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
 
@@ -259,7 +259,7 @@ header_in(r, key)
             ph = (ngx_table_elt_t **) ((char *) &r->headers_in + hh->offset);
 
             if (*ph) {
-                ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len, 0);
+                ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len);
 
                 goto done;
             }
@@ -278,7 +278,7 @@ header_in(r, key)
         ph = r->headers_in.cookies.elts;
 
         if (n == 1) {
-            ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len, 0);
+            ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len);
 
             goto done;
         }
@@ -289,7 +289,7 @@ header_in(r, key)
             size += ph[i]->value.len + sizeof("; ") - 1;
         }
 
-        cookie = ngx_palloc(r->pool, size);
+        cookie = ngx_pnalloc(r->pool, size);
         if (cookie == NULL) {
             XSRETURN_UNDEF;
         }
@@ -306,7 +306,7 @@ header_in(r, key)
             *p++ = ';'; *p++ = ' ';
         }
 
-        ngx_http_perl_set_targ(cookie, size, 0);
+        ngx_http_perl_set_targ(cookie, size);
 
         goto done;
     }
@@ -334,7 +334,7 @@ header_in(r, key)
             continue;
         }
 
-        ngx_http_perl_set_targ(h[i].value.data, h[i].value.len, 0);
+        ngx_http_perl_set_targ(h[i].value.data, h[i].value.len);
 
         goto done;
     }
@@ -402,7 +402,7 @@ request_body(r)
         XSRETURN_UNDEF;
     }
 
-    ngx_http_perl_set_targ(r->request_body->bufs->buf->pos, len, 0);
+    ngx_http_perl_set_targ(r->request_body->bufs->buf->pos, len);
 
     ST(0) = TARG;
 
@@ -421,7 +421,7 @@ request_body_file(r)
     }
 
     ngx_http_perl_set_targ(r->request_body->temp_file->file.name.data,
-                           r->request_body->temp_file->file.name.len, 1);
+                           r->request_body->temp_file->file.name.len);
 
     ST(0) = TARG;
 
@@ -500,7 +500,7 @@ filename(r)
 
     done:
 
-    ngx_http_perl_set_targ(ctx->filename.data, ctx->filename.len, 1);
+    ngx_http_perl_set_targ(ctx->filename.data, ctx->filename.len);
 
     ST(0) = TARG;
 
@@ -532,7 +532,7 @@ print(r, ...)
             sv = SvRV(sv);
         }
 
-        if (SvREADONLY(sv)) {
+        if (SvREADONLY(sv) && SvPOK(sv)) {
 
             p = (u_char *) SvPV(sv, len);
 
@@ -609,7 +609,6 @@ sendfile(r, filename, offset = -1, bytes
     char                      *filename;
     int                        offset;
     size_t                     bytes;
-    ngx_int_t                  rc;
     ngx_str_t                  path;
     ngx_buf_t                 *b;
     ngx_open_file_info_t       of;
@@ -636,26 +635,28 @@ sendfile(r, filename, offset = -1, bytes
         XSRETURN_EMPTY;
     }
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-    
-    of.test_dir = 0;
-    of.retest = clcf->open_file_cache_retest; 
-    of.errors = clcf->open_file_cache_errors; 
-    of.events = clcf->open_file_cache_events; 
-
     path.len = ngx_strlen(filename);
 
-    path.data = ngx_pcalloc(r->pool, path.len + 1);
+    path.data = ngx_pnalloc(r->pool, path.len + 1);
     if (path.data == NULL) {
         XSRETURN_EMPTY;
     }
 
     (void) ngx_cpystrn(path.data, filename, path.len + 1);
- 
-    rc = ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool);
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
-    if (rc == NGX_ERROR) {
+    of.directio = clcf->directio;
+    of.valid = clcf->open_file_cache_valid;
+    of.min_uses = clcf->open_file_cache_min_uses;
+    of.errors = clcf->open_file_cache_errors;
+    of.events = clcf->open_file_cache_events;
 
+    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+        != NGX_OK)
+    {
         if (of.err == 0) {
             XSRETURN_EMPTY;
         }
@@ -766,7 +767,7 @@ unescape(r, text, type = 0)
 
     src = (u_char *) SvPV(text, len);
 
-    p = ngx_palloc(r->pool, len + 1);
+    p = ngx_pnalloc(r->pool, len + 1);
     if (p == NULL) {
         XSRETURN_UNDEF;
     }
@@ -778,7 +779,7 @@ unescape(r, text, type = 0)
     ngx_unescape_uri(&dst, &src, len, (ngx_uint_t) type);
     *dst = '\0';
 
-    ngx_http_perl_set_targ(p, dst - p, 1);
+    ngx_http_perl_set_targ(p, dst - p);
 
     ST(0) = TARG;
 
@@ -823,16 +824,12 @@ variable(r, name, value = NULL)
 
     p = (u_char *) SvPV(name, len);
 
-    lowcase = ngx_palloc(r->pool, len);
+    lowcase = ngx_pnalloc(r->pool, len);
     if (lowcase == NULL) {
         XSRETURN_UNDEF;
     }
 
-    hash = 0;
-    for (i = 0; i < len; i++) {
-        lowcase[i] = ngx_tolower(p[i]);
-        hash = ngx_hash(hash, lowcase[i]);
-    }
+    hash = ngx_hash_strlow(lowcase, p, len);
 
     var.len = len;
     var.data = lowcase;
@@ -875,7 +872,7 @@ variable(r, name, value = NULL)
                     XSRETURN_UNDEF;
                 }
 
-                ngx_http_perl_set_targ(v[i].value.data, v[i].value.len, 0);
+                ngx_http_perl_set_targ(v[i].value.data, v[i].value.len);
 
                 goto done;
             }
@@ -912,14 +909,14 @@ variable(r, name, value = NULL)
     if (value) {
         vv->len = val.len;
         vv->valid = 1;
-        vv->no_cachable = 0;
+        vv->no_cacheable = 0;
         vv->not_found = 0;
         vv->data = val.data;
 
         XSRETURN_UNDEF;
     }
 
-    ngx_http_perl_set_targ(vv->data, vv->len, 0);
+    ngx_http_perl_set_targ(vv->data, vv->len);
 
     done:
 
@@ -930,19 +927,24 @@ void
 sleep(r, sleep, next)
     CODE:
 
-    dXSTARG;
     ngx_http_request_t   *r;
+    ngx_msec_t            sleep;
     ngx_http_perl_ctx_t  *ctx;
 
     ngx_http_perl_set_request(r);
 
+    sleep = (ngx_msec_t) SvIV(ST(1));
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "perl sleep: %M", sleep);
+
     ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
 
-    ctx->sleep = SvIV(ST(1));
     ctx->next = SvRV(ST(2));
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "perl sleep: %d", ctx->sleep);
+    ngx_add_timer(r->connection->write, sleep);
+
+    r->write_event_handler = ngx_http_perl_sleep_handler;
 
 
 void
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -41,7 +41,6 @@ static ngx_int_t ngx_http_perl_ssi(ngx_h
     ngx_http_ssi_ctx_t *ssi_ctx, ngx_str_t **params);
 #endif
 
-static void ngx_http_perl_sleep_handler(ngx_http_request_t *r);
 static char *ngx_http_perl_init_interpreter(ngx_conf_t *cf,
     ngx_http_perl_main_conf_t *pmcf);
 static PerlInterpreter *ngx_http_perl_create_interpreter(ngx_conf_t *cf,
@@ -49,7 +48,7 @@ static PerlInterpreter *ngx_http_perl_cr
 static ngx_int_t ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires,
     ngx_log_t *log);
 static ngx_int_t ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
-    HV *nginx, SV *sub, ngx_str_t **args, ngx_str_t *handler, ngx_str_t *rv);
+    HV *nginx, SV *sub, SV **args, ngx_str_t *handler, ngx_str_t *rv);
 static void ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv);
 
 static ngx_int_t ngx_http_perl_preconfiguration(ngx_conf_t *cf);
@@ -230,6 +229,10 @@ ngx_http_perl_handle_request(ngx_http_re
 
     }
 
+    if (rc == NGX_DONE) {
+        return;
+    }
+
     if (rc > 600) {
         rc = NGX_OK;
     }
@@ -248,12 +251,6 @@ ngx_http_perl_handle_request(ngx_http_re
     ctx->filename.data = NULL;
     ctx->redirect_uri.len = 0;
 
-    if (ctx->sleep) {
-        ngx_add_timer(r->connection->write, (ngx_msec_t) ctx->sleep);
-        r->write_event_handler = ngx_http_perl_sleep_handler;
-        ctx->sleep = 0;
-    }
-
     if (ctx->done || ctx->next) {
         return;
     }
@@ -272,7 +269,7 @@ ngx_http_perl_handle_request(ngx_http_re
 }
 
 
-static void
+void
 ngx_http_perl_sleep_handler(ngx_http_request_t *r)
 {
     ngx_event_t  *wev;
@@ -336,7 +333,7 @@ ngx_http_perl_variable(ngx_http_request_
     if (value.data) {
         v->len = value.len;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = value.data;
 
@@ -360,9 +357,10 @@ static ngx_int_t
 ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx,
     ngx_str_t **params)
 {
-    SV                         *sv;
+    SV                         *sv, **asv;
     ngx_int_t                   rc;
-    ngx_str_t                  *handler;
+    ngx_str_t                  *handler, **args;
+    ngx_uint_t                  i;
     ngx_http_perl_ctx_t        *ctx;
     ngx_http_perl_main_conf_t  *pmcf;
 
@@ -412,9 +410,31 @@ ngx_http_perl_ssi(ngx_http_request_t *r,
 
     sv = newSVpvn((char *) handler->data, handler->len);
 
-    rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sv,
-                                    &params[NGX_HTTP_PERL_SSI_ARG],
-                                    handler, NULL);
+    args = &params[NGX_HTTP_PERL_SSI_ARG];
+
+    if (args) {
+
+        for (i = 0; args[i]; i++) { /* void */ }
+
+        asv = ngx_pcalloc(r->pool, (i + 1) * sizeof(SV *));
+
+        if (asv == NULL) {
+            SvREFCNT_dec(sv);
+            return NGX_ERROR;
+        }
+
+        asv[0] = (SV *) i;
+
+        for (i = 0; args[i]; i++) {
+            asv[i + 1] = newSVpvn((char *) args[i]->data, args[i]->len);
+        }
+
+    } else {
+        asv = NULL;
+    }
+
+    rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sv, asv, handler,
+                                    NULL);
 
     SvREFCNT_dec(sv);
 
@@ -625,14 +645,15 @@ ngx_http_perl_run_requires(pTHX_ ngx_arr
 
 static ngx_int_t
 ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r, HV *nginx, SV *sub,
-    ngx_str_t **args, ngx_str_t *handler, ngx_str_t *rv)
+    SV **args, ngx_str_t *handler, ngx_str_t *rv)
 {
-    SV          *sv;
-    int          n, status;
-    char        *line;
-    STRLEN       len, n_a;
-    ngx_str_t    err;
-    ngx_uint_t   i;
+    SV                *sv;
+    int                n, status;
+    char              *line;
+    STRLEN             len, n_a;
+    ngx_str_t          err;
+    ngx_uint_t         i;
+    ngx_connection_t  *c;
 
     dSP;
 
@@ -647,33 +668,42 @@ ngx_http_perl_call_handler(pTHX_ ngx_htt
     XPUSHs(sv);
 
     if (args) {
-        for (i = 0; args[i]; i++) { /* void */ }
+        EXTEND(sp, (intptr_t) args[0]);
 
-        EXTEND(sp, (int) i);
-
-        for (i = 0; args[i]; i++) {
-            PUSHs(sv_2mortal(newSVpvn((char *) args[i]->data, args[i]->len)));
+        for (i = 1; i <= (ngx_uint_t) args[0]; i++) {
+            PUSHs(sv_2mortal(args[i]));
         }
     }
 
     PUTBACK;
 
+    c = r->connection;
+
     n = call_sv(sub, G_EVAL);
 
     SPAGAIN;
 
+    if (c->destroyed) {
+        PUTBACK;
+
+        FREETMPS;
+        LEAVE;
+
+        return NGX_DONE;
+    }
+
     if (n) {
         if (rv == NULL) {
             status = POPi;
 
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                            "call_sv: %d", status);
 
         } else {
             line = SvPVx(POPs, n_a);
             rv->len = n_a;
 
-            rv->data = ngx_palloc(r->pool, n_a);
+            rv->data = ngx_pnalloc(r->pool, n_a);
             if (rv->data == NULL) {
                 return NGX_ERROR;
             }
@@ -697,9 +727,8 @@ ngx_http_perl_call_handler(pTHX_ ngx_htt
         }
         err.len = len + 1;
 
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "call_sv(\"%V\") failed: \"%V\"",
-                      handler, &err);
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "call_sv(\"%V\") failed: \"%V\"", handler, &err);
 
         if (rv) {
             return NGX_ERROR;
@@ -709,7 +738,7 @@ ngx_http_perl_call_handler(pTHX_ ngx_htt
     }
 
     if (n != 1) {
-        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                       "call_sv(\"%V\") returned %d results", handler, n);
         status = NGX_OK;
     }
@@ -954,7 +983,7 @@ ngx_http_perl_set(ngx_conf_t *cf, ngx_co
     value[1].len--;
     value[1].data++;
 
-    v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGABLE);
+    v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
     if (v == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -1026,8 +1055,20 @@ ngx_http_perl_init_worker(ngx_cycle_t *c
     return NGX_OK;
 }
 
+
 static void
 ngx_http_perl_exit(ngx_cycle_t *cycle)
 {
+    ngx_http_perl_main_conf_t  *pmcf;
+
+    pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_perl_module);
+
+    {
+
+    dTHXa(pmcf->perl);
+    PERL_SET_CONTEXT(pmcf->perl);
+
     PERL_SYS_TERM();
+
+    }
 }
--- a/src/http/modules/perl/ngx_http_perl_module.h
+++ b/src/http/modules/perl/ngx_http_perl_module.h
@@ -25,9 +25,8 @@ typedef struct {
     ngx_str_t                 redirect_args;
 
     SV                       *next;
-    int                       sleep;
 
-    ngx_uint_t                done;   /* unsigned  done:1; */
+    ngx_uint_t                done;       /* unsigned  done:1; */
 
     ngx_array_t              *variables;  /* array of ngx_http_perl_var_t */
 
@@ -61,6 +60,7 @@ extern void boot_DynaLoader(pTHX_ CV* cv
 
 
 void ngx_http_perl_handle_request(ngx_http_request_t *r);
+void ngx_http_perl_sleep_handler(ngx_http_request_t *r);
 
 
 #endif /* _NGX_HTTP_PERL_MODULE_H_INCLUDED_ */
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -11,22 +11,48 @@
 
 
 static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static ngx_int_t ngx_http_init_phases(ngx_conf_t *cf,
+    ngx_http_core_main_conf_t *cmcf);
+static ngx_int_t ngx_http_init_headers_in_hash(ngx_conf_t *cf,
+    ngx_http_core_main_conf_t *cmcf);
+static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf,
+    ngx_http_core_main_conf_t *cmcf);
+
+static ngx_int_t ngx_http_init_server_lists(ngx_conf_t *cf,
+    ngx_array_t *servers, ngx_array_t *in_ports);
 static ngx_int_t ngx_http_add_address(ngx_conf_t *cf,
-    ngx_http_conf_in_port_t *in_port, ngx_http_listen_t *lscf,
-    ngx_http_core_srv_conf_t *cscf);
+    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_in_port_t *in_port,
+    ngx_http_listen_t *listen);
 static ngx_int_t ngx_http_add_names(ngx_conf_t *cf,
-    ngx_http_conf_in_addr_t *in_addr, ngx_http_core_srv_conf_t *cscf);
+    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_in_addr_t *in_addr);
+
 static char *ngx_http_merge_locations(ngx_conf_t *cf,
-    ngx_array_t *locations, void **loc_conf, ngx_http_module_t *module,
+    ngx_queue_t *locations, void **loc_conf, ngx_http_module_t *module,
     ngx_uint_t ctx_index);
-static int ngx_http_cmp_conf_in_addrs(const void *one, const void *two);
+static ngx_int_t ngx_http_init_locations(ngx_conf_t *cf,
+    ngx_http_core_srv_conf_t *cscf, ngx_http_core_loc_conf_t *pclcf);
+static ngx_int_t ngx_http_init_static_location_trees(ngx_conf_t *cf,
+    ngx_http_core_loc_conf_t *pclcf);
+static ngx_int_t ngx_http_cmp_locations(const ngx_queue_t *one,
+    const ngx_queue_t *two);
+static ngx_int_t ngx_http_join_exact_locations(ngx_conf_t *cf,
+    ngx_queue_t *locations);
+static void ngx_http_create_locations_list(ngx_queue_t *locations,
+    ngx_queue_t *q);
+static ngx_http_location_tree_node_t *
+    ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations,
+    size_t prefix);
+
+static ngx_int_t ngx_http_optimize_servers(ngx_conf_t *cf,
+    ngx_http_core_main_conf_t *cmcf, ngx_array_t *in_ports);
+static ngx_int_t ngx_http_cmp_conf_in_addrs(const void *one, const void *two);
 static int ngx_libc_cdecl ngx_http_cmp_dns_wildcards(const void *one,
     const void *two);
 
-ngx_uint_t   ngx_http_max_module;
+static ngx_int_t ngx_http_init_listening(ngx_conf_t *cf,
+    ngx_http_conf_in_port_t *in_port);
 
-ngx_uint_t   ngx_http_total_requests;
-uint64_t     ngx_http_total_sent;
+ngx_uint_t   ngx_http_max_module;
 
 
 ngx_int_t  (*ngx_http_top_header_filter) (ngx_http_request_t *r);
@@ -73,37 +99,14 @@ static char *
 ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     char                        *rv;
-    ngx_int_t                    rc, j;
-    ngx_uint_t                   mi, m, s, l, p, a, i, n;
-    ngx_uint_t                   find_config_index, use_rewrite, use_access;
-    ngx_uint_t                   last, bind_all, done;
+    ngx_uint_t                   mi, m, s;
     ngx_conf_t                   pcf;
-    ngx_array_t                  headers_in, in_ports;
-    ngx_hash_key_t              *hk;
-    ngx_hash_init_t              hash;
-    ngx_listening_t             *ls;
-    ngx_http_listen_t           *lscf;
+    ngx_array_t                  in_ports;
     ngx_http_module_t           *module;
-    ngx_http_header_t           *header;
-    ngx_http_in_port_t          *hip;
-    ngx_http_handler_pt         *h;
     ngx_http_conf_ctx_t         *ctx;
-    ngx_http_conf_in_port_t     *in_port;
-    ngx_http_conf_in_addr_t     *in_addr;
-    ngx_hash_keys_arrays_t       ha;
-    ngx_http_server_name_t      *name;
-    ngx_http_phase_handler_t    *ph;
-    ngx_http_virtual_names_t    *vn;
-    ngx_http_core_srv_conf_t   **cscfp, *cscf;
     ngx_http_core_loc_conf_t    *clcf;
-    ngx_http_phase_handler_pt    checker;
+    ngx_http_core_srv_conf_t   **cscfp;
     ngx_http_core_main_conf_t   *cmcf;
-#if (NGX_PCRE)
-    ngx_uint_t                   regex;
-#endif
-#if (NGX_WIN32)
-    ngx_iocp_conf_t             *iocpcf;
-#endif
 
     /* the main http context */
 
@@ -202,7 +205,6 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
         }
 
         module = ngx_modules[m]->ctx;
-        mi = ngx_modules[m]->ctx_index;
 
         if (module->preconfiguration) {
             if (module->preconfiguration(cf) != NGX_OK) {
@@ -218,8 +220,7 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
     rv = ngx_conf_parse(cf, NULL);
 
     if (rv != NGX_CONF_OK) {
-        *cf = pcf;
-        return rv;
+        goto failed;
     }
 
     /*
@@ -243,8 +244,7 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
         if (module->init_main_conf) {
             rv = module->init_main_conf(cf, ctx->main_conf[mi]);
             if (rv != NGX_CONF_OK) {
-                *cf = pcf;
-                return rv;
+                goto failed;
             }
         }
 
@@ -253,12 +253,10 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
             /* merge the server{}s' srv_conf's */
 
             if (module->merge_srv_conf) {
-                rv = module->merge_srv_conf(cf,
-                                            ctx->srv_conf[mi],
+                rv = module->merge_srv_conf(cf, ctx->srv_conf[mi],
                                             cscfp[s]->ctx->srv_conf[mi]);
                 if (rv != NGX_CONF_OK) {
-                    *cf = pcf;
-                    return rv;
+                    goto failed;
                 }
             }
 
@@ -266,112 +264,48 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
 
                 /* merge the server{}'s loc_conf */
 
-                rv = module->merge_loc_conf(cf,
-                                            ctx->loc_conf[mi],
+                rv = module->merge_loc_conf(cf, ctx->loc_conf[mi],
                                             cscfp[s]->ctx->loc_conf[mi]);
                 if (rv != NGX_CONF_OK) {
-                    *cf = pcf;
-                    return rv;
+                    goto failed;
                 }
 
                 /* merge the locations{}' loc_conf's */
 
-                rv = ngx_http_merge_locations(cf, &cscfp[s]->locations,
+                clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
+
+                rv = ngx_http_merge_locations(cf, clcf->locations,
                                               cscfp[s]->ctx->loc_conf,
                                               module, mi);
                 if (rv != NGX_CONF_OK) {
-                    *cf = pcf;
-                    return rv;
+                    goto failed;
                 }
             }
         }
     }
 
 
-    /* init lists of the handlers */
-
-    if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
-                       cf->pool, 1, sizeof(ngx_http_handler_pt))
-        != NGX_OK)
-    {
-        return NGX_CONF_ERROR;
-    }
+    /* create location trees */
 
+    for (s = 0; s < cmcf->servers.nelts; s++) {
 
-    if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,
-                       cf->pool, 1, sizeof(ngx_http_handler_pt))
-        != NGX_OK)
-    {
-        return NGX_CONF_ERROR;
-    }
-
+        clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
 
-    if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,
-                       cf->pool, 1, sizeof(ngx_http_handler_pt))
-        != NGX_OK)
-    {
-        return NGX_CONF_ERROR;
-    }
-
+        if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
 
-    if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,
-                       cf->pool, 1, sizeof(ngx_http_handler_pt))
-        != NGX_OK)
-    {
-        return NGX_CONF_ERROR;
-    }
-
-
-    if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
-                       cf->pool, 2, sizeof(ngx_http_handler_pt))
-        != NGX_OK)
-    {
-        return NGX_CONF_ERROR;
+        if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
     }
 
 
-    if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,
-                       cf->pool, 4, sizeof(ngx_http_handler_pt))
-        != NGX_OK)
-    {
-        return NGX_CONF_ERROR;
-    }
-
-
-    if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,
-                       cf->pool, 1, sizeof(ngx_http_handler_pt))
-        != NGX_OK)
-    {
+    if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
-
-    if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))
-        != NGX_OK)
-    {
-        return NGX_CONF_ERROR;
-    }
-
-    for (header = ngx_http_headers_in; header->name.len; header++) {
-        hk = ngx_array_push(&headers_in);
-        if (hk == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        hk->key = header->name;
-        hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
-        hk->value = header;
-    }
-
-    hash.hash = &cmcf->headers_in_hash;
-    hash.key = ngx_hash_key_lc;
-    hash.max_size = 512;
-    hash.bucket_size = ngx_align(64, ngx_cacheline_size);
-    hash.name = "headers_in_hash";
-    hash.pool = cf->pool;
-    hash.temp_pool = NULL;
-
-    if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {
+    if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
@@ -382,7 +316,6 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
         }
 
         module = ngx_modules[m]->ctx;
-        mi = ngx_modules[m]->ctx_index;
 
         if (module->postconfiguration) {
             if (module->postconfiguration(cf) != NGX_OK) {
@@ -403,6 +336,148 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
     *cf = pcf;
 
 
+    if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+
+    /*
+     * create the lists of ports, addresses and server names
+     * to find quickly the server core module configuration at run-time
+     */
+
+    /* AF_INET only */
+
+    if (ngx_http_init_server_lists(cf, &cmcf->servers, &in_ports) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+
+    /* optimize the lists of ports, addresses and server names */
+
+    /* AF_INET only */
+
+    if (ngx_http_optimize_servers(cf, cmcf, &in_ports) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+
+failed:
+
+    *cf = pcf;
+
+    return rv;
+}
+
+
+static ngx_int_t
+ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
+{
+    if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
+                       cf->pool, 1, sizeof(ngx_http_handler_pt))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,
+                       cf->pool, 1, sizeof(ngx_http_handler_pt))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,
+                       cf->pool, 1, sizeof(ngx_http_handler_pt))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,
+                       cf->pool, 1, sizeof(ngx_http_handler_pt))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
+                       cf->pool, 2, sizeof(ngx_http_handler_pt))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,
+                       cf->pool, 4, sizeof(ngx_http_handler_pt))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,
+                       cf->pool, 1, sizeof(ngx_http_handler_pt))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_init_headers_in_hash(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
+{
+    ngx_array_t         headers_in;
+    ngx_hash_key_t     *hk;
+    ngx_hash_init_t     hash;
+    ngx_http_header_t  *header;
+
+    if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    for (header = ngx_http_headers_in; header->name.len; header++) {
+        hk = ngx_array_push(&headers_in);
+        if (hk == NULL) {
+            return NGX_ERROR;
+        }
+
+        hk->key = header->name;
+        hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
+        hk->value = header;
+    }
+
+    hash.hash = &cmcf->headers_in_hash;
+    hash.key = ngx_hash_key_lc;
+    hash.max_size = 512;
+    hash.bucket_size = ngx_align(64, ngx_cacheline_size);
+    hash.name = "headers_in_hash";
+    hash.pool = cf->pool;
+    hash.temp_pool = NULL;
+
+    if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
+{
+    ngx_int_t                   j;
+    ngx_uint_t                  i, n;
+    ngx_uint_t                  find_config_index, use_rewrite, use_access;
+    ngx_http_handler_pt        *h;
+    ngx_http_phase_handler_t   *ph;
+    ngx_http_phase_handler_pt   checker;
+
     cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
     cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
     find_config_index = 0;
@@ -418,7 +493,7 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
     ph = ngx_pcalloc(cf->pool,
                      n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
     if (ph == NULL) {
-        return NGX_CONF_ERROR;
+        return NGX_ERROR;
     }
 
     cmcf->phase_engine.handlers = ph;
@@ -496,35 +571,551 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
         }
     }
 
+    return NGX_OK;
+}
 
-    /*
-     * create the lists of ports, addresses and server names
-     * to quickly find the server core module configuration at run-time
-     */
+
+static char *
+ngx_http_merge_locations(ngx_conf_t *cf, ngx_queue_t *locations,
+    void **loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index)
+{
+    char                       *rv;
+    ngx_queue_t                *q;
+    ngx_http_core_loc_conf_t   *clcf;
+    ngx_http_location_queue_t  *lq;
+
+    if (locations == NULL) {
+        return NGX_CONF_OK;
+    }
+
+    for (q = ngx_queue_head(locations);
+         q != ngx_queue_sentinel(locations);
+         q = ngx_queue_next(q))
+    {
+        lq = (ngx_http_location_queue_t *) q;
+
+        clcf = lq->exact ? lq->exact : lq->inclusive;
+
+        rv = module->merge_loc_conf(cf, loc_conf[ctx_index],
+                                    clcf->loc_conf[ctx_index]);
+        if (rv != NGX_CONF_OK) {
+            return rv;
+        }
+
+        rv = ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf,
+                                      module, ctx_index);
+        if (rv != NGX_CONF_OK) {
+            return rv;
+        }
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_init_locations(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+    ngx_http_core_loc_conf_t *pclcf)
+{
+    ngx_uint_t                   n;
+    ngx_queue_t                 *q, *locations, *named, tail;
+    ngx_http_core_loc_conf_t    *clcf;
+    ngx_http_location_queue_t   *lq;
+    ngx_http_core_loc_conf_t   **clcfp;
+#if (NGX_PCRE)
+    ngx_uint_t                   r;
+    ngx_queue_t                 *regex;
+#endif
+
+    locations = pclcf->locations;
+
+    if (locations == NULL) {
+        return NGX_OK;
+    }
+
+    ngx_queue_sort(locations, ngx_http_cmp_locations);
+
+    named = NULL;
+    n = 0;
+#if (NGX_PCRE)
+    regex = NULL;
+    r = 0;
+#endif
+
+    for (q = ngx_queue_head(locations);
+         q != ngx_queue_sentinel(locations);
+         q = ngx_queue_next(q))
+    {
+        lq = (ngx_http_location_queue_t *) q;
+
+        clcf = lq->exact ? lq->exact : lq->inclusive;
+
+        if (ngx_http_init_locations(cf, NULL, clcf) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+#if (NGX_PCRE)
+
+        if (clcf->regex) {
+            r++;
+
+            if (regex == NULL) {
+                regex = q;
+            }
+
+            continue;
+        }
+
+#endif
+
+        if (clcf->named) {
+            n++;
+
+            if (named == NULL) {
+                named = q;
+            }
+
+            continue;
+        }
+
+        if (clcf->noname) {
+            break;
+        }
+    }
+
+    if (q != ngx_queue_sentinel(locations)) {
+        ngx_queue_split(locations, q, &tail);
+    }
+
+    if (named) {
+        clcfp = ngx_palloc(cf->pool,
+                           (n + 1) * sizeof(ngx_http_core_loc_conf_t **));
+        if (clcfp == NULL) {
+            return NGX_ERROR;
+        }
+
+        cscf->named_locations = clcfp;
+
+        for (q = named;
+             q != ngx_queue_sentinel(locations);
+             q = ngx_queue_next(q))
+        {
+            lq = (ngx_http_location_queue_t *) q;
+
+            *(clcfp++) = lq->exact;
+        }
+
+        *clcfp = NULL;
+
+        ngx_queue_split(locations, named, &tail);
+    }
+
+#if (NGX_PCRE)
+
+    if (regex) {
+
+        clcfp = ngx_palloc(cf->pool,
+                           (r + 1) * sizeof(ngx_http_core_loc_conf_t **));
+        if (clcfp == NULL) {
+            return NGX_ERROR;
+        }
+
+        pclcf->regex_locations = clcfp;
+
+        for (q = regex;
+             q != ngx_queue_sentinel(locations);
+             q = ngx_queue_next(q))
+        {
+            lq = (ngx_http_location_queue_t *) q;
+
+            *(clcfp++) = lq->exact;
+        }
+
+        *clcfp = NULL;
+
+        ngx_queue_split(locations, regex, &tail);
+    }
+
+#endif
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_init_static_location_trees(ngx_conf_t *cf,
+    ngx_http_core_loc_conf_t *pclcf)
+{
+    ngx_queue_t                *q, *locations;
+    ngx_http_core_loc_conf_t   *clcf;
+    ngx_http_location_queue_t  *lq;
+
+    locations = pclcf->locations;
+
+    if (locations == NULL) {
+        return NGX_OK;
+    }
+
+    if (ngx_queue_empty(locations)) {
+        return NGX_OK;
+    }
+
+    for (q = ngx_queue_head(locations);
+         q != ngx_queue_sentinel(locations);
+         q = ngx_queue_next(q))
+    {
+        lq = (ngx_http_location_queue_t *) q;
+
+        clcf = lq->exact ? lq->exact : lq->inclusive;
+
+        if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
+
+    if (ngx_http_join_exact_locations(cf, locations) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    ngx_http_create_locations_list(locations, ngx_queue_head(locations));
+
+    pclcf->static_locations = ngx_http_create_locations_tree(cf, locations, 0);
+    if (pclcf->static_locations == NULL) {
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,
+    ngx_http_core_loc_conf_t *clcf)
+{
+    ngx_http_location_queue_t  *lq;
+
+    if (*locations == NULL) {
+        *locations = ngx_palloc(cf->temp_pool,
+                                sizeof(ngx_http_location_queue_t));
+        if (*locations == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_queue_init(*locations);
+    }
+
+    lq = ngx_palloc(cf->temp_pool, sizeof(ngx_http_location_queue_t));
+    if (lq == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (clcf->exact_match
+#if (NGX_PCRE)
+        || clcf->regex
+#endif
+        || clcf->named || clcf->noname)
+    {
+        lq->exact = clcf;
+        lq->inclusive = NULL;
+
+    } else {
+        lq->exact = NULL;
+        lq->inclusive = clcf;
+    }
+
+    lq->name = &clcf->name;
+    lq->file_name = cf->conf_file->file.name.data;
+    lq->line = cf->conf_file->line;
+
+    ngx_queue_init(&lq->list);
+
+    ngx_queue_insert_tail(*locations, &lq->queue);
+
+    return NGX_OK;
+}
 
-    if (ngx_array_init(&in_ports, cf->temp_pool, 2,
+
+static ngx_int_t
+ngx_http_cmp_locations(const ngx_queue_t *one, const ngx_queue_t *two)
+{
+    ngx_int_t                   rc;
+    ngx_http_core_loc_conf_t   *first, *second;
+    ngx_http_location_queue_t  *lq1, *lq2;
+
+    lq1 = (ngx_http_location_queue_t *) one;
+    lq2 = (ngx_http_location_queue_t *) two;
+
+    first = lq1->exact ? lq1->exact : lq1->inclusive;
+    second = lq2->exact ? lq2->exact : lq2->inclusive;
+
+    if (first->noname && !second->noname) {
+        /* shift no named locations to the end */
+        return 1;
+    }
+
+    if (!first->noname && second->noname) {
+        /* shift no named locations to the end */
+        return -1;
+    }
+
+    if (first->noname || second->noname) {
+        /* do not sort no named locations */
+        return 0;
+    }
+
+    if (first->named && !second->named) {
+        /* shift named locations to the end */
+        return 1;
+    }
+
+    if (!first->named && second->named) {
+        /* shift named locations to the end */
+        return -1;
+    }
+
+    if (first->named && second->named) {
+        return ngx_strcmp(first->name.data, second->name.data);
+    }
+
+#if (NGX_PCRE)
+
+    if (first->regex && !second->regex) {
+        /* shift the regex matches to the end */
+        return 1;
+    }
+
+    if (!first->regex && second->regex) {
+        /* shift the regex matches to the end */
+        return -1;
+    }
+
+    if (first->regex || second->regex) {
+        /* do not sort the regex matches */
+        return 0;
+    }
+
+#endif
+
+    rc = ngx_strcmp(first->name.data, second->name.data);
+
+    if (rc == 0 && !first->exact_match && second->exact_match) {
+        /* an exact match must be before the same inclusive one */
+        return 1;
+    }
+
+    return rc;
+}
+
+
+static ngx_int_t
+ngx_http_join_exact_locations(ngx_conf_t *cf, ngx_queue_t *locations)
+{
+    ngx_queue_t                *q, *x;
+    ngx_http_location_queue_t  *lq, *lx;
+
+    q = ngx_queue_head(locations);
+
+    while (q != ngx_queue_last(locations)) {
+
+        x = ngx_queue_next(q);
+
+        lq = (ngx_http_location_queue_t *) q;
+        lx = (ngx_http_location_queue_t *) x;
+
+        if (ngx_strcmp(lq->name->data, lx->name->data) == 0) {
+
+            if ((lq->exact && lx->exact) || (lq->inclusive && lx->inclusive)) {
+                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                              "duplicate location \"%V\" in %s:%ui",
+                              lx->name, lx->file_name, lx->line);
+
+                return NGX_ERROR;
+            }
+
+            lq->inclusive = lx->inclusive;
+
+            ngx_queue_remove(x);
+
+            continue;
+        }
+
+        q = ngx_queue_next(q);
+    }
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_http_create_locations_list(ngx_queue_t *locations, ngx_queue_t *q)
+{
+    u_char                     *name;
+    size_t                      len;
+    ngx_queue_t                *x, tail;
+    ngx_http_location_queue_t  *lq, *lx;
+
+    if (q == ngx_queue_last(locations)) {
+        return;
+    }
+
+    lq = (ngx_http_location_queue_t *) q;
+
+    if (lq->inclusive == NULL) {
+        ngx_http_create_locations_list(locations, ngx_queue_next(q));
+        return;
+    }
+
+    len = lq->name->len;
+    name = lq->name->data;
+
+    for (x = ngx_queue_next(q);
+         x != ngx_queue_sentinel(locations);
+         x = ngx_queue_next(x))
+    {
+        lx = (ngx_http_location_queue_t *) x;
+
+        if (len > lx->name->len
+            || (ngx_strncmp(name, lx->name->data, len) != 0))
+        {
+            break;
+        }
+    }
+
+    q = ngx_queue_next(q);
+
+    if (q == x) {
+        ngx_http_create_locations_list(locations, x);
+        return;
+    }
+
+    ngx_queue_split(locations, q, &tail);
+    ngx_queue_add(&lq->list, &tail);
+
+    if (x == ngx_queue_sentinel(locations)) {
+        ngx_http_create_locations_list(&lq->list, ngx_queue_head(&lq->list));
+        return;
+    }
+
+    ngx_queue_split(&lq->list, x, &tail);
+    ngx_queue_add(locations, &tail);
+
+    ngx_http_create_locations_list(&lq->list, ngx_queue_head(&lq->list));
+
+    ngx_http_create_locations_list(locations, x);
+}
+
+
+/*
+ * to keep cache locality for left leaf nodes, allocate nodes in following
+ * order: node, left subtree, right subtree, inclusive subtree
+ */
+
+static ngx_http_location_tree_node_t *
+ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations,
+    size_t prefix)
+{
+    size_t                          len;
+    ngx_queue_t                    *q, tail;
+    ngx_http_location_queue_t      *lq;
+    ngx_http_location_tree_node_t  *node;
+
+    q = ngx_queue_middle(locations);
+
+    lq = (ngx_http_location_queue_t *) q;
+    len = lq->name->len - prefix;
+
+    node = ngx_palloc(cf->pool,
+                      offsetof(ngx_http_location_tree_node_t, name) + len);
+    if (node == NULL) {
+        return NULL;
+    }
+
+    node->left = NULL;
+    node->right = NULL;
+    node->tree = NULL;
+    node->exact = lq->exact;
+    node->inclusive = lq->inclusive;
+
+    node->auto_redirect = (u_char) ((lq->exact && lq->exact->auto_redirect)
+                           || (lq->inclusive && lq->inclusive->auto_redirect));
+
+    node->len = (u_char) len;
+    ngx_memcpy(node->name, &lq->name->data[prefix], len);
+
+    ngx_queue_split(locations, q, &tail);
+
+    if (ngx_queue_empty(locations)) {
+        /*
+         * ngx_queue_split() insures that if left part is empty,
+         * then right one is empty too
+         */
+        goto inclusive;
+    }
+
+    node->left = ngx_http_create_locations_tree(cf, locations, prefix);
+    if (node->left == NULL) {
+        return NULL;
+    }
+
+    ngx_queue_remove(q);
+
+    if (ngx_queue_empty(&tail)) {
+        goto inclusive;
+    }
+
+    node->right = ngx_http_create_locations_tree(cf, &tail, prefix);
+    if (node->right == NULL) {
+        return NULL;
+    }
+
+inclusive:
+
+    if (ngx_queue_empty(&lq->list)) {
+        return node;
+    }
+
+    node->tree = ngx_http_create_locations_tree(cf, &lq->list, prefix + len);
+    if (node->tree == NULL) {
+        return NULL;
+    }
+
+    return node;
+}
+
+
+static ngx_int_t
+ngx_http_init_server_lists(ngx_conf_t *cf, ngx_array_t *servers,
+    ngx_array_t *in_ports)
+{
+    ngx_uint_t                  s, l, p, a;
+    ngx_http_listen_t          *listen;
+    ngx_http_conf_in_port_t    *in_port;
+    ngx_http_conf_in_addr_t    *in_addr;
+    ngx_http_core_srv_conf_t  **cscfp;
+
+    if (ngx_array_init(in_ports, cf->temp_pool, 2,
                        sizeof(ngx_http_conf_in_port_t))
         != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NGX_ERROR;
     }
 
     /* "server" directives */
 
-    cscfp = cmcf->servers.elts;
-    for (s = 0; s < cmcf->servers.nelts; s++) {
+    cscfp = servers->elts;
+    for (s = 0; s < servers->nelts; s++) {
 
         /* "listen" directives */
 
-        lscf = cscfp[s]->listen.elts;
+        listen = cscfp[s]->listen.elts;
         for (l = 0; l < cscfp[s]->listen.nelts; l++) {
 
             /* AF_INET only */
 
-            in_port = in_ports.elts;
-            for (p = 0; p < in_ports.nelts; p++) {
+            in_port = in_ports->elts;
+            for (p = 0; p < in_ports->nelts; p++) {
 
-                if (lscf[l].port != in_port[p].port) {
+                if (listen[l].port != in_port[p].port) {
                     continue;
                 }
 
@@ -533,15 +1124,15 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                 in_addr = in_port[p].addrs.elts;
                 for (a = 0; a < in_port[p].addrs.nelts; a++) {
 
-                    if (lscf[l].addr != in_addr[a].addr) {
+                    if (listen[l].addr != in_addr[a].addr) {
                         continue;
                     }
 
                     /* the address is already in the address list */
 
-                    if (ngx_http_add_names(cf, &in_addr[a], cscfp[s]) != NGX_OK)
+                    if (ngx_http_add_names(cf, cscfp[s], &in_addr[a]) != NGX_OK)
                     {
-                        return NGX_CONF_ERROR;
+                        return NGX_ERROR;
                     }
 
                     /*
@@ -549,14 +1140,14 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                      * for this address:port
                      */
 
-                    if (lscf[l].conf.default_server) {
+                    if (listen[l].conf.default_server) {
 
                         if (in_addr[a].default_server) {
                             ngx_log_error(NGX_LOG_ERR, cf->log, 0,
                                       "the duplicate default server in %s:%ui",
-                                       &lscf[l].file_name, lscf[l].line);
+                                       listen[l].file_name, listen[l].line);
 
-                            return NGX_CONF_ERROR;
+                            return NGX_ERROR;
                         }
 
                         in_addr[a].core_srv_conf = cscfp[s];
@@ -571,10 +1162,10 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                  * bound to this port
                  */
 
-                if (ngx_http_add_address(cf, &in_port[p], &lscf[l], cscfp[s])
+                if (ngx_http_add_address(cf, cscfp[s], &in_port[p], &listen[l])
                     != NGX_OK)
                 {
-                    return NGX_CONF_ERROR;
+                    return NGX_ERROR;
                 }
 
                 goto found;
@@ -582,17 +1173,18 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
 
             /* add the port to the in_port list */
 
-            in_port = ngx_array_push(&in_ports);
+            in_port = ngx_array_push(in_ports);
             if (in_port == NULL) {
-                return NGX_CONF_ERROR;
+                return NGX_ERROR;
             }
 
-            in_port->port = lscf[l].port;
+            in_port->port = listen[l].port;
             in_port->addrs.elts = NULL;
 
-            if (ngx_http_add_address(cf, in_port, &lscf[l], cscfp[s]) != NGX_OK)
+            if (ngx_http_add_address(cf, cscfp[s], in_port, &listen[l])
+                != NGX_OK)
             {
-                return NGX_CONF_ERROR;
+                return NGX_ERROR;
             }
 
         found:
@@ -601,21 +1193,130 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
         }
     }
 
+    return NGX_OK;
+}
 
-    /* optimize the lists of ports, addresses and server names */
+
+/*
+ * add the server address, the server names and the server core module
+ * configurations to the port (in_port)
+ */
+
+static ngx_int_t
+ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+     ngx_http_conf_in_port_t *in_port, ngx_http_listen_t *listen)
+{
+    ngx_http_conf_in_addr_t  *in_addr;
+
+    if (in_port->addrs.elts == NULL) {
+        if (ngx_array_init(&in_port->addrs, cf->temp_pool, 4,
+                           sizeof(ngx_http_conf_in_addr_t))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
+    }
+
+    in_addr = ngx_array_push(&in_port->addrs);
+    if (in_addr == NULL) {
+        return NGX_ERROR;
+    }
+
+    in_addr->addr = listen->addr;
+    in_addr->hash.buckets = NULL;
+    in_addr->hash.size = 0;
+    in_addr->wc_head = NULL;
+    in_addr->wc_tail = NULL;
+    in_addr->names.elts = NULL;
+#if (NGX_PCRE)
+    in_addr->nregex = 0;
+    in_addr->regex = NULL;
+#endif
+    in_addr->core_srv_conf = cscf;
+    in_addr->default_server = listen->conf.default_server;
+    in_addr->bind = listen->conf.bind;
+    in_addr->listen_conf = &listen->conf;
+
+#if (NGX_DEBUG)
+    {
+    u_char text[20];
+    ngx_inet_ntop(AF_INET, &in_addr->addr, text, 20);
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0, "address: %s:%d",
+                   text, in_port->port);
+    }
+#endif
+
+    return ngx_http_add_names(cf, cscf, in_addr);
+}
+
 
-    /* AF_INET only */
+/*
+ * add the server names and the server core module
+ * configurations to the address:port (in_addr)
+ */
+
+static ngx_int_t
+ngx_http_add_names(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+    ngx_http_conf_in_addr_t *in_addr)
+{
+    ngx_uint_t               i;
+    ngx_http_server_name_t  *server_names, *name;
+
+    if (in_addr->names.elts == NULL) {
+        if (ngx_array_init(&in_addr->names, cf->temp_pool, 4,
+                           sizeof(ngx_http_server_name_t))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
+    }
+
+    server_names = cscf->server_names.elts;
+
+    for (i = 0; i < cscf->server_names.nelts; i++) {
+
+        ngx_strlow(server_names[i].name.data, server_names[i].name.data,
+                   server_names[i].name.len);
 
-    in_port = in_ports.elts;
-    for (p = 0; p < in_ports.nelts; p++) {
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0,
+                       "name: %V", &server_names[i].name);
+
+        name = ngx_array_push(&in_addr->names);
+        if (name == NULL) {
+            return NGX_ERROR;
+        }
+
+        *name = server_names[i];
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
+    ngx_array_t *in_ports)
+{
+    ngx_int_t                  rc;
+    ngx_uint_t                 s, p, a;
+    ngx_hash_init_t            hash;
+    ngx_http_server_name_t    *name;
+    ngx_hash_keys_arrays_t     ha;
+    ngx_http_conf_in_port_t   *in_port;
+    ngx_http_conf_in_addr_t   *in_addr;
+#if (NGX_PCRE)
+    ngx_uint_t                 regex, i;
+#endif
+
+    in_port = in_ports->elts;
+    for (p = 0; p < in_ports->nelts; p++) {
 
         ngx_sort(in_port[p].addrs.elts, (size_t) in_port[p].addrs.nelts,
                  sizeof(ngx_http_conf_in_addr_t), ngx_http_cmp_conf_in_addrs);
 
         /*
-         * check whether all name-based servers have the same configuraiton
-         *     as the default server,
-         * or some servers disable optimizing the server names
+         * check whether all name-based servers have
+         * the same configuraiton as the default server
          */
 
         in_addr = in_port[p].addrs.elts;
@@ -624,18 +1325,15 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
             name = in_addr[a].names.elts;
             for (s = 0; s < in_addr[a].names.nelts; s++) {
 
-                if (in_addr[a].core_srv_conf != name[s].core_srv_conf
-                    || name[s].core_srv_conf->optimize_server_names == 0)
-                {
+                if (in_addr[a].core_srv_conf != name[s].core_srv_conf) {
                     goto virtual_names;
                 }
             }
 
             /*
              * if all name-based servers have the same configuration
-             *         as the default server,
-             *     and no servers disable optimizing the server names
-             * then we do not need to check them at run-time at all
+             * as the default server, then we do not need to check
+             * them at run-time at all
              */
 
             in_addr[a].names.nelts = 0;
@@ -648,14 +1346,13 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
 
             ha.temp_pool = ngx_create_pool(16384, cf->log);
             if (ha.temp_pool == NULL) {
-                return NGX_CONF_ERROR;
+                return NGX_ERROR;
             }
 
             ha.pool = cf->pool;
 
             if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {
-                ngx_destroy_pool(ha.temp_pool);
-                return NGX_CONF_ERROR;
+                goto failed;
             }
 
 #if (NGX_PCRE)
@@ -677,14 +1374,14 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                                       NGX_HASH_WILDCARD_KEY);
 
                 if (rc == NGX_ERROR) {
-                    return NGX_CONF_ERROR;
+                    return NGX_ERROR;
                 }
 
                 if (rc == NGX_DECLINED) {
                     ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                                 "invalid server name or wildcard \"%V\" on %s",
                                 &name[s].name, in_addr[a].listen_conf->addr);
-                    return NGX_CONF_ERROR;
+                    return NGX_ERROR;
                 }
 
                 if (rc == NGX_BUSY) {
@@ -706,8 +1403,7 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
 
                 if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK)
                 {
-                    ngx_destroy_pool(ha.temp_pool);
-                    return NGX_CONF_ERROR;
+                    goto failed;
                 }
             }
 
@@ -725,8 +1421,7 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                                            ha.dns_wc_head.nelts)
                     != NGX_OK)
                 {
-                    ngx_destroy_pool(ha.temp_pool);
-                    return NGX_CONF_ERROR;
+                    goto failed;
                 }
 
                 in_addr[a].wc_head = (ngx_hash_wildcard_t *) hash.hash;
@@ -746,8 +1441,7 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                                            ha.dns_wc_tail.nelts)
                     != NGX_OK)
                 {
-                    ngx_destroy_pool(ha.temp_pool);
-                    return NGX_CONF_ERROR;
+                    goto failed;
                 }
 
                 in_addr[a].wc_tail = (ngx_hash_wildcard_t *) hash.hash;
@@ -766,7 +1460,7 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                                        regex * sizeof(ngx_http_server_name_t));
 
             if (in_addr[a].regex == NULL) {
-                return NGX_CONF_ERROR;
+                return NGX_ERROR;
             }
 
             for (i = 0, s = 0; s < in_addr[a].names.nelts; s++) {
@@ -777,318 +1471,22 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
 #endif
         }
 
-        in_addr = in_port[p].addrs.elts;
-        last = in_port[p].addrs.nelts;
-
-        /*
-         * if there is the binding to the "*:port" then we need to bind()
-         * to the "*:port" only and ignore the other bindings
-         */
-
-        if (in_addr[last - 1].addr == INADDR_ANY) {
-            in_addr[last - 1].bind = 1;
-            bind_all = 0;
-
-        } else {
-            bind_all = 1;
-        }
-
-        for (a = 0; a < last; /* void */ ) {
-
-            if (!bind_all && !in_addr[a].bind) {
-                a++;
-                continue;
-            }
-
-            ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr,
-                                                  in_port[p].port);
-            if (ls == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            ls->addr_ntop = 1;
-
-            ls->handler = ngx_http_init_connection;
-
-            cscf = in_addr[a].core_srv_conf;
-            ls->pool_size = cscf->connection_pool_size;
-            ls->post_accept_timeout = cscf->client_header_timeout;
-
-            clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];
-
-            ls->log = *clcf->err_log;
-            ls->log.data = &ls->addr_text;
-            ls->log.handler = ngx_accept_log_error;
-
-#if (NGX_WIN32)
-            iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);
-            if (iocpcf->acceptex_read) {
-                ls->post_accept_buffer_size = cscf->client_header_buffer_size;
-            }
-#endif
-
-            ls->backlog = in_addr[a].listen_conf->backlog;
-            ls->rcvbuf = in_addr[a].listen_conf->rcvbuf;
-            ls->sndbuf = in_addr[a].listen_conf->sndbuf;
-
-#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
-            ls->accept_filter = in_addr[a].listen_conf->accept_filter;
-#endif
-
-#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
-            ls->deferred_accept = in_addr[a].listen_conf->deferred_accept;
-#endif
-
-            hip = ngx_palloc(cf->pool, sizeof(ngx_http_in_port_t));
-            if (hip == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            hip->port = in_port[p].port;
-
-            hip->port_text.data = ngx_palloc(cf->pool, 7);
-            if (hip->port_text.data == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            ls->servers = hip;
-
-            hip->port_text.len = ngx_sprintf(hip->port_text.data, ":%d",
-                                             hip->port)
-                                 - hip->port_text.data;
-
-            in_addr = in_port[p].addrs.elts;
-
-            if (in_addr[a].bind && in_addr[a].addr != INADDR_ANY) {
-                hip->naddrs = 1;
-                done = 0;
-
-            } else if (in_port[p].addrs.nelts > 1
-                       && in_addr[last - 1].addr == INADDR_ANY)
-            {
-                hip->naddrs = last;
-                done = 1;
-
-            } else {
-                hip->naddrs = 1;
-                done = 0;
-            }
-
-#if 0
-            ngx_log_error(NGX_LOG_ALERT, cf->log, 0,
-                          "%ui: %V %d %ui %ui",
-                          a, &ls->addr_text, in_addr[a].bind,
-                          hip->naddrs, last);
-#endif
-
-            hip->addrs = ngx_pcalloc(cf->pool,
-                                     hip->naddrs * sizeof(ngx_http_in_addr_t));
-            if (hip->addrs == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            for (i = 0; i < hip->naddrs; i++) {
-                hip->addrs[i].addr = in_addr[i].addr;
-                hip->addrs[i].core_srv_conf = in_addr[i].core_srv_conf;
-
-                if (in_addr[i].hash.buckets == NULL
-                    && (in_addr[i].wc_head == NULL
-                        || in_addr[i].wc_head->hash.buckets == NULL)
-                    && (in_addr[i].wc_head == NULL
-                        || in_addr[i].wc_head->hash.buckets == NULL))
-                {
-                    continue;
-                }
-
-                vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));
-                if (vn == NULL) {
-                    return NGX_CONF_ERROR;
-                }
-                hip->addrs[i].virtual_names = vn;
-
-                vn->names.hash = in_addr[i].hash;
-                vn->names.wc_head = in_addr[i].wc_head;
-                vn->names.wc_tail = in_addr[i].wc_tail;
-#if (NGX_PCRE)
-                vn->nregex = in_addr[i].nregex;
-                vn->regex = in_addr[i].regex;
-#endif
-            }
-
-            if (done) {
-                break;
-            }
-
-            in_addr++;
-            in_port[p].addrs.elts = in_addr;
-            last--;
-
-            a = 0;
-        }
-    }
-
-#if 0
-    {
-    u_char      address[20];
-    ngx_uint_t  p, a;
-
-    in_port = in_ports.elts;
-    for (p = 0; p < in_ports.nelts; p++) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0,
-                      "port: %d %p", in_port[p].port, &in_port[p]);
-        in_addr = in_port[p].addrs.elts;
-        for (a = 0; a < in_port[p].addrs.nelts; a++) {
-            ngx_inet_ntop(AF_INET, &in_addr[a].addr, address, 20);
-            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, cf->log, 0,
-                           "%s:%d %p",
-                           address, in_port[p].port, in_addr[a].core_srv_conf);
-            name = in_addr[a].names.elts;
-            for (n = 0; n < in_addr[a].names.nelts; n++) {
-                 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, cf->log, 0,
-                                "%s:%d %V %p",
-                                address, in_port[p].port, &name[n].name,
-                                name[n].core_srv_conf);
-            }
-        }
-    }
-    }
-#endif
-
-    return NGX_CONF_OK;
-}
-
-
-/*
- * add the server address, the server names and the server core module
- * configurations to the port (in_port)
- */
-
-static ngx_int_t
-ngx_http_add_address(ngx_conf_t *cf, ngx_http_conf_in_port_t *in_port,
-    ngx_http_listen_t *lscf, ngx_http_core_srv_conf_t *cscf)
-{
-    ngx_http_conf_in_addr_t  *in_addr;
-
-    if (in_port->addrs.elts == NULL) {
-        if (ngx_array_init(&in_port->addrs, cf->temp_pool, 4,
-                           sizeof(ngx_http_conf_in_addr_t))
-            != NGX_OK)
-        {
+        if (ngx_http_init_listening(cf, &in_port[p]) != NGX_OK) {
             return NGX_ERROR;
         }
     }
 
-    in_addr = ngx_array_push(&in_port->addrs);
-    if (in_addr == NULL) {
-        return NGX_ERROR;
-    }
+    return NGX_OK;
+
+failed:
 
-    in_addr->addr = lscf->addr;
-    in_addr->hash.buckets = NULL;
-    in_addr->hash.size = 0;
-    in_addr->wc_head = NULL;
-    in_addr->wc_tail = NULL;
-    in_addr->names.elts = NULL;
-#if (NGX_PCRE)
-    in_addr->nregex = 0;
-    in_addr->regex = NULL;
-#endif
-    in_addr->core_srv_conf = cscf;
-    in_addr->default_server = lscf->conf.default_server;
-    in_addr->bind = lscf->conf.bind;
-    in_addr->listen_conf = &lscf->conf;
+    ngx_destroy_pool(ha.temp_pool);
 
-#if (NGX_DEBUG)
-    {
-    u_char text[20];
-    ngx_inet_ntop(AF_INET, &in_addr->addr, text, 20);
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0, "address: %s:%d",
-                   text, in_port->port);
-    }
-#endif
-
-    return ngx_http_add_names(cf, in_addr, cscf);
+    return NGX_ERROR;
 }
 
 
-/*
- * add the server names and the server core module
- * configurations to the address:port (in_addr)
- */
-
 static ngx_int_t
-ngx_http_add_names(ngx_conf_t *cf, ngx_http_conf_in_addr_t *in_addr,
-    ngx_http_core_srv_conf_t *cscf)
-{
-    ngx_uint_t               i, n;
-    ngx_http_server_name_t  *server_names, *name;
-
-    if (in_addr->names.elts == NULL) {
-        if (ngx_array_init(&in_addr->names, cf->temp_pool, 4,
-                           sizeof(ngx_http_server_name_t))
-            != NGX_OK)
-        {
-            return NGX_ERROR;
-        }
-    }
-
-    server_names = cscf->server_names.elts;
-
-    for (i = 0; i < cscf->server_names.nelts; i++) {
-
-        for (n = 0; n < server_names[i].name.len; n++) {
-            server_names[i].name.data[n] =
-                                     ngx_tolower(server_names[i].name.data[n]);
-        }
-
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0,
-                       "name: %V", &server_names[i].name);
-
-        name = ngx_array_push(&in_addr->names);
-        if (name == NULL) {
-            return NGX_ERROR;
-        }
-
-        *name = server_names[i];
-    }
-
-    return NGX_OK;
-}
-
-
-static char *
-ngx_http_merge_locations(ngx_conf_t *cf, ngx_array_t *locations,
-    void **loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index)
-{
-    char                       *rv;
-    ngx_uint_t                  i;
-    ngx_http_core_loc_conf_t  **clcfp;
-
-    clcfp = locations->elts;
-
-    for (i = 0; i < locations->nelts; i++) {
-        rv = module->merge_loc_conf(cf, loc_conf[ctx_index],
-                                    clcfp[i]->loc_conf[ctx_index]);
-        if (rv != NGX_CONF_OK) {
-            return rv;
-        }
-
-        if (clcfp[i]->locations == NULL) {
-            continue;
-        }
-
-        rv = ngx_http_merge_locations(cf, clcfp[i]->locations,
-                                      clcfp[i]->loc_conf, module, ctx_index);
-        if (rv != NGX_CONF_OK) {
-            return rv;
-        }
-    }
-
-    return NGX_CONF_OK;
-}
-
-
-static int
 ngx_http_cmp_conf_in_addrs(const void *one, const void *two)
 {
     ngx_http_conf_in_addr_t  *first, *second;
@@ -1127,3 +1525,297 @@ ngx_http_cmp_dns_wildcards(const void *o
 
     return ngx_strcmp(first->key.data, second->key.data);
 }
+
+
+static ngx_int_t
+ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_in_port_t *in_port)
+{
+    ngx_uint_t                 i, a, last, bind_all, done;
+    ngx_listening_t           *ls;
+    ngx_http_in_port_t        *hip;
+    ngx_http_conf_in_addr_t   *in_addr;
+    ngx_http_virtual_names_t  *vn;
+    ngx_http_core_loc_conf_t  *clcf;
+    ngx_http_core_srv_conf_t  *cscf;
+
+    in_addr = in_port->addrs.elts;
+    last = in_port->addrs.nelts;
+
+    /*
+     * if there is a binding to a "*:port" then we need to bind()
+     * to the "*:port" only and ignore other bindings
+     */
+
+    if (in_addr[last - 1].addr == INADDR_ANY) {
+        in_addr[last - 1].bind = 1;
+        bind_all = 0;
+
+    } else {
+        bind_all = 1;
+    }
+
+    a = 0;
+
+    while (a < last) {
+
+        if (!bind_all && !in_addr[a].bind) {
+            a++;
+            continue;
+        }
+
+        ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr,
+                                              in_port->port);
+        if (ls == NULL) {
+            return NGX_ERROR;
+        }
+
+        ls->addr_ntop = 1;
+
+        ls->handler = ngx_http_init_connection;
+
+        cscf = in_addr[a].core_srv_conf;
+        ls->pool_size = cscf->connection_pool_size;
+        ls->post_accept_timeout = cscf->client_header_timeout;
+
+        clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];
+
+        ls->log = *clcf->err_log;
+        ls->log.data = &ls->addr_text;
+        ls->log.handler = ngx_accept_log_error;
+
+#if (NGX_WIN32)
+        {
+        ngx_iocp_conf_t  *iocpcf;
+
+        iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);
+        if (iocpcf->acceptex_read) {
+            ls->post_accept_buffer_size = cscf->client_header_buffer_size;
+        }
+        }
+#endif
+
+        ls->backlog = in_addr[a].listen_conf->backlog;
+        ls->rcvbuf = in_addr[a].listen_conf->rcvbuf;
+        ls->sndbuf = in_addr[a].listen_conf->sndbuf;
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+        ls->accept_filter = in_addr[a].listen_conf->accept_filter;
+#endif
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+        ls->deferred_accept = in_addr[a].listen_conf->deferred_accept;
+#endif
+
+        hip = ngx_palloc(cf->pool, sizeof(ngx_http_in_port_t));
+        if (hip == NULL) {
+            return NGX_ERROR;
+        }
+
+        hip->port = in_port->port;
+
+        hip->port_text.data = ngx_pnalloc(cf->pool, 7);
+        if (hip->port_text.data == NULL) {
+            return NGX_ERROR;
+        }
+
+        ls->servers = hip;
+
+        hip->port_text.len = ngx_sprintf(hip->port_text.data, ":%d", hip->port)
+                             - hip->port_text.data;
+
+        in_addr = in_port->addrs.elts;
+
+        if (in_addr[a].bind && in_addr[a].addr != INADDR_ANY) {
+            hip->naddrs = 1;
+            done = 0;
+
+        } else if (in_port->addrs.nelts > 1
+                   && in_addr[last - 1].addr == INADDR_ANY)
+        {
+            hip->naddrs = last;
+            done = 1;
+
+        } else {
+            hip->naddrs = 1;
+            done = 0;
+        }
+
+        hip->addrs = ngx_pcalloc(cf->pool,
+                                 hip->naddrs * sizeof(ngx_http_in_addr_t));
+        if (hip->addrs == NULL) {
+            return NGX_ERROR;
+        }
+
+        for (i = 0; i < hip->naddrs; i++) {
+            hip->addrs[i].addr = in_addr[i].addr;
+            hip->addrs[i].core_srv_conf = in_addr[i].core_srv_conf;
+
+            if (in_addr[i].hash.buckets == NULL
+                && (in_addr[i].wc_head == NULL
+                    || in_addr[i].wc_head->hash.buckets == NULL)
+                && (in_addr[i].wc_head == NULL
+                    || in_addr[i].wc_head->hash.buckets == NULL))
+            {
+                continue;
+            }
+
+            vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));
+            if (vn == NULL) {
+                return NGX_ERROR;
+            }
+            hip->addrs[i].virtual_names = vn;
+
+            vn->names.hash = in_addr[i].hash;
+            vn->names.wc_head = in_addr[i].wc_head;
+            vn->names.wc_tail = in_addr[i].wc_tail;
+#if (NGX_PCRE)
+            vn->nregex = in_addr[i].nregex;
+            vn->regex = in_addr[i].regex;
+#endif
+        }
+
+        if (done) {
+            return NGX_OK;
+        }
+
+        in_addr++;
+        in_port->addrs.elts = in_addr;
+        last--;
+
+        a = 0;
+    }
+
+    return NGX_OK;
+}
+
+
+char *
+ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    char  *p = conf;
+
+    ngx_array_t     **types;
+    ngx_str_t        *value, *default_type;
+    ngx_uint_t        i, n, hash;
+    ngx_hash_key_t   *type;
+
+    types = (ngx_array_t **) (p + cmd->offset);
+
+    default_type = cmd->post;
+
+    if (*types == NULL) {
+        *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t));
+        if (*types == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        if (default_type) {
+            type = ngx_array_push(*types);
+            if (type == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            type->key = *default_type;
+            type->key_hash = ngx_hash_key(default_type->data,
+                                          default_type->len);
+            type->value = (void *) 4;
+        }
+    }
+
+    value = cf->args->elts;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len);
+        value[i].data[value[i].len] = '\0';
+
+        type = (*types)->elts;
+        for (n = 0; n < (*types)->nelts; n++) {
+
+            if (ngx_strcmp(value[i].data, type[n].key.data) == 0) {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                                   "duplicate MIME type \"%V\"", &value[i]);
+                continue;
+            }
+        }
+
+        type = ngx_array_push(*types);
+        if (type == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        type->key = value[i];
+        type->key_hash = hash;
+        type->value = (void *) 4;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+char *
+ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t *keys, ngx_hash_t *types_hash,
+    ngx_array_t *prev_keys, ngx_hash_t *prev_types_hash, 
+    ngx_str_t *default_types)
+{
+    ngx_hash_init_t  hash;
+
+    if (keys == NULL) {
+
+        if (prev_keys) {
+            *types_hash = *prev_types_hash;
+            return NGX_CONF_OK;
+        }
+
+        if (ngx_http_set_default_types(cf, &keys, default_types)
+            != NGX_CONF_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    hash.hash = types_hash;
+    hash.key = NULL;
+    hash.max_size = 2048;
+    hash.bucket_size = 64;
+    hash.name = "test_types_hash";
+    hash.pool = cf->pool;
+    hash.temp_pool = NULL;
+
+    if (ngx_hash_init(&hash, keys->elts, keys->nelts) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+
+}
+
+
+char *
+ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,
+    ngx_str_t *default_type)
+{
+    ngx_hash_key_t  *type;
+
+    *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t));
+    if (*types == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    while (default_type->len) {
+
+        type = ngx_array_push(*types);
+        if (type == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        type->key = *default_type;
+        type->key_hash = ngx_hash_key(default_type->data,
+                                      default_type->len);
+        type->value = (void *) 4;
+
+        default_type++;
+    }
+
+    return NGX_CONF_OK;
+}
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -47,7 +47,7 @@ typedef u_char *(*ngx_http_log_handler_p
 
 
 struct ngx_http_log_ctx_s {
-    ngx_str_t           *client;
+    ngx_connection_t    *connection;
     ngx_http_request_t  *request;
     ngx_http_request_t  *current_request;
 };
@@ -57,6 +57,10 @@ struct ngx_http_log_ctx_s {
 #define ngx_http_set_ctx(r, c, module)      r->ctx[module.ctx_index] = c;
 
 
+ngx_int_t ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,
+    ngx_http_core_loc_conf_t *clcf);
+
+
 void ngx_http_init_connection(ngx_connection_t *c);
 
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
@@ -64,7 +68,8 @@ int ngx_http_ssl_servername(ngx_ssl_conn
 #endif
 
 ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b);
-ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r);
+ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r,
+    ngx_uint_t merge_slashes);
 ngx_int_t ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
     ngx_str_t *args, ngx_uint_t *flags);
 ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b);
@@ -102,20 +107,19 @@ ngx_int_t ngx_http_discard_request_body(
 void ngx_http_block_reading(ngx_http_request_t *r);
 
 
-extern ngx_module_t  ngx_http_module;
+char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t *keys,
+    ngx_hash_t *types_hash, ngx_array_t *prev_keys, ngx_hash_t *prev_types_hash,
+    ngx_str_t *default_types);
+char *ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,
+    ngx_str_t *default_type);
 
 
-extern ngx_uint_t  ngx_http_total_requests;
-extern uint64_t    ngx_http_total_sent;
+extern ngx_module_t  ngx_http_module;
 
 
 extern ngx_http_output_header_filter_pt  ngx_http_top_header_filter;
 extern ngx_http_output_body_filter_pt    ngx_http_top_body_filter;
 
 
-/* STUB */
-ngx_int_t ngx_http_log_handler(ngx_http_request_t *r);
-/**/
-
-
 #endif /* _NGX_HTTP_H_INCLUDED_ */
--- a/src/http/ngx_http_busy_lock.c
+++ b/src/http/ngx_http_busy_lock.c
@@ -10,9 +10,9 @@
 
 
 
-static int ngx_http_busy_lock_look_cachable(ngx_http_busy_lock_t *bl,
-                                            ngx_http_busy_lock_ctx_t *bc,
-                                            int lock);
+static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl,
+                                             ngx_http_busy_lock_ctx_t *bc,
+                                             int lock);
 
 
 int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc)
@@ -60,12 +60,12 @@ int ngx_http_busy_lock(ngx_http_busy_loc
 }
 
 
-int ngx_http_busy_lock_cachable(ngx_http_busy_lock_t *bl,
-                                ngx_http_busy_lock_ctx_t *bc, int lock)
+int ngx_http_busy_lock_cacheable(ngx_http_busy_lock_t *bl,
+                                 ngx_http_busy_lock_ctx_t *bc, int lock)
 {
     int  rc;
 
-    rc = ngx_http_busy_lock_look_cachable(bl, bc, lock);
+    rc = ngx_http_busy_lock_look_cacheable(bl, bc, lock);
 
     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, bc->event->log, 0,
                    "http busylock: %d w:%d mw::%d",
@@ -121,22 +121,22 @@ void ngx_http_busy_unlock(ngx_http_busy_
 
     if (bl->md5) {
         bl->md5_mask[bc->slot / 8] &= ~(1 << (bc->slot & 7));
-        bl->cachable--;
+        bl->cacheable--;
     }
 
     bl->busy--;
 }
 
 
-static int ngx_http_busy_lock_look_cachable(ngx_http_busy_lock_t *bl,
-                                            ngx_http_busy_lock_ctx_t *bc,
-                                            int lock)
+static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl,
+                                             ngx_http_busy_lock_ctx_t *bc,
+                                             int lock)
 {
-    int    i, b, cachable, free;
+    int    i, b, cacheable, free;
     u_int  mask;
 
     b = 0;
-    cachable = 0;
+    cacheable = 0;
     free = -1;
 
 #if (NGX_SUPPRESS_WARN)
@@ -153,15 +153,15 @@ static int ngx_http_busy_lock_look_cacha
             if (ngx_memcmp(&bl->md5[i * 16], bc->md5, 16) == 0) {
                 return NGX_AGAIN;
             }
-            cachable++;
+            cacheable++;
 
         } else if (free == -1) {
             free = i;
         }
 
 #if 1
-        if (cachable == bl->cachable) {
-            if (free == -1 && cachable < bl->max_busy) {
+        if (cacheable == bl->cacheable) {
+            if (free == -1 && cacheable < bl->max_busy) {
                 free = i + 1;
             }
 
@@ -186,7 +186,7 @@ static int ngx_http_busy_lock_look_cacha
         bl->md5_mask[free / 8] |= 1 << (free & 7);
         bc->slot = free;
 
-        bl->cachable++;
+        bl->cacheable++;
         bl->busy++;
     }
 
--- a/src/http/ngx_http_busy_lock.h
+++ b/src/http/ngx_http_busy_lock.h
@@ -17,7 +17,7 @@
 typedef struct {
     u_char             *md5_mask;
     char               *md5;
-    int                 cachable;
+    int                 cacheable;
 
     int                 busy;
     int                 max_busy;
@@ -41,8 +41,8 @@ typedef struct {
 
 
 int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc);
-int ngx_http_busy_lock_cachable(ngx_http_busy_lock_t *bl,
-                                ngx_http_busy_lock_ctx_t *bc, int lock);
+int ngx_http_busy_lock_cacheable(ngx_http_busy_lock_t *bl,
+                                 ngx_http_busy_lock_ctx_t *bc, int lock);
 void ngx_http_busy_unlock(ngx_http_busy_lock_t *bl,
                           ngx_http_busy_lock_ctx_t *bc);
 
--- a/src/http/ngx_http_copy_filter_module.c
+++ b/src/http/ngx_http_copy_filter_module.c
@@ -121,11 +121,8 @@ ngx_http_copy_filter(ngx_http_request_t 
             r->out = ctx->in;
         }
 
-#if (NGX_DEBUG)
         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args);
-#endif
-
     }
 
     return rc;
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -17,19 +17,15 @@ typedef struct {
 } ngx_http_method_name_t;
 
 
-#define NGX_HTTP_LOCATION_EXACT           1
-#define NGX_HTTP_LOCATION_AUTO_REDIRECT   2
-#define NGX_HTTP_LOCATION_NOREGEX         3
-#define NGX_HTTP_LOCATION_REGEX           4
-
-
 #define NGX_HTTP_REQUEST_BODY_FILE_OFF    0
 #define NGX_HTTP_REQUEST_BODY_FILE_ON     1
 #define NGX_HTTP_REQUEST_BODY_FILE_CLEAN  2
 
 
-static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r,
-    ngx_array_t *locations, ngx_uint_t regex_start, size_t len);
+static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r);
+static ngx_int_t ngx_http_core_find_static_location(ngx_http_request_t *r,
+    ngx_http_location_tree_node_t *node);
+static ngx_int_t ngx_http_core_send_continue(ngx_http_request_t *r);
 
 static ngx_int_t ngx_http_core_preconfiguration(ngx_conf_t *cf);
 static void *ngx_http_core_create_main_conf(ngx_conf_t *cf);
@@ -45,7 +41,6 @@ static char *ngx_http_core_server(ngx_co
     void *dummy);
 static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd,
     void *dummy);
-static int ngx_http_core_cmp_locations(const void *first, const void *second);
 
 static char *ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
@@ -59,6 +54,8 @@ static char *ngx_http_core_server_name(n
 static char *ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -69,6 +66,12 @@ static char *ngx_http_core_keepalive(ngx
     void *conf);
 static char *ngx_http_core_internal(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+#if (NGX_HTTP_GZIP)
+static char *ngx_http_gzip_disable(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);
@@ -79,8 +82,16 @@ static ngx_conf_post_t  ngx_http_core_lo
 static ngx_conf_post_handler_pt  ngx_http_core_pool_size_p =
     ngx_http_core_pool_size;
 
-static ngx_conf_deprecated_t  ngx_conf_deprecated_optimize_host_names = {
-    ngx_conf_deprecated, "optimize_host_names", "optimize_server_names"
+static ngx_conf_deprecated_t  ngx_conf_deprecated_optimize_server_names = {
+    ngx_conf_deprecated, "optimize_server_names", "server_name_in_redirect"
+};
+
+static ngx_conf_deprecated_t  ngx_conf_deprecated_open_file_cache_retest = {
+    ngx_conf_deprecated, "open_file_cache_retest", "open_file_cache_valid"
+};
+
+static ngx_conf_deprecated_t  ngx_conf_deprecated_satisfy_any = {
+    ngx_conf_deprecated, "satisfy_any", "satisfy"
 };
 
 
@@ -92,6 +103,43 @@ static ngx_conf_enum_t  ngx_http_core_re
 };
 
 
+static ngx_conf_enum_t  ngx_http_core_satisfy[] = {
+    { ngx_string("all"), NGX_HTTP_SATISFY_ALL },
+    { ngx_string("any"), NGX_HTTP_SATISFY_ANY },
+    { ngx_null_string, 0 }
+};
+
+
+#if (NGX_HTTP_GZIP)
+
+static ngx_conf_enum_t  ngx_http_gzip_http_version[] = {
+    { ngx_string("1.0"), NGX_HTTP_VERSION_10 },
+    { ngx_string("1.1"), NGX_HTTP_VERSION_11 },
+    { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_bitmask_t  ngx_http_gzip_proxied_mask[] = {
+    { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF },
+    { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED },
+    { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE },
+    { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE },
+    { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE },
+    { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM },
+    { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG },
+    { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH },
+    { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY },
+    { ngx_null_string, 0 }
+};
+
+
+static ngx_str_t  ngx_http_gzip_no_cache = ngx_string("no-cache");
+static ngx_str_t  ngx_http_gzip_no_store = ngx_string("no-store");
+static ngx_str_t  ngx_http_gzip_private = ngx_string("private");
+
+#endif
+
+
 static ngx_command_t  ngx_http_core_commands[] = {
 
     { ngx_string("variables_hash_max_size"),
@@ -167,16 +215,9 @@ static ngx_command_t  ngx_http_core_comm
     { ngx_string("optimize_server_names"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
-      NGX_HTTP_SRV_CONF_OFFSET,
-      offsetof(ngx_http_core_srv_conf_t, optimize_server_names),
-      NULL },
-
-    { ngx_string("optimize_host_names"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
-      ngx_conf_set_flag_slot,
-      NGX_HTTP_SRV_CONF_OFFSET,
-      offsetof(ngx_http_core_srv_conf_t, optimize_server_names),
-      &ngx_conf_deprecated_optimize_host_names },
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, server_name_in_redirect),
+      &ngx_conf_deprecated_optimize_server_names },
 
     { ngx_string("ignore_invalid_headers"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
@@ -185,6 +226,13 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_srv_conf_t, ignore_invalid_headers),
       NULL },
 
+    { ngx_string("merge_slashes"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_core_srv_conf_t, merge_slashes),
+      NULL },
+
     { ngx_string("location"),
       NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
       ngx_http_core_location,
@@ -307,6 +355,13 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, sendfile_max_chunk),
       NULL },
 
+    { ngx_string("directio"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_http_core_directio,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("tcp_nopush"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -357,12 +412,19 @@ static ngx_command_t  ngx_http_core_comm
       0,
       NULL },
 
+    { ngx_string("satisfy"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, satisfy),
+      &ngx_http_core_satisfy },
+
     { ngx_string("satisfy_any"),
       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_core_loc_conf_t, satisfy_any),
-      NULL },
+      offsetof(ngx_http_core_loc_conf_t, satisfy),
+      &ngx_conf_deprecated_satisfy_any },
 
     { ngx_string("internal"),
       NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
@@ -392,6 +454,13 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, reset_timedout_connection),
       NULL },
 
+    { ngx_string("server_name_in_redirect"),
+      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_core_loc_conf_t, server_name_in_redirect),
+      NULL },
+
     { ngx_string("port_in_redirect"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -427,6 +496,13 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, recursive_error_pages),
       NULL },
 
+    { ngx_string("server_tokens"),
+      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_core_loc_conf_t, server_tokens),
+      NULL },
+
     { ngx_string("error_page"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                         |NGX_CONF_2MORE,
@@ -457,11 +533,25 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, open_file_cache),
       NULL },
 
+    { ngx_string("open_file_cache_valid"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_sec_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, open_file_cache_valid),
+      NULL },
+
     { ngx_string("open_file_cache_retest"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_sec_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_core_loc_conf_t, open_file_cache_retest),
+      offsetof(ngx_http_core_loc_conf_t, open_file_cache_valid),
+      &ngx_conf_deprecated_open_file_cache_retest },
+
+    { ngx_string("open_file_cache_min_uses"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_num_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, open_file_cache_min_uses),
       NULL },
 
     { ngx_string("open_file_cache_errors"),
@@ -478,6 +568,52 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, open_file_cache_events),
       NULL },
 
+    { ngx_string("resolver"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_http_core_resolver,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("resolver_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_core_loc_conf_t, resolver_timeout),
+      NULL },
+
+#if (NGX_HTTP_GZIP)
+
+    { ngx_string("gzip_vary"),
+      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_core_loc_conf_t, gzip_vary),
+      NULL },
+
+    { ngx_string("gzip_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_core_loc_conf_t, gzip_http_version),
+      &ngx_http_gzip_http_version },
+
+    { ngx_string("gzip_proxied"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+      ngx_conf_set_bitmask_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, gzip_proxied),
+      &ngx_http_gzip_proxied_mask },
+
+    { ngx_string("gzip_disable"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+      ngx_http_gzip_disable,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+#endif
+
       ngx_null_command
 };
 
@@ -513,6 +649,9 @@ ngx_module_t  ngx_http_core_module = {
 };
 
 
+static ngx_str_t  ngx_http_core_get_method = { 3, (u_char *) "GET " };
+
+
 void
 ngx_http_handler(ngx_http_request_t *r)
 {
@@ -570,6 +709,7 @@ ngx_http_handler(ngx_http_request_t *r)
     }
 
     r->valid_location = 1;
+    r->gzip = 0;
 
     r->write_event_handler = ngx_http_core_run_phases;
     ngx_http_core_run_phases(r);
@@ -641,16 +781,13 @@ ngx_http_core_find_config_phase(ngx_http
 {
     u_char                    *p;
     size_t                     len;
-    ngx_int_t                  rc;
+    ngx_int_t                  rc, expect;
     ngx_http_core_loc_conf_t  *clcf;
-    ngx_http_core_srv_conf_t  *cscf;
 
     r->content_handler = NULL;
     r->uri_changed = 0;
 
-    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
-
-    rc = ngx_http_core_find_location(r, &cscf->locations, cscf->regex_start, 0);
+    rc = ngx_http_core_find_location(r);
 
     if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) {
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
@@ -681,15 +818,23 @@ ngx_http_core_find_config_phase(ngx_http
         && clcf->client_max_body_size < r->headers_in.content_length_n)
     {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "client intented to send too large body: %O bytes",
+                      "client intended to send too large body: %O bytes",
                       r->headers_in.content_length_n);
 
         ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE);
         return NGX_OK;
     }
 
-
-    if (rc == NGX_HTTP_LOCATION_AUTO_REDIRECT) {
+    if (r->headers_in.expect) {
+        expect = ngx_http_core_send_continue(r);
+
+        if (expect != NGX_OK) {
+            ngx_http_finalize_request(r, expect);
+            return NGX_OK;
+        }
+    }
+
+    if (rc == NGX_DONE) {
         r->headers_out.location = ngx_list_push(&r->headers_out.headers);
         if (r->headers_out.location == NULL) {
             ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
@@ -706,7 +851,7 @@ ngx_http_core_find_config_phase(ngx_http
 
         } else {
             len = clcf->name.len + 1 + r->args.len;
-            p = ngx_palloc(r->pool, len);
+            p = ngx_pnalloc(r->pool, len);
 
             if (p == NULL) {
                 ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
@@ -734,6 +879,8 @@ ngx_int_t
 ngx_http_core_post_rewrite_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph)
 {
+    ngx_http_core_srv_conf_t  *cscf;
+
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "post rewrite phase: %ui", r->phase_handler);
 
@@ -765,6 +912,9 @@ ngx_http_core_post_rewrite_phase(ngx_htt
 
     r->phase_handler = ph->next;
 
+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+    r->loc_conf = cscf->ctx->loc_conf;
+
     return NGX_AGAIN;
 }
 
@@ -796,7 +946,7 @@ ngx_http_core_access_phase(ngx_http_requ
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    if (clcf->satisfy_any == 0) {
+    if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
 
         if (rc == NGX_OK) {
             r->phase_handler++;
@@ -896,7 +1046,7 @@ ngx_http_core_content_phase(ngx_http_req
 
         if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "directory index of \"%V\" is forbidden", &path);
+                          "directory index of \"%s\" is forbidden", path.data);
         }
 
         ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
@@ -968,150 +1118,232 @@ ngx_http_update_location_config(ngx_http
 
 
 static ngx_int_t
-ngx_http_core_find_location(ngx_http_request_t *r,
-    ngx_array_t *locations, ngx_uint_t regex_start, size_t len)
+ngx_http_core_find_location(ngx_http_request_t *r)
 {
-    ngx_int_t                  n, rc;
-    ngx_uint_t                 i, found;
+    ngx_int_t                  rc;
+    ngx_http_core_loc_conf_t  *pclcf;
+
+    pclcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    rc = ngx_http_core_find_static_location(r, pclcf->static_locations);
+
+    if (rc == NGX_AGAIN) {
+        /* look up nested locations */
+        rc = ngx_http_core_find_location(r);
+    }
+
+    if (rc == NGX_OK || rc == NGX_DONE) {
+        return rc;
+    }
+
+    /* rc == NGX_DECLINED or rc == NGX_AGAIN in nested location */
+
+#if (NGX_PCRE)
+    {
+    ngx_int_t                  n;
     ngx_http_core_loc_conf_t  *clcf, **clcfp;
-#if (NGX_PCRE)
-    ngx_uint_t                 noregex;
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    if (clcf->noregex == 0 && pclcf->regex_locations) {
+
+        for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) {
+
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "test location: ~ \"%V\"", &(*clcfp)->name);
+
+            n = ngx_regex_exec((*clcfp)->regex, &r->uri, NULL, 0);
+
+            if (n == NGX_REGEX_NO_MATCHED) {
+                continue;
+            }
+
+            if (n < 0) {
+                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                              ngx_regex_exec_n
+                              " failed: %d on \"%V\" using \"%V\"",
+                              n, &r->uri, &(*clcfp)->name);
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+
+            /* match */
+
+            r->loc_conf = (*clcfp)->loc_conf;
+
+            /* look up nested locations */
+
+            return ngx_http_core_find_location(r);
+        }
+    }
+    }
 #endif
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "find location for \"%V\"", &r->uri);
-
-    found = 0;
-#if (NGX_PCRE)
-    noregex = 0;
-#endif
-
-    clcfp = locations->elts;
-    for (i = 0; i < locations->nelts; i++) {
-
-        if (clcfp[i]->noname
-#if (NGX_PCRE)
-            || clcfp[i]->regex
-#endif
-            || clcfp[i]->named)
-        {
-            break;
+    return rc;
+}
+
+
+/*
+ * NGX_OK       - exact match
+ * NGX_DONE     - auto redirect
+ * NGX_AGAIN    - inclusive match
+ * NGX_DECLINED - no match
+ */
+
+static ngx_int_t
+ngx_http_core_find_static_location(ngx_http_request_t *r,
+    ngx_http_location_tree_node_t *node)
+{
+    u_char     *uri;
+    size_t      len, n;
+    ngx_int_t   rc, rv;
+
+    len = r->uri.len;
+    uri = r->uri.data;
+
+    rv = NGX_DECLINED;
+
+    for ( ;; ) {
+
+        if (node == NULL) {
+            return rv;
         }
 
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "find location: %s\"%V\"",
-                       clcfp[i]->exact_match ? "= " : "", &clcfp[i]->name);
-
-        if (clcfp[i]->auto_redirect
-            && r->uri.len == clcfp[i]->name.len - 1
-            && ngx_strncmp(r->uri.data, clcfp[i]->name.data,
-                           clcfp[i]->name.len - 1)
-                == 0)
-        {
-            /* the locations are lexicographically sorted */
-
-            r->loc_conf = clcfp[i]->loc_conf;
-
-            return NGX_HTTP_LOCATION_AUTO_REDIRECT;
-        }
-
-        if (r->uri.len < clcfp[i]->name.len) {
+                       "test location: \"%*s\"", node->len, node->name);
+
+        n = (len <= (size_t) node->len) ? len : node->len;
+
+        rc = ngx_filename_cmp(uri, node->name, n);
+
+        if (rc != 0) {
+            node = (rc < 0) ? node->left : node->right;
+
             continue;
         }
 
-        n = ngx_strncmp(r->uri.data, clcfp[i]->name.data, clcfp[i]->name.len);
-
-        if (n < 0) {
-            /* the locations are lexicographically sorted */
-            break;
-        }
-
-        if (n == 0) {
-            if (clcfp[i]->exact_match) {
-
-                if (r->uri.len == clcfp[i]->name.len) {
-                    r->loc_conf = clcfp[i]->loc_conf;
-                    return NGX_HTTP_LOCATION_EXACT;
-                }
+        if (len > (size_t) node->len) {
+
+            if (node->inclusive) {
+
+                r->loc_conf = node->inclusive->loc_conf;
+                rv = NGX_AGAIN;
+
+                node = node->tree;
+                uri += n;
+                len -= n;
 
                 continue;
             }
 
-            if (len > clcfp[i]->name.len) {
-                /* the previous match is longer */
-                break;
-            }
-
-            found = 1;
-
-            r->loc_conf = clcfp[i]->loc_conf;
-#if (NGX_PCRE)
-            noregex = clcfp[i]->noregex;
-#endif
-        }
-    }
-
-    if (found) {
-        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
-        if (clcf->locations) {
-            rc = ngx_http_core_find_location(r, clcf->locations,
-                                             clcf->regex_start, len);
-
-            if (rc != NGX_OK) {
-                return rc;
-            }
-        }
-    }
-
-#if (NGX_PCRE)
-
-    if (noregex) {
-        return NGX_HTTP_LOCATION_NOREGEX;
-    }
-
-    /* regex matches */
-
-    for (i = regex_start; i < locations->nelts; i++) {
-
-        if (!clcfp[i]->regex) {
-            break;
-        }
-
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "find location: ~ \"%V\"", &clcfp[i]->name);
-
-        n = ngx_regex_exec(clcfp[i]->regex, &r->uri, NULL, 0);
-
-        if (n == NGX_REGEX_NO_MATCHED) {
+            /* exact only */
+
+            node = node->right;
+
             continue;
         }
 
-        if (n < 0) {
-            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                          ngx_regex_exec_n
-                          " failed: %d on \"%V\" using \"%V\"",
-                          n, &r->uri, &clcfp[i]->name);
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        if (len == (size_t) node->len) {
+
+            r->loc_conf = (node->exact) ? node->exact->loc_conf:
+                                          node->inclusive->loc_conf;
+            return NGX_OK;
+        }
+
+        /* len < node->len */
+
+        if (len + 1 == (size_t) node->len && node->auto_redirect) {
+
+            r->loc_conf = (node->exact) ? node->exact->loc_conf:
+                                          node->inclusive->loc_conf;
+            rv = NGX_DONE;
         }
 
-        /* match */
-
-        r->loc_conf = clcfp[i]->loc_conf;
-
-        return NGX_HTTP_LOCATION_REGEX;
+        node = node->left;
+    }
+}
+
+
+static ngx_int_t
+ngx_http_core_send_continue(ngx_http_request_t *r)
+{
+    ngx_int_t   n;
+    ngx_str_t  *expect;
+
+    if (r->expect_tested) {
+        return NGX_OK;
+    }
+
+    r->expect_tested = 1;
+
+    expect = &r->headers_in.expect->value;
+
+    if (expect->len != sizeof("100-continue") - 1
+        || ngx_strncasecmp(expect->data, (u_char *) "100-continue",
+                           sizeof("100-continue") - 1)
+           != 0)
+    {
+        return NGX_OK;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "send 100 Continue");
+
+    n = r->connection->send(r->connection,
+                            (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
+                            sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
+
+    if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
+        return NGX_OK;
     }
 
-#endif /* NGX_PCRE */
-
-    return NGX_OK;
+    /* we assume that such small packet should be send successfully */
+
+    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+}
+
+
+void *
+ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash)
+{
+    u_char       c, *p;
+    ngx_uint_t   i, hash;
+
+    if (r->headers_out.content_type.len == 0) {
+        return NULL;
+    }
+
+    if (r->headers_out.content_type_lowcase == NULL) {
+
+        p = ngx_pnalloc(r->pool, r->headers_out.content_type_len);
+
+        if (p == NULL) {
+            return NULL;
+        }
+
+        r->headers_out.content_type_lowcase = p;
+
+        hash = 0;
+
+        for (i = 0; i < r->headers_out.content_type_len; i++) {
+            c = ngx_tolower(r->headers_out.content_type.data[i]);
+            hash = ngx_hash(hash, c);
+            *p++ = c;
+        }
+
+        r->headers_out.content_type_hash = hash;
+    }
+
+    return ngx_hash_find(types_hash,
+                         r->headers_out.content_type_hash,
+                         r->headers_out.content_type_lowcase,
+                         r->headers_out.content_type_len);
 }
 
 
 ngx_int_t
 ngx_http_set_content_type(ngx_http_request_t *r)
 {
-    u_char                     c, *p, *exten;
+    u_char                     c, *exten;
     ngx_str_t                 *type;
     ngx_uint_t                 i, hash;
     ngx_http_core_loc_conf_t  *clcf;
@@ -1131,19 +1363,12 @@ ngx_http_set_content_type(ngx_http_reque
 
             if (c >= 'A' && c <= 'Z') {
 
-                p = ngx_palloc(r->pool, r->exten.len);
-                if (p == NULL) {
+                exten = ngx_pnalloc(r->pool, r->exten.len);
+                if (exten == NULL) {
                     return NGX_HTTP_INTERNAL_SERVER_ERROR;
                 }
 
-                hash = 0;
-                exten = p;
-
-                for (i = 0; i < r->exten.len; i++) {
-                    c = ngx_tolower(r->exten.data[i]);
-                    hash = ngx_hash(hash, c);
-                    *p++ = c;
-                }
+                hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len);
 
                 r->exten.data = exten;
 
@@ -1254,7 +1479,7 @@ ngx_http_map_uri_to_path(ngx_http_reques
 
         path->len = clcf->root.len + reserved;
 
-        path->data = ngx_palloc(r->pool, path->len);
+        path->data = ngx_pnalloc(r->pool, path->len);
         if (path->data == NULL) {
             return NULL;
         }
@@ -1324,7 +1549,7 @@ ngx_http_auth_basic_user(ngx_http_reques
     }
 
     auth.len = ngx_base64_decoded_length(encoded.len);
-    auth.data = ngx_palloc(r->pool, auth.len + 1);
+    auth.data = ngx_pnalloc(r->pool, auth.len + 1);
     if (auth.data == NULL) {
         return NGX_ERROR;
     }
@@ -1357,6 +1582,196 @@ ngx_http_auth_basic_user(ngx_http_reques
 
 
 ngx_int_t
+ngx_http_server_addr(ngx_http_request_t *r, ngx_str_t *s)
+{
+    socklen_t            len;
+    ngx_connection_t    *c;
+    struct sockaddr_in   sin;
+
+    /* AF_INET only */
+
+    c = r->connection;
+
+    if (r->in_addr == 0) {
+        len = sizeof(struct sockaddr_in);
+        if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) {
+            ngx_connection_error(c, ngx_socket_errno, "getsockname() failed");
+            return NGX_ERROR;
+        }
+
+        r->in_addr = sin.sin_addr.s_addr;
+    }
+
+    if (s == NULL) {
+        return NGX_OK;
+    }
+
+    s->len = ngx_inet_ntop(c->listening->family, &r->in_addr,
+                           s->data, INET_ADDRSTRLEN);
+
+    return NGX_OK;
+}
+
+
+#if (NGX_HTTP_GZIP)
+
+ngx_int_t
+ngx_http_gzip_ok(ngx_http_request_t *r)
+{
+    time_t                     date, expires;
+    ngx_uint_t                 p;
+    ngx_array_t               *cc;
+    ngx_table_elt_t           *e, *d;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    if (r->gzip == 1) {
+        return NGX_OK;
+    }
+
+    if (r->gzip == 2) {
+        return NGX_DECLINED;
+    }
+
+    r->gzip = 2;
+
+    if (r != r->main
+        || r->headers_in.accept_encoding == NULL
+        || ngx_strcasestrn(r->headers_in.accept_encoding->value.data,
+                           "gzip", 4 - 1)
+           == NULL
+
+        /*
+         * if the URL (without the "http://" prefix) is longer than 253 bytes,
+         * then MSIE 4.x can not handle the compressed stream - it waits
+         * too long, hangs up or crashes
+         */
+
+        || (r->headers_in.msie4 && r->unparsed_uri.len > 200))
+    {
+        return NGX_DECLINED;
+    }
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    if (r->http_version < clcf->gzip_http_version) {
+        return NGX_DECLINED;
+    }
+
+    if (r->headers_in.via == NULL) {
+        goto ok;
+    }
+
+    p = clcf->gzip_proxied;
+
+    if (p & NGX_HTTP_GZIP_PROXIED_OFF) {
+        return NGX_DECLINED;
+    }
+
+    if (p & NGX_HTTP_GZIP_PROXIED_ANY) {
+        goto ok;
+    }
+
+    if (r->headers_in.authorization && (p & NGX_HTTP_GZIP_PROXIED_AUTH)) {
+        goto ok;
+    }
+
+    e = r->headers_out.expires;
+
+    if (e) {
+
+        if (!(p & NGX_HTTP_GZIP_PROXIED_EXPIRED)) {
+            return NGX_DECLINED;
+        }
+
+        expires = ngx_http_parse_time(e->value.data, e->value.len);
+        if (expires == NGX_ERROR) {
+            return NGX_DECLINED;
+        }
+
+        d = r->headers_out.date;
+
+        if (d) {
+            date = ngx_http_parse_time(d->value.data, d->value.len);
+            if (date == NGX_ERROR) {
+                return NGX_DECLINED;
+            }
+
+        } else {
+            date = ngx_time();
+        }
+
+        if (expires < date) {
+            goto ok;
+        }
+
+        return NGX_DECLINED;
+    }
+
+    cc = &r->headers_out.cache_control;
+
+    if (cc->elts) {
+
+        if ((p & NGX_HTTP_GZIP_PROXIED_NO_CACHE)
+            && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_no_cache,
+                                                 NULL)
+               >= 0)
+        {
+            goto ok;
+        }
+
+        if ((p & NGX_HTTP_GZIP_PROXIED_NO_STORE)
+            && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_no_store,
+                                                 NULL)
+               >= 0)
+        {
+            goto ok;
+        }
+
+        if ((p & NGX_HTTP_GZIP_PROXIED_PRIVATE)
+            && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_private,
+                                                 NULL)
+               >= 0)
+        {
+            goto ok;
+        }
+
+        return NGX_DECLINED;
+    }
+
+    if ((p & NGX_HTTP_GZIP_PROXIED_NO_LM) && r->headers_out.last_modified) {
+        return NGX_DECLINED;
+    }
+
+    if ((p & NGX_HTTP_GZIP_PROXIED_NO_ETAG) && r->headers_out.etag) {
+        return NGX_DECLINED;
+    }
+
+ok:
+
+#if (NGX_PCRE)
+
+    if (clcf->gzip_disable && r->headers_in.user_agent) {
+
+        if (ngx_regex_exec_array(clcf->gzip_disable,
+                                 &r->headers_in.user_agent->value,
+                                 r->connection->log)
+            != NGX_DECLINED)
+        {
+            return NGX_DECLINED;
+        }
+    }
+
+#endif
+
+    r->gzip = 1;
+
+    return NGX_OK;
+}
+
+#endif
+
+
+ngx_int_t
 ngx_http_subrequest(ngx_http_request_t *r,
     ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,
     ngx_http_post_subrequest_t *ps, ngx_uint_t flags)
@@ -1430,7 +1845,7 @@ ngx_http_subrequest(ngx_http_request_t *
     sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;
 
     sr->unparsed_uri = r->unparsed_uri;
-    sr->method_name = r->method_name;
+    sr->method_name = ngx_http_core_get_method;
     sr->http_protocol = r->http_protocol;
 
     if (ngx_http_set_exten(sr) != NGX_OK) {
@@ -1450,7 +1865,6 @@ ngx_http_subrequest(ngx_http_request_t *
     sr->in_addr = r->in_addr;
     sr->port = r->port;
     sr->port_text = r->port_text;
-    sr->server_name = r->server_name;
 
     sr->variables = r->variables;
 
@@ -1563,44 +1977,46 @@ ngx_http_internal_redirect(ngx_http_requ
 ngx_int_t
 ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name)
 {
-    ngx_uint_t                   i;
     ngx_http_core_srv_conf_t    *cscf;
     ngx_http_core_loc_conf_t   **clcfp;
     ngx_http_core_main_conf_t   *cmcf;
 
     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
 
-    clcfp = cscf->locations.elts;
-
-    for (i = cscf->named_start; i < cscf->locations.nelts; i++) {
-
-        if (name->len != clcfp[i]->name.len
-            || ngx_strncmp(name->data, clcfp[i]->name.data, name->len) != 0)
+    for (clcfp = cscf->named_locations; *clcfp; clcfp++) {
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "test location: \"%V\"", &(*clcfp)->name);
+
+        if (name->len != (*clcfp)->name.len
+            || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0)
         {
             continue;
         }
 
         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "named location: %V \"%V?%V\"", name, &r->uri, &r->args);
+                       "using location: %V \"%V?%V\"", name, &r->uri, &r->args);
 
         r->internal = 1;
-
-        r->loc_conf = clcfp[i]->loc_conf;
+        r->content_handler = NULL;
+        r->loc_conf = (*clcfp)->loc_conf;
 
         ngx_http_update_location_config(r);
 
         cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
 
         r->phase_handler = cmcf->phase_engine.location_rewrite_index;
+
         ngx_http_core_run_phases(r);
 
         return NGX_DONE;
     }
 
     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                  "could not find name location \"%V\"", name);
+                  "could not find named location \"%V\"", name);
 
     ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+
     return NGX_DONE;
 }
 
@@ -1649,7 +2065,6 @@ ngx_http_core_server(ngx_conf_t *cf, ngx
     ngx_http_module_t           *module;
     ngx_http_conf_ctx_t         *ctx, *http_ctx;
     ngx_http_core_srv_conf_t    *cscf, **cscfp;
-    ngx_http_core_loc_conf_t   **clcfp;
     ngx_http_core_main_conf_t   *cmcf;
 
     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
@@ -1727,37 +2142,6 @@ ngx_http_core_server(ngx_conf_t *cf, ngx
 
     *cf = pcf;
 
-    if (rv != NGX_CONF_OK) {
-        return rv;
-    }
-
-    ngx_sort(cscf->locations.elts, (size_t) cscf->locations.nelts,
-             sizeof(ngx_http_core_loc_conf_t *), ngx_http_core_cmp_locations);
-
-    clcfp = cscf->locations.elts;
-
-#if (NGX_PCRE)
-
-    cscf->regex_start = cscf->locations.nelts;
-
-    for (i = 0; i < cscf->locations.nelts; i++) {
-        if (clcfp[i]->regex) {
-            cscf->regex_start = i;
-            break;
-        }
-    }
-
-#endif
-
-    cscf->named_start = cscf->locations.nelts;
-
-    for (i = 0; i < cscf->locations.nelts; i++) {
-        if (clcfp[i]->named) {
-            cscf->named_start = i;
-            break;
-        }
-    }
-
     return rv;
 }
 
@@ -1771,12 +2155,7 @@ ngx_http_core_location(ngx_conf_t *cf, n
     ngx_conf_t                 save;
     ngx_http_module_t         *module;
     ngx_http_conf_ctx_t       *ctx, *pctx;
-    ngx_http_core_srv_conf_t  *cscf;
-    ngx_http_core_loc_conf_t  *clcf, *pclcf, **clcfp;
-#if (NGX_PCRE)
-    ngx_str_t                  err;
-    u_char                     errstr[NGX_MAX_CONF_ERRSTR];
-#endif
+    ngx_http_core_loc_conf_t  *clcf, *pclcf;
 
     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
     if (ctx == NULL) {
@@ -1831,6 +2210,9 @@ ngx_http_core_location(ngx_conf_t *cf, n
                        && value[1].data[1] == '*'))
         {
 #if (NGX_PCRE)
+            ngx_str_t  err;
+            u_char     errstr[NGX_MAX_CONF_ERRSTR];
+
             err.len = NGX_MAX_CONF_ERRSTR;
             err.data = errstr;
 
@@ -1868,15 +2250,10 @@ ngx_http_core_location(ngx_conf_t *cf, n
 
     pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
 
-    if (pclcf->name.len == 0) {
-        cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
-
-        clcfp = ngx_array_push(&cscf->locations);
-        if (clcfp == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-    } else {
+    if (pclcf->name.len) {
+
+        /* nested location */
+
 #if 0
         clcf->prev_location = pclcf;
 #endif
@@ -1897,6 +2274,14 @@ ngx_http_core_location(ngx_conf_t *cf, n
             return NGX_CONF_ERROR;
         }
 
+        if (clcf->named) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "named location \"%V\" must be "
+                               "on server level only",
+                               &clcf->name);
+            return NGX_CONF_ERROR;
+        }
+
 #if (NGX_PCRE)
         if (clcf->regex == NULL
             && ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len)
@@ -1911,22 +2296,11 @@ ngx_http_core_location(ngx_conf_t *cf, n
                                &clcf->name, &pclcf->name);
             return NGX_CONF_ERROR;
         }
-
-        if (pclcf->locations == NULL) {
-            pclcf->locations = ngx_array_create(cf->pool, 2, sizeof(void *));
-
-            if (pclcf->locations == NULL) {
-                return NGX_CONF_ERROR;
-            }
-        }
-
-        clcfp = ngx_array_push(pclcf->locations);
-        if (clcfp == NULL) {
-            return NGX_CONF_ERROR;
-        }
     }
 
-    *clcfp = clcf;
+    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
 
     save = *cf;
     cf->ctx = ctx;
@@ -1936,103 +2310,10 @@ ngx_http_core_location(ngx_conf_t *cf, n
 
     *cf = save;
 
-    if (rv != NGX_CONF_OK) {
-        return rv;
-    }
-
-    if (clcf->locations == NULL) {
-        return rv;
-    }
-
-    ngx_sort(clcf->locations->elts, (size_t) clcf->locations->nelts,
-             sizeof(ngx_http_core_loc_conf_t *), ngx_http_core_cmp_locations);
-
-#if (NGX_PCRE)
-
-    clcf->regex_start = clcf->locations->nelts;
-    clcfp = clcf->locations->elts;
-
-    for (i = 0; i < clcf->locations->nelts; i++) {
-        if (clcfp[i]->regex) {
-            clcf->regex_start = i;
-            break;
-        }
-    }
-
-#endif
-
     return rv;
 }
 
 
-static int
-ngx_http_core_cmp_locations(const void *one, const void *two)
-{
-    ngx_int_t                  rc;
-    ngx_http_core_loc_conf_t  *first, *second;
-
-    first = *(ngx_http_core_loc_conf_t **) one;
-    second = *(ngx_http_core_loc_conf_t **) two;
-
-    if (first->named && !second->named) {
-        /* shift named locations to the end */
-        return 1;
-    }
-
-    if (!first->named && second->named) {
-        /* shift named locations to the end */
-        return -1;
-    }
-
-    if (first->named && second->named) {
-        return ngx_strcmp(first->name.data, second->name.data);
-    }
-
-    if (first->noname && !second->noname) {
-        /* shift no named locations to the end */
-        return 1;
-    }
-
-    if (!first->noname && second->noname) {
-        /* shift no named locations to the end */
-        return -1;
-    }
-
-    if (first->noname || second->noname) {
-        /* do not sort no named locations */
-        return 0;
-    }
-
-#if (NGX_PCRE)
-
-    if (first->regex && !second->regex) {
-        /* shift the regex matches to the end */
-        return 1;
-    }
-
-    if (!first->regex && second->regex) {
-        /* shift the regex matches to the end */
-        return -1;
-    }
-
-    if (first->regex || second->regex) {
-        /* do not sort the regex matches */
-        return 0;
-    }
-
-#endif
-
-    rc = ngx_strcmp(first->name.data, second->name.data);
-
-    if (rc == 0 && second->exact_match) {
-        /* an exact match must be before the same inclusive one */
-        return 1;
-    }
-
-    return (int) rc;
-}
-
-
 static char *
 ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
@@ -2066,7 +2347,7 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c
     ngx_http_core_loc_conf_t *lcf = conf;
 
     ngx_str_t       *value, *content_type, *old, file;
-    ngx_uint_t       i, n;
+    ngx_uint_t       i, n, hash;
     ngx_hash_key_t  *type;
 
     value = cf->args->elts;
@@ -2092,9 +2373,7 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c
 
     for (i = 1; i < cf->args->nelts; i++) {
 
-        for (n = 0; n < value[i].len; n++) {
-            value[i].data[n] = ngx_tolower(value[i].data[n]);
-        }
+        hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len);
 
         type = lcf->types->elts;
         for (n = 0; n < lcf->types->nelts; n++) {
@@ -2118,7 +2397,7 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c
         }
 
         type->key = value[i];
-        type->key_hash = ngx_hash_key(value[i].data, value[i].len);
+        type->key_hash = hash;
         type->value = content_type;
     }
 
@@ -2208,12 +2487,6 @@ ngx_http_core_create_srv_conf(ngx_conf_t
      *     conf->client_large_buffers.num = 0;
      */
 
-    if (ngx_array_init(&cscf->locations, cf->pool, 4, sizeof(void *))
-        == NGX_ERROR)
-    {
-        return NGX_CONF_ERROR;
-    }
-
     if (ngx_array_init(&cscf->listen, cf->pool, 4, sizeof(ngx_http_listen_t))
         == NGX_ERROR)
     {
@@ -2231,8 +2504,8 @@ ngx_http_core_create_srv_conf(ngx_conf_t
     cscf->request_pool_size = NGX_CONF_UNSET_SIZE;
     cscf->client_header_timeout = NGX_CONF_UNSET_MSEC;
     cscf->client_header_buffer_size = NGX_CONF_UNSET_SIZE;
-    cscf->optimize_server_names = NGX_CONF_UNSET;
     cscf->ignore_invalid_headers = NGX_CONF_UNSET;
+    cscf->merge_slashes = NGX_CONF_UNSET;
 
     return cscf;
 }
@@ -2272,29 +2545,19 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
     }
 
     if (conf->server_name.data == NULL) {
-        conf->server_name.data = ngx_palloc(cf->pool, NGX_MAXHOSTNAMELEN);
-        if (conf->server_name.data == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        if (gethostname((char *) conf->server_name.data, NGX_MAXHOSTNAMELEN)
-            == -1)
-        {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
-                               "gethostname() failed");
-            return NGX_CONF_ERROR;
-        }
-
-        conf->server_name.len = ngx_strlen(conf->server_name.data);
+        conf->server_name = cf->cycle->hostname;
 
         sn = ngx_array_push(&conf->server_names);
         if (sn == NULL) {
             return NGX_CONF_ERROR;
         }
 
+#if (NGX_PCRE)
+        sn->regex = NULL;
+#endif
+        sn->core_srv_conf = conf;
         sn->name.len = conf->server_name.len;
         sn->name.data = conf->server_name.data;
-        sn->core_srv_conf = conf;
     }
 
     ngx_conf_merge_size_value(conf->connection_pool_size,
@@ -2316,12 +2579,11 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
         return NGX_CONF_ERROR;
     }
 
-    ngx_conf_merge_value(conf->optimize_server_names,
-                              prev->optimize_server_names, 1);
-
     ngx_conf_merge_value(conf->ignore_invalid_headers,
                               prev->ignore_invalid_headers, 1);
 
+    ngx_conf_merge_value(conf->merge_slashes, prev->merge_slashes, 1);
+
     return NGX_CONF_OK;
 }
 
@@ -2351,16 +2613,18 @@ ngx_http_core_create_loc_conf(ngx_conf_t
      *     lcf->exact_match = 0;
      *     lcf->auto_redirect = 0;
      *     lcf->alias = 0;
+     *     lcf->gzip_proxied = 0;
      */
 
     lcf->client_max_body_size = NGX_CONF_UNSET;
     lcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE;
     lcf->client_body_timeout = NGX_CONF_UNSET_MSEC;
-    lcf->satisfy_any = NGX_CONF_UNSET;
+    lcf->satisfy = NGX_CONF_UNSET_UINT;
     lcf->internal = NGX_CONF_UNSET;
     lcf->client_body_in_file_only = NGX_CONF_UNSET;
     lcf->sendfile = NGX_CONF_UNSET;
     lcf->sendfile_max_chunk = NGX_CONF_UNSET_SIZE;
+    lcf->directio = NGX_CONF_UNSET;
     lcf->tcp_nopush = NGX_CONF_UNSET;
     lcf->tcp_nodelay = NGX_CONF_UNSET;
     lcf->send_timeout = NGX_CONF_UNSET_MSEC;
@@ -2371,19 +2635,32 @@ ngx_http_core_create_loc_conf(ngx_conf_t
     lcf->keepalive_header = NGX_CONF_UNSET;
     lcf->lingering_time = NGX_CONF_UNSET_MSEC;
     lcf->lingering_timeout = NGX_CONF_UNSET_MSEC;
+    lcf->resolver_timeout = NGX_CONF_UNSET_MSEC;
     lcf->reset_timedout_connection = NGX_CONF_UNSET;
+    lcf->server_name_in_redirect = NGX_CONF_UNSET;
     lcf->port_in_redirect = NGX_CONF_UNSET;
     lcf->msie_padding = NGX_CONF_UNSET;
     lcf->msie_refresh = NGX_CONF_UNSET;
     lcf->log_not_found = NGX_CONF_UNSET;
     lcf->recursive_error_pages = NGX_CONF_UNSET;
+    lcf->server_tokens = NGX_CONF_UNSET;
     lcf->types_hash_max_size = NGX_CONF_UNSET_UINT;
     lcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT;
+
     lcf->open_file_cache = NGX_CONF_UNSET_PTR;
-    lcf->open_file_cache_retest = NGX_CONF_UNSET;
+    lcf->open_file_cache_valid = NGX_CONF_UNSET;
+    lcf->open_file_cache_min_uses = NGX_CONF_UNSET_UINT;
     lcf->open_file_cache_errors = NGX_CONF_UNSET;
     lcf->open_file_cache_events = NGX_CONF_UNSET;
 
+#if (NGX_HTTP_GZIP)
+    lcf->gzip_vary = NGX_CONF_UNSET;
+    lcf->gzip_http_version = NGX_CONF_UNSET_UINT;
+#if (NGX_PCRE)
+    lcf->gzip_disable = NGX_CONF_UNSET_PTR;
+#endif
+#endif
+
     return lcf;
 }
 
@@ -2528,13 +2805,16 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
     ngx_conf_merge_msec_value(conf->client_body_timeout,
                               prev->client_body_timeout, 60000);
 
-    ngx_conf_merge_value(conf->satisfy_any, prev->satisfy_any, 0);
+    ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy,
+                              NGX_HTTP_SATISFY_ALL);
     ngx_conf_merge_value(conf->internal, prev->internal, 0);
     ngx_conf_merge_value(conf->client_body_in_file_only,
                               prev->client_body_in_file_only, 0);
     ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
     ngx_conf_merge_size_value(conf->sendfile_max_chunk,
                               prev->sendfile_max_chunk, 0);
+    ngx_conf_merge_off_value(conf->directio, prev->directio,
+                              NGX_MAX_OFF_T_VALUE);
     ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0);
     ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1);
 
@@ -2551,6 +2831,26 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
                               prev->lingering_time, 30000);
     ngx_conf_merge_msec_value(conf->lingering_timeout,
                               prev->lingering_timeout, 5000);
+    ngx_conf_merge_msec_value(conf->resolver_timeout,
+                              prev->resolver_timeout, 30000);
+
+    if (conf->resolver == NULL) {
+
+        if (prev->resolver == NULL) {
+
+            /*
+             * create dummy resolver in http {} context
+             * to inherit it in all servers
+             */
+
+            prev->resolver = ngx_resolver_create(cf, NULL);
+            if (prev->resolver == NULL) {
+                return NGX_CONF_ERROR;
+            }
+        }
+
+        conf->resolver = prev->resolver;
+    }
 
     ngx_conf_merge_path_value(conf->client_body_temp_path,
                               prev->client_body_temp_path,
@@ -2559,24 +2859,43 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
 
     ngx_conf_merge_value(conf->reset_timedout_connection,
                               prev->reset_timedout_connection, 0);
+    ngx_conf_merge_value(conf->server_name_in_redirect,
+                              prev->server_name_in_redirect, 1);
     ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1);
     ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1);
     ngx_conf_merge_value(conf->msie_refresh, prev->msie_refresh, 0);
     ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1);
     ngx_conf_merge_value(conf->recursive_error_pages,
                               prev->recursive_error_pages, 0);
+    ngx_conf_merge_value(conf->server_tokens, prev->server_tokens, 1);
 
     ngx_conf_merge_ptr_value(conf->open_file_cache,
-                             prev->open_file_cache, NULL);
-
-    ngx_conf_merge_sec_value(conf->open_file_cache_retest,
-                             prev->open_file_cache_retest, 60);
+                              prev->open_file_cache, NULL);
+
+    ngx_conf_merge_sec_value(conf->open_file_cache_valid,
+                              prev->open_file_cache_valid, 60);
+
+    ngx_conf_merge_uint_value(conf->open_file_cache_min_uses,
+                              prev->open_file_cache_min_uses, 1);
 
     ngx_conf_merge_sec_value(conf->open_file_cache_errors,
-                             prev->open_file_cache_errors, 0);
+                              prev->open_file_cache_errors, 0);
 
     ngx_conf_merge_sec_value(conf->open_file_cache_events,
-                             prev->open_file_cache_events, 0);
+                              prev->open_file_cache_events, 0);
+#if (NGX_HTTP_GZIP)
+
+    ngx_conf_merge_value(conf->gzip_vary, prev->gzip_vary, 0);
+    ngx_conf_merge_uint_value(conf->gzip_http_version, prev->gzip_http_version,
+                              NGX_HTTP_VERSION_11);
+    ngx_conf_merge_bitmask_value(conf->gzip_proxied, prev->gzip_proxied,
+                              (NGX_CONF_BITMASK_SET|NGX_HTTP_GZIP_PROXIED_OFF));
+
+#if (NGX_PCRE)
+    ngx_conf_merge_ptr_value(conf->gzip_disable, prev->gzip_disable, NULL);
+#endif
+
+#endif
 
     return NGX_CONF_OK;
 }
@@ -2607,7 +2926,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx
     u.listen = 1;
     u.default_port = 80;
 
-    if (ngx_parse_url(cf, &u) != NGX_OK) {
+    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
         if (u.err) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "%s in \"%V\" of the \"listen\" directive",
@@ -2751,30 +3070,12 @@ ngx_http_core_server_name(ngx_conf_t *cf
     ngx_str_t               *value, name;
     ngx_uint_t               i;
     ngx_http_server_name_t  *sn;
-#if (NGX_PCRE)
-    ngx_str_t                err;
-    u_char                   errstr[NGX_MAX_CONF_ERRSTR];
-#endif
 
     value = cf->args->elts;
 
     ch = value[1].data[0];
 
     if (cscf->server_name.data == NULL && value[1].len) {
-        if (ngx_strchr(value[1].data, '*')) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "first server name \"%V\" must not be wildcard",
-                               &value[1]);
-            return NGX_CONF_ERROR;
-        }
-
-        if (value[1].data[0] == '~') {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "first server name \"%V\" "
-                               "must not be regular expression", &value[1]);
-            return NGX_CONF_ERROR;
-        }
-
         name = value[1];
 
         if (ch == '.') {
@@ -2793,11 +3094,6 @@ ngx_http_core_server_name(ngx_conf_t *cf
 
         ch = value[i].data[0];
 
-        if (value[i].len == 1 && ch == '*') {
-            cscf->wildcard = 1;
-            continue;
-        }
-
         if (value[i].len == 0
             || (ch == '*' && (value[i].len < 3 || value[i].data[1] != '.'))
             || (ch == '.' && value[i].len < 2))
@@ -2813,6 +3109,13 @@ ngx_http_core_server_name(ngx_conf_t *cf
                                &value[i]);
         }
 
+        if (value[i].len == 1 && ch == '*') {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "\"server_name *\" is unsupported, use "
+                               "\"server_name_in_redirect off\" instead");
+            return NGX_CONF_ERROR;
+        }
+
         sn = ngx_array_push(&cscf->server_names);
         if (sn == NULL) {
             return NGX_CONF_ERROR;
@@ -2822,31 +3125,32 @@ ngx_http_core_server_name(ngx_conf_t *cf
         sn->regex = NULL;
 #endif
         sn->core_srv_conf = cscf;
-        sn->name.len = value[i].len;
-        sn->name.data = value[i].data;
+        sn->name = value[i];
 
         if (value[i].data[0] != '~') {
             continue;
         }
 
 #if (NGX_PCRE)
+        {
+        ngx_str_t  err;
+        u_char     errstr[NGX_MAX_CONF_ERRSTR];
+
         err.len = NGX_MAX_CONF_ERRSTR;
         err.data = errstr;
 
         value[i].len--;
         value[i].data++;
 
-        sn->regex = ngx_regex_compile(&value[i], NGX_REGEX_CASELESS, cf->pool,
-                                      &err);
+        sn->regex = ngx_regex_compile(&value[i], 0, cf->pool, &err);
 
         if (sn->regex == NULL) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
             return NGX_CONF_ERROR;
         }
 
-        sn->name.len = value[i].len;
-        sn->name.data = value[i].data;
-
+        sn->name = value[i];
+        }
 #else
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "the using of the regex \"%V\" "
@@ -2980,7 +3284,7 @@ static ngx_http_method_name_t  ngx_metho
 static char *
 ngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    ngx_http_core_loc_conf_t *clcf = conf;
+    ngx_http_core_loc_conf_t *pclcf = conf;
 
     char                      *rv;
     void                      *mconf;
@@ -2990,13 +3294,13 @@ ngx_http_core_limit_except(ngx_conf_t *c
     ngx_http_module_t         *module;
     ngx_http_conf_ctx_t       *ctx, *pctx;
     ngx_http_method_name_t    *name;
-    ngx_http_core_loc_conf_t  *lcf, **clcfp;
-
-    if (clcf->limit_except) {
+    ngx_http_core_loc_conf_t  *clcf;
+
+    if (pclcf->limit_except) {
         return "duplicate";
     }
 
-    clcf->limit_except = 0xffffffff;
+    pclcf->limit_except = 0xffffffff;
 
     value = cf->args->elts;
 
@@ -3004,7 +3308,7 @@ ngx_http_core_limit_except(ngx_conf_t *c
         for (name = ngx_methods_names; name->name; name++) {
 
             if (ngx_strcasecmp(value[i].data, name->name) == 0) {
-                clcf->limit_except &= name->method;
+                pclcf->limit_except &= name->method;
                 goto next;
             }
         }
@@ -3017,8 +3321,8 @@ ngx_http_core_limit_except(ngx_conf_t *c
         continue;
     }
 
-    if (!(clcf->limit_except & NGX_HTTP_GET)) {
-        clcf->limit_except &= (uint32_t) ~NGX_HTTP_HEAD;
+    if (!(pclcf->limit_except & NGX_HTTP_GET)) {
+        pclcf->limit_except &= (uint32_t) ~NGX_HTTP_HEAD;
     }
 
     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
@@ -3054,27 +3358,16 @@ ngx_http_core_limit_except(ngx_conf_t *c
     }
 
 
-    lcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
-    clcf->limit_except_loc_conf = ctx->loc_conf;
-    lcf->loc_conf = ctx->loc_conf;
-    lcf->name = clcf->name;
-    lcf->noname = 1;
-
-    if (clcf->locations == NULL) {
-        clcf->locations = ngx_array_create(cf->pool, 2, sizeof(void *));
-        if (clcf->locations == NULL) {
-            return NGX_CONF_ERROR;
-        }
-    }
-
-    clcfp = ngx_array_push(clcf->locations);
-    if (clcfp == NULL) {
+    clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
+    pclcf->limit_except_loc_conf = ctx->loc_conf;
+    clcf->loc_conf = ctx->loc_conf;
+    clcf->name = pclcf->name;
+    clcf->noname = 1;
+
+    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
-    *clcfp = lcf;
-
-
     save = *cf;
     cf->ctx = ctx;
     cf->cmd_type = NGX_HTTP_LMT_CONF;
@@ -3088,10 +3381,38 @@ ngx_http_core_limit_except(ngx_conf_t *c
 
 
 static char *
+ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_core_loc_conf_t *clcf = conf;
+
+    ngx_str_t  *value;
+
+    if (clcf->directio != NGX_CONF_UNSET) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        clcf->directio = NGX_MAX_OFF_T_VALUE;
+        return NGX_CONF_OK;
+    }
+
+    clcf->directio = ngx_parse_offset(&value[1]);
+    if (clcf->directio == (off_t) NGX_ERROR) {
+        return "invalid value";
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
 ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_core_loc_conf_t *lcf = conf;
 
+    u_char                     *args;
     ngx_int_t                   overwrite;
     ngx_str_t                  *value, uri;
     ngx_uint_t                  i, n, nvar;
@@ -3160,6 +3481,8 @@ ngx_http_core_error_page(ngx_conf_t *cf,
         }
     }
 
+    args = (u_char *) ngx_strchr(uri.data, '?');
+
     for (i = 1; i < cf->args->nelts - n; i++) {
         err = ngx_array_push(lcf->error_pages);
         if (err == NULL) {
@@ -3181,9 +3504,36 @@ ngx_http_core_error_page(ngx_conf_t *cf,
             return NGX_CONF_ERROR;
         }
 
-        err->overwrite = (overwrite >= 0) ? overwrite : err->status;
-
-        err->uri = uri;
+        if (overwrite >= 0) {
+            err->overwrite = overwrite;
+
+        } else {
+            switch (err->status) {
+                case NGX_HTTP_TO_HTTPS:
+                case NGX_HTTPS_CERT_ERROR:
+                case NGX_HTTPS_NO_CERT:
+                    err->overwrite = NGX_HTTP_BAD_REQUEST;
+                    break;
+
+                default:
+                    err->overwrite = err->status;
+                    break;
+            }
+        }
+
+        if (args) {
+            err->uri.len = args - uri.data;
+            err->uri.data = uri.data;
+            args++;
+            err->args.len = (uri.data + uri.len) - args;
+            err->args.data = args;
+
+        } else {
+            err->uri = uri;
+            err->args.len = 0;
+            err->args.data = NULL;
+        }
+
         err->uri_lengths = uri_lengths;
         err->uri_values = uri_values;
     }
@@ -3217,7 +3567,7 @@ ngx_http_core_open_file_cache(ngx_conf_t
 
             max = ngx_atoi(value[i].data + 4, value[i].len - 4);
             if (max == NGX_ERROR) {
-                return NGX_CONF_ERROR;
+                goto failed;
             }
 
             continue;
@@ -3230,7 +3580,7 @@ ngx_http_core_open_file_cache(ngx_conf_t
 
             inactive = ngx_parse_time(&s, 1);
             if (inactive < 0) {
-                return NGX_CONF_ERROR;
+                goto failed;
             }
 
             continue;
@@ -3243,6 +3593,8 @@ ngx_http_core_open_file_cache(ngx_conf_t
             continue;
         }
 
+    failed:
+
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid \"open_file_cache\" parameter \"%V\"",
                            &value[i]);
@@ -3339,6 +3691,96 @@ ngx_http_core_internal(ngx_conf_t *cf, n
 
 
 static char *
+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) {
+        return "is duplicate";
+    }
+
+    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]);
+    if (clcf->resolver == NULL) {
+        return NGX_OK;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+#if (NGX_HTTP_GZIP)
+
+static char *
+ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#if (NGX_PCRE)
+    ngx_http_core_loc_conf_t  *clcf = conf;
+
+    ngx_str_t         err, *value;
+    ngx_uint_t        i;
+    ngx_regex_elt_t  *re;
+    u_char            errstr[NGX_MAX_CONF_ERRSTR];
+
+    if (clcf->gzip_disable == NGX_CONF_UNSET_PTR) {
+        clcf->gzip_disable = ngx_array_create(cf->pool, 2,
+                                              sizeof(ngx_regex_elt_t));
+        if (clcf->gzip_disable == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    value = cf->args->elts;
+
+    err.len = NGX_MAX_CONF_ERRSTR;
+    err.data = errstr;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        re = ngx_array_push(clcf->gzip_disable);
+        if (re == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        re->regex = ngx_regex_compile(&value[i], NGX_REGEX_CASELESS, cf->pool,
+                                      &err);
+
+        if (re->regex == NULL) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+            return NGX_CONF_ERROR;
+        }
+
+        re->name = value[i].data;
+    }
+
+    return NGX_CONF_OK;
+
+#else
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "\"gzip_disable\" requires PCRE library");
+
+    return NGX_CONF_ERROR;
+#endif
+}
+
+#endif
+
+
+static char *
 ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data)
 {
 #if (NGX_FREEBSD)
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -13,6 +13,25 @@
 #include <ngx_http.h>
 
 
+#define NGX_HTTP_GZIP_PROXIED_OFF       0x0002
+#define NGX_HTTP_GZIP_PROXIED_EXPIRED   0x0004
+#define NGX_HTTP_GZIP_PROXIED_NO_CACHE  0x0008
+#define NGX_HTTP_GZIP_PROXIED_NO_STORE  0x0010
+#define NGX_HTTP_GZIP_PROXIED_PRIVATE   0x0020
+#define NGX_HTTP_GZIP_PROXIED_NO_LM     0x0040
+#define NGX_HTTP_GZIP_PROXIED_NO_ETAG   0x0080
+#define NGX_HTTP_GZIP_PROXIED_AUTH      0x0100
+#define NGX_HTTP_GZIP_PROXIED_ANY       0x0200
+
+
+#define NGX_HTTP_SATISFY_ALL            0
+#define NGX_HTTP_SATISFY_ANY            1
+
+
+typedef struct ngx_http_location_tree_node_s  ngx_http_location_tree_node_t;
+typedef struct ngx_http_core_loc_conf_s  ngx_http_core_loc_conf_t;
+
+
 typedef struct {
     unsigned                   default_server:1;
     unsigned                   bind:1;
@@ -112,37 +131,29 @@ typedef struct {
 
 
 typedef struct {
-    /*
-     * array of the ngx_http_core_loc_conf_t *,
-     * used in the ngx_http_core_find_location() and in the merge phase
-     */
-    ngx_array_t                locations;
-
-    unsigned                   regex_start:15;
-    unsigned                   named_start:15;
-    unsigned                   wildcard:1;
-
     /* array of the ngx_http_listen_t, "listen" directive */
-    ngx_array_t                listen;
+    ngx_array_t                 listen;
 
     /* array of the ngx_http_server_name_t, "server_name" directive */
-    ngx_array_t                server_names;
+    ngx_array_t                 server_names;
 
     /* server ctx */
-    ngx_http_conf_ctx_t       *ctx;
+    ngx_http_conf_ctx_t        *ctx;
+
+    ngx_str_t                   server_name;
 
-    ngx_str_t                  server_name;
+    size_t                      connection_pool_size;
+    size_t                      request_pool_size;
+    size_t                      client_header_buffer_size;
 
-    size_t                     connection_pool_size;
-    size_t                     request_pool_size;
-    size_t                     client_header_buffer_size;
+    ngx_bufs_t                  large_client_header_buffers;
+
+    ngx_msec_t                  client_header_timeout;
 
-    ngx_bufs_t                 large_client_header_buffers;
+    ngx_flag_t                  ignore_invalid_headers;
+    ngx_flag_t                  merge_slashes;
 
-    ngx_msec_t                 client_header_timeout;
-
-    ngx_flag_t                 optimize_server_names;
-    ngx_flag_t                 ignore_invalid_headers;
+    ngx_http_core_loc_conf_t  **named_locations;
 } ngx_http_core_srv_conf_t;
 
 
@@ -185,7 +196,6 @@ typedef struct {
 #if (NGX_PCRE)
     ngx_uint_t                 nregex;
     ngx_http_server_name_t    *regex;
-
 #endif
 
     /* the default server configuration for this address:port */
@@ -211,13 +221,12 @@ typedef struct {
     ngx_int_t                  status;
     ngx_int_t                  overwrite;
     ngx_str_t                  uri;
+    ngx_str_t                  args;
     ngx_array_t               *uri_lengths;
     ngx_array_t               *uri_values;
 } ngx_http_err_page_t;
 
 
-typedef struct ngx_http_core_loc_conf_s  ngx_http_core_loc_conf_t;
-
 struct ngx_http_core_loc_conf_s {
     ngx_str_t     name;          /* location name */
 
@@ -225,8 +234,6 @@ struct ngx_http_core_loc_conf_s {
     ngx_regex_t  *regex;
 #endif
 
-    unsigned      regex_start:15;
-
     unsigned      noname:1;   /* "if () {}" block or limit_except */
     unsigned      named:1;
 
@@ -236,14 +243,14 @@ struct ngx_http_core_loc_conf_s {
     unsigned      auto_redirect:1;
     unsigned      alias:1;
 
-    /* array of inclusive ngx_http_core_loc_conf_t */
-    ngx_array_t  *locations;
+    ngx_http_location_tree_node_t   *static_locations;
+    ngx_http_core_loc_conf_t       **regex_locations;
 
     /* pointer to the modules' loc_conf */
-    void        **loc_conf ;
+    void        **loc_conf;
 
     uint32_t      limit_except;
-    void        **limit_except_loc_conf ;
+    void        **limit_except_loc_conf;
 
     ngx_http_handler_pt  handler;
 
@@ -258,6 +265,7 @@ struct ngx_http_core_loc_conf_s {
     ngx_str_t     default_type;
 
     off_t         client_max_body_size;    /* client_max_body_size */
+    off_t         directio;                /* directio */
 
     size_t        client_body_buffer_size; /* client_body_buffer_size */
     size_t        send_lowat;              /* send_lowat */
@@ -270,28 +278,46 @@ struct ngx_http_core_loc_conf_s {
     ngx_msec_t    keepalive_timeout;       /* keepalive_timeout */
     ngx_msec_t    lingering_time;          /* lingering_time */
     ngx_msec_t    lingering_timeout;       /* lingering_timeout */
+    ngx_msec_t    resolver_timeout;        /* resolver_timeout */
+
+    ngx_resolver_t  *resolver;             /* resolver */
 
     time_t        keepalive_header;        /* keepalive_timeout */
 
-    ngx_flag_t    satisfy_any;             /* satisfy_any */
+    ngx_uint_t    satisfy;                 /* satisfy */
+
     ngx_flag_t    internal;                /* internal */
     ngx_flag_t    client_body_in_file_only; /* client_body_in_file_only */
     ngx_flag_t    sendfile;                /* sendfile */
     ngx_flag_t    tcp_nopush;              /* tcp_nopush */
     ngx_flag_t    tcp_nodelay;             /* tcp_nodelay */
     ngx_flag_t    reset_timedout_connection; /* reset_timedout_connection */
+    ngx_flag_t    server_name_in_redirect; /* server_name_in_redirect */
     ngx_flag_t    port_in_redirect;        /* port_in_redirect */
     ngx_flag_t    msie_padding;            /* msie_padding */
     ngx_flag_t    msie_refresh;            /* msie_refresh */
     ngx_flag_t    log_not_found;           /* log_not_found */
     ngx_flag_t    recursive_error_pages;   /* recursive_error_pages */
+    ngx_flag_t    server_tokens;           /* server_tokens */
+
+#if (NGX_HTTP_GZIP)
+    ngx_flag_t    gzip_vary;               /* gzip_vary */
+
+    ngx_uint_t    gzip_http_version;       /* gzip_http_version */
+    ngx_uint_t    gzip_proxied;            /* gzip_proxied */
+
+#if (NGX_PCRE)
+    ngx_array_t  *gzip_disable;            /* gzip_disable */
+#endif
+#endif
 
     ngx_array_t  *error_pages;             /* error_page */
 
     ngx_path_t   *client_body_temp_path;   /* client_body_temp_path */
 
     ngx_open_file_cache_t  *open_file_cache;
-    time_t        open_file_cache_retest;
+    time_t        open_file_cache_valid;
+    ngx_uint_t    open_file_cache_min_uses;
     ngx_flag_t    open_file_cache_errors;
     ngx_flag_t    open_file_cache_events;
 
@@ -300,12 +326,39 @@ struct ngx_http_core_loc_conf_s {
     ngx_uint_t    types_hash_max_size;
     ngx_uint_t    types_hash_bucket_size;
 
+    ngx_queue_t  *locations;
+
 #if 0
     ngx_http_core_loc_conf_t  *prev_location;
 #endif
 };
 
 
+typedef struct {
+    ngx_queue_t                      queue;
+    ngx_http_core_loc_conf_t        *exact;
+    ngx_http_core_loc_conf_t        *inclusive;
+    ngx_str_t                       *name;
+    u_char                          *file_name;
+    ngx_uint_t                       line;
+    ngx_queue_t                      list;
+} ngx_http_location_queue_t;
+
+
+struct ngx_http_location_tree_node_s {
+    ngx_http_location_tree_node_t   *left;
+    ngx_http_location_tree_node_t   *right;
+    ngx_http_location_tree_node_t   *tree;
+
+    ngx_http_core_loc_conf_t        *exact;
+    ngx_http_core_loc_conf_t        *inclusive;
+
+    u_char                           auto_redirect;
+    u_char                           len;
+    u_char                           name[1];
+};
+
+
 void ngx_http_core_run_phases(ngx_http_request_t *r);
 ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph);
@@ -320,11 +373,18 @@ ngx_int_t ngx_http_core_post_access_phas
 ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph);
 
+
+void *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash);
 ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);
 ngx_int_t ngx_http_set_exten(ngx_http_request_t *r);
 u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name,
     size_t *root_length, size_t reserved);
 ngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r);
+ngx_int_t ngx_http_server_addr(ngx_http_request_t *r, ngx_str_t *s);
+#if (NGX_HTTP_GZIP)
+ngx_int_t ngx_http_gzip_ok(ngx_http_request_t *r);
+#endif
+
 
 ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,
     ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **sr,
@@ -363,7 +423,7 @@ extern ngx_uint_t ngx_http_max_module;
                                                                               \
     r->allow_ranges = 0;                                                      \
     if (r->headers_out.accept_ranges) {                                       \
-        r->headers_out.accept_ranges->hash = 0 ;                              \
+        r->headers_out.accept_ranges->hash = 0;                               \
         r->headers_out.accept_ranges = NULL;                                  \
     }
 
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -45,7 +45,8 @@ ngx_module_t  ngx_http_header_filter_mod
 };
 
 
-static char ngx_http_server_string[] = "Server: " NGINX_VER CRLF;
+static char ngx_http_server_string[] = "Server: nginx" CRLF;
+static char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;
 
 
 static ngx_str_t ngx_http_status_lines[] = {
@@ -152,12 +153,16 @@ ngx_http_header_filter(ngx_http_request_
 {
     u_char                    *p;
     size_t                     len;
+    ngx_str_t                  host;
     ngx_buf_t                 *b;
     ngx_uint_t                 status, i;
     ngx_chain_t                out;
     ngx_list_part_t           *part;
     ngx_table_elt_t           *header;
     ngx_http_core_loc_conf_t  *clcf;
+    ngx_http_core_srv_conf_t  *cscf;
+    /* AF_INET only */
+    u_char                     addr[INET_ADDRSTRLEN];
 
     r->header_sent = 1;
 
@@ -237,8 +242,11 @@ ngx_http_header_filter(ngx_http_request_
         len += ngx_http_status_lines[status].len;
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
     if (r->headers_out.server == NULL) {
-        len += sizeof(ngx_http_server_string) - 1;
+        len += clcf->server_tokens ? sizeof(ngx_http_server_full_string) - 1:
+                                     sizeof(ngx_http_server_string) - 1;
     }
 
     if (r->headers_out.date == NULL) {
@@ -268,18 +276,31 @@ ngx_http_header_filter(ngx_http_request_
         len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
     }
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
     if (r->headers_out.location
         && r->headers_out.location->value.len
         && r->headers_out.location->value.data[0] == '/')
     {
         r->headers_out.location->hash = 0;
 
+        if (clcf->server_name_in_redirect) {
+            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+            host = cscf->server_name;
+
+        } else if (r->headers_in.server.len) {
+            host = r->headers_in.server;
+
+        } else {
+            host.data = addr;
+
+            if (ngx_http_server_addr(r, &host) != NGX_OK) {
+                return NGX_ERROR;
+            }
+        }
+
 #if (NGX_HTTP_SSL)
         if (r->connection->ssl) {
             len += sizeof("Location: https://") - 1
-                   + r->server_name.len
+                   + host.len
                    + r->headers_out.location->value.len + 2;
 
             if (clcf->port_in_redirect && r->port != 443) {
@@ -290,13 +311,17 @@ ngx_http_header_filter(ngx_http_request_
 #endif
         {
             len += sizeof("Location: http://") - 1
-                   + r->server_name.len
+                   + host.len
                    + r->headers_out.location->value.len + 2;
 
             if (clcf->port_in_redirect && r->port != 80) {
                 len += r->port_text->len;
             }
         }
+
+    } else {
+        host.len = 0;
+        host.data = NULL;
     }
 
     if (r->chunked) {
@@ -322,6 +347,12 @@ ngx_http_header_filter(ngx_http_request_
         len += sizeof("Connection: closed" CRLF) - 1;
     }
 
+#if (NGX_HTTP_GZIP)
+    if (r->gzip && clcf->gzip_vary) {
+        len += sizeof("Vary: Accept-Encoding" CRLF) - 1;
+    }
+#endif
+
     part = &r->headers_out.headers.part;
     header = part->elts;
 
@@ -365,8 +396,16 @@ ngx_http_header_filter(ngx_http_request_
     *b->last++ = CR; *b->last++ = LF;
 
     if (r->headers_out.server == NULL) {
-        b->last = ngx_cpymem(b->last, ngx_http_server_string,
-                             sizeof(ngx_http_server_string) - 1);
+        if (clcf->server_tokens) {
+            p = (u_char *) ngx_http_server_full_string;
+            len = sizeof(ngx_http_server_full_string) - 1;
+
+        } else {
+            p = (u_char *) ngx_http_server_string;
+            len = sizeof(ngx_http_server_string) - 1;
+        }
+
+        b->last = ngx_cpymem(b->last, p, len);
     }
 
     if (r->headers_out.date == NULL) {
@@ -418,10 +457,8 @@ ngx_http_header_filter(ngx_http_request_
         *b->last++ = CR; *b->last++ = LF;
     }
 
-    if (r->headers_out.location
-        && r->headers_out.location->value.len
-        && r->headers_out.location->value.data[0] == '/')
-    {
+    if (host.data) {
+
         p = b->last + sizeof("Location: ") - 1;
 
         b->last = ngx_cpymem(b->last, "Location: http",
@@ -434,7 +471,7 @@ ngx_http_header_filter(ngx_http_request_
 #endif
 
         *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/';
-        b->last = ngx_copy(b->last, r->server_name.data, r->server_name.len);
+        b->last = ngx_copy(b->last, host.data, host.len);
 
         if (clcf->port_in_redirect) {
 #if (NGX_HTTP_SSL)
@@ -485,6 +522,13 @@ ngx_http_header_filter(ngx_http_request_
                              sizeof("Connection: close" CRLF) - 1);
     }
 
+#if (NGX_HTTP_GZIP)
+    if (r->gzip && clcf->gzip_vary) {
+        b->last = ngx_cpymem(b->last, "Vary: Accept-Encoding" CRLF,
+                             sizeof("Vary: Accept-Encoding" CRLF) - 1);
+    }
+#endif
+
     part = &r->headers_out.headers.part;
     header = part->elts;
 
@@ -505,16 +549,14 @@ ngx_http_header_filter(ngx_http_request_
         }
 
         b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
-        *b->last++ = ':' ; *b->last++ = ' ' ;
+        *b->last++ = ':'; *b->last++ = ' ';
 
         b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
         *b->last++ = CR; *b->last++ = LF;
     }
 
-#if (NGX_DEBUG)
-    *b->last = '\0';
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "%s\n", b->pos);
-#endif
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "%*s\n", (size_t) (b->last - b->pos), b->pos);
 
     /* the end of HTTP header */
     *b->last++ = CR; *b->last++ = LF;
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -124,6 +124,7 @@ ngx_http_parse_request_line(ngx_http_req
         sw_major_digit,
         sw_first_minor_digit,
         sw_minor_digit,
+        sw_spaces_after_digit,
         sw_almost_done
     } state;
 
@@ -335,18 +336,26 @@ ngx_http_parse_request_line(ngx_http_req
                 break;
             }
 
+            r->host_end = p;
+
             switch (ch) {
             case ':':
-                r->host_end = p;
                 state = sw_port;
                 break;
             case '/':
-                r->host_end = p;
                 r->uri_start = p;
                 state = sw_after_slash_in_uri;
                 break;
+            case ' ':
+                /*
+                 * use single "/" from request line to preserve pointers,
+                 * if request line will be copied to large client buffer
+                 */
+                r->uri_start = r->schema_end + 1;
+                r->uri_end = r->schema_end + 2;
+                state = sw_http_09;
+                break;
             default:
-                r->host_end = p;
                 return NGX_HTTP_PARSE_INVALID_REQUEST;
             }
             break;
@@ -362,6 +371,16 @@ ngx_http_parse_request_line(ngx_http_req
                 r->uri_start = p;
                 state = sw_after_slash_in_uri;
                 break;
+            case ' ':
+                r->port_end = p;
+                /*
+                 * use single "/" from request line to preserve pointers,
+                 * if request line will be copied to large client buffer
+                 */
+                r->uri_start = r->schema_end + 1;
+                r->uri_end = r->schema_end + 2;
+                state = sw_http_09;
+                break;
             default:
                 return NGX_HTTP_PARSE_INVALID_REQUEST;
             }
@@ -618,6 +637,11 @@ ngx_http_parse_request_line(ngx_http_req
                 goto done;
             }
 
+            if (ch == ' ') {
+                state = sw_spaces_after_digit;
+                break;
+            }
+
             if (ch < '0' || ch > '9') {
                 return NGX_HTTP_PARSE_INVALID_REQUEST;
             }
@@ -625,6 +649,20 @@ ngx_http_parse_request_line(ngx_http_req
             r->http_minor = r->http_minor * 10 + ch - '0';
             break;
 
+        case sw_spaces_after_digit:
+            switch (ch) {
+            case ' ':
+                break;
+            case CR:
+                state = sw_almost_done;
+                break;
+            case LF:
+                goto done;
+            default:
+                return NGX_HTTP_PARSE_INVALID_REQUEST;
+            }
+            break;
+
         /* end of request line */
         case sw_almost_done:
             r->request_end = p - 1;
@@ -737,7 +775,7 @@ ngx_http_parse_header_line(ngx_http_requ
             if (c) {
                 hash = ngx_hash(hash, c);
                 r->lowcase_header[i++] = c;
-                i &= ~NGX_HTTP_LC_HEADER_LEN;
+                i &= (NGX_HTTP_LC_HEADER_LEN - 1);
                 break;
             }
 
@@ -844,10 +882,10 @@ ngx_http_parse_header_line(ngx_http_requ
         /* end of header line */
         case sw_almost_done:
             switch (ch) {
+            case LF:
+                goto done;
             case CR:
                 break;
-            case LF:
-                goto done;
             default:
                 return NGX_HTTP_PARSE_INVALID_HEADER;
             }
@@ -890,7 +928,7 @@ header_done:
 
 
 ngx_int_t
-ngx_http_parse_complex_uri(ngx_http_request_t *r)
+ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
 {
     u_char  c, ch, decoded, *p, *u;
     enum {
@@ -998,8 +1036,12 @@ ngx_http_parse_complex_uri(ngx_http_requ
             switch(ch) {
 #if (NGX_WIN32)
             case '\\':
+                break;
 #endif
             case '/':
+                if (!merge_slashes) {
+                    *u++ = ch;
+                }
                 break;
             case '.':
                 state = sw_dot;
--- a/src/http/ngx_http_parse_time.c
+++ b/src/http/ngx_http_parse_time.c
@@ -11,7 +11,8 @@
 
 static int mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 
-time_t ngx_http_parse_time(u_char *value, size_t len)
+time_t
+ngx_http_parse_time(u_char *value, size_t len)
 {
     u_char  *p, *end;
     int      day, month, year, hour, min, sec;
@@ -239,7 +240,7 @@ time_t ngx_http_parse_time(u_char *value
 
     /*
      * shift new year to March 1 and start months from 1 (not 0),
-     * it is needed for Gauss's formula
+     * it is needed for Gauss' formula
      */
 
     if (--month <= 0) {
@@ -247,11 +248,20 @@ time_t ngx_http_parse_time(u_char *value
         year -= 1;
     }
 
-    /* Gauss's formula for Grigorian days from 1 March 1 BC */
+    /* Gauss' formula for Grigorian days since March 1, 1 BC */
+
+    return (
+            /* days in years including leap years since March 1, 1 BC */
+
+            365 * year + year / 4 - year / 100 + year / 400
 
-    return (365 * year + year / 4 - year / 100 + year / 400
-            + 367 * month / 12 - 31
-            + day
+            /* days before the month */
+
+            + 367 * month / 12 - 30
+
+            /* days before the day */
+
+            + day - 1
 
             /*
              * 719527 days were between March 1, 1 BC and March 1, 1970,
--- a/src/http/ngx_http_postpone_filter_module.c
+++ b/src/http/ngx_http_postpone_filter_module.c
@@ -168,7 +168,7 @@ ngx_http_postpone_filter_output_postpone
         pr = r->postponed;
 
         if (pr == NULL) {
-            return NGX_OK;
+            break;
         }
 
         if (pr->request) {
@@ -196,7 +196,7 @@ ngx_http_postpone_filter_output_postpone
         }
 
         if (pr == NULL) {
-            return NGX_OK;
+            break;
         }
 
         out = pr->out;
@@ -215,6 +215,17 @@ ngx_http_postpone_filter_output_postpone
 
         r->postponed = r->postponed->next;
     }
+
+    if (r->out) {
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http postpone filter out again \"%V?%V\"",
+                       &r->uri, &r->args);
+
+        r->connection->data = r;
+        return NGX_AGAIN;
+    }
+
+    return NGX_OK;
 }
 
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -21,15 +21,20 @@ static ngx_int_t ngx_http_process_header
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_process_host(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_process_cookie(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 
 static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);
 static void ngx_http_process_request(ngx_http_request_t *r);
-static void ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host,
-    size_t len, ngx_uint_t hash);
+static ssize_t ngx_http_validate_host(u_char *host, size_t len);
+static ngx_int_t ngx_http_find_virtual_server(ngx_http_request_t *r,
+    u_char *host, size_t len);
 
 static void ngx_http_request_handler(ngx_event_t *ev);
 static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);
@@ -69,18 +74,15 @@ static char *ngx_http_client_errors[] = 
 
 
 ngx_http_header_t  ngx_http_headers_in[] = {
-    { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host),
-                 ngx_http_process_unique_header_line },
-
-    { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),
-                 ngx_http_process_connection },
+    { ngx_string("Host"), 0, ngx_http_process_host },
+
+    { ngx_string("Connection"), 0, ngx_http_process_connection },
 
     { ngx_string("If-Modified-Since"),
                  offsetof(ngx_http_headers_in_t, if_modified_since),
                  ngx_http_process_unique_header_line },
 
-    { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),
-                 ngx_http_process_header_line },
+    { ngx_string("User-Agent"), 0, ngx_http_process_user_agent },
 
     { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer),
                  ngx_http_process_header_line },
@@ -96,10 +98,18 @@ ngx_http_header_t  ngx_http_headers_in[]
     { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range),
                  ngx_http_process_header_line },
 
+    { ngx_string("If-Range"),
+                 offsetof(ngx_http_headers_in_t, if_range),
+                 ngx_http_process_unique_header_line },
+
     { ngx_string("Transfer-Encoding"),
                  offsetof(ngx_http_headers_in_t, transfer_encoding),
                  ngx_http_process_header_line },
 
+    { ngx_string("Expect"),
+                 offsetof(ngx_http_headers_in_t, expect),
+                 ngx_http_process_unique_header_line },
+
 #if (NGX_HTTP_GZIP)
     { ngx_string("Accept-Encoding"),
                  offsetof(ngx_http_headers_in_t, accept_encoding),
@@ -169,7 +179,7 @@ ngx_http_init_connection(ngx_connection_
         return;
     }
 
-    ctx->client = &c->addr_text;
+    ctx->connection = c;
     ctx->request = NULL;
     ctx->current_request = NULL;
 
@@ -216,9 +226,7 @@ static void
 ngx_http_init_request(ngx_event_t *rev)
 {
     ngx_time_t                 *tp;
-    socklen_t                   len;
     ngx_uint_t                  i;
-    struct sockaddr_in          sin;
     ngx_connection_t           *c;
     ngx_http_request_t         *r;
     ngx_http_in_port_t         *hip;
@@ -228,9 +236,6 @@ ngx_http_init_request(ngx_event_t *rev)
     ngx_http_core_srv_conf_t   *cscf;
     ngx_http_core_loc_conf_t   *clcf;
     ngx_http_core_main_conf_t  *cmcf;
-#if (NGX_HTTP_SSL)
-    ngx_http_ssl_srv_conf_t    *sscf;
-#endif
 
 #if (NGX_STAT_STUB)
     ngx_atomic_fetch_add(ngx_stat_reading, -1);
@@ -294,6 +299,8 @@ ngx_http_init_request(ngx_event_t *rev)
 
     i = 0;
 
+    r->connection = c;
+
     if (hip->naddrs > 1) {
 
         /*
@@ -301,7 +308,7 @@ ngx_http_init_request(ngx_event_t *rev)
          * is the "*:port" wildcard so getsockname() is needed to determine
          * the server address.
          *
-         * AcceptEx() already gave this address.
+         * AcceptEx() already has given this address.
          */
 
 #if (NGX_WIN32)
@@ -312,15 +319,10 @@ ngx_http_init_request(ngx_event_t *rev)
         } else
 #endif
         {
-            len = sizeof(struct sockaddr_in);
-            if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) {
-                ngx_connection_error(c, ngx_socket_errno,
-                                     "getsockname() failed");
+            if (ngx_http_server_addr(r, NULL) != NGX_OK) {
                 ngx_http_close_connection(c);
                 return;
             }
-
-            r->in_addr = sin.sin_addr.s_addr;
         }
 
         /* the last address is "*" */
@@ -344,12 +346,13 @@ ngx_http_init_request(ngx_event_t *rev)
     r->srv_conf = cscf->ctx->srv_conf;
     r->loc_conf = cscf->ctx->loc_conf;
 
-    r->server_name = cscf->server_name;
-
     rev->handler = ngx_http_process_request_line;
 
 #if (NGX_HTTP_SSL)
 
+    {
+    ngx_http_ssl_srv_conf_t  *sscf;
+
     sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
     if (sscf->enable) {
 
@@ -366,6 +369,7 @@ ngx_http_init_request(ngx_event_t *rev)
 
         r->main_filter_need_in_memory = 1;
     }
+    }
 
 #endif
 
@@ -421,8 +425,6 @@ ngx_http_init_request(ngx_event_t *rev)
     c->single_connection = 1;
     c->destroyed = 0;
 
-    r->connection = c;
-
     r->main = r;
 
     tp = ngx_timeofday();
@@ -483,6 +485,15 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
     n = recv(c->fd, (char *) buf, 1, MSG_PEEK);
 
     if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {
+
+        if (!rev->timer_set) {
+            ngx_add_timer(rev, c->listening->post_accept_timeout);
+        }
+
+        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        }
+
         return;
     }
 
@@ -494,6 +505,11 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
             rc = ngx_ssl_handshake(c);
 
             if (rc == NGX_AGAIN) {
+
+                if (!rev->timer_set) {
+                    ngx_add_timer(rev, c->listening->post_accept_timeout);
+                }
+
                 c->ssl->handler = ngx_http_ssl_handshake_handler;
                 return;
             }
@@ -552,8 +568,6 @@ ngx_http_ssl_handshake_handler(ngx_conne
 int
 ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
 {
-    u_char                   *p;
-    ngx_uint_t                hash;
     const char               *servername;
     ngx_connection_t         *c;
     ngx_http_request_t       *r;
@@ -572,21 +586,13 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *
 
     r = c->data;
 
-    if (r->virtual_names == NULL) {
+    if (ngx_http_find_virtual_server(r, (u_char *) servername,
+                                     ngx_strlen(servername))
+        != NGX_OK)
+    {
         return SSL_TLSEXT_ERR_NOACK;
     }
 
-    /* it seems browsers send low case server name */
-
-    hash = 0;
-
-    for (p = (u_char *) servername; *p; p++) {
-        hash = ngx_hash(hash, *p);
-    }
-
-    ngx_http_find_virtual_server(r, (u_char *) servername,
-                                 p - (u_char *) servername, hash);
-
     sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
 
     SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx);
@@ -602,10 +608,11 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *
 static void
 ngx_http_process_request_line(ngx_event_t *rev)
 {
-    ssize_t              n;
-    ngx_int_t            rc, rv;
-    ngx_connection_t    *c;
-    ngx_http_request_t  *r;
+    ssize_t                    n;
+    ngx_int_t                  rc, rv;
+    ngx_connection_t          *c;
+    ngx_http_request_t        *r;
+    ngx_http_core_srv_conf_t  *cscf;
 
     c = rev->data;
     r = c->data;
@@ -640,6 +647,7 @@ ngx_http_process_request_line(ngx_event_
 
             r->request_line.len = r->request_end - r->request_start;
             r->request_line.data = r->request_start;
+            *r->request_end = '\0';
 
 
             if (r->args_start) {
@@ -651,13 +659,15 @@ ngx_http_process_request_line(ngx_event_
 
             if (r->complex_uri || r->quoted_uri) {
 
-                r->uri.data = ngx_palloc(r->pool, r->uri.len + 1);
+                r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1);
                 if (r->uri.data == NULL) {
                     ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                     return;
                 }
 
-                rc = ngx_http_parse_complex_uri(r);
+                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+                rc = ngx_http_parse_complex_uri(r, cscf->merge_slashes);
 
                 if (rc == NGX_HTTP_PARSE_INVALID_REQUEST) {
                     ngx_log_error(NGX_LOG_INFO, c->log, 0,
@@ -713,7 +723,31 @@ ngx_http_process_request_line(ngx_event_
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                            "http exten: \"%V\"", &r->exten);
 
+            if (r->host_start && r->host_end) {
+                n = ngx_http_validate_host(r->host_start,
+                                           r->host_end - r->host_start);
+
+                if (n <= 0) {
+                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                                  "client sent invalid host in request line");
+                    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+                    return;
+                }
+
+                r->headers_in.server.len = n;
+                r->headers_in.server.data = r->host_start;
+            }
+
             if (r->http_version < NGX_HTTP_VERSION_10) {
+
+                if (ngx_http_find_virtual_server(r, r->headers_in.server.data,
+                                                 r->headers_in.server.len)
+                    == NGX_ERROR)
+                {
+                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                    return;
+                }
+
                 ngx_http_process_request(r);
                 return;
             }
@@ -785,7 +819,6 @@ ngx_http_process_request_headers(ngx_eve
     ssize_t                     n;
     ngx_int_t                   rc, rv;
     ngx_str_t                   header;
-    ngx_uint_t                  i;
     ngx_table_elt_t            *h;
     ngx_connection_t           *c;
     ngx_http_header_t          *hh;
@@ -885,7 +918,7 @@ ngx_http_process_request_headers(ngx_eve
             h->value.data = r->header_start;
             h->value.data[h->value.len] = '\0';
 
-            h->lowcase_key = ngx_palloc(r->pool, h->key.len);
+            h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
             if (h->lowcase_key == NULL) {
                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                 return;
@@ -895,9 +928,7 @@ ngx_http_process_request_headers(ngx_eve
                 ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
 
             } else {
-                for (i = 0; i < h->key.len; i++) {
-                    h->lowcase_key[i] = ngx_tolower(h->key.data[i]);
-                }
+                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
             }
 
             hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
@@ -981,10 +1012,9 @@ ngx_http_read_request_header(ngx_http_re
     }
 
     if (n == NGX_AGAIN) {
-        if (!r->header_timeout_set) {
+        if (!rev->timer_set) {
             cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
             ngx_add_timer(rev, cscf->client_header_timeout);
-            r->header_timeout_set = 1;
         }
 
         if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
@@ -1147,6 +1177,10 @@ ngx_http_alloc_large_header_buffer(ngx_h
             r->args_start = new + (r->args_start - old);
         }
 
+        if (r->http_protocol.data) {
+            r->http_protocol.data = new + (r->http_protocol.data - old);
+        }
+
     } else {
         r->header_name_start = new;
         r->header_name_end = new + (r->header_name_end - old);
@@ -1201,13 +1235,43 @@ ngx_http_process_unique_header_line(ngx_
 
 
 static ngx_int_t
+ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    ssize_t  len;
+
+    if (r->headers_in.host == NULL) {
+        r->headers_in.host = h;
+    }
+
+    len = ngx_http_validate_host(h->value.data, h->value.len);
+
+    if (len <= 0) {
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                      "client sent invalid host header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return NGX_ERROR;
+    }
+
+    if (r->headers_in.server.len) {
+        return NGX_OK;
+    }
+
+    r->headers_in.server.len = len;
+    r->headers_in.server.data = h->value.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
 {
-    if (ngx_strstr(h->value.data, "close")) {
+    if (ngx_strcasestrn(h->value.data, "close", 5 - 1)) {
         r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
 
-    } else if (ngx_strstr(h->value.data, "keep-alive")) {
+    } else if (ngx_strcasestrn(h->value.data, "keep-alive", 10 - 1)) {
         r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;
     }
 
@@ -1216,6 +1280,60 @@ ngx_http_process_connection(ngx_http_req
 
 
 static ngx_int_t
+ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    u_char  *ua, *user_agent;
+
+    if (r->headers_in.user_agent) {
+        return NGX_OK;
+    }
+
+    r->headers_in.user_agent = h;
+
+    /* check some widespread browsers while the header is in CPU cache */
+
+    user_agent = h->value.data;
+
+    ua = ngx_strstrn(user_agent, "MSIE", 4 - 1);
+
+    if (ua && ua + 8 < user_agent + h->value.len) {
+
+        r->headers_in.msie = 1;
+
+        if (ua[4] == ' ' && ua[5] == '4' && ua[6] == '.') {
+            r->headers_in.msie4 = 1;
+        }
+
+#if 0
+        /* MSIE ignores the SSL "close notify" alert */
+        if (c->ssl) {
+            c->ssl->no_send_shutdown = 1;
+        }
+#endif
+    }
+
+    if (ngx_strstrn(user_agent, "Opera", 5 - 1)) {
+        r->headers_in.opera = 1;
+        r->headers_in.msie = 0;
+        r->headers_in.msie4 = 0;
+    }
+
+    if (!r->headers_in.msie && !r->headers_in.opera) {
+
+        if (ngx_strstrn(user_agent, "Gecko/", 6 - 1)) {
+            r->headers_in.gecko = 1;
+
+        } else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) {
+            r->headers_in.konqueror = 1;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
 {
@@ -1236,57 +1354,19 @@ ngx_http_process_cookie(ngx_http_request
 static ngx_int_t
 ngx_http_process_request_header(ngx_http_request_t *r)
 {
-    size_t       len;
-    u_char      *host, *ua, *user_agent, ch;
-    ngx_uint_t   hash;
-
-    if (r->headers_in.host) {
-
-        hash = 0;
-
-        for (len = 0; len < r->headers_in.host->value.len; len++) {
-            ch = r->headers_in.host->value.data[len];
-
-            if (ch == ':') {
-                break;
-            }
-
-            ch = ngx_tolower(ch);
-            r->headers_in.host->value.data[len] = ch;
-            hash = ngx_hash(hash, ch);
-        }
-
-        if (len && r->headers_in.host->value.data[len - 1] == '.') {
-            len--;
-            hash = ngx_hash_key(r->headers_in.host->value.data, len);
-        }
-
-        r->headers_in.host_name_len = len;
-
-        if (r->virtual_names) {
-
-            host = r->host_start;
-
-            if (host == NULL) {
-                host = r->headers_in.host->value.data;
-                len = r->headers_in.host_name_len;
-
-            } else {
-                len = r->host_end - host;
-            }
-
-            ngx_http_find_virtual_server(r, host, len, hash);
-        }
-
-    } else {
-        if (r->http_version > NGX_HTTP_VERSION_10) {
-            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
-                       "client sent HTTP/1.1 request without \"Host\" header");
-            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
-            return NGX_ERROR;
-        }
-
-        r->headers_in.host_name_len = 0;
+    if (ngx_http_find_virtual_server(r, r->headers_in.server.data,
+                                     r->headers_in.server.len)
+        == NGX_ERROR)
+    {
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return NGX_ERROR;
+    }
+
+    if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) {
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                   "client sent HTTP/1.1 request without \"Host\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return NGX_ERROR;
     }
 
     if (r->headers_in.content_length) {
@@ -1306,7 +1386,8 @@ ngx_http_process_request_header(ngx_http
         && r->headers_in.content_length_n == -1)
     {
         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
-                  "client sent POST method without \"Content-Length\" header");
+                  "client sent %V method without \"Content-Length\" header",
+                  &r->method_name);
         ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);
         return NGX_ERROR;
     }
@@ -1319,7 +1400,8 @@ ngx_http_process_request_header(ngx_http
     }
 
     if (r->headers_in.transfer_encoding
-        && ngx_strstr(r->headers_in.transfer_encoding->value.data, "chunked"))
+        && ngx_strcasestrn(r->headers_in.transfer_encoding->value.data,
+                           "chunked", 7 - 1))
     {
         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                       "client sent \"Transfer-Encoding: chunked\" header");
@@ -1327,13 +1409,6 @@ ngx_http_process_request_header(ngx_http
         return NGX_ERROR;
     }
 
-    if (r->plain_http) {
-        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
-                      "client sent plain HTTP request to HTTPS port");
-        ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
-        return NGX_ERROR;
-    }
-
     if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {
         if (r->headers_in.keep_alive) {
             r->headers_in.keep_alive_n =
@@ -1342,50 +1417,6 @@ ngx_http_process_request_header(ngx_http
         }
     }
 
-    if (r->headers_in.user_agent) {
-
-        /*
-         * check some widespread browsers while the headers are still
-         * in CPU cache
-         */
-
-        user_agent = r->headers_in.user_agent->value.data;
-
-        ua = (u_char *) ngx_strstr(user_agent, "MSIE");
-
-        if (ua && ua + 8 < user_agent + r->headers_in.user_agent->value.len) {
-
-            r->headers_in.msie = 1;
-
-            if (ua[4] == ' ' && ua[5] == '4' && ua[6] == '.') {
-                r->headers_in.msie4 = 1;
-            }
-
-#if 0
-            /* MSIE ignores the SSL "close notify" alert */
-            if (c->ssl) {
-                c->ssl->no_send_shutdown = 1;
-            }
-#endif
-        }
-
-        if (ngx_strstr(user_agent, "Opera")) {
-            r->headers_in.opera = 1;
-            r->headers_in.msie = 0;
-            r->headers_in.msie4 = 0;
-        }
-
-        if (!r->headers_in.msie && !r->headers_in.opera) {
-
-            if (ngx_strstr(user_agent, "Gecko/")) {
-                r->headers_in.gecko = 1;
-
-            } else if (ngx_strstr(user_agent, "Konqueror")) {
-                r->headers_in.konqueror = 1;
-            }
-        }
-    }
-
     return NGX_OK;
 }
 
@@ -1393,38 +1424,55 @@ ngx_http_process_request_header(ngx_http
 static void
 ngx_http_process_request(ngx_http_request_t *r)
 {
-    ngx_connection_t         *c;
-#if (NGX_HTTP_SSL)
-    long                      rc;
-    ngx_http_ssl_srv_conf_t  *sscf;
-#endif
+    ngx_connection_t  *c;
 
     c = r->connection;
 
+    if (r->plain_http) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "client sent plain HTTP request to HTTPS port");
+        ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
+        return;
+    }
+
 #if (NGX_HTTP_SSL)
 
     if (c->ssl) {
+        long                      rc;
+        X509                     *cert;
+        ngx_http_ssl_srv_conf_t  *sscf;
+
         sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
 
-        if (sscf->verify) {
+        if (sscf->verify == 1) {
             rc = SSL_get_verify_result(c->ssl->connection);
 
             if (rc != X509_V_OK) {
                 ngx_log_error(NGX_LOG_INFO, c->log, 0,
                               "client SSL certificate verify error: (%l:%s)",
                               rc, X509_verify_cert_error_string(rc));
+
+                ngx_ssl_remove_cached_session(sscf->ssl.ctx,
+                                       (SSL_get0_session(c->ssl->connection)));
+
                 ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);
                 return;
             }
 
-            if (SSL_get_peer_certificate(c->ssl->connection)
-                == NULL)
-            {
+            cert = SSL_get_peer_certificate(c->ssl->connection);
+
+            if (cert == NULL) {
                 ngx_log_error(NGX_LOG_INFO, c->log, 0,
                               "client sent no required SSL certificate");
+
+                ngx_ssl_remove_cached_session(sscf->ssl.ctx,
+                                       (SSL_get0_session(c->ssl->connection)));
+
                 ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);
                 return;
             }
+
+            X509_free(cert);
         }
     }
 
@@ -1451,20 +1499,80 @@ ngx_http_process_request(ngx_http_reques
 }
 
 
-static void
-ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len,
-    ngx_uint_t hash)
+static ssize_t
+ngx_http_validate_host(u_char *host, size_t len)
 {
+    u_char      ch;
+    size_t      i, last;
+    ngx_uint_t  dot;
+
+    last = len;
+    dot = 0;
+
+    for (i = 0; i < len; i++) {
+        ch = host[i];
+
+        if (ch == '.') {
+            if (dot) {
+                return -1;
+            }
+
+            dot = 1;
+            continue;
+        }
+
+        dot = 0;
+
+        if (ch == ':') {
+            last = i;
+            continue;
+        }
+
+        if (ch == '/' || ch == '\0') {
+            return -1;
+        }
+
+#if (NGX_WIN32)
+        if (ch == '\\') {
+            return -1;
+        }
+#endif
+    }
+
+    if (dot) {
+        last--;
+    }
+
+    return last;
+}
+
+
+static ngx_int_t
+ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len)
+{
+    u_char                    *server;
+    ngx_uint_t                 hash;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
-#if (NGX_PCRE)
-    ngx_int_t                  n;
-    ngx_uint_t                 i;
-    ngx_str_t                  name;
-    ngx_http_server_name_t    *sn;
-#endif
-
-    cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, host, len);
+    u_char                     buf[32];
+
+    if (len == 0 || r->virtual_names == NULL) {
+        return NGX_DECLINED;
+    }
+
+    if (len <= 32) {
+        server = buf;
+
+    } else {
+        server = ngx_pnalloc(r->pool, len);
+        if (server == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+    hash = ngx_hash_strlow(server, host, len);
+
+    cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, server, len);
 
     if (cscf) {
         goto found;
@@ -1473,9 +1581,13 @@ ngx_http_find_virtual_server(ngx_http_re
 #if (NGX_PCRE)
 
     if (r->virtual_names->nregex) {
+        ngx_int_t                n;
+        ngx_uint_t               i;
+        ngx_str_t                name;
+        ngx_http_server_name_t  *sn;
 
         name.len = len;
-        name.data = host;
+        name.data = server;
 
         sn = r->virtual_names->regex;
 
@@ -1492,7 +1604,7 @@ ngx_http_find_virtual_server(ngx_http_re
                               ngx_regex_exec_n
                               " failed: %d on \"%V\" using \"%V\"",
                               n, &name, &sn[i].name);
-                return;
+                return NGX_ERROR;
             }
 
             /* match */
@@ -1505,20 +1617,10 @@ ngx_http_find_virtual_server(ngx_http_re
 
 #endif
 
-    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
-
-    if (cscf->wildcard) {
-        r->server_name.len = len;
-        r->server_name.data = host;
-    }
-
-    return;
+    return NGX_OK;
 
 found:
 
-    r->server_name.len = len;
-    r->server_name.data = host;
-
     r->srv_conf = cscf->ctx->srv_conf;
     r->loc_conf = cscf->ctx->loc_conf;
 
@@ -1529,7 +1631,7 @@ found:
         r->connection->log->log_level = clcf->err_log->log_level;
     }
 
-    return;
+    return NGX_OK;
 }
 
 
@@ -1585,7 +1687,11 @@ ngx_http_finalize_request(ngx_http_reque
         rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc);
     }
 
-    if (rc == NGX_ERROR || rc == NGX_HTTP_REQUEST_TIME_OUT || c->error) {
+    if (rc == NGX_ERROR
+        || rc == NGX_HTTP_REQUEST_TIME_OUT
+        || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST
+        || c->error)
+    {
         if (rc > 0 && r->headers_out.status == 0) {
             r->headers_out.status = rc;
         }
@@ -1667,8 +1773,8 @@ ngx_http_finalize_request(ngx_http_reque
                 }
 
                 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                           "http fast subrequest: \"%V?%V\" done",
-                           &r->uri, &r->args);
+                               "http fast subrequest: \"%V?%V\" done",
+                               &r->uri, &r->args);
                 return;
             }
 
@@ -1723,7 +1829,7 @@ ngx_http_finalize_request(ngx_http_reque
 
     if (!ngx_terminate
          && !ngx_exiting
-         && r->keepalive != 0
+         && r->keepalive
          && clcf->keepalive_timeout > 0)
     {
         ngx_http_set_keepalive(r);
@@ -2044,7 +2150,8 @@ ngx_http_set_keepalive(ngx_http_request_
         hc->pipeline = 1;
         c->log->action = "reading client pipelined request line";
 
-        ngx_http_init_request(rev);
+        rev->handler = ngx_http_init_request;
+        ngx_post_event(rev, &ngx_posted_events);
         return;
     }
 
@@ -2083,7 +2190,7 @@ ngx_http_set_keepalive(ngx_http_request_
 
     if (hc->free) {
         for (i = 0; i < hc->nfree; i++) {
-            ngx_pfree(c->pool, hc->free[i]);
+            ngx_pfree(c->pool, hc->free[i]->start);
             hc->free[i] = NULL;
         }
 
@@ -2095,13 +2202,19 @@ ngx_http_set_keepalive(ngx_http_request_
 
     if (hc->busy) {
         for (i = 0; i < hc->nbusy; i++) {
-            ngx_pfree(c->pool, hc->busy[i]);
+            ngx_pfree(c->pool, hc->busy[i]->start);
             hc->busy[i] = NULL;
         }
 
         hc->nbusy = 0;
     }
 
+#if (NGX_HTTP_SSL)
+    if (c->ssl) {
+        ngx_ssl_free_buffer(c);
+    }
+#endif
+
     rev->handler = ngx_http_keepalive_handler;
 
     if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
@@ -2154,7 +2267,7 @@ ngx_http_set_keepalive(ngx_http_request_
     c->idle = 1;
 
     if (rev->ready) {
-        ngx_http_keepalive_handler(rev);
+        ngx_post_event(rev, &ngx_posted_events);
     }
 }
 
@@ -2333,7 +2446,7 @@ ngx_http_lingering_close_handler(ngx_eve
         return;
     }
 
-    timer = r->lingering_time - ngx_time();
+    timer = (ngx_msec_t) (r->lingering_time - ngx_time());
     if (timer <= 0) {
         ngx_http_close_request(r, 0);
         return;
@@ -2433,7 +2546,12 @@ ngx_http_post_action(ngx_http_request_t 
 
     r->read_event_handler = ngx_http_block_reading;
 
-    ngx_http_internal_redirect(r, &clcf->post_action, NULL);
+    if (clcf->post_action.data[0] == '/') {
+        ngx_http_internal_redirect(r, &clcf->post_action, NULL);
+
+    } else {
+        ngx_http_named_location(r, &clcf->post_action);
+    }
 
     return NGX_OK;
 }
@@ -2494,6 +2612,8 @@ ngx_http_request_done(ngx_http_request_t
         r->headers_out.status = error;
     }
 
+    log->action = "logging request";
+
     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
 
     log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;
@@ -2502,6 +2622,8 @@ ngx_http_request_done(ngx_http_request_t
         log_handler[i](r);
     }
 
+    log->action = "closing request";
+
     if (r->connection->timedout) {
         clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
@@ -2578,13 +2700,17 @@ ngx_http_log_error(ngx_log_t *log, u_cha
 
     ctx = log->data;
 
-    p = ngx_snprintf(buf, len, ", client: %V", ctx->client);
+    p = ngx_snprintf(buf, len, ", client: %V", &ctx->connection->addr_text);
     len -= p - buf;
 
     r = ctx->request;
 
     if (r) {
         return r->log_handler(r, ctx->current_request, p, len);
+
+    } else {
+        p = ngx_snprintf(p, len, ", server: %V",
+                         &ctx->connection->listening->addr_text);
     }
 
     return p;
@@ -2595,38 +2721,32 @@ static u_char *
 ngx_http_log_error_handler(ngx_http_request_t *r, ngx_http_request_t *sr,
     u_char *buf, size_t len)
 {
-    char                 *uri_separator;
-    u_char               *p;
-    ngx_http_upstream_t  *u;
-
-    if (r->server_name.data) {
-        p = ngx_snprintf(buf, len, ", server: %V", &r->server_name);
-        len -= p - buf;
-        buf = p;
+    char                      *uri_separator;
+    u_char                    *p;
+    ngx_http_upstream_t       *u;
+    ngx_http_core_srv_conf_t  *cscf;
+
+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+    p = ngx_snprintf(buf, len, ", server: %V", &cscf->server_name);
+    len -= p - buf;
+    buf = p;
+
+    if (r->request_line.data == NULL && r->request_start) {
+        for (p = r->request_start; p < r->header_in->last; p++) {
+            if (*p == CR || *p == LF) {
+                break;
+            }
+        }
+
+        r->request_line.len = p - r->request_start;
+        r->request_line.data = r->request_start;
     }
 
-    if (r->unparsed_uri.data) {
-        p = ngx_snprintf(buf, len, ", URL: \"%V\"", &r->unparsed_uri);
+    if (r->request_line.len) {
+        p = ngx_snprintf(buf, len, ", request: \"%V\"", &r->request_line);
         len -= p - buf;
         buf = p;
-
-    } else {
-        if (r->request_line.data == NULL && r->request_start) {
-            for (p = r->request_start; p < r->header_in->last; p++) {
-                if (*p == CR || *p == LF) {
-                    break;
-                }
-            }
-
-            r->request_line.len = p - r->request_start;
-            r->request_line.data = r->request_start;
-        }
-
-        if (r->request_line.len) {
-            p = ngx_snprintf(buf, len, ", request: \"%V\"", &r->request_line);
-            len -= p - buf;
-            buf = p;
-        }
     }
 
     if (r != sr) {
@@ -2648,7 +2768,7 @@ ngx_http_log_error_handler(ngx_http_requ
 #endif
 
         p = ngx_snprintf(buf, len, ", upstream: \"%V%V%s%V\"",
-                         &u->conf->schema, u->peer.name,
+                         &u->schema, u->peer.name,
                          uri_separator, &u->uri);
         len -= p - buf;
         buf = p;
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -168,8 +168,10 @@ typedef struct {
     ngx_table_elt_t                  *content_type;
 
     ngx_table_elt_t                  *range;
+    ngx_table_elt_t                  *if_range;
 
     ngx_table_elt_t                  *transfer_encoding;
+    ngx_table_elt_t                  *expect;
 
 #if (NGX_HTTP_GZIP)
     ngx_table_elt_t                  *accept_encoding;
@@ -205,7 +207,7 @@ typedef struct {
 
     ngx_array_t                       cookies;
 
-    size_t                            host_name_len;
+    ngx_str_t                         server;
     off_t                             content_length_n;
     time_t                            keep_alive_n;
 
@@ -229,6 +231,7 @@ typedef struct {
     ngx_table_elt_t                  *content_length;
     ngx_table_elt_t                  *content_encoding;
     ngx_table_elt_t                  *location;
+    ngx_table_elt_t                  *refresh;
     ngx_table_elt_t                  *last_modified;
     ngx_table_elt_t                  *content_range;
     ngx_table_elt_t                  *accept_ranges;
@@ -241,6 +244,8 @@ typedef struct {
     size_t                            content_type_len;
     ngx_str_t                         content_type;
     ngx_str_t                         charset;
+    u_char                           *content_type_lowcase;
+    ngx_uint_t                        content_type_hash;
 
     ngx_array_t                       cache_control;
 
@@ -371,7 +376,6 @@ struct ngx_http_request_s {
     uint32_t                          in_addr;
     ngx_uint_t                        port;
     ngx_str_t                        *port_text;    /* ":80" */
-    ngx_str_t                         server_name;
     ngx_http_virtual_names_t         *virtual_names;
 
     ngx_int_t                         phase_handler;
@@ -426,7 +430,7 @@ struct ngx_http_request_s {
     unsigned                          fast_subrequest:1;
     unsigned                          subrequest_in_memory:1;
 
-    unsigned                          header_timeout_set:1;
+    unsigned                          gzip:2;
 
     unsigned                          proxy:1;
     unsigned                          bypass_cache:1;
@@ -449,7 +453,7 @@ struct ngx_http_request_s {
     unsigned                          limit_zone_set:1;
 
 #if 0
-    unsigned                          cachable:1;
+    unsigned                          cacheable:1;
 #endif
 
     unsigned                          pipeline:1;
@@ -466,6 +470,8 @@ struct ngx_http_request_s {
     unsigned                          request_complete:1;
     unsigned                          request_output:1;
     unsigned                          header_sent:1;
+    unsigned                          expect_tested:1;
+    unsigned                          root_tested:1;
     unsigned                          done:1;
     unsigned                          utf8:1;
 
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -442,7 +442,7 @@ ngx_http_discard_request_body(ngx_http_r
         ngx_del_timer(rev);
     }
 
-    if (r->headers_in.content_length_n <= 0) {
+    if (r->headers_in.content_length_n <= 0 || r->request_body) {
         return NGX_OK;
     }
 
@@ -493,7 +493,7 @@ ngx_http_read_discarded_request_body_han
     }
 
     if (r->lingering_time) {
-        timer = r->lingering_time - ngx_time();
+        timer = (ngx_msec_t) (r->lingering_time - ngx_time());
 
         if (timer <= 0) {
             r->discard_body = 0;
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -221,6 +221,14 @@ ngx_http_script_compile(ngx_http_script_
             sc->args = 1;
             sc->compile_args = 0;
 
+            code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t),
+                                            NULL);
+            if (code == NULL) {
+                return NGX_ERROR;
+            }
+
+            *code = (uintptr_t) ngx_http_script_mark_args_code;
+
             code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),
                                             &sc->main);
             if (code == NULL) {
@@ -314,7 +322,7 @@ ngx_http_script_run(ngx_http_request_t *
     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
 
     for (i = 0; i < cmcf->variables.nelts; i++) {
-        if (r->variables[i].no_cachable) {
+        if (r->variables[i].no_cacheable) {
             r->variables[i].valid = 0;
             r->variables[i].not_found = 0;
         }
@@ -333,7 +341,7 @@ ngx_http_script_run(ngx_http_request_t *
 
 
     value->len = len;
-    value->data = ngx_palloc(r->pool, len);
+    value->data = ngx_pnalloc(r->pool, len);
     if (value->data == NULL) {
         return NULL;
     }
@@ -351,7 +359,7 @@ ngx_http_script_run(ngx_http_request_t *
 
 
 void
-ngx_http_script_flush_no_cachable_variables(ngx_http_request_t *r,
+ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r,
     ngx_array_t *indices)
 {
     ngx_uint_t  n, *index;
@@ -359,7 +367,7 @@ ngx_http_script_flush_no_cachable_variab
     if (indices) {
         index = indices->elts;
         for (n = 0; n < indices->nelts; n++) {
-            if (r->variables[index[n]].no_cachable) {
+            if (r->variables[index[n]].no_cacheable) {
                 r->variables[index[n]].valid = 0;
                 r->variables[index[n]].not_found = 0;
             }
@@ -504,7 +512,7 @@ ngx_http_script_copy_capture_len_code(ng
     e->ip += sizeof(ngx_http_script_copy_capture_code_t);
 
     if (code->n < e->ncaptures) {
-        if ((e->args || e->quote)
+        if ((e->is_args || e->quote)
             && (e->request->quoted_uri || e->request->plus_in_uri))
         {
             return e->captures[code->n + 1] - e->captures[code->n]
@@ -531,7 +539,7 @@ ngx_http_script_copy_capture_code(ngx_ht
     e->ip += sizeof(ngx_http_script_copy_capture_code_t);
 
     if (code->n < e->ncaptures) {
-        if ((e->args || e->quote)
+        if ((e->is_args || e->quote)
             && (e->request->quoted_uri || e->request->plus_in_uri))
         {
             e->pos = (u_char *) ngx_escape_uri(e->pos,
@@ -550,6 +558,16 @@ ngx_http_script_copy_capture_code(ngx_ht
 }
 
 
+size_t
+ngx_http_script_mark_args_code(ngx_http_script_engine_t *e)
+{
+    e->is_args = 1;
+    e->ip += sizeof(uintptr_t);
+
+    return 1;
+}
+
+
 void
 ngx_http_script_start_args_code(ngx_http_script_engine_t *e)
 {
@@ -700,7 +718,7 @@ ngx_http_script_regex_start_code(ngx_htt
         le.ncaptures = e->ncaptures;
         le.quote = code->redirect;
 
-        len = 1;  /* reserve 1 byte for possible "?" */
+        len = 0;
 
         while (*(uintptr_t *) le.ip) {
             lcode = *(ngx_http_script_len_code_pt *) le.ip;
@@ -708,13 +726,14 @@ ngx_http_script_regex_start_code(ngx_htt
         }
 
         e->buf.len = len;
+        e->is_args = le.is_args;
     }
 
     if (code->add_args && r->args.len) {
         e->buf.len += r->args.len + 1;
     }
 
-    e->buf.data = ngx_palloc(r->pool, e->buf.len);
+    e->buf.data = ngx_pnalloc(r->pool, e->buf.len);
     if (e->buf.data == NULL) {
         e->ip = ngx_http_script_exit;
         e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -750,7 +769,8 @@ ngx_http_script_regex_end_code(ngx_http_
         dst = e->buf.data;
         src = e->buf.data;
 
-        ngx_unescape_uri(&dst, &src, e->pos - e->buf.data, NGX_UNESCAPE_URI);
+        ngx_unescape_uri(&dst, &src, e->pos - e->buf.data,
+                         NGX_UNESCAPE_REDIRECT);
 
         if (src < e->pos) {
             dst = ngx_copy(dst, src, e->pos - src);
@@ -974,13 +994,16 @@ ngx_http_script_file_code(ngx_http_scrip
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    of.test_dir = 0;
-    of.retest = clcf->open_file_cache_retest;
+    ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+    of.directio = clcf->directio;
+    of.valid = clcf->open_file_cache_valid;
+    of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
     of.events = clcf->open_file_cache_events;
 
     if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
-        == NGX_ERROR)
+        != NGX_OK)
     {
         if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR) {
             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
@@ -993,69 +1016,69 @@ ngx_http_script_file_code(ngx_http_scrip
         case ngx_http_script_file_dir:
         case ngx_http_script_file_exists:
         case ngx_http_script_file_exec:
-             goto false;
+             goto false_value;
 
         case ngx_http_script_file_not_plain:
         case ngx_http_script_file_not_dir:
         case ngx_http_script_file_not_exists:
         case ngx_http_script_file_not_exec:
-             goto true;
+             goto true_value;
         }
 
-        goto false;
+        goto false_value;
     }
 
     switch (code->op) {
     case ngx_http_script_file_plain:
         if (of.is_file) {
-             goto true;
+             goto true_value;
         }
-        goto false;
+        goto false_value;
 
     case ngx_http_script_file_not_plain:
         if (of.is_file) {
-            goto false;
+            goto false_value;
         }
-        goto true;
+        goto true_value;
 
     case ngx_http_script_file_dir:
         if (of.is_dir) {
-             goto true;
+             goto true_value;
         }
-        goto false;
+        goto false_value;
 
     case ngx_http_script_file_not_dir:
         if (of.is_dir) {
-            goto false;
+            goto false_value;
         }
-        goto true;
+        goto true_value;
 
     case ngx_http_script_file_exists:
         if (of.is_file || of.is_dir || of.is_link) {
-             goto true;
+             goto true_value;
         }
-        goto false;
+        goto false_value;
 
     case ngx_http_script_file_not_exists:
         if (of.is_file || of.is_dir || of.is_link) {
-            goto false;
+            goto false_value;
         }
-        goto true;
+        goto true_value;
 
     case ngx_http_script_file_exec:
         if (of.is_exec) {
-             goto true;
+             goto true_value;
         }
-        goto false;
+        goto false_value;
 
     case ngx_http_script_file_not_exec:
         if (of.is_exec) {
-            goto false;
+            goto false_value;
         }
-        goto true;
+        goto true_value;
     }
 
-false:
+false_value:
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http script file op false");
@@ -1063,7 +1086,7 @@ false:
     *value = ngx_http_variable_null_value;
     return;
 
-true:
+true_value:
 
     *value = ngx_http_variable_true_value;
     return;
@@ -1099,7 +1122,7 @@ ngx_http_script_complex_value_code(ngx_h
     }
 
     e->buf.len = len;
-    e->buf.data = ngx_palloc(e->request->pool, len);
+    e->buf.data = ngx_pnalloc(e->request->pool, len);
     if (e->buf.data == NULL) {
         e->ip = ngx_http_script_exit;
         e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -1127,7 +1150,7 @@ ngx_http_script_value_code(ngx_http_scri
     e->sp->data = (u_char *) code->text_data;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                   "http script value: \"%V\"", e->sp);
+                   "http script value: \"%v\"", e->sp);
 
     e->sp++;
 }
@@ -1152,7 +1175,7 @@ ngx_http_script_set_var_code(ngx_http_sc
 
     r->variables[code->index].len = e->sp->len;
     r->variables[code->index].valid = 1;
-    r->variables[code->index].no_cachable = 0;
+    r->variables[code->index].no_cacheable = 0;
     r->variables[code->index].not_found = 0;
     r->variables[code->index].data = e->sp->data;
 }
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -27,6 +27,7 @@ typedef struct {
     unsigned                    flushed:1;
     unsigned                    skip:1;
     unsigned                    quote:1;
+    unsigned                    is_args:1;
     unsigned                    log:1;
 
     int                        *captures;
@@ -181,7 +182,7 @@ ngx_uint_t ngx_http_script_variables_cou
 ngx_int_t ngx_http_script_compile(ngx_http_script_compile_t *sc);
 u_char *ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,
     void *code_lengths, size_t reserved, void *code_values);
-void ngx_http_script_flush_no_cachable_variables(ngx_http_request_t *r,
+void ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r,
     ngx_array_t *indices);
 
 void *ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes,
@@ -194,6 +195,7 @@ size_t ngx_http_script_copy_var_len_code
 void ngx_http_script_copy_var_code(ngx_http_script_engine_t *e);
 size_t ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e);
 void ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e);
+size_t ngx_http_script_mark_args_code(ngx_http_script_engine_t *e);
 void ngx_http_script_start_args_code(ngx_http_script_engine_t *e);
 #if (NGX_PCRE)
 void ngx_http_script_regex_start_code(ngx_http_script_engine_t *e);
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -10,13 +10,27 @@
 #include <nginx.h>
 
 
-static u_char error_tail[] =
+static ngx_int_t ngx_http_send_error_page(ngx_http_request_t *r,
+    ngx_http_err_page_t *err_page);
+static ngx_int_t ngx_http_send_special_response(ngx_http_request_t *r,
+    ngx_http_core_loc_conf_t *clcf, ngx_uint_t err);
+static ngx_int_t ngx_http_send_refresh(ngx_http_request_t *r);
+
+
+static u_char ngx_http_error_full_tail[] =
 "<hr><center>" NGINX_VER "</center>" CRLF
 "</body>" CRLF
 "</html>" CRLF
 ;
 
 
+static u_char ngx_http_error_tail[] =
+"<hr><center>nginx</center>" CRLF
+"</body>" CRLF
+"</html>" CRLF
+;
+
+
 static u_char ngx_http_msie_stub[] =
 "<!-- The padding to disable MSIE's friendly error page -->" CRLF
 "<!-- The padding to disable MSIE's friendly error page -->" CRLF
@@ -35,7 +49,7 @@ static u_char ngx_http_msie_refresh_tail
 "\"></head><body></body></html>" CRLF;
 
 
-static char error_301_page[] =
+static char ngx_http_error_301_page[] =
 "<html>" CRLF
 "<head><title>301 Moved Permanently</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -43,7 +57,7 @@ static char error_301_page[] =
 ;
 
 
-static char error_302_page[] =
+static char ngx_http_error_302_page[] =
 "<html>" CRLF
 "<head><title>302 Found</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -51,7 +65,7 @@ static char error_302_page[] =
 ;
 
 
-static char error_400_page[] =
+static char ngx_http_error_400_page[] =
 "<html>" CRLF
 "<head><title>400 Bad Request</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -59,7 +73,7 @@ static char error_400_page[] =
 ;
 
 
-static char error_401_page[] =
+static char ngx_http_error_401_page[] =
 "<html>" CRLF
 "<head><title>401 Authorization Required</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -67,7 +81,7 @@ static char error_401_page[] =
 ;
 
 
-static char error_402_page[] =
+static char ngx_http_error_402_page[] =
 "<html>" CRLF
 "<head><title>402 Payment Required</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -75,7 +89,7 @@ static char error_402_page[] =
 ;
 
 
-static char error_403_page[] =
+static char ngx_http_error_403_page[] =
 "<html>" CRLF
 "<head><title>403 Forbidden</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -83,7 +97,7 @@ static char error_403_page[] =
 ;
 
 
-static char error_404_page[] =
+static char ngx_http_error_404_page[] =
 "<html>" CRLF
 "<head><title>404 Not Found</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -91,7 +105,7 @@ static char error_404_page[] =
 ;
 
 
-static char error_405_page[] =
+static char ngx_http_error_405_page[] =
 "<html>" CRLF
 "<head><title>405 Not Allowed</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -99,7 +113,7 @@ static char error_405_page[] =
 ;
 
 
-static char error_406_page[] =
+static char ngx_http_error_406_page[] =
 "<html>" CRLF
 "<head><title>406 Not Acceptable</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -107,7 +121,7 @@ static char error_406_page[] =
 ;
 
 
-static char error_408_page[] =
+static char ngx_http_error_408_page[] =
 "<html>" CRLF
 "<head><title>408 Request Time-out</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -115,7 +129,7 @@ static char error_408_page[] =
 ;
 
 
-static char error_409_page[] =
+static char ngx_http_error_409_page[] =
 "<html>" CRLF
 "<head><title>409 Conflict</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -123,7 +137,7 @@ static char error_409_page[] =
 ;
 
 
-static char error_410_page[] =
+static char ngx_http_error_410_page[] =
 "<html>" CRLF
 "<head><title>410 Gone</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -131,7 +145,7 @@ static char error_410_page[] =
 ;
 
 
-static char error_411_page[] =
+static char ngx_http_error_411_page[] =
 "<html>" CRLF
 "<head><title>411 Length Required</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -139,7 +153,7 @@ static char error_411_page[] =
 ;
 
 
-static char error_412_page[] =
+static char ngx_http_error_412_page[] =
 "<html>" CRLF
 "<head><title>412 Precondition Failed</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -147,7 +161,7 @@ static char error_412_page[] =
 ;
 
 
-static char error_413_page[] =
+static char ngx_http_error_413_page[] =
 "<html>" CRLF
 "<head><title>413 Request Entity Too Large</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -155,7 +169,7 @@ static char error_413_page[] =
 ;
 
 
-static char error_414_page[] =
+static char ngx_http_error_414_page[] =
 "<html>" CRLF
 "<head><title>414 Request-URI Too Large</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -163,7 +177,7 @@ static char error_414_page[] =
 ;
 
 
-static char error_415_page[] =
+static char ngx_http_error_415_page[] =
 "<html>" CRLF
 "<head><title>415 Unsupported Media Type</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -171,7 +185,7 @@ static char error_415_page[] =
 ;
 
 
-static char error_416_page[] =
+static char ngx_http_error_416_page[] =
 "<html>" CRLF
 "<head><title>416 Requested Range Not Satisfiable</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -179,7 +193,7 @@ static char error_416_page[] =
 ;
 
 
-static char error_495_page[] =
+static char ngx_http_error_495_page[] =
 "<html>" CRLF
 "<head><title>400 The SSL certificate error</title></head>"
 CRLF
@@ -189,7 +203,7 @@ CRLF
 ;
 
 
-static char error_496_page[] =
+static char ngx_http_error_496_page[] =
 "<html>" CRLF
 "<head><title>400 No required SSL certificate was sent</title></head>"
 CRLF
@@ -199,7 +213,7 @@ CRLF
 ;
 
 
-static char error_497_page[] =
+static char ngx_http_error_497_page[] =
 "<html>" CRLF
 "<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>"
 CRLF
@@ -209,7 +223,7 @@ CRLF
 ;
 
 
-static char error_500_page[] =
+static char ngx_http_error_500_page[] =
 "<html>" CRLF
 "<head><title>500 Internal Server Error</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -217,7 +231,7 @@ static char error_500_page[] =
 ;
 
 
-static char error_501_page[] =
+static char ngx_http_error_501_page[] =
 "<html>" CRLF
 "<head><title>501 Method Not Implemented</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -225,7 +239,7 @@ static char error_501_page[] =
 ;
 
 
-static char error_502_page[] =
+static char ngx_http_error_502_page[] =
 "<html>" CRLF
 "<head><title>502 Bad Gateway</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -233,7 +247,7 @@ static char error_502_page[] =
 ;
 
 
-static char error_503_page[] =
+static char ngx_http_error_503_page[] =
 "<html>" CRLF
 "<head><title>503 Service Temporarily Unavailable</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -241,7 +255,7 @@ static char error_503_page[] =
 ;
 
 
-static char error_504_page[] =
+static char ngx_http_error_504_page[] =
 "<html>" CRLF
 "<head><title>504 Gateway Time-out</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -249,7 +263,7 @@ static char error_504_page[] =
 ;
 
 
-static char error_507_page[] =
+static char ngx_http_error_507_page[] =
 "<html>" CRLF
 "<head><title>507 Insufficient Storage</title></head>" CRLF
 "<body bgcolor=\"white\">" CRLF
@@ -257,53 +271,53 @@ static char error_507_page[] =
 ;
 
 
-static ngx_str_t error_pages[] = {
+static ngx_str_t ngx_http_error_pages[] = {
 
-    ngx_null_string,             /* 201, 204 */
+    ngx_null_string,                     /* 201, 204 */
 
 #define NGX_HTTP_LEVEL_200  1
 
-    /* ngx_null_string, */       /* 300 */
-    ngx_string(error_301_page),
-    ngx_string(error_302_page),
-    ngx_null_string,             /* 303 */
+    /* ngx_null_string, */               /* 300 */
+    ngx_string(ngx_http_error_301_page),
+    ngx_string(ngx_http_error_302_page),
+    ngx_null_string,                     /* 303 */
 
 #define NGX_HTTP_LEVEL_300  3
 
-    ngx_string(error_400_page),
-    ngx_string(error_401_page),
-    ngx_string(error_402_page),
-    ngx_string(error_403_page),
-    ngx_string(error_404_page),
-    ngx_string(error_405_page),
-    ngx_string(error_406_page),
-    ngx_null_string,             /* 407 */
-    ngx_string(error_408_page),
-    ngx_string(error_409_page),
-    ngx_string(error_410_page),
-    ngx_string(error_411_page),
-    ngx_string(error_412_page),
-    ngx_string(error_413_page),
-    ngx_string(error_414_page),
-    ngx_string(error_415_page),
-    ngx_string(error_416_page),
+    ngx_string(ngx_http_error_400_page),
+    ngx_string(ngx_http_error_401_page),
+    ngx_string(ngx_http_error_402_page),
+    ngx_string(ngx_http_error_403_page),
+    ngx_string(ngx_http_error_404_page),
+    ngx_string(ngx_http_error_405_page),
+    ngx_string(ngx_http_error_406_page),
+    ngx_null_string,                     /* 407 */
+    ngx_string(ngx_http_error_408_page),
+    ngx_string(ngx_http_error_409_page),
+    ngx_string(ngx_http_error_410_page),
+    ngx_string(ngx_http_error_411_page),
+    ngx_string(ngx_http_error_412_page),
+    ngx_string(ngx_http_error_413_page),
+    ngx_string(ngx_http_error_414_page),
+    ngx_string(ngx_http_error_415_page),
+    ngx_string(ngx_http_error_416_page),
 
 #define NGX_HTTP_LEVEL_400  17
 
-    ngx_string(error_495_page),  /* 495, https certificate error */
-    ngx_string(error_496_page),  /* 496, https no certificate */
-    ngx_string(error_497_page),  /* 497, http to https */
-    ngx_string(error_404_page),  /* 498, invalid host name */
-    ngx_null_string,             /* 499, client had closed connection */
+    ngx_string(ngx_http_error_495_page), /* 495, https certificate error */
+    ngx_string(ngx_http_error_496_page), /* 496, https no certificate */
+    ngx_string(ngx_http_error_497_page), /* 497, http to https */
+    ngx_string(ngx_http_error_404_page), /* 498, canceled */
+    ngx_null_string,                     /* 499, client has closed connection */
 
-    ngx_string(error_500_page),
-    ngx_string(error_501_page),
-    ngx_string(error_502_page),
-    ngx_string(error_503_page),
-    ngx_string(error_504_page),
-    ngx_null_string,             /* 505 */
-    ngx_null_string,             /* 506 */
-    ngx_string(error_507_page)
+    ngx_string(ngx_http_error_500_page),
+    ngx_string(ngx_http_error_501_page),
+    ngx_string(ngx_http_error_502_page),
+    ngx_string(ngx_http_error_503_page),
+    ngx_string(ngx_http_error_504_page),
+    ngx_null_string,                     /* 505 */
+    ngx_null_string,                     /* 506 */
+    ngx_string(ngx_http_error_507_page)
 };
 
 
@@ -313,19 +327,14 @@ static ngx_str_t  ngx_http_get_name = { 
 ngx_int_t
 ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error)
 {
-    u_char                    *p;
-    size_t                     msie_refresh;
-    uintptr_t                  escape;
     ngx_int_t                  rc;
-    ngx_buf_t                 *b;
-    ngx_str_t                 *uri, *location;
-    ngx_uint_t                 i, n, err, msie_padding;
-    ngx_chain_t               *out, *cl;
+    ngx_uint_t                 i, err;
     ngx_http_err_page_t       *err_page;
     ngx_http_core_loc_conf_t  *clcf;
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http special response: %d, \"%V\"", error, &r->uri);
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http special response: %d, \"%V?%V\"",
+                   error, &r->uri, &r->args);
 
     rc = ngx_http_discard_request_body(r);
 
@@ -335,7 +344,7 @@ ngx_http_special_response_handler(ngx_ht
 
     r->err_status = error;
 
-    if (r->keepalive != 0) {
+    if (r->keepalive) {
         switch (error) {
             case NGX_HTTP_BAD_REQUEST:
             case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE:
@@ -371,68 +380,20 @@ ngx_http_special_response_handler(ngx_ht
         err_page = clcf->error_pages->elts;
 
         for (i = 0; i < clcf->error_pages->nelts; i++) {
-
             if (err_page[i].status == error) {
-                r->err_status = err_page[i].overwrite;
-
-                r->method = NGX_HTTP_GET;
-                r->method_name = ngx_http_get_name;
-
-                uri = &err_page[i].uri;
-
-                if (err_page[i].uri_lengths) {
-                    if (ngx_http_script_run(r, uri,
-                                            err_page[i].uri_lengths->elts, 0,
-                                            err_page[i].uri_values->elts)
-                        == NULL)
-                    {
-                        return NGX_ERROR;
-                    }
-
-                    if (r->zero_in_uri) {
-                        for (n = 0; n < uri->len; n++) {
-                            if (uri->data[n] == '\0') {
-                                goto zero;
-                            }
-                        }
-
-                        r->zero_in_uri = 0;
-                    }
-
-                } else {
-                    r->zero_in_uri = 0;
-                }
-
-            zero:
-
-                if (uri->data[0] == '/') {
-                    return ngx_http_internal_redirect(r, uri, NULL);
-                }
-
-                if (uri->data[0] == '@') {
-                    return ngx_http_named_location(r, uri);
-                }
-
-                r->headers_out.location =
-                                        ngx_list_push(&r->headers_out.headers);
-
-                if (r->headers_out.location) {
-                    error = NGX_HTTP_MOVED_TEMPORARILY;
-
-                    r->err_status = NGX_HTTP_MOVED_TEMPORARILY;
-
-                    r->headers_out.location->hash = 1;
-                    r->headers_out.location->key.len = sizeof("Location") - 1;
-                    r->headers_out.location->key.data = (u_char *) "Location";
-                    r->headers_out.location->value = *uri;
-
-                } else {
-                    return NGX_ERROR;
-                }
+                return ngx_http_send_error_page(r, &err_page[i]);
             }
         }
     }
 
+    if (clcf->msie_refresh
+        && r->headers_in.msie
+        && (error == NGX_HTTP_MOVED_PERMANENTLY
+            || error == NGX_HTTP_MOVED_TEMPORARILY))
+    {
+        return ngx_http_send_refresh(r);
+    }
+
     if (error == NGX_HTTP_CREATED) {
         /* 201 */
         err = 0;
@@ -461,29 +422,151 @@ ngx_http_special_response_handler(ngx_ht
             case NGX_HTTPS_CERT_ERROR:
             case NGX_HTTPS_NO_CERT:
                 r->err_status = NGX_HTTP_BAD_REQUEST;
-                error = NGX_HTTP_BAD_REQUEST;
                 break;
         }
     }
 
+    return ngx_http_send_special_response(r, clcf, err);
+}
+
+
+static ngx_int_t
+ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)
+{
+    u_char                     ch, *p, *last;
+    ngx_str_t                 *uri, *args, u, a;
+    ngx_table_elt_t           *location;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    r->err_status = err_page->overwrite;
+
+    r->zero_in_uri = 0;
+
+    if (err_page->uri_lengths) {
+        if (ngx_http_script_run(r, &u, err_page->uri_lengths->elts, 0,
+                                err_page->uri_values->elts)
+            == NULL)
+        {
+            return NGX_ERROR;
+        }
+
+        p = u.data;
+        uri = &u;
+        args = NULL;
+
+        if (*p == '/') {
+
+            last = p + uri->len;
+
+            while (p < last) {
+
+                ch = *p++;
+
+                if (ch == '?') {
+                    a.len = last - p;
+                    a.data = p;
+                    args = &a;
+
+                    u.len = p - 1 - u.data;
+
+                    while (p < last) {
+                        if (*p++ == '\0') {
+                            r->zero_in_uri = 1;
+                            break;
+                        }
+                    }
+
+                    break;
+                }
+
+                if (ch == '\0') {
+                    r->zero_in_uri = 1;
+                    continue;
+                }
+            }
+        }
+
+    } else {
+        uri = &err_page->uri;
+        args = &err_page->args;
+    }
+
+    if (uri->data[0] == '/') {
+
+        r->method = NGX_HTTP_GET;
+        r->method_name = ngx_http_get_name;
+
+        return ngx_http_internal_redirect(r, uri, args);
+    }
+
+    if (uri->data[0] == '@') {
+        return ngx_http_named_location(r, uri);
+    }
+
+    location = ngx_list_push(&r->headers_out.headers);
+
+    if (location == NULL) {
+        return NGX_ERROR;
+    }
+
+    r->err_status = NGX_HTTP_MOVED_TEMPORARILY;
+
+    location->hash = 1;
+    location->key.len = sizeof("Location") - 1;
+    location->key.data = (u_char *) "Location";
+    location->value = *uri;
+
+    r->headers_out.location = location;
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    if (clcf->msie_refresh && r->headers_in.msie) {
+        return ngx_http_send_refresh(r);
+    }
+
+    return ngx_http_send_special_response(r, clcf, NGX_HTTP_MOVED_TEMPORARILY
+                                                   - NGX_HTTP_MOVED_PERMANENTLY
+                                                   + NGX_HTTP_LEVEL_200);
+}
+
+
+static ngx_int_t
+ngx_http_send_special_response(ngx_http_request_t *r,
+    ngx_http_core_loc_conf_t *clcf, ngx_uint_t err)
+{
+    u_char       *tail;
+    size_t        len;
+    ngx_int_t     rc;
+    ngx_buf_t    *b;
+    ngx_uint_t    msie_padding;
+    ngx_chain_t   out[3];
+
+    if (clcf->server_tokens) {
+        len = sizeof(ngx_http_error_full_tail) - 1;
+        tail = ngx_http_error_full_tail;
+
+    } else {
+        len = sizeof(ngx_http_error_tail) - 1;
+        tail = ngx_http_error_tail;
+    }
+
     msie_padding = 0;
 
     if (!r->zero_body) {
-        if (error_pages[err].len) {
-            r->headers_out.content_length_n = error_pages[err].len
-                                              + sizeof(error_tail) - 1;
-
+        if (ngx_http_error_pages[err].len) {
+            r->headers_out.content_length_n = ngx_http_error_pages[err].len
+                                              + len;
             if (clcf->msie_padding
                 && r->headers_in.msie
                 && r->http_version >= NGX_HTTP_VERSION_10
-                && error >= NGX_HTTP_BAD_REQUEST
-                && error != NGX_HTTP_REQUEST_URI_TOO_LARGE)
+                && err >= NGX_HTTP_LEVEL_300)
             {
                 r->headers_out.content_length_n +=
                                                 sizeof(ngx_http_msie_stub) - 1;
                 msie_padding = 1;
             }
 
+            r->headers_out.content_type_len = sizeof("text/html") - 1;
             r->headers_out.content_type.len = sizeof("text/html") - 1;
             r->headers_out.content_type.data = (u_char *) "text/html";
 
@@ -501,31 +584,102 @@ ngx_http_special_response_handler(ngx_ht
         r->headers_out.content_length = NULL;
     }
 
-    if (clcf->msie_refresh
-        && r->headers_in.msie
-        && (error == NGX_HTTP_MOVED_PERMANENTLY
-            || error == NGX_HTTP_MOVED_TEMPORARILY))
-    {
+    ngx_http_clear_accept_ranges(r);
+    ngx_http_clear_last_modified(r);
+
+    rc = ngx_http_send_header(r);
+
+    if (rc == NGX_ERROR || r->header_only) {
+        return rc;
+    }
+
+    if (ngx_http_error_pages[err].len == 0) {
+        return NGX_OK;
+    }
+
+    b = ngx_calloc_buf(r->pool);
+    if (b == NULL) {
+        return NGX_ERROR;
+    }
+
+    b->memory = 1;
+    b->pos = ngx_http_error_pages[err].data;
+    b->last = ngx_http_error_pages[err].data + ngx_http_error_pages[err].len;
 
-        location = &r->headers_out.location->value;
+    out[0].buf = b;
+    out[0].next = &out[1];
+
+    b = ngx_calloc_buf(r->pool);
+    if (b == NULL) {
+        return NGX_ERROR;
+    }
+
+    b->memory = 1;
+
+    b->pos = tail;
+    b->last = tail + len;
 
-        escape = 2 * ngx_escape_uri(NULL, location->data, location->len,
-                                    NGX_ESCAPE_REFRESH);
+    out[1].buf = b;
+    out[1].next = NULL;
+
+    if (msie_padding) {
+        b = ngx_calloc_buf(r->pool);
+        if (b == NULL) {
+            return NGX_ERROR;
+        }
+
+        b->memory = 1;
+        b->pos = ngx_http_msie_stub;
+        b->last = ngx_http_msie_stub + sizeof(ngx_http_msie_stub) - 1;
 
-        msie_refresh = sizeof(ngx_http_msie_refresh_head) - 1
-                       + escape + location->len
-                       + sizeof(ngx_http_msie_refresh_tail) - 1;
+        out[1].next = &out[2];
+        out[2].buf = b;
+        out[2].next = NULL;
+    }
+
+    if (r == r->main) {
+        b->last_buf = 1;
+    }
+
+    b->last_in_chain = 1;
+
+    return ngx_http_output_filter(r, &out[0]);
+}
+
 
-        r->err_status = NGX_HTTP_OK;
-        r->headers_out.content_type_len = sizeof("text/html") - 1;
-        r->headers_out.content_length_n = msie_refresh;
-        r->headers_out.location->hash = 0;
-        r->headers_out.location = NULL;
+static ngx_int_t
+ngx_http_send_refresh(ngx_http_request_t *r)
+{
+    u_char       *p, *location;
+    size_t        len, size;
+    uintptr_t     escape;
+    ngx_int_t     rc;
+    ngx_buf_t    *b;
+    ngx_chain_t   out;
+
+    len = r->headers_out.location->value.len;
+    location = r->headers_out.location->value.data;
+
+    escape = 2 * ngx_escape_uri(NULL, location, len, NGX_ESCAPE_REFRESH);
 
-    } else {
-        location = NULL;
-        escape = 0;
-        msie_refresh = 0;
+    size = sizeof(ngx_http_msie_refresh_head) - 1
+           + escape + len
+           + sizeof(ngx_http_msie_refresh_tail) - 1;
+
+    r->err_status = NGX_HTTP_OK;
+
+    r->headers_out.content_type_len = sizeof("text/html") - 1;
+    r->headers_out.content_type.len = sizeof("text/html") - 1;
+    r->headers_out.content_type.data = (u_char *) "text/html";
+
+    r->headers_out.location->hash = 0;
+    r->headers_out.location = NULL;
+
+    r->headers_out.content_length_n = size;
+
+    if (r->headers_out.content_length) {
+        r->headers_out.content_length->hash = 0;
+        r->headers_out.content_length = NULL;
     }
 
     ngx_http_clear_accept_ranges(r);
@@ -537,103 +691,29 @@ ngx_http_special_response_handler(ngx_ht
         return rc;
     }
 
-
-    if (msie_refresh == 0) {
-
-        if (error_pages[err].len == 0) {
-            return NGX_OK;
-        }
-
-        b = ngx_calloc_buf(r->pool);
-        if (b == NULL) {
-            return NGX_ERROR;
-        }
-
-        b->memory = 1;
-        b->pos = error_pages[err].data;
-        b->last = error_pages[err].data + error_pages[err].len;
-
-        cl = ngx_alloc_chain_link(r->pool);
-        if (cl == NULL) {
-            return NGX_ERROR;
-        }
-
-        cl->buf = b;
-        out = cl;
-
-
-        b = ngx_calloc_buf(r->pool);
-        if (b == NULL) {
-            return NGX_ERROR;
-        }
+    b = ngx_create_temp_buf(r->pool, size);
+    if (b == NULL) {
+        return NGX_ERROR;
+    }
 
-        b->memory = 1;
-        b->pos = error_tail;
-        b->last = error_tail + sizeof(error_tail) - 1;
-
-        cl->next = ngx_alloc_chain_link(r->pool);
-        if (cl->next == NULL) {
-            return NGX_ERROR;
-        }
-
-        cl = cl->next;
-        cl->buf = b;
+    p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head,
+                   sizeof(ngx_http_msie_refresh_head) - 1);
 
-        if (msie_padding) {
-            b = ngx_calloc_buf(r->pool);
-            if (b == NULL) {
-                return NGX_ERROR;
-            }
-
-            b->memory = 1;
-            b->pos = ngx_http_msie_stub;
-            b->last = ngx_http_msie_stub + sizeof(ngx_http_msie_stub) - 1;
-
-            cl->next = ngx_alloc_chain_link(r->pool);
-            if (cl->next == NULL) {
-                return NGX_ERROR;
-            }
-
-            cl = cl->next;
-            cl->buf = b;
-        }
+    if (escape == 0) {
+        p = ngx_cpymem(p, location, len);
 
     } else {
-        b = ngx_create_temp_buf(r->pool, msie_refresh);
-        if (b == NULL) {
-            return NGX_ERROR;
-        }
-
-        p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head,
-                       sizeof(ngx_http_msie_refresh_head) - 1);
-
-        if (escape == 0) {
-            p = ngx_cpymem(p, location->data, location->len);
-
-        } else {
-            p = (u_char *) ngx_escape_uri(p, location->data, location->len,
-                                          NGX_ESCAPE_REFRESH);
-        }
-
-        b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail,
-                             sizeof(ngx_http_msie_refresh_tail) - 1);
-
-        cl = ngx_alloc_chain_link(r->pool);
-        if (cl == NULL) {
-            return NGX_ERROR;
-        }
-
-        cl->buf = b;
-        out = cl;
+        p = (u_char *) ngx_escape_uri(p, location, len, NGX_ESCAPE_REFRESH);
     }
 
-    if (r == r->main) {
-        b->last_buf = 1;
-    }
+    b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail,
+                         sizeof(ngx_http_msie_refresh_tail) - 1);
 
+    b->last_buf = 1;
     b->last_in_chain = 1;
 
-    cl->next = NULL;
+    out.buf = b;
+    out.next = NULL;
 
-    return ngx_http_output_filter(r, out);
+    return ngx_http_output_filter(r, &out);
 }
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -9,6 +9,7 @@
 #include <ngx_http.h>
 
 
+static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx);
 static void ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r);
 static void ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r);
 static void ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
@@ -136,7 +137,8 @@ ngx_http_upstream_header_t  ngx_http_ups
                  ngx_http_upstream_copy_header_line, 0, 0 },
 
     { ngx_string("Location"),
-                 ngx_http_upstream_ignore_header_line, 0,
+                 ngx_http_upstream_process_header_line,
+                 offsetof(ngx_http_upstream_headers_in_t, location),
                  ngx_http_upstream_rewrite_location, 0, 0 },
 
     { ngx_string("Refresh"),
@@ -283,10 +285,15 @@ static ngx_http_variable_t  ngx_http_ups
 void
 ngx_http_upstream_init(ngx_http_request_t *r)
 {
-    ngx_connection_t          *c;
-    ngx_http_cleanup_t        *cln;
-    ngx_http_upstream_t       *u;
-    ngx_http_core_loc_conf_t  *clcf;
+    ngx_str_t                      *host;
+    ngx_uint_t                      i;
+    ngx_connection_t               *c;
+    ngx_resolver_ctx_t             *ctx, temp;
+    ngx_http_cleanup_t             *cln;
+    ngx_http_upstream_t            *u;
+    ngx_http_core_loc_conf_t       *clcf;
+    ngx_http_upstream_srv_conf_t   *uscf, **uscfp;
+    ngx_http_upstream_main_conf_t  *umcf;
 
     c = r->connection;
 
@@ -320,11 +327,6 @@ ngx_http_upstream_init(ngx_http_request_
         u->request_bufs = r->request_body->bufs;
     }
 
-    if (u->conf->upstream->peer.init(r, u->conf->upstream) != NGX_OK) {
-        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-        return;
-    }
-
     if (u->create_request(r) != NGX_OK) {
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         return;
@@ -332,7 +334,6 @@ ngx_http_upstream_init(ngx_http_request_
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    u->output.sendfile = r->connection->sendfile;
     u->output.pool = r->pool;
     u->output.bufs.num = 1;
     u->output.bufs.size = clcf->client_body_buffer_size;
@@ -374,11 +375,129 @@ ngx_http_upstream_init(ngx_http_request_
 
     u->store = (u->conf->store || u->conf->store_lengths);
 
+    if (u->resolved == NULL) {
+
+        uscf = u->conf->upstream;
+
+    } else {
+
+        host = &u->resolved->host;
+
+        umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
+
+        uscfp = umcf->upstreams.elts;
+
+        for (i = 0; i < umcf->upstreams.nelts; i++) {
+
+            uscf = uscfp[i];
+
+            if (uscf->host.len == host->len
+                && ((uscf->port == 0 && u->resolved->default_port)
+                     || uscf->port == u->resolved->port)
+                && ngx_memcmp(uscf->host.data, host->data, host->len) == 0)
+            {
+                goto found;
+            }
+        }
+
+        temp.name = *host;
+
+        ctx = ngx_resolve_start(clcf->resolver, &temp);
+        if (ctx == NULL) {
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return;
+        }
+
+        if (ctx == NGX_NO_RESOLVER) {
+            ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                          "no resolver defined to resolve %V", host);
+
+            ngx_http_finalize_request(r, NGX_HTTP_BAD_GATEWAY);
+            return;
+        }
+
+        ctx->name = *host;
+        ctx->type = NGX_RESOLVE_A;
+        ctx->handler = ngx_http_upstream_resolve_handler;
+        ctx->data = r;
+        ctx->timeout = clcf->resolver_timeout;
+
+        u->resolved->ctx = ctx;
+
+        if (ngx_resolve_name(ctx) != NGX_OK) {
+            u->resolved->ctx = NULL;
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return;
+        }
+
+        return;
+    }
+
+found:
+
+    if (uscf->peer.init(r, uscf) != NGX_OK) {
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
     ngx_http_upstream_connect(r, u);
 }
 
 
 static void
+ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx)
+{
+    ngx_http_request_t            *r;
+    ngx_http_upstream_resolved_t  *ur;
+
+    r = ctx->data;
+
+    r->upstream->resolved->ctx = NULL;
+
+    if (ctx->state) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "%V could not be resolved (%i: %s)",
+                      &ctx->name, ctx->state,
+                      ngx_resolver_strerror(ctx->state));
+
+        ngx_resolve_name_done(ctx);
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_GATEWAY);
+        return;
+    }
+
+    ur = r->upstream->resolved;
+    ur->naddrs = ctx->naddrs;
+    ur->addrs = ctx->addrs;
+
+#if (NGX_DEBUG)
+    {
+    in_addr_t   addr;
+    ngx_uint_t  i;
+
+    for (i = 0; i < ctx->naddrs; i++) {
+        addr = ntohl(ur->addrs[i]);
+
+        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "name was resolved to %ud.%ud.%ud.%ud",
+                       (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+                       (addr >> 8) & 0xff, addr & 0xff);
+    }
+    }
+#endif
+
+    if (ngx_http_upstream_create_round_robin_peer(r, ur) != NGX_OK) {
+        ngx_resolve_name_done(ctx);
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
+    ngx_resolve_name_done(ctx);
+
+    ngx_http_upstream_connect(r, r->upstream);
+}
+
+
+static void
 ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r)
 {
     ngx_http_upstream_check_broken_connection(r, r->connection->read);
@@ -434,7 +553,7 @@ ngx_http_upstream_check_broken_connectio
             ev->error = 1;
         }
 
-        if (!u->cachable && !u->store && u->peer.connection) {
+        if (!u->cacheable && !u->store && u->peer.connection) {
             ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,
                           "kevent() reported that client closed prematurely "
                           "connection, so upstream connection is closed too");
@@ -500,7 +619,7 @@ ngx_http_upstream_check_broken_connectio
     ev->eof = 1;
     c->error = 1;
 
-    if (!u->cachable && !u->store && u->peer.connection) {
+    if (!u->cacheable && !u->store && u->peer.connection) {
         ngx_log_error(NGX_LOG_INFO, ev->log, err,
                       "client closed prematurely connection, "
                       "so upstream connection is closed too");
@@ -584,6 +703,7 @@ ngx_http_upstream_connect(ngx_http_reque
     c->read->handler = ngx_http_upstream_process_header;
 
     c->sendfile &= r->connection->sendfile;
+    u->output.sendfile = c->sendfile;
 
     c->pool = r->pool;
     c->read->log = c->write->log = c->log = r->connection->log;
@@ -638,7 +758,7 @@ ngx_http_upstream_connect(ngx_http_reque
 
 #if (NGX_HTTP_SSL)
 
-    if (u->conf->ssl && c->ssl == NULL) {
+    if (u->ssl && c->ssl == NULL) {
         ngx_http_upstream_ssl_init_connection(r, u, c);
         return;
     }
@@ -725,10 +845,9 @@ ngx_http_upstream_reinit(ngx_http_reques
         return NGX_ERROR;
     }
 
-    ngx_memzero(&r->upstream->headers_in,
-                sizeof(ngx_http_upstream_headers_in_t));
-
-    if (ngx_list_init(&r->upstream->headers_in.headers, r->pool, 8,
+    ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
+
+    if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
                       sizeof(ngx_table_elt_t))
         != NGX_OK)
     {
@@ -893,7 +1012,7 @@ ngx_http_upstream_send_request_handler(n
 
 #if (NGX_HTTP_SSL)
 
-    if (u->conf->ssl && c->ssl == NULL) {
+    if (u->ssl && c->ssl == NULL) {
         ngx_http_upstream_ssl_init_connection(r, u, c);
         return;
     }
@@ -963,7 +1082,7 @@ ngx_http_upstream_process_header(ngx_eve
 
         u->buffer.tag = u->output.tag;
 
-        if (ngx_list_init(&r->upstream->headers_in.headers, r->pool, 8,
+        if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
                           sizeof(ngx_table_elt_t))
             != NGX_OK)
         {
@@ -980,8 +1099,7 @@ ngx_http_upstream_process_header(ngx_eve
 #endif
     }
 
-    n = u->peer.connection->recv(u->peer.connection, u->buffer.last,
-                                 u->buffer.end - u->buffer.last);
+    n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);
 
     if (n == NGX_AGAIN) {
 #if 0
@@ -1044,7 +1162,7 @@ ngx_http_upstream_process_header(ngx_eve
         return;
     }
 
-    if (rc == NGX_ERROR || rc == NGX_HTTP_INTERNAL_SERVER_ERROR) {
+    if (rc == NGX_ERROR) {
         ngx_http_upstream_finalize_request(r, u,
                                            NGX_HTTP_INTERNAL_SERVER_ERROR);
         return;
@@ -1133,11 +1251,11 @@ ngx_http_upstream_process_header(ngx_eve
 
     umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
 
-    if (r->upstream->headers_in.x_accel_redirect) {
+    if (u->headers_in.x_accel_redirect) {
 
         ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);
 
-        part = &r->upstream->headers_in.headers.part;
+        part = &u->headers_in.headers.part;
         h = part->elts;
 
         for (i = 0; /* void */; i++) {
@@ -1164,7 +1282,7 @@ ngx_http_upstream_process_header(ngx_eve
             }
         }
 
-        uri = &r->upstream->headers_in.x_accel_redirect->value;
+        uri = &u->headers_in.x_accel_redirect->value;
         args.len = 0;
         args.data = NULL;
         flags = 0;
@@ -1186,7 +1304,7 @@ ngx_http_upstream_process_header(ngx_eve
         return;
     }
 
-    part = &r->upstream->headers_in.headers.part;
+    part = &u->headers_in.headers.part;
     h = part->elts;
 
     for (i = 0; /* void */; i++) {
@@ -1238,6 +1356,8 @@ ngx_http_upstream_process_header(ngx_eve
     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;
+
     if (r->headers_out.content_length_n != -1) {
         u->length = (size_t) r->headers_out.content_length_n;
 
@@ -1514,7 +1634,7 @@ ngx_http_upstream_send_response(ngx_http
         }
     }
 
-    if (u->cachable) {
+    if (u->cacheable) {
         header = (ngx_http_cache_header_t *) u->buffer->start;
 
         header->expires = u->cache->ctx.expires;
@@ -1542,7 +1662,7 @@ ngx_http_upstream_send_response(ngx_http
     p->pool = r->pool;
     p->log = c->log;
 
-    p->cachable = u->cachable || u->store;
+    p->cacheable = u->cacheable || u->store;
 
     p->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
     if (p->temp_file == NULL) {
@@ -1555,7 +1675,7 @@ ngx_http_upstream_send_response(ngx_http
     p->temp_file->path = u->conf->temp_path;
     p->temp_file->pool = r->pool;
 
-    if (u->cachable || u->store) {
+    if (u->cacheable || u->store) {
         p->temp_file->persistent = 1;
 
     } else {
@@ -1579,7 +1699,7 @@ ngx_http_upstream_send_response(ngx_http
 
     p->preread_size = u->buffer.last - u->buffer.pos;
 
-    if (u->cachable) {
+    if (u->cacheable) {
 
         p->buf_to_file = ngx_calloc_buf(r->pool);
         if (p->buf_to_file == NULL) {
@@ -1820,6 +1940,7 @@ ngx_http_upstream_non_buffered_filter(vo
     cl->buf->pos = b->last;
     b->last += bytes;
     cl->buf->last = b->last;
+    cl->buf->tag = u->output.tag;
 
     if (u->length == NGX_MAX_SIZE_T_VALUE) {
         return NGX_OK;
@@ -1841,6 +1962,7 @@ ngx_http_upstream_process_downstream(ngx
 static void
 ngx_http_upstream_process_body(ngx_event_t *ev)
 {
+    ngx_temp_file_t      *tf;
     ngx_event_pipe_t     *p;
     ngx_connection_t     *c, *downstream;
     ngx_http_log_ctx_t   *ctx;
@@ -1935,18 +2057,22 @@ ngx_http_upstream_process_body(ngx_event
 
         if (u->store) {
 
-            if (p->upstream_eof && u->headers_in.status_n == NGX_HTTP_OK) {
-
+            tf = u->pipe->temp_file;
+
+            if (p->upstream_eof
+                && u->headers_in.status_n == NGX_HTTP_OK
+                && (u->headers_in.content_length_n == -1
+                    || (u->headers_in.content_length_n == tf->offset)))
+            {
                 ngx_http_upstream_store(r, u);
 
             } else if ((p->upstream_error
                         || (p->upstream_eof
                             && u->headers_in.status_n != NGX_HTTP_OK))
-                       && u->pipe->temp_file->file.fd != NGX_INVALID_FILE)
+                       && tf->file.fd != NGX_INVALID_FILE)
             {
-                if (ngx_delete_file(u->pipe->temp_file->file.name.data)
-                    == NGX_FILE_ERROR)
-                {
+                if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {
+
                     ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                                   ngx_delete_file_n " \"%s\" failed",
                                   u->pipe->temp_file->file.name.data);
@@ -1956,14 +2082,14 @@ ngx_http_upstream_process_body(ngx_event
 
 #if (NGX_HTTP_FILE_CACHE)
 
-        if (p->upstream_done && u->cachable) {
+        if (p->upstream_done && u->cacheable) {
             if (ngx_http_cache_update(r) == NGX_ERROR) {
                 ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock);
                 ngx_http_upstream_finalize_request(r, u, 0);
                 return;
             }
 
-        } else if (p->upstream_eof && u->cachable) {
+        } else if (p->upstream_eof && u->cacheable) {
 
             /* TODO: check length & update cache */
 
@@ -1991,7 +2117,7 @@ ngx_http_upstream_process_body(ngx_event
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                        "http upstream downstream error");
 
-        if (!u->cachable && u->peer.connection) {
+        if (!u->cacheable && u->peer.connection) {
             ngx_http_upstream_finalize_request(r, u, 0);
         }
     }
@@ -2001,15 +2127,15 @@ ngx_http_upstream_process_body(ngx_event
 static void
 ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
-    char             *failed;
-    u_char           *name;
-    size_t            root;
-    time_t            lm;
-    ngx_err_t         err;
-    ngx_str_t        *temp, path, *last_modified;
-    ngx_temp_file_t  *tf;
-
-    if (u->pipe->temp_file->file.fd == NGX_INVALID_FILE) {
+    size_t                  root;
+    time_t                  lm;
+    ngx_str_t               path;
+    ngx_temp_file_t        *tf;
+    ngx_ext_rename_file_t   ext;
+
+    tf = u->pipe->temp_file;
+
+    if (tf->file.fd == NGX_INVALID_FILE) {
 
         /* create file for empty 200 response */
 
@@ -2034,38 +2160,20 @@ ngx_http_upstream_store(ngx_http_request
         u->pipe->temp_file = tf;
     }
 
-    temp = &u->pipe->temp_file->file.name;
-
-#if !(NGX_WIN32)
-
-    if (ngx_change_file_access(temp->data, u->conf->store_access)
-        == NGX_FILE_ERROR)
-    {
-        err = ngx_errno;
-        failed = ngx_change_file_access_n;
-        name = temp->data;
-
-        goto failed;
-    }
-
-#endif
-
-    if (r->upstream->headers_in.last_modified) {
-
-        last_modified = &r->upstream->headers_in.last_modified->value;
-
-        lm = ngx_http_parse_time(last_modified->data, last_modified->len);
+    ext.access = u->conf->store_access;
+    ext.time = -1;
+    ext.create_path = 1;
+    ext.delete_file = 1;
+    ext.log = r->connection->log;
+
+    if (u->headers_in.last_modified) {
+
+        lm = ngx_http_parse_time(u->headers_in.last_modified->value.data,
+                                 u->headers_in.last_modified->value.len);
 
         if (lm != NGX_ERROR) {
-            if (ngx_set_file_time(temp->data, u->pipe->temp_file->file.fd, lm)
-                != NGX_OK)
-            {
-                err = ngx_errno;
-                failed = ngx_set_file_time_n;
-                name = temp->data;
-
-                goto failed;
-            }
+            ext.time = lm;
+            ext.fd = tf->file.fd;
         }
     }
 
@@ -2083,55 +2191,10 @@ ngx_http_upstream_store(ngx_http_request
     }
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "upstream stores \"%s\" to \"%s\"", temp->data, path.data);
-
-    failed = ngx_rename_file_n;
-    name = path.data;
-
-    if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) {
-        return;
-    }
-
-    err = ngx_errno;
-
-    if (err == NGX_ENOENT) {
-
-        err = ngx_create_full_path(path.data,
-                                   ngx_dir_access(u->conf->store_access));
-        if (err == 0) {
-            if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) {
-                return;
-            }
-
-            err = ngx_errno;
-        }
-    }
-
-#if (NGX_WIN32)
-
-    if (err == NGX_EEXIST) {
-        if (ngx_win32_rename_file(temp, &path, r->pool) != NGX_ERROR) {
-
-            if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) {
-                return;
-            }
-        }
-
-        err = ngx_errno;
-    }
-
-#endif
-
-failed:
-
-    if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
-                      ngx_delete_file_n " \"%s\" failed",
-                      temp->data);
-    }
-
-    ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
-                  "%s \"%s\" failed", failed, name);
+                   "upstream stores \"%s\" to \"%s\"",
+                   tf->file.name.data, path.data);
+
+    (void) ngx_ext_rename_file(&tf->file.name, &path, &ext);
 }
 
 
@@ -2258,10 +2321,18 @@ ngx_http_upstream_cleanup(void *data)
 {
     ngx_http_request_t *r = data;
 
+    ngx_http_upstream_t  *u;
+
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "cleanup http upstream request: \"%V\"", &r->uri);
 
-    ngx_http_upstream_finalize_request(r, r->upstream, NGX_DONE);
+    u = r->upstream;
+
+    if (u->resolved && u->resolved->ctx) {
+        ngx_resolve_name_done(u->resolved->ctx);
+    }
+
+    ngx_http_upstream_finalize_request(r, u, NGX_DONE);
 }
 
 
@@ -2276,7 +2347,7 @@ ngx_http_upstream_finalize_request(ngx_h
 
     *u->cleanup = NULL;
 
-    if (u->state->response_sec) {
+    if (u->state && u->state->response_sec) {
         tp = ngx_timeofday();
         u->state->response_sec = tp->sec - u->state->response_sec;
         u->state->response_msec = tp->msec - u->state->response_msec;
@@ -2284,7 +2355,9 @@ ngx_http_upstream_finalize_request(ngx_h
 
     u->finalize_request(r, rc);
 
-    u->peer.free(&u->peer, u->peer.data, 0);
+    if (u->peer.free) {
+        u->peer.free(&u->peer, u->peer.data, 0);
+    }
 
     if (u->peer.connection) {
 
@@ -2541,6 +2614,10 @@ ngx_http_upstream_copy_content_type(ngx_
 
         while (*++p == ' ') { /* void */ }
 
+        if (*p == '\0') {
+            return NGX_OK;
+        }
+
         if (ngx_strncasecmp(p, (u_char *) "charset=", 8) != 0) {
             continue;
         }
@@ -2610,6 +2687,10 @@ ngx_http_upstream_rewrite_location(ngx_h
         return rc;
     }
 
+    if (ho->value.data[0] != '/') {
+        r->headers_out.location = ho;
+    }
+
     /*
      * we do not set r->headers_out.location here to avoid the handling
      * the local redirects without a host name by ngx_http_header_filter()
@@ -2636,7 +2717,7 @@ ngx_http_upstream_rewrite_refresh(ngx_ht
 
     if (r->upstream->rewrite_redirect) {
 
-        p = (u_char *) ngx_strstr(ho->value.data, "url=");
+        p = ngx_strcasestrn(ho->value.data, "url=", 4 - 1);
 
         if (p) {
             rc = r->upstream->rewrite_redirect(r, ho, p + 4 - ho->value.data);
@@ -2649,16 +2730,18 @@ ngx_http_upstream_rewrite_refresh(ngx_ht
             return NGX_OK;
         }
 
-#if (NGX_DEBUG)
         if (rc == NGX_OK) {
+            r->headers_out.refresh = ho;
+
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "rewritten refresh: \"%V\"", &ho->value);
         }
-#endif
 
         return rc;
     }
 
+    r->headers_out.refresh = ho;
+
     return NGX_OK;
 }
 
@@ -2715,7 +2798,7 @@ ngx_http_upstream_addr_variable(ngx_http
     ngx_http_upstream_state_t  *state;
 
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
 
     if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
@@ -2735,7 +2818,7 @@ ngx_http_upstream_addr_variable(ngx_http
         }
     }
 
-    p = ngx_palloc(r->pool, len);
+    p = ngx_pnalloc(r->pool, len);
     if (p == NULL) {
         return NGX_ERROR;
     }
@@ -2786,7 +2869,7 @@ ngx_http_upstream_status_variable(ngx_ht
     ngx_http_upstream_state_t  *state;
 
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
 
     if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
@@ -2796,7 +2879,7 @@ ngx_http_upstream_status_variable(ngx_ht
 
     len = r->upstream_states->nelts * (3 + 2);
 
-    p = ngx_palloc(r->pool, len);
+    p = ngx_pnalloc(r->pool, len);
     if (p == NULL) {
         return NGX_ERROR;
     }
@@ -2852,7 +2935,7 @@ ngx_http_upstream_response_time_variable
     ngx_http_upstream_state_t  *state;
 
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
 
     if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
@@ -2862,7 +2945,7 @@ ngx_http_upstream_response_time_variable
 
     len = r->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2);
 
-    p = ngx_palloc(r->pool, len);
+    p = ngx_pnalloc(r->pool, len);
     if (p == NULL) {
         return NGX_ERROR;
     }
@@ -2874,7 +2957,8 @@ ngx_http_upstream_response_time_variable
 
     for ( ;; ) {
         if (state[i].status) {
-            ms = state[i].response_sec * 1000 + state[i].response_msec;
+            ms = (ngx_msec_int_t)
+                     (state[i].response_sec * 1000 + state[i].response_msec);
             ms = (ms >= 0) ? ms : 0;
             p = ngx_sprintf(p, "%d.%03d", ms / 1000, ms % 1000);
 
@@ -3066,7 +3150,7 @@ ngx_http_upstream_server(ngx_conf_t *cf,
     u.url = value[1];
     u.default_port = 80;
 
-    if (ngx_parse_url(cf, &u) != NGX_OK) {
+    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
         if (u.err) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "%s in upstream \"%V\"", u.err, &u.url);
@@ -3122,7 +3206,7 @@ ngx_http_upstream_server(ngx_conf_t *cf,
 
             fail_timeout = ngx_parse_time(&s, 1);
 
-            if (fail_timeout < 0) {
+            if (fail_timeout == NGX_ERROR) {
                 goto invalid;
             }
 
@@ -3181,7 +3265,7 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng
 
     if (!(flags & NGX_HTTP_UPSTREAM_CREATE)) {
 
-        if (ngx_parse_url(cf, u) != NGX_OK) {
+        if (ngx_parse_url(cf->pool, u) != NGX_OK) {
             if (u->err) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                    "%s in upstream \"%V\"", u->err, &u->url);
@@ -3281,6 +3365,113 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng
 }
 
 
+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)
+{
+    ngx_str_t       *h;
+    ngx_uint_t       i, j;
+    ngx_array_t      hide_headers;
+    ngx_hash_key_t  *hk;
+
+    if (conf->hide_headers == NGX_CONF_UNSET_PTR
+        && conf->pass_headers == NGX_CONF_UNSET_PTR)
+    {
+        conf->hide_headers_hash = prev->hide_headers_hash;
+
+        if (conf->hide_headers_hash.buckets) {
+            return NGX_OK;
+        }
+
+        conf->hide_headers = prev->hide_headers;
+        conf->pass_headers = prev->pass_headers;
+
+    } else {
+        if (conf->hide_headers == NGX_CONF_UNSET_PTR) {
+            conf->hide_headers = prev->hide_headers;
+        }
+
+        if (conf->pass_headers == NGX_CONF_UNSET_PTR) {
+            conf->pass_headers = prev->pass_headers;
+        }
+    }
+
+    if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    for (h = default_hide_headers; h->len; h++) {
+        hk = ngx_array_push(&hide_headers);
+        if (hk == NULL) {
+            return NGX_ERROR;
+        }
+
+        hk->key = *h;
+        hk->key_hash = ngx_hash_key_lc(h->data, h->len);
+        hk->value = (void *) 1;
+    }
+
+    if (conf->hide_headers != NGX_CONF_UNSET_PTR) {
+
+        h = conf->hide_headers->elts;
+
+        for (i = 0; i < conf->hide_headers->nelts; i++) {
+
+            hk = hide_headers.elts;
+
+            for (j = 0; j < hide_headers.nelts; j++) {
+                if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {
+                    goto exist;
+                }
+            }
+
+            hk = ngx_array_push(&hide_headers);
+            if (hk == NULL) {
+                return NGX_ERROR;
+            }
+
+            hk->key = h[i];
+            hk->key_hash = ngx_hash_key_lc(h[i].data, h[i].len);
+            hk->value = (void *) 1;
+
+        exist:
+
+            continue;
+        }
+    }
+
+    if (conf->pass_headers != NGX_CONF_UNSET_PTR) {
+
+        h = conf->pass_headers->elts;
+        hk = hide_headers.elts;
+
+        for (i = 0; i < conf->pass_headers->nelts; i++) {
+            for (j = 0; j < hide_headers.nelts; j++) {
+
+                if (hk[j].key.data == NULL) {
+                    continue;
+                }
+
+                if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {
+                    hk[j].key.data = NULL;
+                    break;
+                }
+            }
+        }
+    }
+
+    hash->hash = &conf->hide_headers_hash;
+    hash->key = ngx_hash_key_lc;
+    hash->pool = cf->pool;
+    hash->temp_pool = NULL;
+
+    return ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts);
+}
+
+
 static void *
 ngx_http_upstream_create_main_conf(ngx_conf_t *cf)
 {
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -138,9 +138,6 @@ typedef struct {
     ngx_array_t                    *pass_headers;
 
     ngx_str_t                       schema;
-    ngx_str_t                       uri;
-    ngx_str_t                       location;
-    ngx_str_t                       url;  /* used in proxy_rewrite_location */
 
     ngx_array_t                    *store_lengths;
     ngx_array_t                    *store_values;
@@ -195,10 +192,22 @@ typedef struct {
     ngx_table_elt_t                *content_encoding;
 #endif
 
+    off_t                           content_length_n;
+
     ngx_array_t                     cache_control;
 } ngx_http_upstream_headers_in_t;
 
 
+typedef struct {
+    ngx_str_t                       host;
+    in_port_t                       port;
+    ngx_uint_t                      default_port; /* unsigned  default_port:1 */
+    ngx_uint_t                      naddrs;
+    in_addr_t                      *addrs;
+    ngx_resolver_ctx_t             *ctx;
+} ngx_http_upstream_resolved_t;
+
+
 struct ngx_http_upstream_s {
     ngx_peer_connection_t           peer;
 
@@ -213,6 +222,8 @@ struct ngx_http_upstream_s {
 
     ngx_http_upstream_headers_in_t  headers_in;
 
+    ngx_http_upstream_resolved_t   *resolved;
+
     ngx_buf_t                       buffer;
     size_t                          length;
 
@@ -235,17 +246,18 @@ struct ngx_http_upstream_s {
 
     ngx_msec_t                      timeout;
 
-    ngx_str_t                       method;
-
     ngx_http_upstream_state_t      *state;
 
+    ngx_str_t                       method;
+    ngx_str_t                       schema;
     ngx_str_t                       uri;
 
     ngx_http_cleanup_pt            *cleanup;
 
     unsigned                        store:1;
-    unsigned                        cachable:1;
+    unsigned                        cacheable:1;
     unsigned                        accel:1;
+    unsigned                        ssl:1;
 
     unsigned                        buffering:1;
 
@@ -260,6 +272,9 @@ ngx_int_t ngx_http_upstream_header_varia
 void ngx_http_upstream_init(ngx_http_request_t *r);
 ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf,
     ngx_url_t *u, ngx_uint_t flags);
+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);
 
 
 #define ngx_http_conf_upstream_srv_conf(uscf, module)                         \
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -9,7 +9,8 @@
 #include <ngx_http.h>
 
 
-static int ngx_http_upstream_cmp_servers(const void *one, const void *two);
+static ngx_int_t ngx_http_upstream_cmp_servers(const void *one,
+    const void *two);
 static ngx_uint_t
 ngx_http_upstream_get_peer(ngx_http_upstream_rr_peers_t *peers);
 
@@ -145,7 +146,7 @@ ngx_http_upstream_init_round_robin(ngx_c
     u.host = us->host;
     u.port = (in_port_t) (us->port ? us->port : us->default_port);
 
-    if (ngx_inet_resolve_host(cf, &u) != NGX_OK) {
+    if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
         if (u.err) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                           "%s in upstream \"%V\" in %s:%ui",
@@ -167,17 +168,14 @@ ngx_http_upstream_init_round_robin(ngx_c
     peers->number = n;
     peers->name = &us->host;
 
-    n = 0;
-
     for (i = 0; i < u.naddrs; i++) {
-        peers->peer[n].sockaddr = u.addrs[i].sockaddr;
-        peers->peer[n].socklen = u.addrs[i].socklen;
-        peers->peer[n].name = u.addrs[i].name;
-        peers->peer[n].weight = 1;
-        peers->peer[n].current_weight = 1;
-        peers->peer[n].max_fails = 1;
-        peers->peer[n].fail_timeout = 10;
-        n++;
+        peers->peer[i].sockaddr = u.addrs[i].sockaddr;
+        peers->peer[i].socklen = u.addrs[i].socklen;
+        peers->peer[i].name = u.addrs[i].name;
+        peers->peer[i].weight = 1;
+        peers->peer[i].current_weight = 1;
+        peers->peer[i].max_fails = 1;
+        peers->peer[i].fail_timeout = 10;
     }
 
     us->peer.data = peers;
@@ -188,7 +186,7 @@ ngx_http_upstream_init_round_robin(ngx_c
 }
 
 
-static int
+static ngx_int_t
 ngx_http_upstream_cmp_servers(const void *one, const void *two)
 {
     ngx_http_upstream_rr_peer_t  *first, *second;
@@ -250,6 +248,100 @@ ngx_http_upstream_init_round_robin_peer(
 
 
 ngx_int_t
+ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
+    ngx_http_upstream_resolved_t *ur)
+{
+    u_char                            *p;
+    size_t                             len;
+    ngx_uint_t                         i, n;
+    struct sockaddr_in                *sin;
+    ngx_http_upstream_rr_peers_t      *peers;
+    ngx_http_upstream_rr_peer_data_t  *rrp;
+
+    rrp = r->upstream->peer.data;
+
+    if (rrp == NULL) {
+        rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));
+        if (rrp == NULL) {
+            return NGX_ERROR;
+        }
+
+        r->upstream->peer.data = rrp;
+    }
+
+    peers = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peers_t)
+                     + sizeof(ngx_http_upstream_rr_peer_t) * (ur->naddrs - 1));
+    if (peers == NULL) {
+        return NGX_ERROR;
+    }
+
+    peers->single = (ur->naddrs == 1);
+    peers->number = ur->naddrs;
+    peers->name = &ur->host;
+
+    for (i = 0; i < ur->naddrs; i++) {
+
+        len = INET_ADDRSTRLEN - 1 + 1 + sizeof(":65536") - 1;
+
+        p = ngx_pnalloc(r->pool, len);
+        if (p == NULL) {
+            return NGX_ERROR;
+        }
+
+        len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, INET_ADDRSTRLEN);
+        len = ngx_sprintf(&p[len], ":%d", ur->port) - p;
+
+        sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in));
+        if (sin == NULL) {
+            return NGX_ERROR;
+        }
+
+        sin->sin_family = AF_INET;
+        sin->sin_port = htons(ur->port);
+        sin->sin_addr.s_addr = ur->addrs[i];
+
+        peers->peer[i].sockaddr = (struct sockaddr *) sin;
+        peers->peer[i].socklen = sizeof(struct sockaddr_in);
+        peers->peer[i].name.len = len;
+        peers->peer[i].name.data = p;
+        peers->peer[i].weight = 1;
+        peers->peer[i].current_weight = 1;
+        peers->peer[i].max_fails = 1;
+        peers->peer[i].fail_timeout = 10;
+    }
+
+    rrp->peers = peers;
+    rrp->current = 0;
+
+    if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {
+        rrp->tried = &rrp->data;
+        rrp->data = 0;
+
+    } else {
+        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
+                / (8 * sizeof(uintptr_t));
+
+        rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));
+        if (rrp->tried == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+    r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
+    r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
+    r->upstream->peer.tries = rrp->peers->number;
+#if (NGX_HTTP_SSL)
+    r->upstream->peer.set_session =
+                               ngx_http_upstream_set_round_robin_peer_session;
+    r->upstream->peer.save_session =
+                               ngx_http_upstream_save_round_robin_peer_session;
+#endif
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
 {
     ngx_http_upstream_rr_peer_data_t  *rrp = data;
@@ -514,13 +606,7 @@ ngx_http_upstream_get_peer(ngx_http_upst
         }
 
         for (i = 0; i < peers->number; i++) {
-            if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
-                peer[i].current_weight += peer[i].weight;
-
-            } else {
-                /* 1 allows to go to quick recovery when all peers failed */
-                peer[i].current_weight = 1;
-            }
+            peer[i].current_weight = peer[i].weight;
         }
     }
 }
--- a/src/http/ngx_http_upstream_round_robin.h
+++ b/src/http/ngx_http_upstream_round_robin.h
@@ -65,6 +65,8 @@ ngx_int_t ngx_http_upstream_init_round_r
     ngx_http_upstream_srv_conf_t *us);
 ngx_int_t ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
     ngx_http_upstream_srv_conf_t *us);
+ngx_int_t ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
+    ngx_http_upstream_resolved_t *ur);
 ngx_int_t ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,
     void *data);
 void ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -26,6 +26,8 @@ static ngx_int_t ngx_http_variable_unkno
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 
 static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
@@ -47,6 +49,8 @@ static ngx_int_t ngx_http_variable_docum
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_request_filename(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_server_name(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_request_method(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_remote_user(ngx_http_request_t *r,
@@ -73,6 +77,8 @@ static ngx_int_t ngx_http_variable_sent_
 
 static ngx_int_t ngx_http_variable_nginx_version(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_hostname(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 
 /*
  * TODO:
@@ -143,37 +149,36 @@ static ngx_http_variable_t  ngx_http_cor
 
     { ngx_string("uri"), NULL, ngx_http_variable_request,
       offsetof(ngx_http_request_t, uri),
-      NGX_HTTP_VAR_NOCACHABLE, 0 },
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("document_uri"), NULL, ngx_http_variable_request,
       offsetof(ngx_http_request_t, uri),
-      NGX_HTTP_VAR_NOCACHABLE, 0 },
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("request"), NULL, ngx_http_variable_request,
       offsetof(ngx_http_request_t, request_line), 0, 0 },
 
     { ngx_string("document_root"), NULL,
-      ngx_http_variable_document_root, 0, NGX_HTTP_VAR_NOCACHABLE, 0 },
+      ngx_http_variable_document_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("query_string"), NULL, ngx_http_variable_request,
       offsetof(ngx_http_request_t, args),
-      NGX_HTTP_VAR_NOCACHABLE, 0 },
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("args"),
       ngx_http_variable_request_set,
       ngx_http_variable_request,
       offsetof(ngx_http_request_t, args),
-      NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOCACHABLE, 0 },
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("is_args"), NULL, ngx_http_variable_is_args,
-      0, NGX_HTTP_VAR_NOCACHABLE, 0 },
+      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("request_filename"), NULL,
       ngx_http_variable_request_filename, 0,
-      NGX_HTTP_VAR_NOCACHABLE, 0 },
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
-    { ngx_string("server_name"), NULL, ngx_http_variable_request,
-      offsetof(ngx_http_request_t, server_name), 0, 0 },
+    { ngx_string("server_name"), NULL, ngx_http_variable_server_name, 0, 0, 0 },
 
     { ngx_string("request_method"), NULL,
       ngx_http_variable_request_method, 0, 0, 0 },
@@ -215,11 +220,14 @@ static ngx_http_variable_t  ngx_http_cor
     { ngx_string("limit_rate"), ngx_http_variable_request_set_size,
       ngx_http_variable_request,
       offsetof(ngx_http_request_t, limit_rate),
-      NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOCACHABLE, 0 },
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("nginx_version"), NULL, ngx_http_variable_nginx_version,
       0, 0, 0 },
 
+    { ngx_string("hostname"), NULL, ngx_http_variable_hostname,
+      0, 0, 0 },
+
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
 
@@ -251,7 +259,7 @@ ngx_http_add_variable(ngx_conf_t *cf, ng
 
         v = key[i].value;
 
-        if (!(v->flags & NGX_HTTP_VAR_CHANGABLE)) {
+        if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "the duplicate \"%V\" variable", name);
             return NULL;
@@ -266,14 +274,12 @@ ngx_http_add_variable(ngx_conf_t *cf, ng
     }
 
     v->name.len = name->len;
-    v->name.data = ngx_palloc(cf->pool, name->len);
+    v->name.data = ngx_pnalloc(cf->pool, name->len);
     if (v->name.data == NULL) {
         return NULL;
     }
 
-    for (i = 0; i < name->len; i++) {
-        v->name.data[i] = ngx_tolower(name->data[i]);
-    }
+    ngx_strlow(v->name.data, name->data, name->len);
 
     v->set_handler = NULL;
     v->get_handler = NULL;
@@ -333,14 +339,12 @@ ngx_http_get_variable_index(ngx_conf_t *
     }
 
     v->name.len = name->len;
-    v->name.data = ngx_palloc(cf->pool, name->len);
+    v->name.data = ngx_pnalloc(cf->pool, name->len);
     if (v->name.data == NULL) {
         return NGX_ERROR;
     }
 
-    for (i = 0; i < name->len; i++) {
-        v->name.data[i] = ngx_tolower(name->data[i]);
-    }
+    ngx_strlow(v->name.data, name->data, name->len);
 
     v->set_handler = NULL;
     v->get_handler = NULL;
@@ -375,8 +379,8 @@ ngx_http_get_indexed_variable(ngx_http_r
     if (v[index].get_handler(r, &r->variables[index], v[index].data)
         == NGX_OK)
     {
-        if (v[index].flags & NGX_HTTP_VAR_NOCACHABLE) {
-            r->variables[index].no_cachable = 1;
+        if (v[index].flags & NGX_HTTP_VAR_NOCACHEABLE) {
+            r->variables[index].no_cacheable = 1;
         }
 
         return &r->variables[index];
@@ -397,7 +401,7 @@ ngx_http_get_flushed_variable(ngx_http_r
     v = &r->variables[index];
 
     if (v->valid) {
-        if (!v->no_cachable) {
+        if (!v->no_cacheable) {
             return v;
         }
 
@@ -475,6 +479,15 @@ ngx_http_get_variable(ngx_http_request_t
         return NULL;
     }
 
+    if (ngx_strncmp(name->data, "arg_", 4) == 0) {
+
+        if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) {
+            return vv;
+        }
+
+        return NULL;
+    }
+
     vv->not_found = 1;
 
     if (nowarn == 0) {
@@ -497,7 +510,7 @@ ngx_http_variable_request(ngx_http_reque
     if (s->data) {
         v->len = s->len;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = s->data;
 
@@ -559,7 +572,7 @@ ngx_http_variable_header(ngx_http_reques
     if (h) {
         v->len = h->value.len;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = h->value.data;
 
@@ -591,7 +604,7 @@ ngx_http_variable_headers(ngx_http_reque
     }
 
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
 
     h = a->elts;
@@ -609,7 +622,7 @@ ngx_http_variable_headers(ngx_http_reque
         len += h[i]->value.len + sizeof("; ") - 1;
     }
 
-    p = ngx_palloc(r->pool, len);
+    p = ngx_pnalloc(r->pool, len);
     if (p == NULL) {
         return NGX_ERROR;
     }
@@ -691,7 +704,7 @@ ngx_http_variable_unknown_header(ngx_htt
         if (n + prefix == var->len && n == header[i].key.len) {
             v->len = header[i].value.len;
             v->valid = 1;
-            v->no_cachable = 0;
+            v->no_cacheable = 0;
             v->not_found = 0;
             v->data = header[i].value.data;
 
@@ -706,31 +719,80 @@ ngx_http_variable_unknown_header(ngx_htt
 
 
 static ngx_int_t
+ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    ngx_str_t *name = (ngx_str_t *) data;
+
+    u_char  *p, *arg;
+    size_t   len;
+
+    if (r->args.len == 0) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    len = name->len - 1 - (sizeof("arg_") - 1);
+    arg = name->data + sizeof("arg_") - 1;
+
+    for (p = r->args.data; *p && *p != ' '; p++) {
+
+        /*
+         * although r->args.data is not null-terminated by itself,
+         * however, there is null in the end of request line
+         */
+
+        p = ngx_strcasestrn(p, (char *) arg, len);
+
+        if (p == NULL) {
+            v->not_found = 1;
+            return NGX_OK;
+        }
+
+        if ((p == r->args.data || *(p - 1) == '&') && *(p + len + 1) == '=') {
+
+            v->data = p + len + 2;
+
+            p = (u_char *) ngx_strchr(p, '&');
+
+            if (p == NULL) {
+                p = r->args.data + r->args.len;
+            }
+
+            v->len = p - v->data;
+            v->valid = 1;
+            v->no_cacheable = 0;
+            v->not_found = 0;
+
+            return NGX_OK;
+        }
+    }
+
+    v->not_found = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v,
     uintptr_t data)
 {
-    if (r->host_start == NULL) {
-
-        if (r->headers_in.host) {
-            v->len = r->headers_in.host_name_len;
-            v->data = r->headers_in.host->value.data;
+    ngx_http_core_srv_conf_t  *cscf;
 
-        } else {
-            v->len = r->server_name.len;
-            v->data = r->server_name.data;
-        }
-
-    } else if (r->host_end) {
-        v->len = r->host_end - r->host_start;
-        v->data = r->host_start;
+    if (r->headers_in.server.len) {
+        v->len = r->headers_in.server.len;
+        v->data = r->headers_in.server.data;
 
     } else {
-        v->not_found = 1;
-        return NGX_OK;
+        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+        v->len = cscf->server_name.len;
+        v->data = cscf->server_name.data;
     }
 
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
 
     return NGX_OK;
@@ -749,7 +811,7 @@ ngx_http_variable_binary_remote_addr(ngx
 
     v->len = sizeof(in_addr_t);
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
     v->data = (u_char *) &sin->sin_addr.s_addr;
 
@@ -763,7 +825,7 @@ ngx_http_variable_remote_addr(ngx_http_r
 {
     v->len = r->connection->addr_text.len;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
     v->data = r->connection->addr_text.data;
 
@@ -780,10 +842,10 @@ ngx_http_variable_remote_port(ngx_http_r
 
     v->len = 0;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
 
-    v->data = ngx_palloc(r->pool, sizeof("65535") - 1);
+    v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
     if (v->data == NULL) {
         return NGX_ERROR;
     }
@@ -808,32 +870,22 @@ static ngx_int_t
 ngx_http_variable_server_addr(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
-    socklen_t            len;
-    ngx_connection_t    *c;
-    struct sockaddr_in   sin;
+    ngx_str_t  s;
 
-    v->data = ngx_palloc(r->pool, INET_ADDRSTRLEN);
-    if (v->data == NULL) {
+    s.data = ngx_pnalloc(r->pool, INET_ADDRSTRLEN);
+    if (s.data == NULL) {
         return NGX_ERROR;
     }
 
-    c = r->connection;
-
-    if (r->in_addr == 0) {
-        len = sizeof(struct sockaddr_in);
-        if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) {
-            ngx_connection_error(c, ngx_socket_errno, "getsockname() failed");
-            return NGX_ERROR;
-        }
-
-        r->in_addr = sin.sin_addr.s_addr;
+    if (ngx_http_server_addr(r, &s) != NGX_OK) {
+        return NGX_ERROR;
     }
 
-    v->len = ngx_inet_ntop(c->listening->family, &r->in_addr,
-                           v->data, INET_ADDRSTRLEN);
+    v->len = s.len;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
+    v->data = s.data;
 
     return NGX_OK;
 }
@@ -845,7 +897,7 @@ ngx_http_variable_server_port(ngx_http_r
 {
     v->len = r->port_text->len - 1;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
     v->data = r->port_text->data + 1;
 
@@ -862,7 +914,7 @@ ngx_http_variable_scheme(ngx_http_reques
     if (r->connection->ssl) {
         v->len = sizeof("https") - 1;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = (u_char *) "https";
 
@@ -873,7 +925,7 @@ ngx_http_variable_scheme(ngx_http_reques
 
     v->len = sizeof("http") - 1;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
     v->data = (u_char *) "http";
 
@@ -886,7 +938,7 @@ ngx_http_variable_is_args(ngx_http_reque
     ngx_http_variable_value_t *v, uintptr_t data)
 {
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
 
     if (r->args.len == 0) {
@@ -914,7 +966,7 @@ ngx_http_variable_document_root(ngx_http
     if (clcf->root_lengths == NULL) {
         v->len = clcf->root.len;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = clcf->root.data;
 
@@ -934,7 +986,7 @@ ngx_http_variable_document_root(ngx_http
 
         v->len = path.len;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = path.data;
     }
@@ -958,7 +1010,7 @@ ngx_http_variable_request_filename(ngx_h
 
     v->len = path.len - 1;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
     v->data = path.data;
 
@@ -967,13 +1019,31 @@ ngx_http_variable_request_filename(ngx_h
 
 
 static ngx_int_t
+ngx_http_variable_server_name(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_http_core_srv_conf_t  *cscf;
+
+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+    v->len = cscf->server_name.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = cscf->server_name.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_request_method(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
     if (r->main->method_name.data) {
         v->len = r->main->method_name.len;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = r->main->method_name.data;
 
@@ -1004,7 +1074,7 @@ ngx_http_variable_remote_user(ngx_http_r
 
     v->len = r->headers_in.user.len;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
     v->data = r->headers_in.user.data;
 
@@ -1025,14 +1095,14 @@ ngx_http_variable_body_bytes_sent(ngx_ht
         sent = 0;
     }
 
-    p = ngx_palloc(r->pool, NGX_OFF_T_LEN);
+    p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
     if (p == NULL) {
         return NGX_ERROR;
     }
 
     v->len = ngx_sprintf(p, "%O", sent) - p;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
     v->data = p;
 
@@ -1047,7 +1117,7 @@ ngx_http_variable_sent_content_type(ngx_
     if (r->headers_out.content_type.len) {
         v->len = r->headers_out.content_type.len;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = r->headers_out.content_type.data;
 
@@ -1068,7 +1138,7 @@ ngx_http_variable_sent_content_length(ng
     if (r->headers_out.content_length) {
         v->len = r->headers_out.content_length->value.len;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = r->headers_out.content_length->value.data;
 
@@ -1076,14 +1146,14 @@ ngx_http_variable_sent_content_length(ng
     }
 
     if (r->headers_out.content_length_n >= 0) {
-        p = ngx_palloc(r->pool, NGX_OFF_T_LEN);
+        p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
         if (p == NULL) {
             return NGX_ERROR;
         }
 
         v->len = ngx_sprintf(p, "%O", r->headers_out.content_length_n) - p;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = p;
 
@@ -1105,7 +1175,7 @@ ngx_http_variable_sent_last_modified(ngx
     if (r->headers_out.last_modified) {
         v->len = r->headers_out.last_modified->value.len;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = r->headers_out.last_modified->value.data;
 
@@ -1113,7 +1183,7 @@ ngx_http_variable_sent_last_modified(ngx
     }
 
     if (r->headers_out.last_modified_time >= 0) {
-        p = ngx_palloc(r->pool,
+        p = ngx_pnalloc(r->pool,
                    sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT") - 1);
         if (p == NULL) {
             return NGX_ERROR;
@@ -1121,7 +1191,7 @@ ngx_http_variable_sent_last_modified(ngx
 
         v->len = ngx_http_time(p, r->headers_out.last_modified_time) - p;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = p;
 
@@ -1152,7 +1222,7 @@ ngx_http_variable_sent_connection(ngx_ht
 
     v->len = len;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
     v->data = (u_char *) p;
 
@@ -1172,14 +1242,14 @@ ngx_http_variable_sent_keep_alive(ngx_ht
 
         if (clcf->keepalive_header) {
 
-            p = ngx_palloc(r->pool, sizeof("timeout=") - 1 + NGX_TIME_T_LEN);
+            p = ngx_pnalloc(r->pool, sizeof("timeout=") - 1 + NGX_TIME_T_LEN);
             if (p == NULL) {
                 return NGX_ERROR;
             }
 
             v->len = ngx_sprintf(p, "timeout=%T", clcf->keepalive_header) - p;
             v->valid = 1;
-            v->no_cachable = 0;
+            v->no_cacheable = 0;
             v->not_found = 0;
             v->data = p;
 
@@ -1200,7 +1270,7 @@ ngx_http_variable_sent_transfer_encoding
     if (r->chunked) {
         v->len = sizeof("chunked") - 1;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = (u_char *) "chunked";
 
@@ -1219,7 +1289,7 @@ ngx_http_variable_request_completion(ngx
     if (r->request_complete) {
         v->len = 2;
         v->valid = 1;
-        v->no_cachable = 0;
+        v->no_cacheable = 0;
         v->not_found = 0;
         v->data = (u_char *) "OK";
 
@@ -1228,7 +1298,7 @@ ngx_http_variable_request_completion(ngx
 
     v->len = 0;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
     v->data = (u_char *) "";
 
@@ -1248,7 +1318,7 @@ ngx_http_variable_request_body_file(ngx_
 
     v->len = r->request_body->temp_file->file.name.len;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
     v->data = r->request_body->temp_file->file.name.data;
 
@@ -1262,7 +1332,7 @@ ngx_http_variable_nginx_version(ngx_http
 {
     v->len = sizeof(NGINX_VERSION) - 1;
     v->valid = 1;
-    v->no_cachable = 0;
+    v->no_cacheable = 0;
     v->not_found = 0;
     v->data = (u_char *) NGINX_VERSION;
 
@@ -1270,6 +1340,20 @@ ngx_http_variable_nginx_version(ngx_http
 }
 
 
+static ngx_int_t
+ngx_http_variable_hostname(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    v->len = ngx_cycle->hostname.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = ngx_cycle->hostname.data;
+
+    return NGX_OK;
+}
+
+
 ngx_int_t
 ngx_http_variables_add_core_vars(ngx_conf_t *cf)
 {
@@ -1370,7 +1454,14 @@ ngx_http_variables_init_vars(ngx_conf_t 
         if (ngx_strncmp(v[i].name.data, "upstream_http_", 14) == 0) {
             v[i].get_handler = ngx_http_upstream_header_variable;
             v[i].data = (uintptr_t) &v[i].name;
-            v[i].flags = NGX_HTTP_VAR_NOCACHABLE;
+            v[i].flags = NGX_HTTP_VAR_NOCACHEABLE;
+
+            continue;
+        }
+
+        if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) {
+            v[i].get_handler = ngx_http_variable_argument;
+            v[i].data = (uintptr_t) &v[i].name;
 
             continue;
         }
--- a/src/http/ngx_http_variables.h
+++ b/src/http/ngx_http_variables.h
@@ -16,7 +16,7 @@
 
 typedef ngx_variable_value_t  ngx_http_variable_value_t;
 
-#define ngx_http_variable(v)     { sizeof(v) - 1, 1, 0, 0, (u_char *) v }
+#define ngx_http_variable(v)     { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v }
 
 typedef struct ngx_http_variable_s  ngx_http_variable_t;
 
@@ -26,10 +26,10 @@ typedef ngx_int_t (*ngx_http_get_variabl
     ngx_http_variable_value_t *v, uintptr_t data);
 
 
-#define NGX_HTTP_VAR_CHANGABLE   1
-#define NGX_HTTP_VAR_NOCACHABLE  2
-#define NGX_HTTP_VAR_INDEXED     4
-#define NGX_HTTP_VAR_NOHASH      8
+#define NGX_HTTP_VAR_CHANGEABLE   1
+#define NGX_HTTP_VAR_NOCACHEABLE  2
+#define NGX_HTTP_VAR_INDEXED      4
+#define NGX_HTTP_VAR_NOHASH       8
 
 
 struct ngx_http_variable_s {
--- a/src/http/ngx_http_write_filter_module.c
+++ b/src/http/ngx_http_write_filter_module.c
@@ -49,6 +49,7 @@ ngx_http_write_filter(ngx_http_request_t
 {
     off_t                      size, sent, limit;
     ngx_uint_t                 last, flush;
+    ngx_msec_t                 delay;
     ngx_chain_t               *cl, *ln, **ll, *chain;
     ngx_connection_t          *c;
     ngx_http_core_loc_conf_t  *clcf;
@@ -245,14 +246,17 @@ ngx_http_write_filter(ngx_http_request_t
     }
 
     if (r->limit_rate) {
-        sent = c->sent - sent;
-        c->write->delayed = 1;
-        ngx_add_timer(c->write, (ngx_msec_t) (sent * 1000 / r->limit_rate + 1));
+        delay = (ngx_msec_t) ((c->sent - sent) * 1000 / r->limit_rate + 1);
+
+        if (delay > 0) {
+            c->write->delayed = 1;
+            ngx_add_timer(c->write, delay);
+        }
 
     } else if (c->write->ready
                && clcf->sendfile_max_chunk
                && (size_t) (c->sent - sent)
-                                >= clcf->sendfile_max_chunk - 2 * ngx_pagesize)
+                      >= clcf->sendfile_max_chunk - 2 * ngx_pagesize)
     {
         c->write->delayed = 1;
         ngx_add_timer(c->write, 1);
--- a/src/mail/ngx_mail.c
+++ b/src/mail/ngx_mail.c
@@ -11,7 +11,7 @@
 
 
 static char *ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
-static int ngx_mail_cmp_conf_in_addrs(const void *one, const void *two);
+static ngx_int_t ngx_mail_cmp_conf_in_addrs(const void *one, const void *two);
 
 
 ngx_uint_t  ngx_mail_max_module;
@@ -357,8 +357,8 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma
                 imip->addrs[i].addr = in_addr[i].addr;
                 imip->addrs[i].ctx = in_addr[i].ctx;
 
-                text = ngx_palloc(cf->pool,
-                                  INET_ADDRSTRLEN - 1 + sizeof(":65535") - 1);
+                text = ngx_pnalloc(cf->pool,
+                                   INET_ADDRSTRLEN - 1 + sizeof(":65535") - 1);
                 if (text == NULL) {
                     return NGX_CONF_ERROR;
                 }
@@ -388,7 +388,7 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma
 }
 
 
-static int
+static ngx_int_t
 ngx_mail_cmp_conf_in_addrs(const void *one, const void *two)
 {
     ngx_mail_conf_in_addr_t  *first, *second;
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -81,6 +81,7 @@ typedef struct {
     ngx_mail_protocol_t    *protocol;
 
     ngx_msec_t              timeout;
+    ngx_msec_t              resolver_timeout;
 
     ngx_flag_t              so_keepalive;
 
@@ -89,6 +90,8 @@ typedef struct {
     u_char                 *file_name;
     ngx_int_t               line;
 
+    ngx_resolver_t         *resolver;
+
     /* server ctx */
     ngx_mail_conf_ctx_t    *ctx;
 } ngx_mail_core_srv_conf_t;
@@ -102,7 +105,7 @@ typedef enum {
     ngx_pop3_auth_login_password,
     ngx_pop3_auth_plain,
     ngx_pop3_auth_cram_md5
-} ngx_po3_state_e;
+} ngx_pop3_state_e;
 
 
 typedef enum {
@@ -151,6 +154,8 @@ typedef struct {
     void                  **main_conf;
     void                  **srv_conf;
 
+    ngx_resolver_ctx_t     *resolver_ctx;
+
     ngx_mail_proxy_ctx_t   *proxy;
 
     ngx_uint_t              mail_state;
@@ -175,6 +180,7 @@ typedef struct {
     ngx_str_t               text;
 
     ngx_str_t              *addr_text;
+    ngx_str_t               host;
     ngx_str_t               smtp_helo;
     ngx_str_t               smtp_from;
     ngx_str_t               smtp_to;
--- a/src/mail/ngx_mail_auth_http_module.c
+++ b/src/mail/ngx_mail_auth_http_module.c
@@ -529,7 +529,7 @@ ngx_mail_auth_http_process_headers(ngx_m
                     continue;
                 }
 
-                p = ngx_pcalloc(s->connection->pool, size);
+                p = ngx_pnalloc(s->connection->pool, size);
                 if (p == NULL) {
                     ngx_close_connection(ctx->peer.connection);
                     ngx_destroy_pool(ctx->pool);
@@ -594,7 +594,7 @@ ngx_mail_auth_http_process_headers(ngx_m
             {
                 s->login.len = ctx->header_end - ctx->header_start;
 
-                s->login.data = ngx_palloc(s->connection->pool, s->login.len);
+                s->login.data = ngx_pnalloc(s->connection->pool, s->login.len);
                 if (s->login.data == NULL) {
                     ngx_close_connection(ctx->peer.connection);
                     ngx_destroy_pool(ctx->pool);
@@ -615,7 +615,8 @@ ngx_mail_auth_http_process_headers(ngx_m
             {
                 s->passwd.len = ctx->header_end - ctx->header_start;
 
-                s->passwd.data = ngx_palloc(s->connection->pool, s->passwd.len);
+                s->passwd.data = ngx_pnalloc(s->connection->pool,
+                                             s->passwd.len);
                 if (s->passwd.data == NULL) {
                     ngx_close_connection(ctx->peer.connection);
                     ngx_destroy_pool(ctx->pool);
@@ -652,8 +653,8 @@ ngx_mail_auth_http_process_headers(ngx_m
             {
                 ctx->errcode.len = ctx->header_end - ctx->header_start;
 
-                ctx->errcode.data = ngx_palloc(s->connection->pool,
-                                               ctx->errcode.len);
+                ctx->errcode.data = ngx_pnalloc(s->connection->pool,
+                                                ctx->errcode.len);
                 if (ctx->errcode.data == NULL) {
                     ngx_close_connection(ctx->peer.connection);
                     ngx_destroy_pool(ctx->pool);
@@ -692,7 +693,7 @@ ngx_mail_auth_http_process_headers(ngx_m
                     ctx->err.len = ctx->errcode.len + ctx->errmsg.len
                                    + sizeof(" " CRLF) - 1;
 
-                    p = ngx_palloc(s->connection->pool, ctx->err.len);
+                    p = ngx_pnalloc(s->connection->pool, ctx->err.len);
                     if (p == NULL) {
                         ngx_close_connection(ctx->peer.connection);
                         ngx_destroy_pool(ctx->pool);
@@ -719,7 +720,7 @@ ngx_mail_auth_http_process_headers(ngx_m
                     return;
                 }
 
-                ngx_add_timer(s->connection->read, timer * 1000);
+                ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
 
                 s->connection->read->handler = ngx_mail_auth_sleep_handler;
 
@@ -736,7 +737,7 @@ ngx_mail_auth_http_process_headers(ngx_m
                     return;
                 }
 
-                ngx_add_timer(s->connection->read, timer * 1000);
+                ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
 
                 s->connection->read->handler = ngx_mail_auth_sleep_handler;
 
@@ -811,7 +812,7 @@ ngx_mail_auth_http_process_headers(ngx_m
 
             peer->name.len = len;
 
-            peer->name.data = ngx_palloc(s->connection->pool, len);
+            peer->name.data = ngx_pnalloc(s->connection->pool, len);
             if (peer->name.data == NULL) {
                 ngx_destroy_pool(ctx->pool);
                 ngx_mail_session_internal_server_error(s);
@@ -1280,7 +1281,7 @@ ngx_mail_auth_http_escape(ngx_pool_t *po
 
     escaped->len = text->len + n * 2;
 
-    p = ngx_palloc(pool, escaped->len);
+    p = ngx_pnalloc(pool, escaped->len);
     if (p == NULL) {
         return NGX_ERROR;
     }
@@ -1351,7 +1352,7 @@ ngx_mail_auth_http_merge_conf(ngx_conf_t
             len += header[i].key.len + 2 + header[i].value.len + 2;
         }
 
-        p = ngx_palloc(cf->pool, len);
+        p = ngx_pnalloc(cf->pool, len);
         if (p == NULL) {
             return NGX_CONF_ERROR;
         }
@@ -1393,7 +1394,7 @@ ngx_mail_auth_http(ngx_conf_t *cf, ngx_c
         u.url.data += 7;
     }
 
-    if (ngx_parse_url(cf, &u) != NGX_OK) {
+    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
         if (u.err) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "%s in auth_http \"%V\"", u.err, &u.url);
--- a/src/mail/ngx_mail_core_module.c
+++ b/src/mail/ngx_mail_core_module.c
@@ -20,6 +20,8 @@ static char *ngx_mail_core_listen(ngx_co
     void *conf);
 static char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 
 
 static ngx_command_t  ngx_mail_core_commands[] = {
@@ -66,6 +68,20 @@ static ngx_command_t  ngx_mail_core_comm
       offsetof(ngx_mail_core_srv_conf_t, server_name),
       NULL },
 
+    { ngx_string("resolver"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_mail_core_resolver,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("resolver_timeout"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_msec_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_core_srv_conf_t, resolver_timeout),
+      NULL },
+
       ngx_null_command
 };
 
@@ -141,8 +157,14 @@ ngx_mail_core_create_srv_conf(ngx_conf_t
      */
 
     cscf->timeout = NGX_CONF_UNSET_MSEC;
+    cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;
     cscf->so_keepalive = NGX_CONF_UNSET;
 
+    cscf->resolver = NGX_CONF_UNSET_PTR;
+
+    cscf->file_name = cf->conf_file->file.name.data;
+    cscf->line = cf->conf_file->line;
+
     return cscf;
 }
 
@@ -154,6 +176,8 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
     ngx_mail_core_srv_conf_t *conf = child;
 
     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
+    ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout,
+                              30000);
 
     ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0);
 
@@ -161,20 +185,7 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
     ngx_conf_merge_str_value(conf->server_name, prev->server_name, "");
 
     if (conf->server_name.len == 0) {
-        conf->server_name.data = ngx_palloc(cf->pool, NGX_MAXHOSTNAMELEN);
-        if (conf->server_name.data == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        if (gethostname((char *) conf->server_name.data, NGX_MAXHOSTNAMELEN)
-            == -1)
-        {
-            ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,
-                          "gethostname() failed");
-            return NGX_CONF_ERROR;
-        }
-
-        conf->server_name.len = ngx_strlen(conf->server_name.data);
+        conf->server_name = cf->cycle->hostname;
     }
 
     if (conf->protocol == NULL) {
@@ -184,6 +195,8 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
         return NGX_CONF_ERROR;
     }
 
+    ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL);
+
     return NGX_CONF_OK;
 }
 
@@ -237,9 +250,6 @@ ngx_mail_core_server(ngx_conf_t *cf, ngx
     cscf = ctx->srv_conf[ngx_mail_core_module.ctx_index];
     cscf->ctx = ctx;
 
-    cscf->file_name = cf->conf_file->file.name.data;
-    cscf->line = cf->conf_file->line;
-
     cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index];
 
     cscfp = ngx_array_push(&cmcf->servers);
@@ -285,7 +295,7 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx
     u.url = value[1];
     u.listen = 1;
 
-    if (ngx_parse_url(cf, &u) != NGX_OK) {
+    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
         if (u.err) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "%s in \"%V\" of the \"listen\" directive",
@@ -389,6 +399,44 @@ ngx_mail_core_protocol(ngx_conf_t *cf, n
 }
 
 
+static char *
+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;
+
+    if (cscf->resolver != NGX_CONF_UNSET_PTR) {
+        return "is duplicate";
+    }
+
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        cscf->resolver = NULL;
+        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]);
+    if (cscf->resolver == NULL) {
+        return NGX_CONF_OK;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
 char *
 ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -29,10 +29,6 @@ ngx_mail_init_connection(ngx_connection_
     ngx_mail_in_port_t   *imip;
     ngx_mail_in_addr_t   *imia;
     ngx_mail_session_t   *s;
-#if (NGX_MAIL_SSL)
-    ngx_mail_ssl_conf_t  *sslcf;
-#endif
-
 
     /* find the server configuration for the address:port */
 
@@ -116,6 +112,8 @@ ngx_mail_init_connection(ngx_connection_
     c->log_error = NGX_ERROR_INFO;
 
 #if (NGX_MAIL_SSL)
+    {
+    ngx_mail_ssl_conf_t  *sslcf;
 
     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
 
@@ -123,7 +121,7 @@ ngx_mail_init_connection(ngx_connection_
         ngx_mail_ssl_init_connection(&sslcf->ssl, c);
         return;
     }
-
+    }
 #endif
 
     ngx_mail_init_session(c);
@@ -238,10 +236,10 @@ ngx_int_t
 ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,
     ngx_mail_core_srv_conf_t *cscf)
 {
-    s->salt.data = ngx_palloc(c->pool,
-                              sizeof(" <18446744073709551616.@>" CRLF) - 1
-                              + NGX_TIME_T_LEN
-                              + cscf->server_name.len);
+    s->salt.data = ngx_pnalloc(c->pool,
+                               sizeof(" <18446744073709551616.@>" CRLF) - 1
+                               + NGX_TIME_T_LEN
+                               + cscf->server_name.len);
     if (s->salt.data == NULL) {
         return NGX_ERROR;
     }
@@ -290,7 +288,7 @@ ngx_mail_auth_plain(ngx_mail_session_t *
                    "mail auth plain: \"%V\"", &arg[n]);
 #endif
 
-    plain.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[n].len));
+    plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
     if (plain.data == NULL){
         return NGX_ERROR;
     }
@@ -346,7 +344,7 @@ ngx_mail_auth_login_username(ngx_mail_se
     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
                    "mail auth login username: \"%V\"", &arg[0]);
 
-    s->login.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[0].len));
+    s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
     if (s->login.data == NULL){
         return NGX_ERROR;
     }
@@ -376,7 +374,8 @@ ngx_mail_auth_login_password(ngx_mail_se
                    "mail auth login password: \"%V\"", &arg[0]);
 #endif
 
-    s->passwd.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[0].len));
+    s->passwd.data = ngx_pnalloc(c->pool,
+                                 ngx_base64_decoded_length(arg[0].len));
     if (s->passwd.data == NULL){
         return NGX_ERROR;
     }
@@ -404,7 +403,7 @@ ngx_mail_auth_cram_md5_salt(ngx_mail_ses
     ngx_str_t    salt;
     ngx_uint_t   n;
 
-    p = ngx_palloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);
+    p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);
     if (p == NULL) {
         return NGX_ERROR;
     }
@@ -436,7 +435,7 @@ ngx_mail_auth_cram_md5(ngx_mail_session_
     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
                    "mail auth cram-md5: \"%V\"", &arg[0]);
 
-    s->login.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[0].len));
+    s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
     if (s->login.data == NULL){
         return NGX_ERROR;
     }
--- a/src/mail/ngx_mail_imap_handler.c
+++ b/src/mail/ngx_mail_imap_handler.c
@@ -259,7 +259,7 @@ ngx_mail_imap_auth_state(ngx_event_t *re
 
         if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) {
             s->tagged_line.len = s->tag.len + s->text.len + s->out.len;
-            s->tagged_line.data = ngx_palloc(c->pool, s->tagged_line.len);
+            s->tagged_line.data = ngx_pnalloc(c->pool, s->tagged_line.len);
             if (s->tagged_line.data == NULL) {
                 ngx_mail_close_connection(c);
                 return;
@@ -302,7 +302,7 @@ ngx_mail_imap_auth_state(ngx_event_t *re
 static ngx_int_t
 ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c)
 {
-    ngx_str_t            *arg;
+    ngx_str_t  *arg;
 
 #if (NGX_MAIL_SSL)
     if (ngx_mail_starttls_only(s, c)) {
@@ -317,7 +317,7 @@ ngx_mail_imap_login(ngx_mail_session_t *
     }
 
     s->login.len = arg[0].len;
-    s->login.data = ngx_palloc(c->pool, s->login.len);
+    s->login.data = ngx_pnalloc(c->pool, s->login.len);
     if (s->login.data == NULL) {
         return NGX_ERROR;
     }
@@ -325,7 +325,7 @@ ngx_mail_imap_login(ngx_mail_session_t *
     ngx_memcpy(s->login.data, arg[0].data, s->login.len);
 
     s->passwd.len = arg[1].len;
-    s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
+    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
     if (s->passwd.data == NULL) {
         return NGX_ERROR;
     }
@@ -410,15 +410,14 @@ static ngx_int_t
 ngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c)
 {
     ngx_mail_imap_srv_conf_t  *iscf;
-#if (NGX_MAIL_SSL)
-    ngx_mail_ssl_conf_t       *sslcf;
-#endif
 
     iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);
 
 #if (NGX_MAIL_SSL)
 
     if (c->ssl == NULL) {
+        ngx_mail_ssl_conf_t  *sslcf;
+
         sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
 
         if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
--- a/src/mail/ngx_mail_imap_module.c
+++ b/src/mail/ngx_mail_imap_module.c
@@ -184,7 +184,7 @@ ngx_mail_imap_merge_srv_conf(ngx_conf_t 
         }
     }
 
-    p = ngx_palloc(cf->pool, size);
+    p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -217,7 +217,7 @@ ngx_mail_imap_merge_srv_conf(ngx_conf_t 
 
     size += sizeof(" STARTTLS") - 1;
 
-    p = ngx_palloc(cf->pool, size);
+    p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -234,7 +234,7 @@ ngx_mail_imap_merge_srv_conf(ngx_conf_t 
     size = (auth - conf->capability.data) + sizeof(CRLF) - 1
             + sizeof(" STARTTLS LOGINDISABLED") - 1;
 
-    p = ngx_palloc(cf->pool, size);
+    p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
     }
--- a/src/mail/ngx_mail_pop3_handler.c
+++ b/src/mail/ngx_mail_pop3_handler.c
@@ -46,7 +46,7 @@ ngx_mail_pop3_init_session(ngx_mail_sess
             return;
         }
 
-        s->out.data = ngx_palloc(c->pool, sizeof(pop3_greeting) + s->salt.len);
+        s->out.data = ngx_pnalloc(c->pool, sizeof(pop3_greeting) + s->salt.len);
         if (s->out.data == NULL) {
             ngx_mail_session_internal_server_error(s);
             return;
@@ -283,7 +283,7 @@ ngx_mail_pop3_auth_state(ngx_event_t *re
 static ngx_int_t
 ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c)
 {
-    ngx_str_t            *arg;
+    ngx_str_t  *arg;
 
 #if (NGX_MAIL_SSL)
     if (ngx_mail_starttls_only(s, c)) {
@@ -297,7 +297,7 @@ ngx_mail_pop3_user(ngx_mail_session_t *s
 
     arg = s->args.elts;
     s->login.len = arg[0].len;
-    s->login.data = ngx_palloc(c->pool, s->login.len);
+    s->login.data = ngx_pnalloc(c->pool, s->login.len);
     if (s->login.data == NULL) {
         return NGX_ERROR;
     }
@@ -324,7 +324,7 @@ ngx_mail_pop3_pass(ngx_mail_session_t *s
 
     arg = s->args.elts;
     s->passwd.len = arg[0].len;
-    s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
+    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
     if (s->passwd.data == NULL) {
         return NGX_ERROR;
     }
@@ -344,15 +344,14 @@ static ngx_int_t
 ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls)
 {
     ngx_mail_pop3_srv_conf_t  *pscf;
-#if (NGX_MAIL_SSL)
-    ngx_mail_ssl_conf_t       *sslcf;
-#endif
 
     pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
 
 #if (NGX_MAIL_SSL)
 
     if (stls && c->ssl == NULL) {
+        ngx_mail_ssl_conf_t  *sslcf;
+
         sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
 
         if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
@@ -418,7 +417,7 @@ ngx_mail_pop3_apop(ngx_mail_session_t *s
     arg = s->args.elts;
 
     s->login.len = arg[0].len;
-    s->login.data = ngx_palloc(c->pool, s->login.len);
+    s->login.data = ngx_pnalloc(c->pool, s->login.len);
     if (s->login.data == NULL) {
         return NGX_ERROR;
     }
@@ -426,7 +425,7 @@ ngx_mail_pop3_apop(ngx_mail_session_t *s
     ngx_memcpy(s->login.data, arg[0].data, s->login.len);
 
     s->passwd.len = arg[1].len;
-    s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
+    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
     if (s->passwd.data == NULL) {
         return NGX_ERROR;
     }
--- a/src/mail/ngx_mail_pop3_module.c
+++ b/src/mail/ngx_mail_pop3_module.c
@@ -183,7 +183,7 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t 
         size += sizeof("SASL LOGIN PLAIN" CRLF) - 1;
     }
 
-    p = ngx_palloc(cf->pool, size);
+    p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -213,7 +213,7 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t 
 
     size += sizeof("STLS" CRLF) - 1;
 
-    p = ngx_palloc(cf->pool, size);
+    p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -236,7 +236,7 @@ ngx_mail_pop3_merge_srv_conf(ngx_conf_t 
     }
 
 
-    p = ngx_palloc(cf->pool, stls_only_size);
+    p = ngx_pnalloc(cf->pool, stls_only_size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
     }
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -172,6 +172,8 @@ ngx_mail_proxy_init(ngx_mail_session_t *
         return;
     }
 
+    s->out.len = 0;
+
     switch (s->protocol) {
 
     case NGX_MAIL_POP3_PROTOCOL:
@@ -252,7 +254,7 @@ ngx_mail_proxy_pop3_handler(ngx_event_t 
         s->connection->log->action = "sending user name to upstream";
 
         line.len = sizeof("USER ")  - 1 + s->login.len + 2;
-        line.data = ngx_palloc(c->pool, line.len);
+        line.data = ngx_pnalloc(c->pool, line.len);
         if (line.data == NULL) {
             ngx_mail_proxy_internal_server_error(s);
             return;
@@ -271,7 +273,7 @@ ngx_mail_proxy_pop3_handler(ngx_event_t 
         s->connection->log->action = "sending password to upstream";
 
         line.len = sizeof("PASS ")  - 1 + s->passwd.len + 2;
-        line.data = ngx_palloc(c->pool, line.len);
+        line.data = ngx_pnalloc(c->pool, line.len);
         if (line.data == NULL) {
             ngx_mail_proxy_internal_server_error(s);
             return;
@@ -368,7 +370,7 @@ ngx_mail_proxy_imap_handler(ngx_event_t 
 
         line.len = s->tag.len + sizeof("LOGIN ") - 1
                    + 1 + NGX_SIZE_T_LEN + 1 + 2;
-        line.data = ngx_palloc(c->pool, line.len);
+        line.data = ngx_pnalloc(c->pool, line.len);
         if (line.data == NULL) {
             ngx_mail_proxy_internal_server_error(s);
             return;
@@ -387,7 +389,7 @@ ngx_mail_proxy_imap_handler(ngx_event_t 
         s->connection->log->action = "sending user name to upstream";
 
         line.len = s->login.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2;
-        line.data = ngx_palloc(c->pool, line.len);
+        line.data = ngx_pnalloc(c->pool, line.len);
         if (line.data == NULL) {
             ngx_mail_proxy_internal_server_error(s);
             return;
@@ -407,7 +409,7 @@ ngx_mail_proxy_imap_handler(ngx_event_t 
         s->connection->log->action = "sending password to upstream";
 
         line.len = s->passwd.len + 2;
-        line.data = ngx_palloc(c->pool, line.len);
+        line.data = ngx_pnalloc(c->pool, line.len);
         if (line.data == NULL) {
             ngx_mail_proxy_internal_server_error(s);
             return;
@@ -504,7 +506,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
 
         line.len = sizeof("HELO ")  - 1 + cscf->server_name.len + 2;
-        line.data = ngx_palloc(c->pool, line.len);
+        line.data = ngx_pnalloc(c->pool, line.len);
         if (line.data == NULL) {
             ngx_mail_proxy_internal_server_error(s);
             return;
@@ -531,12 +533,12 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
 
         s->connection->log->action = "sending XCLIENT to upstream";
 
-        line.len = sizeof("XCLIENT PROTO=SMTP HELO= ADDR= LOGIN= "
-                          "NAME=[UNAVAILABLE]" CRLF) - 1
+        line.len = sizeof("XCLIENT PROTO=SMTP HELO= ADDR= LOGIN= NAME="
+                          CRLF) - 1
                    + s->esmtp + s->smtp_helo.len
-                   + s->connection->addr_text.len + s->login.len;
+                   + s->connection->addr_text.len + s->login.len + s->host.len;
 
-        line.data = ngx_palloc(c->pool, line.len);
+        line.data = ngx_pnalloc(c->pool, line.len);
         if (line.data == NULL) {
             ngx_mail_proxy_internal_server_error(s);
             return;
@@ -544,11 +546,11 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
 
         line.len = ngx_sprintf(line.data,
                        "XCLIENT PROTO=%sSMTP%s%V ADDR=%V%s%V "
-                       "NAME=[UNAVAILABLE]" CRLF,
+                       "NAME=%V" CRLF,
                        (s->esmtp ? "E" : ""), 
                        (s->smtp_helo.len ? " HELO=" : ""), &s->smtp_helo,
                        &s->connection->addr_text,
-                       (s->login.len ? " LOGIN=" : ""), &s->login)
+                       (s->login.len ? " LOGIN=" : ""), &s->login, &s->host)
                    - line.data;
 
         s->mail_state = s->auth_method == NGX_MAIL_AUTH_NONE ?
@@ -564,7 +566,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
         s->connection->log->action = "sending MAIL FROM to upstream";
 
         line.len = s->smtp_from.len + sizeof(CRLF) - 1;
-        line.data = ngx_palloc(c->pool, line.len);
+        line.data = ngx_pnalloc(c->pool, line.len);
         if (line.data == NULL) {
             ngx_mail_proxy_internal_server_error(s);
             return;
@@ -584,7 +586,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
         s->connection->log->action = "sending RCPT TO to upstream";
 
         line.len = s->smtp_to.len + sizeof(CRLF) - 1;
-        line.data = ngx_palloc(c->pool, line.len);
+        line.data = ngx_pnalloc(c->pool, line.len);
         if (line.data == NULL) {
             ngx_mail_proxy_internal_server_error(s);
             return;
--- a/src/mail/ngx_mail_smtp_handler.c
+++ b/src/mail/ngx_mail_smtp_handler.c
@@ -11,6 +11,9 @@
 #include <ngx_mail_smtp_module.h>
 
 
+static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c);
 static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev);
 static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s,
     ngx_connection_t *c);
@@ -31,6 +34,7 @@ static void ngx_mail_smtp_log_rejected_c
 
 static u_char  smtp_ok[] = "250 2.0.0 OK" CRLF;
 static u_char  smtp_bye[] = "221 2.0.0 Bye" CRLF;
+static u_char  smtp_starttls[] = "220 2.0.0 Start TLS" CRLF;
 static u_char  smtp_next[] = "334 " CRLF;
 static u_char  smtp_username[] = "334 VXNlcm5hbWU6" CRLF;
 static u_char  smtp_password[] = "334 UGFzc3dvcmQ6" CRLF;
@@ -42,13 +46,182 @@ static u_char  smtp_auth_required[] = "5
 static u_char  smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF;
 
 
+static ngx_str_t  smtp_unavailable = ngx_string("[UNAVAILABLE]");
+static ngx_str_t  smtp_tempunavail = ngx_string("[TEMPUNAVAIL]");
+
+
 void
 ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
 {
+    struct sockaddr_in        *sin;
+    ngx_resolver_ctx_t        *ctx;
+    ngx_mail_core_srv_conf_t  *cscf;
+
+    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+    if (cscf->resolver == NULL) {
+        s->host = smtp_unavailable;
+        ngx_mail_smtp_greeting(s, c);
+        return;
+    }
+
+    c->log->action = "in resolving client address";
+
+    ctx = ngx_resolve_start(cscf->resolver, NULL);
+    if (ctx == NULL) {
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    /* AF_INET only */
+
+    sin = (struct sockaddr_in *) c->sockaddr;
+
+    ctx->addr = sin->sin_addr.s_addr;
+    ctx->handler = ngx_mail_smtp_resolve_addr_handler;
+    ctx->data = s;
+    ctx->timeout = cscf->resolver_timeout;
+
+    if (ngx_resolve_addr(ctx) != NGX_OK) {
+        ngx_mail_close_connection(c);
+    }
+}
+
+
+static void
+ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx)
+{
+    ngx_connection_t          *c;
+    ngx_mail_session_t        *s;
+    ngx_mail_core_srv_conf_t  *cscf;
+
+    s = ctx->data;
+    c = s->connection;
+
+    if (ctx->state) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "%V could not be resolved (%i: %s)",
+                      &c->addr_text, ctx->state,
+                      ngx_resolver_strerror(ctx->state));
+
+        if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
+            s->host = smtp_unavailable;
+
+        } else {
+            s->host = smtp_tempunavail;
+        }
+
+        ngx_resolve_addr_done(ctx);
+
+        ngx_mail_smtp_greeting(s, s->connection);
+
+        return;
+    }
+
+    c->log->action = "in resolving client hostname";
+
+    s->host.data = ngx_pstrdup(c->pool, &ctx->name);
+    if (s->host.data == NULL) {
+        ngx_resolve_addr_done(ctx);
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    s->host.len = ctx->name.len;
+
+    ngx_resolve_addr_done(ctx);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "address resolved: %V", &s->host);
+
+    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+    ctx = ngx_resolve_start(cscf->resolver, NULL);
+    if (ctx == NULL) {
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    ctx->name = s->host;
+    ctx->type = NGX_RESOLVE_A;
+    ctx->handler = ngx_mail_smtp_resolve_name_handler;
+    ctx->data = s;
+    ctx->timeout = cscf->resolver_timeout;
+
+    if (ngx_resolve_name(ctx) != NGX_OK) {
+        ngx_mail_close_connection(c);
+    }
+}
+
+
+static void
+ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx)
+{
+    in_addr_t            addr;
+    ngx_uint_t           i;
+    ngx_connection_t    *c;
+    struct sockaddr_in  *sin;
+    ngx_mail_session_t  *s;
+
+    s = ctx->data;
+    c = s->connection;
+
+    if (ctx->state) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "%V could not be resolved (%i: %s)",
+                      &ctx->name, ctx->state,
+                      ngx_resolver_strerror(ctx->state));
+
+        if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
+            s->host = smtp_unavailable;
+
+        } else {
+            s->host = smtp_tempunavail;
+        }
+
+    } else {
+
+        /* AF_INET only */
+
+        sin = (struct sockaddr_in *) c->sockaddr;
+
+        for (i = 0; i < ctx->naddrs; i++) {
+
+            addr = ctx->addrs[i];
+
+            ngx_log_debug4(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                           "name was resolved to %ud.%ud.%ud.%ud",
+                           (ntohl(addr) >> 24) & 0xff,
+                           (ntohl(addr) >> 16) & 0xff,
+                           (ntohl(addr) >> 8) & 0xff,
+                           ntohl(addr) & 0xff);
+
+            if (addr == sin->sin_addr.s_addr) {
+                goto found;
+            }
+        }
+
+        s->host = smtp_unavailable;
+    }
+
+found:
+
+    ngx_resolve_name_done(ctx);
+
+    ngx_mail_smtp_greeting(s, c);
+}
+
+
+static void
+ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c)
+{
     ngx_msec_t                 timeout;
     ngx_mail_core_srv_conf_t  *cscf;
     ngx_mail_smtp_srv_conf_t  *sscf;
 
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "smtp greeting for \"%V\"", &s->host);
+
     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
 
@@ -260,6 +433,8 @@ ngx_mail_smtp_auth_state(ngx_event_t *re
 
             case NGX_SMTP_STARTTLS:
                 rc = ngx_mail_smtp_starttls(s, c);
+                s->out.len = sizeof(smtp_starttls) - 1;
+                s->out.data = smtp_starttls;
                 break;
 
             default:
@@ -329,9 +504,6 @@ ngx_mail_smtp_helo(ngx_mail_session_t *s
 {
     ngx_str_t                 *arg;
     ngx_mail_smtp_srv_conf_t  *sscf;
-#if (NGX_MAIL_SSL)
-    ngx_mail_ssl_conf_t       *sslcf;
-#endif
 
     if (s->args.nelts != 1) {
         s->out.len = sizeof(smtp_invalid_argument) - 1;
@@ -344,7 +516,7 @@ ngx_mail_smtp_helo(ngx_mail_session_t *s
 
     s->smtp_helo.len = arg[0].len;
 
-    s->smtp_helo.data = ngx_palloc(c->pool, arg[0].len);
+    s->smtp_helo.data = ngx_pnalloc(c->pool, arg[0].len);
     if (s->smtp_helo.data == NULL) {
         return NGX_ERROR;
     }
@@ -367,6 +539,8 @@ ngx_mail_smtp_helo(ngx_mail_session_t *s
 #if (NGX_MAIL_SSL)
 
         if (c->ssl == NULL) {
+            ngx_mail_ssl_conf_t  *sslcf;
+
             sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
 
             if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
--- a/src/mail/ngx_mail_smtp_module.c
+++ b/src/mail/ngx_mail_smtp_module.c
@@ -162,7 +162,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
 
     size = sizeof("220  ESMTP ready" CRLF) - 1 + cscf->server_name.len;
 
-    p = ngx_palloc(cf->pool, size);
+    p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -177,7 +177,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
 
     size = sizeof("250 " CRLF) - 1 + cscf->server_name.len;
 
-    p = ngx_palloc(cf->pool, size);
+    p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -216,7 +216,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
         size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1;
     }
 
-    p = ngx_palloc(cf->pool, size);
+    p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -263,7 +263,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
 
     size += sizeof("250 STARTTLS" CRLF) - 1;
 
-    p = ngx_palloc(cf->pool, size);
+    p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -284,7 +284,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     size = (auth - conf->capability.data)
             + sizeof("250 STARTTLS" CRLF) - 1;
 
-    p = ngx_palloc(cf->pool, size);
+    p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
     }
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -9,9 +9,9 @@
 #include <ngx_mail.h>
 
 
-#define NGX_DEFLAUT_CERTIFICATE      "cert.pem"
-#define NGX_DEFLAUT_CERTIFICATE_KEY  "cert.pem"
-#define NGX_DEFLAUT_CIPHERS  "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
+#define NGX_DEFAULT_CERTIFICATE      "cert.pem"
+#define NGX_DEFAULT_CERTIFICATE_KEY  "cert.pem"
+#define NGX_DEFAULT_CIPHERS  "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
 
 
 static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf);
@@ -76,6 +76,13 @@ static ngx_command_t  ngx_mail_ssl_comma
       offsetof(ngx_mail_ssl_conf_t, certificate_key),
       NULL },
 
+    { ngx_string("ssl_dhparam"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_ssl_conf_t, dhparam),
+      NULL },
+
     { ngx_string("ssl_protocols"),
       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
       ngx_conf_set_bitmask_slot,
@@ -163,10 +170,9 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf)
      * set by ngx_pcalloc():
      *
      *     scf->protocols = 0;
-     *     scf->certificate.len = 0;
-     *     scf->certificate.data = NULL;
-     *     scf->certificate_key.len = 0;
-     *     scf->certificate_key.data = NULL;
+     *     scf->certificate = { 0, NULL };
+     *     scf->certificate_key = { 0, NULL };
+     *     scf->dhparam = { 0, NULL };
      *     scf->ciphers.len = 0;
      *     scf->ciphers.data = NULL;
      *     scf->shm_zone = NULL;
@@ -208,12 +214,14 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, 
                           |NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1));
 
     ngx_conf_merge_str_value(conf->certificate, prev->certificate,
-                             NGX_DEFLAUT_CERTIFICATE);
+                         NGX_DEFAULT_CERTIFICATE);
 
     ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key,
-                             NGX_DEFLAUT_CERTIFICATE_KEY);
+                         NGX_DEFAULT_CERTIFICATE_KEY);
 
-    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFLAUT_CIPHERS);
+    ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
+
+    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
 
     conf->ssl.log = cf->log;
@@ -260,9 +268,12 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, 
         return NGX_CONF_ERROR;
     }
 
+    if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
     ngx_conf_merge_value(conf->builtin_session_cache,
-                         prev->builtin_session_cache,
-                         NGX_SSL_DFLT_BUILTIN_SCACHE);
+                         prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);
 
     if (conf->shm_zone == NULL) {
         conf->shm_zone = prev->shm_zone;
@@ -294,6 +305,16 @@ ngx_mail_ssl_session_cache(ngx_conf_t *c
 
     for (i = 1; i < cf->args->nelts; i++) {
 
+        if (ngx_strcmp(value[i].data, "off") == 0) {
+            scf->builtin_session_cache = NGX_SSL_NO_SCACHE;
+            continue;
+        }
+
+        if (ngx_strcmp(value[i].data, "none") == 0) {
+            scf->builtin_session_cache = NGX_SSL_NONE_SCACHE;
+            continue;
+        }
+
         if (ngx_strcmp(value[i].data, "builtin") == 0) {
             scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
             continue;
--- a/src/mail/ngx_mail_ssl_module.h
+++ b/src/mail/ngx_mail_ssl_module.h
@@ -34,6 +34,7 @@ typedef struct {
 
     ngx_str_t        certificate;
     ngx_str_t        certificate_key;
+    ngx_str_t        dhparam;
 
     ngx_str_t        ciphers;
 
new file mode 100644
--- /dev/null
+++ b/src/misc/ngx_google_perftools_module.c
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+/*
+ * declare Profiler here interface because
+ * <google/profiler.h> is C++ header file
+ */
+
+int ProfilerStart(u_char* fname);
+void ProfilerStop(void);
+void ProfilerRegisterThread(void);
+
+
+static void *ngx_google_perftools_create_conf(ngx_cycle_t *cycle);
+static ngx_int_t ngx_google_perftools_worker(ngx_cycle_t *cycle);
+
+
+typedef struct {
+    ngx_str_t  profiles;
+} ngx_google_perftools_conf_t;
+
+
+static ngx_command_t  ngx_google_perftools_commands[] = {
+
+    { ngx_string("google_perftools_profiles"),
+      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      0,
+      offsetof(ngx_google_perftools_conf_t, profiles),
+      NULL },
+
+    ngx_null_command
+};
+
+
+static ngx_core_module_t  ngx_google_perftools_module_ctx = {
+    ngx_string("google_perftools"),
+    ngx_google_perftools_create_conf,
+    NULL
+};
+
+
+ngx_module_t  ngx_google_perftools_module = {
+    NGX_MODULE_V1,
+    &ngx_google_perftools_module_ctx,      /* module context */
+    ngx_google_perftools_commands,         /* module directives */
+    NGX_CORE_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    ngx_google_perftools_worker,           /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_google_perftools_create_conf(ngx_cycle_t *cycle)
+{
+    ngx_google_perftools_conf_t  *gptcf;
+
+    gptcf = ngx_pcalloc(cycle->pool, sizeof(ngx_google_perftools_conf_t));
+    if (gptcf == NULL) {
+        return NULL;
+    }
+
+    /*
+     * set by pcalloc()
+     *
+     *     gptcf->profiles = { 0, NULL };
+     */
+
+    return gptcf;
+}
+
+
+static ngx_int_t
+ngx_google_perftools_worker(ngx_cycle_t *cycle)
+{
+    u_char                       *profile;
+    ngx_google_perftools_conf_t  *gptcf;
+
+    gptcf = (ngx_google_perftools_conf_t *)
+                ngx_get_conf(cycle->conf_ctx, ngx_google_perftools_module);
+
+    if (gptcf->profiles.len == 0) {
+        return NGX_OK;
+    }
+
+    profile = ngx_alloc(gptcf->profiles.len + NGX_INT_T_LEN + 2, cycle->log);
+    if (profile == NULL) {
+        return NGX_OK;
+    }
+
+    if (getenv("CPUPROFILE")) {
+
+        /* disable inherited Profiler enabled in master process */
+        ProfilerStop();
+    }
+
+    ngx_sprintf(profile, "%V.%d%Z", &gptcf->profiles, ngx_pid);
+
+    if (ProfilerStart(profile)) {
+
+        /* start ITIMER_PROF timer */
+        ProfilerRegisterThread();
+
+    } else {
+        ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_errno,
+                      "ProfilerStart(%s) failed", profile);
+    }
+
+    ngx_free(profile);
+
+    return NGX_OK;
+}
+
+
+/* ProfilerStop() is called on Profiler destruction */
--- a/src/os/unix/ngx_alloc.h
+++ b/src/os/unix/ngx_alloc.h
@@ -21,8 +21,8 @@ void *ngx_calloc(size_t size, ngx_log_t 
 /*
  * Linux has memalign() or posix_memalign()
  * Solaris has memalign()
- * FreeBSD has not memalign() or posix_memalign() but its malloc() alignes
- * allocations bigger than page size at the page boundary.
+ * FreeBSD 7.0 has posix_memalign(), besides, early version's malloc()
+ * aligns allocations bigger than page size at the page boundary
  */
 
 #if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN)
--- a/src/os/unix/ngx_atomic.h
+++ b/src/os/unix/ngx_atomic.h
@@ -21,13 +21,8 @@
 
 #include <libkern/OSAtomic.h>
 
-/* "bool" conflicts with perl's CORE/handy.h
- * "true" and "false" conflict with nginx, and of course we can rename them,
- * but we need to undef "bool" anyway
- */
+/* "bool" conflicts with perl's CORE/handy.h */
 #undef bool
-#undef true
-#undef false
 
 
 #define NGX_HAVE_ATOMIC_OPS  1
--- a/src/os/unix/ngx_channel.c
+++ b/src/os/unix/ngx_channel.c
@@ -33,7 +33,7 @@ ngx_write_channel(ngx_socket_t s, ngx_ch
         msg.msg_control = (caddr_t) &cmsg;
         msg.msg_controllen = sizeof(cmsg);
 
-        cmsg.cm.cmsg_len = sizeof(cmsg);
+        cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));
         cmsg.cm.cmsg_level = SOL_SOCKET;
         cmsg.cm.cmsg_type = SCM_RIGHTS;
         *(int *) CMSG_DATA(&cmsg.cm) = ch->fd;
@@ -138,7 +138,7 @@ ngx_read_channel(ngx_socket_t s, ngx_cha
 
     if (ch->command == NGX_CMD_OPEN_CHANNEL) {
 
-        if (cmsg.cm.cmsg_len < (socklen_t) sizeof(cmsg)) {
+        if (cmsg.cm.cmsg_len < (socklen_t) CMSG_LEN(sizeof(int))) {
             ngx_log_error(NGX_LOG_ALERT, log, 0,
                           "recvmsg() returned too small ancillary data");
             return NGX_ERROR;
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_darwin.h
@@ -0,0 +1,19 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_DARWIN_H_INCLUDED_
+#define _NGX_DARWIN_H_INCLUDED_
+
+
+ngx_chain_t *ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,
+    off_t limit);
+
+extern int       ngx_darwin_kern_osreldate;
+extern int       ngx_darwin_hw_ncpu;
+extern u_long    ngx_darwin_net_inet_tcp_sendspace;
+
+
+#endif /* _NGX_DARWIN_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_darwin_config.h
@@ -0,0 +1,87 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_DARWIN_CONFIG_H_INCLUDED_
+#define _NGX_DARWIN_CONFIG_H_INCLUDED_
+
+
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stddef.h>             /* offsetof() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+#include <glob.h>
+
+#include <sys/filio.h>          /* FIONBIO */
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>        /* TCP_NODELAY */
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#include <sys/sysctl.h>
+#include <xlocale.h>
+
+
+#ifndef IOV_MAX
+#define IOV_MAX   64
+#endif
+
+
+#include <ngx_auto_config.h>
+
+
+#if (NGX_HAVE_POLL)
+#include <poll.h>
+#endif
+
+
+#if (NGX_HAVE_KQUEUE)
+#include <sys/event.h>
+#endif
+
+
+#define NGX_LISTEN_BACKLOG  -1
+
+
+#ifndef NGX_HAVE_INHERITED_NONBLOCK
+#define NGX_HAVE_INHERITED_NONBLOCK  1
+#endif
+
+
+#ifndef NGX_HAVE_CASELESS_FILESYSTEM
+#define NGX_HAVE_CASELESS_FILESYSTEM  1
+#endif
+
+
+#define NGX_HAVE_OS_SPECIFIC_INIT    1
+
+
+extern char **environ;
+
+
+#endif /* _NGX_DARWIN_CONFIG_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_darwin_init.c
@@ -0,0 +1,155 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+char    ngx_darwin_kern_ostype[16];
+char    ngx_darwin_kern_osrelease[128];
+int     ngx_darwin_hw_ncpu;
+int     ngx_darwin_kern_ipc_somaxconn;
+u_long  ngx_darwin_net_inet_tcp_sendspace;
+
+
+static ngx_os_io_t ngx_darwin_io = {
+    ngx_unix_recv,
+    ngx_readv_chain,
+    ngx_udp_unix_recv,
+    ngx_unix_send,
+#if (NGX_HAVE_SENDFILE)
+    ngx_darwin_sendfile_chain,
+    NGX_IO_SENDFILE
+#else
+    ngx_writev_chain,
+    0
+#endif
+};
+
+
+typedef struct {
+    char        *name;
+    void        *value;
+    size_t       size;
+    ngx_uint_t   exists;
+} sysctl_t;
+
+
+sysctl_t sysctls[] = {
+    { "hw.ncpu",
+      &ngx_darwin_hw_ncpu,
+      sizeof(ngx_darwin_hw_ncpu), 0 },
+
+    { "net.inet.tcp.sendspace",
+      &ngx_darwin_net_inet_tcp_sendspace,
+      sizeof(ngx_darwin_net_inet_tcp_sendspace), 0 },
+
+    { "kern.ipc.somaxconn",
+      &ngx_darwin_kern_ipc_somaxconn,
+      sizeof(ngx_darwin_kern_ipc_somaxconn), 0 },
+
+    { NULL, NULL, 0, 0 }
+};
+
+
+ngx_int_t
+ngx_os_specific_init(ngx_log_t *log)
+{
+    int         somaxconn;
+    size_t      size;
+    ngx_err_t   err;
+    ngx_uint_t  i;
+
+    size = sizeof(ngx_darwin_kern_ostype);
+    if (sysctlbyname("kern.ostype",
+                     ngx_darwin_kern_ostype, &size, NULL, 0) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "sysctlbyname(kern.ostype) failed");
+
+        if (ngx_errno != NGX_ENOMEM) {
+            return NGX_ERROR;
+        }
+
+        ngx_darwin_kern_ostype[size - 1] = '\0';
+    }
+
+    size = sizeof(ngx_darwin_kern_osrelease);
+    if (sysctlbyname("kern.osrelease",
+                     ngx_darwin_kern_osrelease, &size, NULL, 0) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "sysctlbyname(kern.osrelease) failed");
+
+        if (ngx_errno != NGX_ENOMEM) {
+            return NGX_ERROR;
+        }
+
+        ngx_darwin_kern_osrelease[size - 1] = '\0';
+    }
+
+
+    for (i = 0; sysctls[i].name; i++) {
+        size = sysctls[i].size;
+
+        if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0)
+            == 0)
+        {
+            sysctls[i].exists = 1;
+            continue;
+        }
+
+        err = ngx_errno;
+
+        if (err == NGX_ENOENT) {
+            continue;
+        }
+
+        ngx_log_error(NGX_LOG_ALERT, log, err,
+                      "sysctlbyname(%s) failed", sysctls[i].name);
+        return NGX_ERROR;
+    }
+
+    ngx_ncpu = ngx_darwin_hw_ncpu;
+
+    somaxconn = 32676;
+
+    if (ngx_darwin_kern_ipc_somaxconn > somaxconn) {
+        ngx_log_error(NGX_LOG_ALERT, log, 0,
+                      "sysctl kern.ipc.somaxconn must be no more than %d",
+                      somaxconn);
+        return NGX_ERROR;
+    }
+
+    ngx_tcp_nodelay_and_tcp_nopush = 1;
+
+    ngx_os_io = ngx_darwin_io;
+
+    return NGX_OK;
+}
+
+
+void
+ngx_os_specific_status(ngx_log_t *log)
+{
+    u_long      value;
+    ngx_uint_t  i;
+
+    ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
+                  ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease);
+
+    for (i = 0; sysctls[i].name; i++) {
+        if (sysctls[i].exists) {
+            if (sysctls[i].size == sizeof(long)) {
+                value = *(long *) sysctls[i].value;
+
+            } else {
+                value = *(int *) sysctls[i].value;
+            }
+
+            ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l",
+                          sysctls[i].name, value);
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_darwin_sendfile_chain.c
@@ -0,0 +1,363 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+/*
+ * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same
+ * old bug as early FreeBSD sendfile() syscall:
+ * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
+ *
+ * Besides sendfile() has another bug: if one calls sendfile()
+ * with both a header and a trailer, then sendfile() ignores a file part
+ * at all and sends only the header and the trailer together.
+ * For this reason we send a trailer only if there is no a header.
+ *
+ * Although sendfile() allows to pass a header or a trailer,
+ * it may send the header or the trailer and a part of the file
+ * in different packets.  And FreeBSD workaround (TCP_NOPUSH option)
+ * does not help.
+ */
+
+
+#if (IOV_MAX > 64)
+#define NGX_HEADERS   64
+#define NGX_TRAILERS  64
+#else
+#define NGX_HEADERS   IOV_MAX
+#define NGX_TRAILERS  IOV_MAX
+#endif
+
+
+ngx_chain_t *
+ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
+{
+    int              rc;
+    u_char          *prev;
+    off_t            size, send, prev_send, aligned, sent, fprev;
+    off_t            header_size, file_size;
+    ngx_uint_t       eintr, eagain, complete;
+    ngx_err_t        err;
+    ngx_buf_t       *file;
+    ngx_array_t      header, trailer;
+    ngx_event_t     *wev;
+    ngx_chain_t     *cl;
+    struct sf_hdtr   hdtr;
+    struct iovec    *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS];
+
+    wev = c->write;
+
+    if (!wev->ready) {
+        return in;
+    }
+
+#if (NGX_HAVE_KQUEUE)
+
+    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
+        (void) ngx_connection_error(c, wev->kq_errno,
+                               "kevent() reported about an closed connection");
+        wev->error = 1;
+        return NGX_CHAIN_ERROR;
+    }
+
+#endif
+
+    /* the maximum limit size is the maximum size_t value - the page size */
+
+    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
+        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
+    }
+
+    send = 0;
+    eagain = 0;
+
+    header.elts = headers;
+    header.size = sizeof(struct iovec);
+    header.nalloc = NGX_HEADERS;
+    header.pool = c->pool;
+
+    trailer.elts = trailers;
+    trailer.size = sizeof(struct iovec);
+    trailer.nalloc = NGX_TRAILERS;
+    trailer.pool = c->pool;
+
+    for ( ;; ) {
+        file = NULL;
+        file_size = 0;
+        header_size = 0;
+        eintr = 0;
+        complete = 0;
+        prev_send = send;
+
+        header.nelts = 0;
+        trailer.nelts = 0;
+
+        /* create the header iovec and coalesce the neighbouring bufs */
+
+        prev = NULL;
+        iov = NULL;
+
+        for (cl = in;
+             cl && header.nelts < IOV_MAX && send < limit;
+             cl = cl->next)
+        {
+            if (ngx_buf_special(cl->buf)) {
+                continue;
+            }
+
+            if (!ngx_buf_in_memory_only(cl->buf)) {
+                break;
+            }
+
+            size = cl->buf->last - cl->buf->pos;
+
+            if (send + size > limit) {
+                size = limit - send;
+            }
+
+            if (prev == cl->buf->pos) {
+                iov->iov_len += (size_t) size;
+
+            } else {
+                iov = ngx_array_push(&header);
+                if (iov == NULL) {
+                    return NGX_CHAIN_ERROR;
+                }
+
+                iov->iov_base = (void *) cl->buf->pos;
+                iov->iov_len = (size_t) size;
+            }
+
+            prev = cl->buf->pos + (size_t) size;
+            header_size += size;
+            send += size;
+        }
+
+
+        if (cl && cl->buf->in_file && send < limit) {
+            file = cl->buf;
+
+            /* coalesce the neighbouring file bufs */
+
+            do {
+                size = cl->buf->file_last - cl->buf->file_pos;
+
+                if (send + size > limit) {
+                    size = limit - send;
+
+                    aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
+                               & ~((off_t) ngx_pagesize - 1);
+
+                    if (aligned <= cl->buf->file_last) {
+                        size = aligned - cl->buf->file_pos;
+                    }
+                }
+
+                file_size += size;
+                send += size;
+                fprev = cl->buf->file_pos + size;
+                cl = cl->next;
+
+            } while (cl
+                     && cl->buf->in_file
+                     && send < limit
+                     && file->file->fd == cl->buf->file->fd
+                     && fprev == cl->buf->file_pos);
+        }
+
+        if (file && header.nelts == 0) {
+
+            /* create the tailer iovec and coalesce the neighbouring bufs */
+
+            prev = NULL;
+            iov = NULL;
+
+            while (cl && header.nelts < IOV_MAX && send < limit) {
+
+                if (ngx_buf_special(cl->buf)) {
+                    cl = cl->next;
+                    continue;
+                }
+
+                if (!ngx_buf_in_memory_only(cl->buf)) {
+                    break;
+                }
+
+                size = cl->buf->last - cl->buf->pos;
+
+                if (send + size > limit) {
+                    size = limit - send;
+                }
+
+                if (prev == cl->buf->pos) {
+                    iov->iov_len += (size_t) size;
+
+                } else {
+                    iov = ngx_array_push(&trailer);
+                    if (iov == NULL) {
+                        return NGX_CHAIN_ERROR;
+                    }
+
+                    iov->iov_base = (void *) cl->buf->pos;
+                    iov->iov_len = (size_t) size;
+                }
+
+                prev = cl->buf->pos + (size_t) size;
+                send += size;
+                cl = cl->next;
+            }
+        }
+
+        if (file) {
+
+            /*
+             * sendfile() returns EINVAL if sf_hdtr's count is 0,
+             * but corresponding pointer is not NULL
+             */
+
+            hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL;
+            hdtr.hdr_cnt = header.nelts;
+            hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL;
+            hdtr.trl_cnt = trailer.nelts;
+
+            sent = header_size + file_size;
+
+            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "sendfile: @%O %O h:%O",
+                           file->file_pos, sent, header_size);
+
+            rc = sendfile(file->file->fd, c->fd, file->file_pos,
+                          &sent, &hdtr, 0);
+
+            if (rc == -1) {
+                err = ngx_errno;
+
+                if (err == NGX_EAGAIN || err == NGX_EINTR) {
+                    if (err == NGX_EINTR) {
+                        eintr = 1;
+
+                    } else {
+                        eagain = 1;
+                    }
+
+                    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
+                                   "sendfile() sent only %O bytes", sent);
+
+                } else {
+                    wev->error = 1;
+                    (void) ngx_connection_error(c, err, "sendfile() failed");
+                    return NGX_CHAIN_ERROR;
+                }
+            }
+
+            if (rc == 0 && sent == 0) {
+
+                /*
+                 * if rc and sent equal to zero, then someone
+                 * has truncated the file, so the offset became beyond
+                 * the end of the file
+                 */
+
+                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                              "sendfile() reported that \"%s\" was truncated",
+                              file->file->name.data);
+
+                return NGX_CHAIN_ERROR;
+            }
+
+            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "sendfile: %d, @%O %O:%O",
+                           rc, file->file_pos, sent, file_size + header_size);
+
+        } else {
+            rc = writev(c->fd, header.elts, header.nelts);
+
+            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "writev: %d of %uz", rc, send);
+
+            if (rc == -1) {
+                err = ngx_errno;
+
+                if (err == NGX_EAGAIN || err == NGX_EINTR) {
+                    if (err == NGX_EINTR) {
+                        eintr = 1;
+                    }
+
+                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+                                   "writev() not ready");
+
+                } else {
+                    wev->error = 1;
+                    ngx_connection_error(c, err, "writev() failed");
+                    return NGX_CHAIN_ERROR;
+                }
+            }
+
+            sent = rc > 0 ? rc : 0;
+        }
+
+        if (send - prev_send == sent) {
+            complete = 1;
+        }
+
+        c->sent += sent;
+
+        for (cl = in; cl; cl = cl->next) {
+
+            if (ngx_buf_special(cl->buf)) {
+                continue;
+            }
+
+            if (sent == 0) {
+                break;
+            }
+
+            size = ngx_buf_size(cl->buf);
+
+            if (sent >= size) {
+                sent -= size;
+
+                if (ngx_buf_in_memory(cl->buf)) {
+                    cl->buf->pos = cl->buf->last;
+                }
+
+                if (cl->buf->in_file) {
+                    cl->buf->file_pos = cl->buf->file_last;
+                }
+
+                continue;
+            }
+
+            if (ngx_buf_in_memory(cl->buf)) {
+                cl->buf->pos += (size_t) sent;
+            }
+
+            if (cl->buf->in_file) {
+                cl->buf->file_pos += sent;
+            }
+
+            break;
+        }
+
+        if (eintr) {
+            continue;
+        }
+
+        if (!complete) {
+            wev->ready = 0;
+            return cl;
+        }
+
+        if (send >= limit || cl == NULL) {
+            return cl;
+        }
+
+        in = cl;
+    }
+}
--- a/src/os/unix/ngx_errno.h
+++ b/src/os/unix/ngx_errno.h
@@ -37,6 +37,9 @@ typedef int               ngx_err_t;
 #define NGX_ETIMEDOUT     ETIMEDOUT
 #define NGX_ECONNREFUSED  ECONNREFUSED
 #define NGX_ENAMETOOLONG  ENAMETOOLONG
+#define NGX_ENETDOWN      ENETDOWN
+#define NGX_ENETUNREACH   ENETUNREACH
+#define NGX_EHOSTDOWN     EHOSTDOWN
 #define NGX_EHOSTUNREACH  EHOSTUNREACH
 #define NGX_ENOSYS        ENOSYS
 #define NGX_ECANCELED     ECANCELED
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -257,7 +257,15 @@ ngx_open_dir(ngx_str_t *name, ngx_dir_t 
 ngx_int_t
 ngx_open_glob(ngx_glob_t *gl)
 {
-    if (glob((char *) gl->pattern, GLOB_NOSORT, NULL, &gl->pglob) == 0) {
+    int  n;
+
+    n = glob((char *) gl->pattern, GLOB_NOSORT, NULL, &gl->pglob);
+
+    if (n == 0) {
+        return NGX_OK;
+    }
+
+    if (n == GLOB_NOMATCH && gl->test) {
         return NGX_OK;
     }
 
@@ -343,3 +351,22 @@ ngx_unlock_fd(ngx_fd_t fd)
 
     return 0;
 }
+
+
+#if (NGX_HAVE_O_DIRECT)
+
+ngx_int_t
+ngx_directio(ngx_fd_t fd)
+{
+    int  flags;
+
+    flags = fcntl(fd, F_GETFL);
+
+    if (flags == -1) {
+        return -1;
+    }
+
+    return fcntl(fd, F_SETFL, flags | O_DIRECT);
+}
+
+#endif
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -17,8 +17,20 @@
 
 
 
+#ifdef __CYGWIN__
+
+#define NGX_HAVE_CASELESS_FILESYSTEM  1
+
+#define ngx_open_file(name, mode, create, access)                            \
+    open((const char *) name, mode|create|O_BINARY, access)
+
+#else
+
 #define ngx_open_file(name, mode, create, access)                            \
     open((const char *) name, mode|create, access)
+
+#endif
+
 #define ngx_open_file_n          "open()"
 
 #define NGX_FILE_RDONLY          O_RDONLY
@@ -93,6 +105,16 @@ ngx_int_t ngx_set_file_time(u_char *name
 #define ngx_file_uniq(sb)        (sb)->st_ino
 
 
+#if (NGX_HAVE_CASELESS_FILESYSTEM)
+
+#define ngx_filename_cmp(s1, s2, n)  strncasecmp((char *) s1, (char *) s2, n)
+
+#else
+
+#define ngx_filename_cmp         ngx_memcmp
+
+#endif
+
 
 #define ngx_getcwd(buf, size)    (getcwd(buf, size) != NULL)
 #define ngx_getcwd_n             "getcwd()"
@@ -144,10 +166,11 @@ ngx_int_t ngx_open_dir(ngx_str_t *name, 
 
 
 typedef struct {
-    size_t      n;
-    glob_t      pglob;
-    u_char     *pattern;
-    ngx_log_t  *log;
+    size_t       n;
+    glob_t       pglob;
+    u_char      *pattern;
+    ngx_log_t   *log;
+    ngx_uint_t   test;
 } ngx_glob_t;
 
 
@@ -166,4 +189,27 @@ ngx_err_t ngx_unlock_fd(ngx_fd_t fd);
 #define ngx_unlock_fd_n          "fcntl(F_SETLK, F_UNLCK)"
 
 
+#if (NGX_HAVE_O_DIRECT)
+
+ngx_int_t ngx_directio(ngx_fd_t fd);
+#define ngx_directio_n           "fcntl(O_DIRECT)"
+
+#elif (NGX_HAVE_F_NOCACHE)
+
+#define ngx_directio(fd)         fcntl(fd, F_NOCACHE, 1)
+#define ngx_directio_n           "fcntl(F_NOCACHE)"
+
+#elif (NGX_HAVE_DIRECTIO)
+
+#define ngx_directio(fd)         directio(fd, DIRECTIO_ON)
+#define ngx_directio_n           "directio(DIRECTIO_ON)"
+
+#else
+
+#define ngx_directio(fd)         0
+#define ngx_directio_n           "ngx_directio_n"
+
+#endif
+
+
 #endif /* _NGX_FILES_H_INCLUDED_ */
--- a/src/os/unix/ngx_freebsd.h
+++ b/src/os/unix/ngx_freebsd.h
@@ -11,13 +11,14 @@
 ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,
     off_t limit);
 
-extern int     ngx_freebsd_kern_osreldate;
-extern int     ngx_freebsd_hw_ncpu;
-extern u_long  ngx_freebsd_net_inet_tcp_sendspace;
-extern int     ngx_freebsd_kern_ipc_zero_copy_send;
+extern int         ngx_freebsd_kern_osreldate;
+extern int         ngx_freebsd_hw_ncpu;
+extern u_long      ngx_freebsd_net_inet_tcp_sendspace;
+extern int         ngx_freebsd_kern_ipc_zero_copy_send;
 
-extern ngx_uint_t ngx_freebsd_sendfile_nbytes_bug;
-extern ngx_uint_t ngx_freebsd_use_tcp_nopush;
+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;
 
 
 #endif /* _NGX_FREEBSD_H_INCLUDED_ */
--- a/src/os/unix/ngx_freebsd_config.h
+++ b/src/os/unix/ngx_freebsd_config.h
@@ -48,11 +48,16 @@
 
 #if __FreeBSD_version < 400017
 
-/* FreeBSD 3.x has no CMSG_SPACE() at all and has the broken CMSG_DATA() */
+/*
+ * FreeBSD 3.x has no CMSG_SPACE() and CMSG_LEN() and has the broken CMSG_DATA()
+ */
 
 #undef  CMSG_SPACE
 #define CMSG_SPACE(l)       (ALIGN(sizeof(struct cmsghdr)) + ALIGN(l))
 
+#undef  CMSG_LEN
+#define CMSG_LEN(l)         (ALIGN(sizeof(struct cmsghdr)) + (l))
+
 #undef  CMSG_DATA
 #define CMSG_DATA(cmsg)     ((u_char *)(cmsg) + ALIGN(sizeof(struct cmsghdr)))
 
--- a/src/os/unix/ngx_freebsd_init.c
+++ b/src/os/unix/ngx_freebsd_init.c
@@ -23,13 +23,15 @@ int     ngx_freebsd_machdep_hlt_logical_
 int     ngx_freebsd_kern_ipc_zero_copy_send;
 
 
-ngx_uint_t ngx_freebsd_sendfile_nbytes_bug;
-ngx_uint_t ngx_freebsd_use_tcp_nopush;
+ngx_uint_t  ngx_freebsd_sendfile_nbytes_bug;
+ngx_uint_t  ngx_freebsd_use_tcp_nopush;
+ngx_uint_t  ngx_freebsd_debug_malloc;
 
 
 static ngx_os_io_t ngx_freebsd_io = {
     ngx_unix_recv,
     ngx_readv_chain,
+    ngx_udp_unix_recv,
     ngx_unix_send,
 #if (NGX_HAVE_SENDFILE)
     ngx_freebsd_sendfile_chain,
@@ -85,6 +87,16 @@ ngx_debug_init()
     malloc_options = "J";
 #endif
 
+    ngx_freebsd_debug_malloc = 1;
+
+#else
+    char  *mo;
+
+    mo = getenv("MALLOC_OPTIONS");
+
+    if (mo && ngx_strchr(mo, 'J')) {
+        ngx_freebsd_debug_malloc = 1;
+    }
 #endif
 }
 
--- a/src/os/unix/ngx_linux_config.h
+++ b/src/os/unix/ngx_linux_config.h
@@ -47,9 +47,11 @@
 
 #include <time.h>               /* tzset() */
 #include <malloc.h>             /* memalign() */
+#include <limits.h>             /* IOV_MAX */
 #include <sys/ioctl.h>
 #include <sys/sysctl.h>
 #include <crypt.h>
+#include <sys/utsname.h>        /* uname() */
 
 
 #include <ngx_auto_config.h>
@@ -68,7 +70,7 @@ extern ssize_t sendfile(int s, int fd, i
 #endif
 
 
-#if (NGX_HAVE_POLL)
+#if (NGX_HAVE_POLL || NGX_HAVE_RTSIG)
 #include <poll.h>
 #endif
 
--- a/src/os/unix/ngx_linux_init.c
+++ b/src/os/unix/ngx_linux_init.c
@@ -8,19 +8,16 @@
 #include <ngx_core.h>
 
 
-static ngx_int_t ngx_linux_procfs(char *name, char *buf, size_t len,
-    ngx_log_t *log);
-
+u_char  ngx_linux_kern_ostype[50];
+u_char  ngx_linux_kern_osrelease[50];
 
-char  ngx_linux_kern_ostype[50];
-char  ngx_linux_kern_osrelease[50];
-
-int   ngx_linux_rtsig_max;
+int     ngx_linux_rtsig_max;
 
 
 static ngx_os_io_t ngx_linux_io = {
     ngx_unix_recv,
     ngx_readv_chain,
+    ngx_udp_unix_recv,
     ngx_unix_send,
 #if (NGX_HAVE_SENDFILE)
     ngx_linux_sendfile_chain,
@@ -35,27 +32,25 @@ static ngx_os_io_t ngx_linux_io = {
 ngx_int_t
 ngx_os_specific_init(ngx_log_t *log)
 {
+    struct utsname  u;
+
+    if (uname(&u) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "uname() failed");
+        return NGX_ERROR;
+    }
+
+    (void) ngx_cpystrn(ngx_linux_kern_ostype, (u_char *) u.sysname,
+                       sizeof(ngx_linux_kern_ostype));
+
+    (void) ngx_cpystrn(ngx_linux_kern_osrelease, (u_char *) u.release,
+                       sizeof(ngx_linux_kern_osrelease));
+
+#if (NGX_HAVE_RTSIG)
+    {
     int        name[2];
     size_t     len;
     ngx_err_t  err;
 
-    if (ngx_linux_procfs("/proc/sys/kernel/ostype",
-                         ngx_linux_kern_ostype,
-                         sizeof(ngx_linux_kern_ostype), log)
-        == -1)
-    {
-        return NGX_ERROR;
-    }
-
-    if (ngx_linux_procfs("/proc/sys/kernel/osrelease",
-                         ngx_linux_kern_osrelease,
-                         sizeof(ngx_linux_kern_osrelease), log)
-        == -1)
-    {
-        return NGX_ERROR;
-    }
-
-
     name[0] = CTL_KERN;
     name[1] = KERN_RTSIGMAX;
     len = sizeof(ngx_linux_rtsig_max);
@@ -73,6 +68,8 @@ ngx_os_specific_init(ngx_log_t *log)
         ngx_linux_rtsig_max = 0;
     }
 
+    }
+#endif
 
     ngx_os_io = ngx_linux_io;
 
@@ -86,39 +83,8 @@ ngx_os_specific_status(ngx_log_t *log)
     ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
                   ngx_linux_kern_ostype, ngx_linux_kern_osrelease);
 
+#if (NGX_HAVE_RTSIG)
     ngx_log_error(NGX_LOG_NOTICE, log, 0, "sysctl(KERN_RTSIGMAX): %d",
                   ngx_linux_rtsig_max);
+#endif
 }
-
-
-static ngx_int_t
-ngx_linux_procfs(char *name, char *buf, size_t len, ngx_log_t *log)
-{
-    int       n;
-    ngx_fd_t  fd;
-
-    fd = open(name, O_RDONLY);
-
-    if (fd == NGX_INVALID_FILE) {
-        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                      "open(\"%s\") failed", name);
-
-        return NGX_ERROR;
-    }
-
-    n = read(fd, buf, len);
-
-    if (n == -1) {
-        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                      "read(\"%s\") failed", name);
-
-    } else {
-        if (buf[n - 1] == '\n') {
-            buf[--n] = '\0';
-        }
-    }
-
-    ngx_close_file(fd);
-
-    return n;
-}
--- a/src/os/unix/ngx_os.h
+++ b/src/os/unix/ngx_os.h
@@ -25,6 +25,7 @@ typedef ngx_chain_t *(*ngx_send_chain_pt
 typedef struct {
     ngx_recv_pt        recv;
     ngx_recv_chain_pt  recv_chain;
+    ngx_recv_pt        udp_recv;
     ngx_send_pt        send;
     ngx_send_chain_pt  send_chain;
     ngx_uint_t         flags;
@@ -41,6 +42,7 @@ ngx_int_t ngx_daemon(ngx_log_t *log);
 
 ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size);
 ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *entry);
+ssize_t ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size);
 ssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size);
 ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in,
     off_t limit);
@@ -64,6 +66,10 @@ extern ngx_uint_t   ngx_tcp_nodelay_and_
 
 #elif (NGX_SOLARIS)
 #include <ngx_solaris.h>
+
+
+#elif (NGX_DARWIN)
+#include <ngx_darwin.h>
 #endif
 
 
--- a/src/os/unix/ngx_posix_config.h
+++ b/src/os/unix/ngx_posix_config.h
@@ -19,6 +19,12 @@
 #endif
 
 
+#ifdef __CYGWIN__
+#define timezonevar             /* timezone is variable */
+#define NGX_BROKEN_SCM_RIGHTS   1
+#endif
+
+
 #include <sys/types.h>
 #include <sys/time.h>
 #if (NGX_HAVE_UNISTD_H)
@@ -64,6 +70,15 @@
 #include <limits.h>             /* IOV_MAX */
 #endif
 
+#ifdef __CYGWIN__
+#include <malloc.h>             /* memalign() */
+#endif
+
+#if (NGX_HAVE_CRYPT_H)
+#include <crypt.h>
+#endif
+
+
 #ifndef IOV_MAX
 #define IOV_MAX   16
 #endif
@@ -95,11 +110,16 @@
 
 #include <sys/param.h>          /* ALIGN() */
 
-/* FreeBSD 3.x has no CMSG_SPACE() at all and has the broken CMSG_DATA() */
+/*
+ * FreeBSD 3.x has no CMSG_SPACE() and CMSG_LEN() and has the broken CMSG_DATA()
+ */
 
 #undef  CMSG_SPACE
 #define CMSG_SPACE(l)       (ALIGN(sizeof(struct cmsghdr)) + ALIGN(l))
 
+#undef  CMSG_LEN
+#define CMSG_LEN(l)         (ALIGN(sizeof(struct cmsghdr)) + (l))
+
 #undef  CMSG_DATA
 #define CMSG_DATA(cmsg)     ((u_char *)(cmsg) + ALIGN(sizeof(struct cmsghdr)))
 
--- a/src/os/unix/ngx_posix_init.c
+++ b/src/os/unix/ngx_posix_init.c
@@ -21,6 +21,7 @@ struct rlimit  rlmt;
 ngx_os_io_t ngx_os_io = {
     ngx_unix_recv,
     ngx_readv_chain,
+    ngx_udp_unix_recv,
     NULL,
     ngx_writev_chain,
     0
--- a/src/os/unix/ngx_process.c
+++ b/src/os/unix/ngx_process.c
@@ -452,7 +452,7 @@ ngx_process_get_status(void)
              *
              * When several processes exit at the same time FreeBSD may
              * erroneously call the signal handler for exited process
-             * despite waitpid() may be already called for this process
+             * despite waitpid() may be already called for this process.
              */
 
             if (err == NGX_ECHILD) {
@@ -507,8 +507,9 @@ ngx_process_get_status(void)
 
         if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) {
             ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
-                        "%s %P exited with fatal code %d and could not respawn",
-                        process, pid, WEXITSTATUS(status));
+                          "%s %P exited with fatal code %d "
+                          "and can not be respawn",
+                          process, pid, WEXITSTATUS(status));
             ngx_processes[i].respawn = 0;
         }
     }
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -14,7 +14,7 @@ static void ngx_start_worker_processes(n
     ngx_int_t type);
 static void ngx_start_garbage_collector(ngx_cycle_t *cycle, ngx_int_t type);
 static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo);
-static ngx_uint_t ngx_reap_childs(ngx_cycle_t *cycle);
+static ngx_uint_t ngx_reap_children(ngx_cycle_t *cycle);
 static void ngx_master_process_exit(ngx_cycle_t *cycle);
 static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data);
 static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_uint_t priority);
@@ -108,7 +108,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
         size += ngx_strlen(ngx_argv[i]) + 1;
     }
 
-    title = ngx_palloc(cycle->pool, size);
+    title = ngx_pnalloc(cycle->pool, size);
 
     p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);
     for (i = 0; i < ngx_argc; i++) {
@@ -157,9 +157,9 @@ ngx_master_process_cycle(ngx_cycle_t *cy
 
         if (ngx_reap) {
             ngx_reap = 0;
-            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap childs");
+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
 
-            live = ngx_reap_childs(cycle);
+            live = ngx_reap_children(cycle);
         }
 
         if (!live && (ngx_terminate || ngx_quit)) {
@@ -409,6 +409,12 @@ ngx_signal_worker_processes(ngx_cycle_t 
     ngx_err_t      err;
     ngx_channel_t  ch;
 
+#if (NGX_BROKEN_SCM_RIGHTS)
+
+    ch.command = 0;
+
+#else
+
     switch (signo) {
 
     case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
@@ -427,6 +433,8 @@ ngx_signal_worker_processes(ngx_cycle_t 
         ch.command = 0;
     }
 
+#endif
+
     ch.fd = -1;
 
 
@@ -496,7 +504,7 @@ ngx_signal_worker_processes(ngx_cycle_t 
 
 
 static ngx_uint_t
-ngx_reap_childs(ngx_cycle_t *cycle)
+ngx_reap_children(ngx_cycle_t *cycle)
 {
     ngx_int_t         i, n;
     ngx_uint_t        live;
@@ -679,17 +687,16 @@ ngx_worker_process_cycle(ngx_cycle_t *cy
 {
     ngx_uint_t         i;
     ngx_connection_t  *c;
-#if (NGX_THREADS)
-    ngx_int_t          n;
-    ngx_err_t          err;
-    ngx_core_conf_t   *ccf;
-#endif
 
     ngx_worker_process_init(cycle, 1);
 
     ngx_setproctitle("worker process");
 
 #if (NGX_THREADS)
+    {
+    ngx_int_t         n;
+    ngx_err_t         err;
+    ngx_core_conf_t  *ccf;
 
     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
 
@@ -728,7 +735,7 @@ ngx_worker_process_cycle(ngx_cycle_t *cy
             }
         }
     }
-
+    }
 #endif
 
     for ( ;; ) {
@@ -987,18 +994,18 @@ ngx_worker_process_exit(ngx_cycle_t *cyc
         }
     }
 
-    if (ngx_quit) {
+    if (ngx_exiting) {
         c = cycle->connections;
         for (i = 0; i < cycle->connection_n; i++) {
             if (c[i].fd != -1
                 && c[i].read
                 && !c[i].read->accept
-                && !c[i].read->channel)
+                && !c[i].read->channel
+                && !c[i].read->resolver)
             {
                 ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
-                              "open socket #%d left in %ui connection, "
-                              "aborting",
-                              c[i].fd, i);
+                              "open socket #%d left in %ui connection %s",
+                              c[i].fd, i, ngx_debug_quit ? ", aborting" : "");
                 ngx_debug_point();
             }
         }
@@ -1035,7 +1042,6 @@ static void
 ngx_channel_handler(ngx_event_t *ev)
 {
     ngx_int_t          n;
-    ngx_socket_t       fd;
     ngx_channel_t      ch;
     ngx_connection_t  *c;
 
@@ -1048,75 +1054,74 @@ ngx_channel_handler(ngx_event_t *ev)
 
     ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel handler");
 
-    n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
+    for ( ;; ) {
 
-    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel: %i", n);
+        n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
 
-    if (n == NGX_ERROR) {
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel: %i", n);
 
-        ngx_free_connection(c);
+        if (n == NGX_ERROR) {
 
-        fd = c->fd;
-        c->fd = (ngx_socket_t) -1;
+            if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {
+                ngx_del_conn(c, 0);
+            }
 
-        if (close(fd) == -1) {
-            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
-                          "close() channel failed");
+            ngx_close_connection(c);
+            return;
         }
 
-        return;
-    }
+        if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
+            if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+                return;
+            }
+        }
 
-    if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
-        if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+        if (n == NGX_AGAIN) {
             return;
         }
-    }
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,
+                       "channel command: %d", ch.command);
 
-    if (n == NGX_AGAIN) {
-        return;
-    }
+        switch (ch.command) {
 
-    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,
-                   "channel command: %d", ch.command);
-
-    switch (ch.command) {
+        case NGX_CMD_QUIT:
+            ngx_quit = 1;
+            break;
 
-    case NGX_CMD_QUIT:
-        ngx_quit = 1;
-        break;
+        case NGX_CMD_TERMINATE:
+            ngx_terminate = 1;
+            break;
 
-    case NGX_CMD_TERMINATE:
-        ngx_terminate = 1;
-        break;
+        case NGX_CMD_REOPEN:
+            ngx_reopen = 1;
+            break;
 
-    case NGX_CMD_REOPEN:
-        ngx_reopen = 1;
-        break;
+        case NGX_CMD_OPEN_CHANNEL:
 
-    case NGX_CMD_OPEN_CHANNEL:
-
-        ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0,
-                       "get channel s:%i pid:%P fd:%d", ch.slot, ch.pid, ch.fd);
+            ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0,
+                           "get channel s:%i pid:%P fd:%d",
+                           ch.slot, ch.pid, ch.fd);
 
-        ngx_processes[ch.slot].pid = ch.pid;
-        ngx_processes[ch.slot].channel[0] = ch.fd;
-        break;
+            ngx_processes[ch.slot].pid = ch.pid;
+            ngx_processes[ch.slot].channel[0] = ch.fd;
+            break;
 
-    case NGX_CMD_CLOSE_CHANNEL:
+        case NGX_CMD_CLOSE_CHANNEL:
 
-        ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0,
-                       "close channel s:%i pid:%P our:%P fd:%d",
-                       ch.slot, ch.pid, ngx_processes[ch.slot].pid,
-                       ngx_processes[ch.slot].channel[0]);
+            ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0,
+                           "close channel s:%i pid:%P our:%P fd:%d",
+                           ch.slot, ch.pid, ngx_processes[ch.slot].pid,
+                           ngx_processes[ch.slot].channel[0]);
 
-        if (close(ngx_processes[ch.slot].channel[0]) == -1) {
-            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
-                          "close() channel failed");
+            if (close(ngx_processes[ch.slot].channel[0]) == -1) {
+                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+                              "close() channel failed");
+            }
+
+            ngx_processes[ch.slot].channel[0] = -1;
+            break;
         }
-
-        ngx_processes[ch.slot].channel[0] = -1;
-        break;
     }
 }
 
--- a/src/os/unix/ngx_recv.c
+++ b/src/os/unix/ngx_recv.c
@@ -11,7 +11,8 @@
 
 #if (NGX_HAVE_KQUEUE)
 
-ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
+ssize_t
+ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
 {
     ssize_t       n;
     ngx_err_t     err;
@@ -40,6 +41,7 @@ ssize_t ngx_unix_recv(ngx_connection_t *
                 return 0;
 
             } else {
+                rev->ready = 0;
                 return NGX_AGAIN;
             }
         }
@@ -77,12 +79,6 @@ ssize_t ngx_unix_recv(ngx_connection_t *
                      * even if kqueue reported about available data
                      */
 
-#if 0
-                    ngx_log_error(NGX_LOG_ALERT, c->log, 0,
-                                  "recv() returned 0 while kevent() reported "
-                                  "%d available bytes", rev->available);
-#endif
-
                     rev->eof = 1;
                     rev->available = 0;
                 }
@@ -126,7 +122,8 @@ ssize_t ngx_unix_recv(ngx_connection_t *
 
 #else /* ! NGX_HAVE_KQUEUE */
 
-ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
+ssize_t
+ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
 {
     ssize_t       n;
     ngx_err_t     err;
--- a/src/os/unix/ngx_send.c
+++ b/src/os/unix/ngx_send.c
@@ -9,7 +9,8 @@
 #include <ngx_event.h>
 
 
-ssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size)
+ssize_t
+ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size)
 {
     ssize_t       n;
     ngx_err_t     err;
--- a/src/os/unix/ngx_solaris_init.c
+++ b/src/os/unix/ngx_solaris_init.c
@@ -16,6 +16,7 @@ char ngx_solaris_version[50];
 static ngx_os_io_t ngx_solaris_io = {
     ngx_unix_recv,
     ngx_readv_chain,
+    ngx_udp_unix_recv,
     ngx_unix_send,
 #if (NGX_HAVE_SENDFILE)
     ngx_solaris_sendfilev_chain,
@@ -57,7 +58,7 @@ ngx_os_specific_init(ngx_log_t *log)
 
     ngx_os_io = ngx_solaris_io;
 
-    return NGX_OK;;
+    return NGX_OK;
 }
 
 
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_udp_recv.c
@@ -0,0 +1,114 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (NGX_HAVE_KQUEUE)
+
+ssize_t
+ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
+{
+    ssize_t       n;
+    ngx_err_t     err;
+    ngx_event_t  *rev;
+
+    rev = c->read;
+
+    do {
+        n = recv(c->fd, buf, size, 0);
+
+        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "recv: fd:%d %d of %d", c->fd, n, size);
+
+        if (n >= 0) {
+            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+                rev->available -= n;
+
+                /*
+                 * rev->available may be negative here because some additional
+                 * bytes may be received between kevent() and recv()
+                 */
+
+                if (rev->available <= 0) {
+                    rev->ready = 0;
+                    rev->available = 0;
+                }
+            }
+
+            return n;
+        }
+
+        err = ngx_socket_errno;
+
+        if (err == NGX_EAGAIN || err == NGX_EINTR) {
+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+                           "recv() not ready");
+            n = NGX_AGAIN;
+
+        } else {
+            n = ngx_connection_error(c, err, "recv() failed");
+            break;
+        }
+
+    } while (err == NGX_EINTR);
+
+    rev->ready = 0;
+
+    if (n == NGX_ERROR){
+        rev->error = 1;
+    }
+
+    return n;
+}
+
+#else /* ! NGX_HAVE_KQUEUE */
+
+ssize_t
+ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
+{
+    ssize_t       n;
+    ngx_err_t     err;
+    ngx_event_t  *rev;
+
+    rev = c->read;
+
+    do {
+        n = recv(c->fd, buf, size, 0);
+
+        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "recv: fd:%d %d of %d", c->fd, n, size);
+
+        if (n >= 0) {
+            return n;
+        }
+
+        err = ngx_socket_errno;
+
+        if (err == NGX_EAGAIN || err == NGX_EINTR) {
+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+                           "recv() not ready");
+            n = NGX_AGAIN;
+
+        } else {
+            n = ngx_connection_error(c, err, "recv() failed");
+            break;
+        }
+
+    } while (err == NGX_EINTR);
+
+    rev->ready = 0;
+
+    if (n == NGX_ERROR){
+        rev->error = 1;
+    }
+
+    return n;
+}
+
+#endif /* NGX_HAVE_KQUEUE */
--- a/src/os/unix/ngx_user.c
+++ b/src/os/unix/ngx_user.c
@@ -43,7 +43,7 @@ ngx_crypt(ngx_pool_t *pool, u_char *key,
     if (err == 0) {
         len = ngx_strlen(value);
 
-        *encrypted = ngx_palloc(pool, len);
+        *encrypted = ngx_pnalloc(pool, len);
         if (*encrypted) {
             ngx_memcpy(*encrypted, value, len + 1);
             return NGX_OK;
@@ -81,7 +81,7 @@ ngx_crypt(ngx_pool_t *pool, u_char *key,
     if (value) {
         len = ngx_strlen(value);
 
-        *encrypted = ngx_palloc(pool, len);
+        *encrypted = ngx_pnalloc(pool, len);
         if (*encrypted) {
             ngx_memcpy(*encrypted, value, len + 1);
         }