changeset 517:8fbdd980b527

Merge with current.
author Maxim Dounin <mdounin@mdounin.ru>
date Mon, 13 Jul 2009 23:56:24 +0400
parents 44a61c599bb2 (current diff) 0001f4fa0501 (diff)
children 40fd8d7b82f9
files auto/lib/pcre/patch.config.in auto/lib/pcre/patch.pcre.c auto/lib/pcre/patch.pcre.in auto/lib/pcre/patch.pcre.in.owc auto/modules auto/sources src/core/ngx_garbage_collector.c src/core/ngx_garbage_collector.h src/http/modules/ngx_http_range_filter_module.c src/http/ngx_http_cache.c src/http/ngx_http_postpone_filter_module.c src/http/ngx_http_request.h src/os/unix/ngx_sunpro_x86.map
diffstat 199 files changed, 17686 insertions(+), 6733 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags
+++ b/.hgtags
@@ -206,3 +206,49 @@ a2a3905c04abe7ff22d33176682cdba5eb6cf186
 a8e3f1441eec1b608b77f39110e16f0b82b983d5 NGINX_0_7_17
 b246022ef4543cf779e3a0e040922d069c109171 NGINX_0_7_18
 4b19c15c95eb9f49f48295d139dcdfd77bb22f38 NGINX_0_7_19
+b4f69f2ef02c188a31125d0774191becd3c3a10d NGINX_0_7_20
+ff86d646f9df4cf9ec14662fe09269d57d9598ea NGINX_0_7_21
+ad0a34a8efa6b727cfb4bbc7d94cab0e882fd87b NGINX_0_7_22
+88d3e895bdf94af51c38b5da5a986ff942dd52c5 NGINX_0_7_23
+9da1d9d94d1860d5a209110197fe4826766faa60 NGINX_0_7_24
+e7dbea1ee115c746298309e05501fc13ce942547 NGINX_0_7_25
+21aff1b3da48c501503d5e47d373c6382413c5e4 NGINX_0_7_26
+dac47e9ef0d5fc6ec8cd1e0ff433c86e96d1a358 NGINX_0_7_27
+fd759445d8a890a9db4ab645581358fdce277908 NGINX_0_7_28
+49a0eb7ce20c1114dd25a73373b9ad5f77d02ed7 NGINX_0_7_29
+dc98ed169c03366ef89869d49da3b21b4b6663fe NGINX_0_7_30
+ce4f9ff90bfa58834c5b0db35395fd980c8c4aa0 NGINX_0_7_31
+6281966854a55e092674b01b6861bd025fe158ee NGINX_0_7_32
+670af56a1158749e82d7b1fce1ce348f8e10472a NGINX_0_7_33
+33394d1255b058295bc596287d249ccd612fb41e NGINX_0_7_34
+15a022ee809bb6234f9ca4993714d9820ef954dd NGINX_0_7_35
+76a79816b77167fb3fc7591b21de449d9cd71322 NGINX_0_7_36
+20962db0117cbc0996dc82ff31f8abc6c3598451 NGINX_0_7_37
+fc5ebf0e5f98c4a7c1c370234e3f9be09b94a2a1 NGINX_0_7_38
+a8424ffa495cf3a21769c567df89bef2a9c0c3fd NGINX_0_7_39
+ca8f7f6cab16b29a7be5b2b789a743bad2d8f0a8 NGINX_0_7_40
+2e2b57743e871f3e1aa5994b693da5d248df5800 NGINX_0_7_41
+bb941a2996a6026f35c76f60ec2cd56c23f34ae5 NGINX_0_7_42
+dcb6b5f9d526e677c2860164d9c03b33caa0bd35 NGINX_0_7_43
+c8cfb6c462efa1a49e51fd193e4c645a5cfb212c NGINX_0_7_44
+9eda3153223b42932fec477da26be67a8a33b781 NGINX_0_7_45
+56baf312c1b57b425c331a325b7875fd92aff5b8 NGINX_0_7_46
+6866b490272ef912cd285ce6edc2c393546ea947 NGINX_0_7_47
+09f0ef15d5446d925a7b556925235563ef7932f7 NGINX_0_7_48
+3a4102b00f9753c6b851becebe0d04996b7ff50e NGINX_0_7_49
+09b45263c8178db911e60992a21df9278736dfe4 NGINX_0_7_50
+f2c6a737327477de6844021269e95333e03eba19 NGINX_0_7_51
+549994537f15f7212b5f09f1f4d6e00f2dc562f9 NGINX_0_7_52
+392c16f2d85815c32add2715607598013ce58014 NGINX_0_7_53
+ed5e10fb40fc02b64d6c5719141e33f74cccd02a NGINX_0_7_54
+6484cbba022236c1cd094abfa117dd3aa1b70181 NGINX_0_7_55
+829f9a66a65923d1765961bf1c5075869d421633 NGINX_0_7_56
+e66f886a83052c6a09dfbd77bfc78310dd7fd2d9 NGINX_0_7_57
+98143f74eb3d9d87babc0934c9cd7c40b7af277a NGINX_0_7_58
+499474178a112350895b502f16a2d1abe1b5b532 NGINX_0_7_59
+f39b9e29530de0464264608c33d84abf2e10b9f2 NGINX_0_8_0
+207ae3ff044452b1fb8ad28951ff566615a51939 NGINX_0_8_1
+13b908d5338a1db8365c33efc6189569bb9d58a7 NGINX_0_8_2
+f0cac61857ae53b080bb44fa899762c901079dee NGINX_0_8_3
+c178dbab489a192d9e40f7747777a0890e35e93b NGINX_0_8_4
+e8b686f230a83ac654d963d1d8e0f96893e0c372 NGINX_0_8_5
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,693 @@
 
+Changes with nginx 0.8.5                                         13 Jul 2009
+
+    *) Bugfix: now nginx allows underscores in a request method.
+
+    *) Bugfix: a 500 error code was returned for invalid login/password 
+       while HTTP Basic authentication on Windows.
+
+    *) Bugfix: ngx_http_perl_module responses did not work in subrequests.
+
+    *) Bugfix: in ngx_http_limit_req_module.
+       Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.4                                         22 Jun 2009
+
+    *) Bugfix: nginx could not be built --without-http-cache; the bug had 
+       appeared in 0.8.3.
+
+
+Changes with nginx 0.8.3                                         19 Jun 2009
+
+    *) Feature: the $upstream_cache_status variable.
+
+    *) Bugfix: nginx could not be built on MacOSX 10.6. the bug had 
+       appeared in 0.8.2.
+
+    *) Bugfix: nginx could not be built --without-http-cache; the bug had 
+       appeared in 0.8.2.
+
+    *) Bugfix: a segmentation fault occurred in worker process, if a 
+       backend 401 error was intercepted and the backend did not set the 
+       "WWW-Authenticate" response header line.
+       Thanks to Eugene Mychlo.
+
+
+Changes with nginx 0.8.2                                         15 Jun 2009
+
+    *) Bugfix: in open_file_cache and proxy/fastcgi cache interaction on 
+       start up.
+
+    *) Bugfix: open_file_cache might cache open file descriptors too long; 
+       the bug had appeared in 0.7.4.
+
+
+Changes with nginx 0.8.1                                         08 Jun 2009
+
+    *) Feature: the "updating" parameter in "proxy_cache_use_stale" and 
+       "fastcgi_cache_use_stale" directives.
+
+    *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request 
+       header lines were passed to backend while caching if no 
+       "proxy_set_header" directive was used with any parameters.
+
+    *) Bugfix: the "Set-Cookie" and "P3P" response header lines were not 
+       hidden while caching if no "proxy_hide_header/fastcgi_hide_header" 
+       directives were used with any parameters.
+
+    *) Bugfix: the ngx_http_image_filter_module did not support GIF87a 
+       format.
+       Thanks to Denis Ilyinyh.
+
+    *) Bugfix: nginx could not be built modules on Solaris 10 and early; 
+       the bug had appeared in 0.7.56.
+
+
+Changes with nginx 0.8.0                                         02 Jun 2009
+
+    *) Feature: the "keepalive_requests" directive.
+
+    *) Feature: the "limit_rate_after" directive.
+       Thanks to Ivan Debnar.
+
+    *) Bugfix: XLST filter did not work in subrequests.
+
+    *) Bugfix: in relative paths handling in nginx/Windows.
+
+    *) Bugfix: in proxy_store, fastcgi_store, proxy_cache, and 
+       fastcgi_cache in nginx/Windows.
+
+    *) Bugfix: in memory allocation error handling.
+       Thanks to Maxim Dounin and Kirill A. Korinskiy.
+
+
+Changes with nginx 0.7.59                                        25 May 2009
+
+    *) Feature: the "proxy_cache_methods" and "fastcgi_cache_methods" 
+       directives.
+
+    *) Bugfix: socket leak; the bug had appeared in 0.7.25.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: a segmentation fault occurred in worker process, 
+       if a request had no body and the $request_body variable was used;
+       the bug had appeared in 0.7.58.
+
+    *) Bugfix: the SSL modules might not built on Solaris and Linux;
+       the bug had appeared in 0.7.56.
+
+    *) Bugfix: ngx_http_xslt_filter_module responses were not handled by 
+       SSI, charset, and gzip filters.
+
+    *) Bugfix: a "charset" directive did not set a charset to 
+       ngx_http_gzip_static_module responses.
+
+
+Changes with nginx 0.7.58                                        18 May 2009
+
+    *) Feature: a "listen" directive of the mail proxy module supports IPv6.
+
+    *) Feature: the "image_filter_jpeg_quality" directive.
+
+    *) Feature: the "client_body_in_single_buffer" directive.
+
+    *) Feature: the $request_body variable.
+
+    *) Bugfix: in ngx_http_autoindex_module in file name links having a ":" 
+       symbol in the name.
+
+    *) Bugfix: "make upgrade" procedure did not work; the bug had appeared 
+       in 0.7.53.
+       Thanks to Denis F. Latypoff.
+
+
+Changes with nginx 0.7.57                                        12 May 2009
+
+    *) Bugfix: a floating-point fault occurred in worker process, if the 
+       ngx_http_image_filter_module errors were redirected to named 
+       location; the bug had appeared in 0.7.56.
+
+
+Changes with nginx 0.7.56                                        11 May 2009
+
+    *) Feature: nginx/Windows supports IPv6 in a "listen" directive of the 
+       HTTP module.
+
+    *) Bugfix: in ngx_http_image_filter_module.
+
+
+Changes with nginx 0.7.55                                        06 May 2009
+
+    *) Bugfix: the http_XXX parameters in "proxy_cache_use_stale" and 
+       "fastcgi_cache_use_stale" directives did not work.
+
+    *) Bugfix: fastcgi cache did not cache header only responses.
+
+    *) Bugfix: of "select() failed (9: Bad file descriptor)" error in 
+       nginx/Unix and "select() failed (10038: ...)" error in nginx/Windows.
+
+    *) Bugfix: a segmentation fault might occur in worker process, if an 
+       "debug_connection" directive was used; the bug had appeared in 
+       0.7.54.
+
+    *) Bugfix: fix ngx_http_image_filter_module building errors.
+
+    *) Bugfix: the files bigger than 2G could not be transferred using 
+       $r->sendfile.
+       Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.7.54                                        01 May 2009
+
+    *) Feature: the ngx_http_image_filter_module.
+
+    *) Feature: the "proxy_ignore_headers" and "fastcgi_ignore_headers" 
+       directives.
+
+    *) Bugfix: a segmentation fault might occur in worker process, if an 
+       "open_file_cache_errors off" directive was used; the bug had 
+       appeared in 0.7.53.
+
+    *) Bugfix: the "port_in_redirect off" directive did not work; the bug 
+       had appeared in 0.7.39.
+
+    *) Bugfix: improve handling of "select" method errors.
+
+    *) Bugfix: of "select() failed (10022: ...)" error in nginx/Windows.
+
+    *) Bugfix: in error text descriptions in nginx/Windows; the bug had 
+       appeared in 0.7.53.
+
+
+Changes with nginx 0.7.53                                        27 Apr 2009
+
+    *) Change: now a log set by --error-log-path is created from the very 
+       start-up.
+
+    *) Feature: now the start up errors and warnings are outputted to an 
+       error_log and stderr.
+
+    *) Feature: the empty --prefix= configure parameter forces nginx to use 
+       a directory where it was run as prefix.
+
+    *) Feature: the -p switch.
+
+    *) Feature: the -s switch on Unix platforms.
+
+    *) Feature: the -? and -h switches.
+       Thanks to Jerome Loyet.
+
+    *) Feature: now switches may be set in condensed form.
+
+    *) Bugfix: nginx/Windows did not work if configuration file was given 
+       by the -c switch.
+
+    *) Bugfix: temporary files might be not removed if the "proxy_store", 
+       "fastcgi_store", "proxy_cache", or "fastcgi_cache" were used.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: an incorrect value was passed to mail proxy authentication 
+       server in "Auth-Method" header line; the bug had appeared
+       in 0.7.34.
+       Thanks to Simon Lecaille.
+
+    *) Bugfix: system error text descriptions were not logged on Linux;
+       the bug had appeared in 0.7.45.
+
+    *) Bugfix: the "fastcgi_cache_min_uses" directive did not work.
+       Thanks to Andrew Vorobyoff.
+
+
+Changes with nginx 0.7.52                                        20 Apr 2009
+
+    *) Feature: the first native Windows binary release.
+
+    *) Bugfix: in processing HEAD method while caching.
+
+    *) Bugfix: in processing the "If-Modified-Since", "If-Range", etc. 
+       client request header lines while caching.
+
+    *) Bugfix: now the "Set-Cookie" and "P3P" header lines are hidden in 
+       cacheable responses.
+
+    *) Bugfix: if nginx was built with the ngx_http_perl_module and with a 
+       perl which supports threads, then during a master process exit the 
+       message "panic: MUTEX_LOCK" might be issued.
+
+    *) Bugfix: nginx could not be built --without-http-cache; the bug had 
+       appeared in 0.7.48.
+
+    *) Bugfix: nginx could not be built on platforms different from i386, 
+       amd64, sparc, and ppc; the bug had appeared in 0.7.42.
+
+
+Changes with nginx 0.7.51                                        12 Apr 2009
+
+    *) Feature: the "try_files" directive supports a response code in the 
+       fallback parameter.
+
+    *) Feature: now any response code can be used in the "return" directive.
+
+    *) Bugfix: the "error_page" directive made an external redirect without 
+       query string; the bug had appeared in 0.7.44.
+
+    *) Bugfix: if servers listened on several defined explicitly addresses, 
+       then virtual servers might not work; the bug had appeared in 0.7.39.
+
+
+Changes with nginx 0.7.50                                        06 Apr 2009
+
+    *) Bugfix: the $arg_... variables did not work; the bug had appeared in 
+       0.7.49.
+
+
+Changes with nginx 0.7.49                                        06 Apr 2009
+
+    *) Bugfix: a segmentation fault might occur in worker process, if the 
+       $arg_... variables were used; the bug had appeared in 0.7.48.
+
+
+Changes with nginx 0.7.48                                        06 Apr 2009
+
+    *) Feature: the "proxy_cache_key" directive.
+
+    *) Bugfix: now nginx takes into account the "X-Accel-Expires", 
+       "Expires", and "Cache-Control" header lines in a backend response.
+
+    *) Bugfix: now nginx caches responses for the GET requests only.
+
+    *) Bugfix: the "fastcgi_cache_key" directive was not inherited.
+
+    *) Bugfix: the $arg_... variables did not work with SSI subrequests.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: nginx could not be built with uclibc library.
+       Thanks to Timothy Redaelli.
+
+    *) Bugfix: nginx could not be built on OpenBSD; the bug had 
+       appeared in 0.7.46.
+
+
+Changes with nginx 0.7.47                                        01 Apr 2009
+
+    *) Bugfix: nginx could not be built on FreeBSD 6 and early versions; 
+       the bug had appeared in 0.7.46.
+
+    *) Bugfix: nginx could not be built on MacOSX; the bug had 
+       appeared in 0.7.46.
+
+    *) Bugfix: if the "max_size" parameter was set, then the cache manager 
+       might purge a whole cache; the bug had appeared in 0.7.46.
+
+    *) Change: a segmentation fault might occur in worker process, if the 
+       "proxy_cache"/"fastcgi_cache" and the "proxy_cache_valid"/ 
+       "fastcgi_cache_valid" were set on different levels; the bug had 
+       appeared in 0.7.46.
+
+    *) Bugfix: a segmentation fault might occur in worker process, if a 
+       request was redirected to a proxied or FastCGI server via error_page 
+       or try_files; the bug had appeared in 0.7.44.
+
+
+Changes with nginx 0.7.46                                        30 Mar 2009
+
+    *) Bugfix: the previous release tarball was incorrect.
+
+
+Changes with nginx 0.7.45                                        30 Mar 2009
+
+    *) Change: now the "proxy_cache" and the "proxy_cache_valid" directives 
+       can be set on different levels.
+
+    *) Change: the "clean_time" parameter of the "proxy_cache_path" 
+       directive is canceled.
+
+    *) Feature: the "max_size" parameter of the "proxy_cache_path" 
+       directive.
+
+    *) Feature: the ngx_http_fastcgi_module preliminary cache support.
+
+    *) Feature: now on shared memory allocation errors directive and zone 
+       names are logged.
+
+    *) Bugfix: the directive "add_header last-modified ''" did not delete a 
+       "Last-Modified" response header line; the bug had appeared in 0.7.44.
+
+    *) Bugfix: a relative path in the "auth_basic_user_file" directive 
+       given without variables did not work; the bug had appeared in 
+       0.7.44.
+       Thanks to Jerome Loyet.
+
+    *) Bugfix: in an "alias" directive given using variables without 
+       references to captures of regular expressions; the bug had appeared 
+       in 0.7.42.
+
+
+Changes with nginx 0.7.44                                        23 Mar 2009
+
+    *) Feature: the ngx_http_proxy_module preliminary cache support.
+
+    *) Feature: the --with-pcre option in the configure.
+
+    *) Feature: the "try_files" directive is now allowed on the server 
+       block level.
+
+    *) Bugfix: the "try_files" directive handled incorrectly a query string 
+       in a fallback parameter.
+
+    *) Bugfix: the "try_files" directive might test incorrectly directories.
+
+    *) Bugfix: if there is the single server for given address:port pair, 
+       then captures in regular expressions in a "server_name" directive 
+       did not work.
+
+
+Changes with nginx 0.7.43                                        18 Mar 2009
+
+    *) Bugfix: a request was handled incorrectly, if a "root" directive 
+       used variables; the bug had appeared in 0.7.42.
+
+    *) Bugfix: if a server listened on wildcard address, then the 
+       $server_addr variable value was "0.0.0.0"; the bug had appeared in 
+       0.7.36.
+
+
+Changes with nginx 0.7.42                                        16 Mar 2009
+
+    *) Change: now the "Invalid argument" error returned by 
+       setsockopt(TCP_NODELAY) on Solaris, is ignored.
+
+    *) Change: now if a file specified in a "auth_basic_user_file" 
+       directive is absent, then the 403 error is returned instead of the 
+       500 one.
+
+    *) Feature: the "auth_basic_user_file" directive supports variables.
+       Thanks to Kirill A. Korinskiy.
+
+    *) Feature: the "listen" directive supports the "ipv6only" parameter. 
+       Thanks to Zhang Hua.
+
+    *) Bugfix: in an "alias" directive with references to captures of 
+       regular expressions; the bug had appeared in 0.7.40.
+
+    *) Bugfix: compatibility with Tru64 UNIX.
+       Thanks to Dustin Marquess.
+
+    *) Bugfix: nginx could not be built without PCRE library; the bug had 
+       appeared in 0.7.41.
+
+
+Changes with nginx 0.7.41                                        11 Mar 2009
+
+    *) Bugfix: a segmentation fault might occur in worker process, if a 
+       "server_name" or a "location" directives had captures in regular 
+       expressions; the issue had appeared in 0.7.40.
+       Thanks to Vladimir Sopot.
+
+
+Changes with nginx 0.7.40                                        09 Mar 2009
+
+    *) Feature: the "location" directive supports captures in regular 
+       expressions.
+
+    *) Feature: an "alias" directive with capture references may be used 
+       inside a location given by a regular expression with captures.
+
+    *) Feature: the "server_name" directive supports captures in regular 
+       expressions.
+
+    *) Workaround: the ngx_http_autoindex_module did not show the trailing 
+       slash in directories on XFS filesystem; the issue had appeared in 
+       0.7.15.
+       Thanks to Dmitry Kuzmenko.
+
+
+Changes with nginx 0.7.39                                        02 Mar 2009
+
+    *) Bugfix: large response with SSI might hang, if gzipping was enabled; 
+       the bug had appeared in 0.7.28.
+       Thanks to Artem Bokhan.
+
+    *) Bugfix: a segmentation fault might occur in worker process, if short 
+       static variants are used in a "try_files" directive.
+
+
+Changes with nginx 0.7.38                                        23 Feb 2009
+
+    *) Feature: authentication failures logging.
+
+    *) Bugfix: name/password in auth_basic_user_file were ignored after odd 
+       number of empty lines.
+       Thanks to Alexander Zagrebin.
+
+    *) Bugfix: a segmentation fault occurred in a master process, if long 
+       path was used in unix domain socket; the bug had appeared in 0.7.36.
+
+
+Changes with nginx 0.7.37                                        21 Feb 2009
+
+    *) Bugfix: directives using upstreams did not work; the bug had 
+       appeared in 0.7.36.
+
+
+Changes with nginx 0.7.36                                        21 Feb 2009
+
+    *) Feature: a preliminary IPv6 support; the "listen" directive of the 
+       HTTP module supports IPv6.
+
+    *) Bugfix: the $ancient_browser variable did not work for browsers 
+       preset by a "modern_browser" directives.
+
+
+Changes with nginx 0.7.35                                        16 Feb 2009
+
+    *) Bugfix: a "ssl_engine" directive did not use a SSL-accelerator for 
+       asymmetric ciphers.
+       Thanks to Marcin Gozdalik.
+
+    *) Bugfix: a "try_files" directive set MIME type depending on an 
+       original request extension.
+
+    *) Bugfix: "*domain.tld" names were handled incorrectly in 
+       "server_name", "valid_referers", and "map" directives, if 
+       ".domain.tld" and ".subdomain.domain.tld" wildcards were used; 
+       the bug had appeared in 0.7.9.
+
+
+Changes with nginx 0.7.34                                        10 Feb 2009
+
+    *) Feature: the "off" parameter of the "if_modified_since" directive.
+
+    *) Feature: now nginx sends an HELO/EHLO command after a XCLIENT 
+       command.
+       Thanks to Maxim Dounin.
+
+    *) Feature: Microsoft specific "AUTH LOGIN with User Name" mode support 
+       in mail proxy server.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: in a redirect rewrite directive original arguments were 
+       concatenated with new arguments by a "?" rather than an "&";
+       the bug had appeared in 0.1.18.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: nginx could not be built on AIX.
+
+
+Changes with nginx 0.7.33                                        02 Feb 2009
+
+    *) Bugfix: a double response might be returned if the epoll or rtsig 
+       methods are used and a redirect was returned to a request with 
+       body.
+       Thanks to Eden Li.
+
+    *) Bugfix: the $sent_http_location variable was empty for some 
+       redirects types.
+
+    *) Bugfix: a segmentation fault might occur in worker process if 
+       "resolver" directive was used in SMTP proxy.
+
+
+Changes with nginx 0.7.32                                        26 Jan 2009
+
+    *) Feature: now a directory existence testing can be set explicitly in 
+       the "try_files" directive.
+
+    *) Bugfix: fastcgi_store stored files not always.
+
+    *) Bugfix: in geo ranges.
+
+    *) Bugfix: in shared memory allocations if nginx was built without 
+       debugging.
+       Thanks to Andrey Kvasov.
+
+
+Changes with nginx 0.7.31                                        19 Jan 2009
+
+    *) Change: now the "try_files" directive tests files only and ignores 
+       directories.
+
+    *) Feature: the "fastcgi_split_path_info" directive.
+
+    *) Bugfixes in an "Expect" request header line support.
+
+    *) Bugfixes in geo ranges.
+
+    *) Bugfix: in a miss case ngx_http_memcached_module returned the "END" 
+       line as response body instead of default 404 page body; the bug had 
+       appeared in 0.7.18.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: while SMTP proxying nginx issued message "250 2.0.0 OK" 
+       instead of "235 2.0.0 OK"; the bug had appeared in 0.7.22.
+       Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.7.30                                        24 Dec 2008
+
+    *) Bugfix: a segmentation fault occurred in worker process, if 
+       variables were used in the "fastcgi_pass" or "proxy_pass" directives 
+       and host name must be resolved; the bug had appeared in 0.7.29.
+
+
+Changes with nginx 0.7.29                                        24 Dec 2008
+
+    *) Bugfix: the "fastcgi_pass" and "proxy_pass" directives did not 
+       support variables if unix domain sockets were used.
+
+    *) Bugfixes in subrequest processing; the bugs had appeared in 0.7.25.
+
+    *) Bugfix: a "100 Continue" response was issued for HTTP/1.0 
+       requests;
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: in memory allocation in the ngx_http_gzip_filter_module on 
+       Cygwin.
+
+
+Changes with nginx 0.7.28                                        22 Dec 2008
+
+    *) Change: in memory allocation in the ngx_http_gzip_filter_module.
+
+    *) Change: the default "gzip_buffers" directive values have been 
+       changed to 32 4k or 16 8k from 4 4k/8k.
+
+
+Changes with nginx 0.7.27                                        15 Dec 2008
+
+    *) Feature: the "try_files" directive.
+
+    *) Feature: variables support in the "fastcgi_pass" directive.
+
+    *) Feature: now the $geo variable may get an address from a 
+       variable.
+       Thanks to Andrei Nigmatulin.
+
+    *) Feature: now a location's modifier may be used without space before 
+       name.
+
+    *) Feature: the $upstream_response_length variable.
+
+    *) Bugfix: now a "add_header" directive does not add an empty value.
+
+    *) Bugfix: if zero length static file was requested, then nginx just 
+       closed connection; the bug had appeared in 0.7.25.
+
+    *) Bugfix: a MOVE method could not move file in non-existent directory.
+
+    *) Bugfix: a segmentation fault occurred in worker process, if no one 
+       named location was defined in server, but some one was used in an 
+       error_page directive.
+       Thanks to Sergey Bochenkov.
+
+
+Changes with nginx 0.7.26                                        08 Dec 2008
+
+    *) Bugfix: in subrequest processing; the bug had appeared in 0.7.25.
+
+
+Changes with nginx 0.7.25                                        08 Dec 2008
+
+    *) Change: in subrequest processing.
+
+    *) Change: now POSTs without "Content-Length" header line are allowed.
+
+    *) Bugfix: now the "limit_req" and "limit_conn" directives log a 
+       prohibition reason.
+
+    *) Bugfix: in the "delete" parameter of the "geo" directive.
+
+
+Changes with nginx 0.7.24                                        01 Dec 2008
+
+    *) Feature: the "if_modified_since" directive.
+
+    *) Bugfix: nginx did not process a FastCGI server response, if the 
+       server send too many messages to stderr before response.
+
+    *) Bugfix: the "$cookie_..." variables did not work in the SSI and the 
+       perl module.
+
+
+Changes with nginx 0.7.23                                        27 Nov 2008
+
+    *) Feature: the "delete" and "ranges" parameters in the "geo" directive.
+
+    *) Feature: speeding up loading of geo base with large number of values.
+
+    *) Feature: decrease of memory required for geo base load.
+
+
+Changes with nginx 0.7.22                                        20 Nov 2008
+
+    *) Feature: the "none" parameter in the "smtp_auth" directive.
+       Thanks to Maxim Dounin.
+
+    *) Feature: the "$cookie_..." variables.
+
+    *) Bugfix: the "directio" directive did not work in XFS filesystem.
+
+    *) Bugfix: the resolver did not understand big DNS responses.
+       Thanks to Zyb.
+
+
+Changes with nginx 0.7.21                                        11 Nov 2008
+
+    *) Changes in the ngx_http_limit_req_module.
+
+    *) Feature: the EXSLT support in the ngx_http_xslt_module.
+       Thanks to Denis F. Latypoff.
+
+    *) Workaround: compatibility with glibc 2.3.
+       Thanks to Eric Benson and Maxim Dounin.
+
+    *) Bugfix: nginx could not run on MacOSX 10.4 and earlier; the bug had 
+       appeared in 0.7.6.
+
+
+Changes with nginx 0.7.20                                        10 Nov 2008
+
+    *) Changes in the ngx_http_gzip_filter_module.
+
+    *) Feature: the ngx_http_limit_req_module.
+
+    *) Bugfix: worker processes might exit on a SIGBUS signal on sparc and 
+       ppc platforms; the bug had appeared in 0.7.3.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: the "proxy_pass http://host/some:uri" directives did not 
+       work; the bug had appeared in 0.7.12.
+
+    *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" 
+       error.
+
+    *) Bugfix: the ngx_http_secure_link_module did not work inside 
+       locations, whose names are less than 3 characters.
+
+    *) Bugfix: $server_addr variable might have no value.
+
+
 Changes with nginx 0.7.19                                        13 Oct 2008
 
     *) Bugfix: version number update.
@@ -169,7 +858,7 @@ Changes with nginx 0.7.9                
 
     *) Bugfix: if the "server_name", "valid_referers", and "map" directives 
        used an "*.domain.tld" wildcard and exact name "domain.tld" was not 
-       set, then the exact name was matched by the wildcard; the bugs had 
+       set, then the exact name was matched by the wildcard; the bug had 
        appeared in 0.3.18.
 
 
@@ -1716,7 +2405,7 @@ Changes with nginx 0.3.55               
     *) Bugfix: if the request contained "//" or "/./" and escaped symbols 
        after them, then the proxied request was sent unescaped.
 
-    *) Bugfix: the $r->headers_in("Cookie") of the ngx_http_perl_module now 
+    *) Bugfix: the $r->header_in("Cookie") of the ngx_http_perl_module now 
        returns all "Cookie" header lines.
 
     *) Bugfix: a segmentation fault occurred if 
@@ -3230,8 +3919,8 @@ Changes with nginx 0.1.18               
     *) Bugfix: the proxy_set_x_var and fastcgi_set_var directives were not 
        inherited.
 
-    *) Bugfix: in the redirect rewrite directive the arguments were 
-       concatenated with URI by the "&" rather than the "?".
+    *) Bugfix: in a redirect rewrite directive arguments were concatenated 
+       with URI by an "&" rather than a "?".
 
     *) Bugfix: the lines without trailing ";" in the file being included by 
        the ngx_http_geo_module were silently ignored.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,695 @@
 
+éÚÍÅÎÅÎÉÑ × nginx 0.8.5                                           13.07.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ nginx ÒÁÚÒÅÛÁÅÔ ÐÏÄÞ£ÒËÉ×ÁÎÉÑ × ÍÅÔÏÄÅ ÚÁÐÒÏÓÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ HTTP Basic-ÁÕÔÅÎÔÉÆÉËÁÃÉÉ ÎÁ Windows 
+       ÄÌÑ ÎÅ×ÅÒÎÙÈ ÉÍÅÎÉ/ÐÁÒÏÌÑ ×ÏÚ×ÒÁÝÁÌÁÓØ 500-ÁÑ ÏÛÉÂËÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÔ×ÅÔÙ ÍÏÄÕÌÑ ngx_http_perl_module ÎÅ ÒÁÂÏÔÁÌÉ × 
+       ÐÏÄÚÁÐÒÏÓÁÈ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÍÏÄÕÌÅ ngx_http_limit_req_module.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.8.4                                           22.06.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ Ó ÐÁÒÁÍÅÔÒÏÍ --without-http-cache; 
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.8.3.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.8.3                                           19.06.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÁÑ $upstream_cache_status.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÎÁ MacOSX 10.6.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ Ó ÐÁÒÁÍÅÔÒÏÍ --without-http-cache; 
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.8.2.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ÉÓÐÏÌØÚÏ×ÁÌÓÑ ÐÅÒÅÈ×ÁÔ 401 ÏÛÉÂËÉ ÏÔ ÂÜËÅÎÄÁ É 
+       ÂÜËÅÎÄ ÎÅ ×ÏÚ×ÒÁÝÁÌ ÓÔÒÏËÕ "WWW-Authenticate" × ÚÁÇÏÌÏ×ËÅ ÏÔ×ÅÔÁ, ÔÏ 
+       × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation fault.
+       óÐÁÓÉÂÏ å×ÇÅÎÉÀ íÙÞÌÏ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.8.2                                           15.06.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ×Ï ×ÚÁÉÍÏÄÅÊÓÔ×ÉÉ open_file_cache É proxy/fastcgi ËÜÛÁ 
+       ÎÁ ÓÔÁÒÔÅ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: open_file_cache ÍÏÇ ËÜÛÉÒÏ×ÁÔØ ÏÔËÒÙÔÙÅ ÆÁÊÌÙ ÏÞÅÎØ 
+       ÄÏÌÇÏ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.4.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.8.1                                           08.06.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒ updating × ÄÉÒÅËÔÉ×ÁÈ proxy_cache_use_stale É 
+       fastcgi_cache_use_stale.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÓÔÒÏËÉ "If-Modified-Since", "If-Range" É ÉÍ ÐÏÄÏÂÎÙÅ × 
+       ÚÁÇÏÌÏ×ËÅ ÚÁÐÒÏÓÁ ËÌÉÅÎÔÁ ÐÅÒÅÄÁ×ÁÌÉÓØ ÂÜËÅÎÄÕ ÐÒÉ ËÜÛÉÒÏ×ÁÎÉÉ, ÅÓÌÉ 
+       ÎÅ ÉÓÐÏÌØÚÏ×ÁÌÁÓØ ÄÉÒÅËÔÉ×Á proxy_set_header Ó ÌÀÂÙÍÉ ÐÁÒÁÍÅÔÒÁÍÉ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÓÔÒÏËÉ "Set-Cookie" É "P3P" × ÚÁÇÏÌÏ×ËÅ ÏÔ×ÅÔÁ ÂÜËÅÎÄÁ 
+       ÎÅ ÓËÒÙ×ÁÌÉÓØ ÐÒÉ ËÜÛÉÒÏ×ÁÎÉÉ, ÅÓÌÉ ÎÅ ÉÓÐÏÌØÚÏ×ÁÌÉÓØ ÄÉÒÅËÔÉ×Ù 
+       proxy_hide_header/fastcgi_hide_header Ó ÌÀÂÙÍÉ ÐÁÒÁÍÅÔÒÁÍÉ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÏÄÕÌØ ngx_http_image_filter_module ÎÅ ÐÏÎÉÍÁÌ ÆÏÒÍÁÔ 
+       GIF87a.
+       óÐÁÓÉÂÏ äÅÎÉÓÕ éÌØÉÎÙÈ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÎÁ Solaris 10 É ÂÏÌÅÅ ÒÁÎÎÉÈ; ÏÛÉÂËÁ 
+       ÐÏÑ×ÉÌÁÓØ × 0.7.56.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.8.0                                           02.06.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á keepalive_requests.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á limit_rate_after.
+       óÐÁÓÉÂÏ Ivan Debnar.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: XSLT-ÆÉÌØÔÒ ÎÅ ÒÁÂÏÔÁÌ × ÐÏÄÚÁÐÒÏÓÁÈ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÂÒÁÂÏÔËÅ ÏÔÎÏÓÉÔÅÌØÎÙÈ ÐÕÔÅÊ × nginx/Windows.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × proxy_store, fastcgi_store, proxy_cache É 
+       fastcgi_cache × nginx/Windows.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÏÂÒÁÂÏÔËÅ ÏÛÉÂÏË ×ÙÄÅÌÅÎÉÑ ÐÁÍÑÔÉ.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ É ëÉÒÉÌÌÕ ëÏÒÉÎÓËÏÍÕ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.59                                          25.05.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù proxy_cache_methods É fastcgi_cache_methods.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÕÔÅÞËÉ ÓÏËÅÔÏ×; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.25.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÅÒÅÍÅÎÎÏÊ $request_body × ÒÁÂÏÞÅÍ 
+       ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation fault, ÅÓÌÉ × ÚÁÐÒÏÓÅ ÎÅ ÂÙÌÏ ÔÅÌÁ; 
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.58.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: SSL-ÍÏÄÕÌÉ ÍÏÇÌÉ ÎÅ ÓÏÂÉÒÁÔØÓÑ ÎÁ Solaris É Linux; 
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.56.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÔ×ÅÔÙ ÍÏÄÕÌÑ ngx_http_xslt_filter_module ÎÅ 
+       ÏÂÒÁÂÁÔÙ×ÁÌÉÓØ SSI-, charset- É gzip-ÆÉÌØÔÒÁÍÉ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á charset ÎÅ ÓÔÁ×ÉÌÁ ËÏÄÉÒÏ×ËÕ ÄÌÑ ÏÔ×ÅÔÏ× 
+       ÍÏÄÕÌÑ ngx_http_gzip_static_module.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.58                                          18.05.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á listen ÐÏÞÔÏ×ÏÇÏ ÐÒÏËÓÉ-ÓÅÒ×ÅÒÁ ÐÏÄÄÅÒÖÉ×ÁÅÔ 
+       IPv6.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á image_filter_jpeg_quality.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á client_body_in_single_buffer.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÁÑ $request_body.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÍÏÄÕÌÅ ngx_http_autoindex_module × ÓÓÙÌËÁÈ ÎÁ ÉÍÅÎÁ 
+       ÆÁÊÌÏ×, ÓÏÄÅÒÖÁÝÉÈ ÓÉÍ×ÏÌ ":".
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÏÃÅÄÕÒÁ "make upgrade" ÎÅ ÒÁÂÏÔÁÌÁ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ 
+       × 0.7.53.
+       óÐÁÓÉÂÏ äÅÎÉÓÕ ìÁÔÙÐÏ×Õ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.57                                          12.05.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÐÅÒÅÎÁÐÒÁ×ÌÅÎÉÉ ÏÛÉÂÏË ÍÏÄÕÌÑ 
+       ngx_http_image_filter_module × ÉÍÅÎÏ×ÁÎÎÙÊ location × ÒÁÂÏÞÅÍ 
+       ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ floating-point fault; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.56.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.56                                          11.05.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: nginx/Windows ÐÏÄÄÅÒÖÉ×ÁÅÔ IPv6 × ÄÉÒÅËÔÉ×Å listen 
+       ÍÏÄÕÌÑ HTTP.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÍÏÄÕÌÅ ngx_http_image_filter_module.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.55                                          06.05.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒÙ http_XXX × ÄÉÒÅËÔÉ×ÁÈ proxy_cache_use_stale É 
+       fastcgi_cache_use_stale ÎÅ ÒÁÂÏÔÁÌÉ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: fastcgi ËÜÛ ÎÅ ËÜÛÉÒÏ×ÁÌ ÏÔ×ÅÔÙ, ÓÏÓÔÏÑÝÉÅ ÔÏÌØËÏ ÉÚ 
+       ÚÁÇÏÌÏ×ËÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÛÉÂËÉ "select() failed (9: Bad file descriptor)" × 
+       nginx/Unix É "select() failed (10038: ...)" × nginx/Windows.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÄÉÒÅËÔÉ×Ù debug_connection × ÒÁÂÏÞÅÍ 
+       ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.54.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÓÂÏÒËÅ ÍÏÄÕÌÑ ngx_http_image_filter_module.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÆÁÊÌÙ ÂÏÌØÛÅ 2G ÎÅ ÐÅÒÅÄÁ×ÁÌÉÓØ Ó ÉÓÐÏÌØÚÏ×ÁÎÉÅÍ 
+       $r->sendfile.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.54                                          01.05.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÍÏÄÕÌØ ngx_http_image_filter_module.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù proxy_ignore_headers É fastcgi_ignore_headers.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÅÒÅÍÅÎÎÙÈ "open_file_cache_errors 
+       on" × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault; ÏÛÉÂËÁ 
+       ÐÏÑ×ÉÌÁÓØ × 0.7.53.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á "port_in_redirect off" ÎÅ ÒÁÂÏÔÁÌÁ; ÏÛÉÂËÁ 
+       ÐÏÑ×ÉÌÁÓØ × 0.7.39.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÕÌÕÞÛÅÎÉÅ ÏÂÒÁÂÏÔËÉ ÏÛÉÂÏË ÍÅÔÏÄÁ select.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÛÉÂËÉ "select() failed (10022: ...)" × nginx/Windows.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÔÅËÓÔÏ×ÙÈ ÓÏÏÂÝÅÎÉÑÈ Ï ÏÛÉÂËÁÈ × nginx/Windows; 
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.53.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.53                                          27.04.2009
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÌÏÇ, ÕËÁÚÁÎÎÙÊ × --error-log-path, ÓÏÚÄÁ£ÔÓÑ Ó 
+       ÓÁÍÏÇÏ ÎÁÞÁÌÁ ÒÁÂÏÔÙ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÏÛÉÂËÉ É ÐÒÅÄÕÐÒÅÖÄÅÎÉÑ ÐÒÉ ÓÔÁÒÔÅ ÚÁÐÉÓÙ×ÁÀÔÓÑ × 
+       error_log É ×Ù×ÏÄÑÔÓÑ ÎÁ stderr.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÒÉ ÓÂÏÒËÅ Ó ÐÕÓÔÙÍ ÐÁÒÁÍÅÔÒÏÍ --prefix= nginx 
+       ÉÓÐÏÌØÚÕÅÔ ËÁË ÐÒÅÆÉËÓ ËÁÔÁÌÏÇ, × ËÏÔÏÒÏÍ ÏÎ ÂÙÌ ÚÁÐÕÝÅÎ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ËÌÀÞ -p.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ËÌÀÞ -s ÎÁ Unix-ÐÌÁÔÆÏÒÍÁÈ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ËÌÀÞÉ -? É -h.
+       óÐÁÓÉÂÏ Jerome Loyet.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ËÌÀÞÉ ÍÏÖÎÏ ÚÁÄÁ×ÁÔØ × ÓÖÁÔÏÊ ÆÏÒÍÅ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx/Windows ÎÅ ÒÁÂÏÔÁÌ, ÅÓÌÉ ÆÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÂÙÌ 
+       ÚÁÄÁÎ ËÌÀÞÏÍ -c.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÄÉÒÅËÔÉ× proxy_store, fastcgi_store, 
+       proxy_cache ÉÌÉ fastcgi_cache ×ÒÅÍÅÎÎÙÅ ÆÁÊÌÙ ÍÏÇÌÉ ÎÅ ÕÄÁÌÑÔØÓÑ.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÚÁÇÏÌÏ×ËÅ Auth-Method ÚÁÐÒÏÓÁ ÓÅÒ×ÅÒÕ ÁÕÔÅÎÔÉÆÉËÁÃÉÉ 
+       ÐÏÞÔÏ×ÏÇÏ ÐÒÏËÓÉ-ÓÅÒ×ÅÒÁ ÐÅÒÅÄÁ×ÁÌÏÓØ ÎÅ×ÅÒÎÏÅ ÚÎÁÞÅÎÉÅ; ÏÛÉÂËÁ 
+       ÐÏÑ×ÉÌÁÓØ × 0.7.34.
+       óÐÁÓÉÂÏ Simon Lecaille.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÌÏÇÇÉÒÏ×ÁÎÉÉ ÎÁ Linux ÎÅ ÐÉÓÁÌÉÓØ ÔÅËÓÔÏ×ÙÅ 
+       ÏÐÉÓÁÎÉÑ ÓÉÓÔÅÍÎÙÈ ÏÛÉÂÏË; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.45.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á fastcgi_cache_min_uses ÎÅ ÒÁÂÏÔÁÌÁ.
+       óÐÁÓÉÂÏ áÎÄÒÅÀ ÷ÏÒÏÂØ£×Õ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.52                                          20.04.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÅÒ×ÁÑ ÂÉÎÁÒÎÁÑ ×ÅÒÓÉÑ ÐÏÄ Windows.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ËÏÒÒÅËÔÎÁÑ ÏÂÒÁÂÏÔËÁ ÍÅÔÏÄÁ HEAD ÐÒÉ ËÜÛÉÒÏ×ÁÎÉÉ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ËÏÒÒÅËÔÎÁÑ ÏÂÒÁÂÏÔËÁ ÓÔÒÏË "If-Modified-Since", 
+       "If-Range" É ÉÍ ÐÏÄÏÂÎÙÈ × ÚÁÇÏÌÏ×ËÅ ÚÁÐÒÏÓÁ ËÌÉÅÎÔÁ ÐÒÉ ËÜÛÉÒÏ×ÁÎÉÉ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÓÔÒÏËÉ "Set-Cookie" É "P3P" ÓËÒÙ×ÁÀÔÓÑ × 
+       ÚÁÇÏÌÏ×ËÅ ÏÔ×ÅÔÁ ÄÌÑ ÚÁËÜÛÉÒÏ×ÁÎÎÙÈ ÏÔ×ÅÔÏ×.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ nginx ÂÙÌ ÓÏÂÒÁÎ Ó ÍÏÄÕÌÅÍ ngx_http_perl_module É 
+       perl ÐÏÄÄÅÒÖÉ×ÁÌ ÐÏÔÏËÉ, ÔÏ ÐÒÉ ×ÙÈÏÄÅ ÏÓÎÏ×ÎÏÇÏ ÐÒÏÃÅÓÓÁ ÍÏÇÌÁ 
+       ×ÙÄÁ×ÁÔØÓÑ ÏÛÉÂËÁ "panic: MUTEX_LOCK".
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ Ó ÐÁÒÁÍÅÔÒÏÍ --without-http-cache; 
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.48.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÎÁ ÐÌÁÔÆÏÒÍÁÈ, ÏÔÌÉÞÎÙÈ ÏÔ i386, 
+       amd64, sparc É ppc; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.42.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.51                                          12.04.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á try_files ÐÏÄÄÅÒÖÉ×ÁÅÔ ËÏÄ ÏÔ×ÅÔÁ × ÐÏÓÌÅÄÎÅÍ 
+       ÐÁÒÁÍÅÔÒÅ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ × ÄÉÒÅËÔÉ×Å return ÍÏÖÎÏ ÉÓÐÏÌØÚÏ×ÁÔØ ÌÀÂÏÊ ËÏÄ 
+       ÏÔ×ÅÔÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á error_page ÄÅÌÁÌÁ ×ÎÅÛÎÉÊ ÒÅÄÉÒÅËÔ ÂÅÚ ÓÔÒÏËÉ 
+       ÚÁÐÒÏÓÁ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.44.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ÓÅÒ×ÅÒÁ ÓÌÕÛÁÌÉ ÎÁ ÎÅÓËÏÌØËÉÈ Ñ×ÎÏ ÏÐÉÓÁÎÎÙÈ 
+       ÁÄÒÅÓÁÈ, ÔÏ ×ÉÒÔÕÁÌØÎÙÅ ÓÅÒ×ÅÒÁ ÍÏÇÌÉ ÎÅ ÒÁÂÏÔÁÔØ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ 
+       × 0.7.39.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.50                                          06.04.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÙÅ $arg_... ÎÅ ÒÁÂÏÔÁÌÉ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 
+       0.7.49.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.49                                          06.04.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÅÒÅÍÅÎÎÙÈ $arg_... × ÒÁÂÏÞÅÍ 
+       ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.48.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.48                                          06.04.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á proxy_cache_key.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ nginx ÕÞÉÔÙ×ÁÅÔ ÐÒÉ ËÜÛÉÒÏ×ÁÎÉÉ ÓÔÒÏËÉ 
+       "X-Accel-Expires", "Expires" É "Cache-Control" × ÚÁÇÏÌÏ×ËÅ ÏÔ×ÅÔÁ 
+       ÂÜËÅÎÄÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ nginx ËÜÛÉÒÕÅÔ ÔÏÌØËÏ ÏÔ×ÅÔÙ ÎÁ ÚÁÐÒÏÓÙ GET.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á fastcgi_cache_key ÎÅ ÎÁÓÌÅÄÏ×ÁÌÁÓØ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÙÅ $arg_... ÎÅ ÒÁÂÏÔÁÌÉ Ó SSI-ÐÏÄÚÁÐÒÏÓÁÍÉ.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ Ó ÂÉÂÌÉÏÔÅËÏÊ uclibc.
+       óÐÁÓÉÂÏ Timothy Redaelli.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÎÁ OpenBSD; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ 
+       × 0.7.46.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.47                                          01.04.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÎÁ FreeBSD 6 É ÂÏÌÅÅ ÒÁÎÎÉÈ ×ÅÒÓÉÑÈ; 
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.46.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÎÁ MacOSX; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.46.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ÉÓÐÏÌØÚÏ×ÁÌÓÑ ÐÁÒÁÍÅÔÒ max_size, ÔÏ cache manager 
+       ÍÏÇ ÕÄÁÌÉÔØ ×ÅÓØ ËÜÛ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.46.
+
+    *) éÚÍÅÎÅÎÉÅ: × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault, ÅÓÌÉ 
+       ÄÉÒÅËÔÉ×Ù proxy_cache/fastcgi_cache É proxy_cache_valid/ 
+       fastcgi_cache_valid ÎÅ ÂÙÌÉ ÚÁÄÁÎÙ ÎÁ ÏÄÎÏÍ ÕÒÏ×ÎÅ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ 
+       × 0.7.46.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault ÐÒÉ 
+       ÐÅÒÅÎÁÐÒÁ×ÌÅÎÉÉ ÚÁÐÒÏÓÁ ÐÒÏËÓÉÒÏ×ÁÎÎÏÍÕ ÉÌÉ FastCGI-ÓÅÒ×ÅÒÕ Ó 
+       ÐÏÍÏÝØÀ error_page ÉÌÉ try_files; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.44.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.46                                          30.03.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÁÒÈÉ× ÐÒÅÄÙÄÕÝÅÇÏ ÒÅÌÉÚÁ ÂÙÌ ÎÅ×ÅÒÎÙÍ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.45                                          30.03.2009
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÄÉÒÅËÔÉ×Ù proxy_cache É proxy_cache_valid ÍÏÖÎÏ 
+       ÚÁÄÁ×ÁÔØ ÎÁ ÒÁÚÎÙÈ ÕÒÏ×ÎÑÈ.
+
+    *) éÚÍÅÎÅÎÉÅ: ÐÁÒÁÍÅÔÒ clean_time × ÄÉÒÅËÔÉ×Å proxy_cache_path ÕÄÁÌ£Î.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒ max_size × ÄÉÒÅËÔÉ×Å proxy_cache_path.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÒÅÄ×ÁÒÉÔÅÌØÎÁÑ ÐÏÄÄÅÒÖËÁ ËÜÛÉÒÏ×ÁÎÉÑ × ÍÏÄÕÌÅ 
+       ngx_http_fastcgi_module.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÐÒÉ ÏÛÉÂËÁÈ ×ÙÄÅÌÅÎÉÑ × ÒÁÚÄÅÌÑÅÍÏÊ ÐÁÍÑÔÉ × ÌÏÇÅ 
+       ÕËÁÚÙ×ÁÀÔÓÑ ÎÁÚ×ÁÎÉÑ ÄÉÒÅËÔÉ×Ù É ÚÏÎÙ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á "add_header last-modified ''" ÎÅ ÕÄÁÌÑÌÁ × 
+       ÚÁÇÏÌÏ×ËÅ ÏÔ×ÅÔÁ ÓÔÒÏËÕ "Last-Modified"; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.44.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÄÉÒÅËÔÉ×Å auth_basic_user_file ÎÅ ÒÁÂÏÔÁÌ 
+       ÏÔÎÏÓÉÔÅÌØÎÙÊ ÐÕÔØ, ÚÁÄÁÎÎÙÊ ÓÔÒÏËÏÊ ÂÅÚ ÐÅÒÅÍÅÎÎÙÈ; ÏÛÉÂËÁ 
+       ÐÏÑ×ÉÌÁÓØ × 0.7.44.
+       óÐÁÓÉÂÏ Jerome Loyet.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÄÉÒÅËÔÉ×Å alias, ÚÁÄÁÎÎÏÊ ÐÅÒÅÍÅÎÎÙÍÉ ÂÅÚ ÓÓÙÌÏË ÎÁ 
+       ×ÙÄÅÌÅÎÉÑ × ÒÅÇÕÌÑÒÎÙÈ ×ÙÒÁÖÅÎÉÑÈ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.42.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.44                                          23.03.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÒÅÄ×ÁÒÉÔÅÌØÎÁÑ ÐÏÄÄÅÒÖËÁ ËÜÛÉÒÏ×ÁÎÉÑ × ÍÏÄÕÌÅ 
+       ngx_http_proxy_module.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒ --with-pcre × configure.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÄÉÒÅËÔÉ×Á try_files ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎÁ ÎÁ 
+       ÕÒÏ×ÎÅ server.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á try_files ÎÅÐÒÁ×ÉÌØÎÏ ÏÂÒÁÂÁÔÙ×ÁÌÁ ÓÔÒÏËÕ 
+       ÚÁÐÒÏÓÁ × ÐÏÓÌÅÄÎÅÍ ÐÁÒÁÍÅÔÒÅ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á try_files ÍÏÇÌÁ ÎÅ×ÅÒÎÏ ÔÅÓÔÉÒÏ×ÁÔØ ËÁÔÁÌÏÇÉ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ÄÌÑ ÐÁÒÙ ÁÄÒÅÓ:ÐÏÒÔ ÏÐÉÓÁÎ ÔÏÌØËÏ ÏÄÉÎ ÓÅÒ×ÅÒ, ÔÏ 
+       ×ÙÄÅÌÅÎÉÑ × ÒÅÇÕÌÑÒÎÙÈ ×ÙÒÁÖÅÎÉÑÈ × ÄÉÒÅËÔÉ×Å server_name ÎÅ 
+       ÒÁÂÏÔÁÌÉ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.43                                          18.03.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÚÁÐÒÏÓ ÏÂÒÁÂÁÔÙ×ÁÌÓÑ ÎÅ×ÅÒÎÏ, ÅÓÌÉ ÄÉÒÅËÔÉ×Á root 
+       ÉÓÐÏÌØÚÏ×ÁÌÁ ÐÅÒÅÍÅÎÎÙÅ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.42.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ÓÅÒ×ÅÒ ÓÌÕÛÁÌ ÎÁ ÁÄÒÅÓÁÈ ÔÉÐÁ "*", ÔÏ ÚÎÁÞÅÎÉÅ 
+       ÐÅÒÅÍÅÎÎÏÊ $server_addr ÂÙÌÏ "0.0.0.0"; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.36.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.42                                          16.03.2009
+
+    *) éÚÍÅÎÅÎÉÅ: ÏÛÉÂËÁ "Invalid argument", ×ÏÚ×ÒÁÝÁÅÍÁÑ 
+       setsockopt(TCP_NODELAY) ÎÁ Solaris, ÔÅÐÅÒØ ÉÇÎÏÒÉÒÕÅÔÓÑ.
+
+    *) éÚÍÅÎÅÎÉÅ: ÐÒÉ ÏÔÓÕÔÓÔ×ÉÉ ÆÁÊÌÁ, ÕËÁÚÁÎÎÏÇÏ × ÄÉÒÅËÔÉ×Å 
+       auth_basic_user_file, ÔÅÐÅÒØ ×ÏÚ×ÒÁÝÁÅÔÓÑ ÏÛÉÂËÁ 403 ×ÍÅÓÔÏ 500.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á auth_basic_user_file ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÅÒÅÍÅÎÎÙÅ. 
+       óÐÁÓÉÂÏ ëÉÒÉÌÌÕ ëÏÒÉÎÓËÏÍÕ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á listen ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÁÒÁÍÅÔÒ ipv6only.
+       óÐÁÓÉÂÏ Zhang Hua.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÄÉÒÅËÔÉ×Å alias ÓÏ ÓÓÙÌËÁÍÉ ÎÁ ×ÙÄÅÌÅÎÉÑ × ÒÅÇÕÌÑÒÎÙÈ 
+       ×ÙÒÁÖÅÎÉÑÈ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.40.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÓÏ×ÍÅÓÔÉÍÏÓÔØ Ó Tru64 UNIX.
+       óÐÁÓÉÂÏ Dustin Marquess.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÂÅÚ ÂÉÂÌÉÏÔÅËÉ PCRE; ÏÛÉÂËÁ 
+       ÐÏÑ×ÉÌÁÓØ × 0.7.41.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.41                                          11.03.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault, 
+       ÅÓÌÉ × server_name ÉÌÉ location ÂÙÌÉ ×ÙÄÅÌÅÎÉÑ × ÒÅÇÕÌÑÒÎÙÈ 
+       ×ÙÒÁÖÅÎÉÑÈ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.40.
+       óÐÁÓÉÂÏ ÷ÌÁÄÉÍÉÒÕ óÏÐÏÔÕ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.40                                          09.03.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á location ÐÏÄÄÅÒÖÉ×ÁÅÔ ×ÙÄÅÌÅÎÉÑ × ÒÅÇÕÌÑÒÎÙÈ 
+       ×ÙÒÁÖÅÎÉÑÈ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Õ alias Ó ÓÓÙÌËÁÍÉ ÎÁ ×ÙÄÅÌÅÎÉÑ × ÒÅÇÕÌÑÒÎÙÈ 
+       ×ÙÒÁÖÅÎÉÑÈ ÍÏÖÎÏ ÉÓÐÏÌØÚÏ×ÁÔØ ×ÎÕÔÒÉ location'Á, ÚÁÄÁÎÎÏÇÏ 
+       ÒÅÇÕÌÑÒÎÙÍ ×ÙÒÁÖÅÎÉÅÍ Ó ×ÙÄÅÌÅÎÉÑÍÉ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á server_name ÐÏÄÄÅÒÖÉ×ÁÅÔ ×ÙÄÅÌÅÎÉÑ × 
+       ÒÅÇÕÌÑÒÎÙÈ ×ÙÒÁÖÅÎÉÑÈ.
+
+    *) éÚÍÅÎÅÎÉÅ: ÍÏÄÕÌØ ngx_http_autoindex_module ÎÅ ÐÏËÁÚÙ×ÁÌ ÐÏÓÌÅÄÎÉÊ 
+       ÓÌÜÛ ÄÌÑ ËÁÔÁÌÏÇÏ× ÎÁ ÆÁÊÌÏ×ÏÊ ÓÉÓÔÅÍÅ XFS; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 
+       0.7.15.
+       óÐÁÓÉÂÏ äÍÉÔÒÉÀ ëÕÚØÍÅÎËÏ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.39                                          02.03.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ×ËÌÀÞ£ÎÎÏÍ ÓÖÁÔÉÉ ÂÏÌØÛÉÅ ÏÔ×ÅÔÙ Ó ÉÓÐÏÌØÚÏ×ÁÎÉÅÍ 
+       SSI ÍÏÇÌÉ ÚÁ×ÉÓÁÔØ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.28.
+       óÐÁÓÉÂÏ áÒÔ£ÍÕ âÏÈÁÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ËÏÒÏÔËÉÈ ÓÔÁÔÉÞÅÓËÉÈ ×ÁÒÉÁÎÔÏ× × 
+       ÄÉÒÅËÔÉ×Å try_files × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation 
+       fault.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.38                                          23.02.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÌÏÇÇÉÒÏ×ÁÎÉÅ ÏÛÉÂÏË ÁÕÔÅÎÔÉÆÉËÁÃÉÉ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÉÍÑ/ÐÁÒÏÌØ, ÚÁÄÁÎÎÙÅ × auth_basic_user_file, 
+       ÉÇÎÏÒÉÒÏ×ÁÌÉÓØ ÐÏÓÌÅ ÎÅÞ£ÔÎÏÇÏ ÞÉÓÌÁ ÐÕÓÔÙÈ ÓÔÒÏË.
+       óÐÁÓÉÂÏ áÌÅËÓÁÎÄÒÕ úÁÇÒÅÂÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÄÌÉÎÎÏÇÏ ÐÕÔÉ × unix domain ÓÏËÅÔÅ × 
+       ÇÌÁ×ÎÏÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation fault; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 
+       0.7.36.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.37                                          21.02.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù, ÉÓÐÏÌØÚÕÀÝÉÅ upstream'Ù, ÎÅ ÒÁÂÏÔÁÌÉ; ÏÛÉÂËÁ 
+       ÐÏÑ×ÉÌÁÓØ × 0.7.36.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.36                                          21.02.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÒÅÄ×ÁÒÉÔÅÌØÎÁÑ ÐÏÄÄÅÒÖËÁ IPv6; ÄÉÒÅËÔÉ×Á listen ÍÏÄÕÌÑ 
+       HTTP ÐÏÄÄÅÒÖÉ×ÁÅÔ IPv6.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÁÑ $ancient_browser ÎÅ ÒÁÂÏÔÁÌÁ ÄÌÑ ÂÒÁÕÚÅÒÏ×, 
+       ÚÁÄÁÎÎÙÈ ÄÉÒÅËÔÉ×ÁÍÉ modern_browser.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.35                                          16.02.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á ssl_engine ÎÅ ÉÓÐÏÌØÚÏ×ÁÌÁ SSL-ÁËÓÅÌÅÒÁÔÏÒ 
+       ÄÌÑ ÁÓÉÍÍÅÔÒÉÞÎÙÈ ÛÉÆÒÏ×.
+       óÐÁÓÉÂÏ Marcin Gozdalik.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á try_files ×ÙÓÔÁ×ÌÑÌÁ MIME-type, ÉÓÈÏÄÑ ÉÚ 
+       ÒÁÓÛÉÒÅÎÉÑ ÐÅÒ×ÏÎÁÞÁÌØÎÏÇÏ ÚÁÐÒÏÓÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÄÉÒÅËÔÉ×ÁÈ server_name, valid_referers É map 
+       ÎÅÐÒÁ×ÉÌØÎÏ ÏÂÒÁÂÁÔÙ×ÁÌÉÓØ ÉÍÅÎÁ ×ÉÄÁ "*domain.tld", ÅÓÌÉ 
+       ÉÓÐÏÌØÚÏ×ÁÌÉÓØ ÍÁÓËÉ ×ÉÄÁ ".domain.tld" É ".subdomain.domain.tld"; 
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.9.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.34                                          10.02.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒ off × ÄÉÒÅËÔÉ×Å if_modified_since.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÐÏÓÌÅ ËÏÍÁÎÄÙ XCLIENT nginx ÐÏÓÙÌÁÅÔ ËÏÍÁÎÄÕ 
+       HELO/EHLO.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÏÄÄÅÒÖËÁ Microsoft-ÓÐÅÃÉÆÉÞÎÏÇÏ ÒÅÖÉÍÁ 
+       "AUTH LOGIN with User Name" × ÐÏÞÔÏ×ÏÍ ÐÒÏËÓÉ-ÓÅÒ×ÅÒÅ.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÄÉÒÅËÔÉ×Å rewrite, ×ÏÚ×ÒÁÝÁÀÝÅÊ ÒÅÄÉÒÅËÔ, ÓÔÁÒÙÅ 
+       ÁÒÇÕÍÅÎÔÙ ÐÒÉÓÏÅÄÉÎÑÌÉÓØ Ë ÎÏ×ÙÍ ÞÅÒÅÚ ÓÉÍ×ÏÌ "?" ×ÍÅÓÔÏ "&";
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.1.18.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÎÁ AIX.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.33                                          02.02.2009
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ÎÁ ÚÁÐÒÏÓ Ó ÔÅÌÏÍ ×ÏÚ×ÒÁÝÁÌÓÑ ÒÅÄÉÒÅËÔ, ÔÏ ÏÔ×ÅÔ 
+       ÍÏÇ ÂÙÔØ Ä×ÏÊÎÙÍ ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÍÅÔÏÄÏ× epoll ÉÌÉ rtsig.
+       óÐÁÓÉÂÏ Eden Li.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÌÑ ÎÅËÏÔÏÒÙÈ ÔÉÐÏ× ÒÅÄÉÒÅËÔÏ× × ÐÅÒÅÍÅÎÎÏÊ 
+       $sent_http_location ÂÙÌÏ ÐÕÓÔÏÅ ÚÎÁÞÅÎÉÅ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÄÉÒÅËÔÉ×Ù resolver × SMTP 
+       ÐÒÏËÓÉ-ÓÅÒ×ÅÒÅ × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.32                                          26.01.2009
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ × ÄÉÒÅËÔÉ×Å try_files ÍÏÖÎÏ Ñ×ÎÏ ÕËÁÚÁÔØ ÐÒÏ×ÅÒËÕ 
+       ËÁÔÁÌÏÇÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: fastcgi_store ÎÅ ×ÓÅÇÄÁ ÓÏÈÒÁÎÑÌ ÆÁÊÌÙ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÇÅÏ-ÄÉÁÐÁÚÏÎÁÈ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÛÉÂËÉ ×ÙÄÅÌÅÎÉÑ ÂÏÌØÛÉÈ ÂÌÏËÏ× × ÒÁÚÄÅÌÑÅÍÏÊ ÐÁÍÑÔÉ, 
+       ÅÓÌÉ nginx ÂÙÌ ÓÏÂÒÁÎ ÂÅÚ ÏÔÌÁÄËÉ.
+       óÐÁÓÉÂÏ áÎÄÒÅÀ ë×ÁÓÏ×Õ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.31                                          19.01.2009
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÄÉÒÅËÔÉ×Á try_files ÐÒÏ×ÅÒÑÅÔ ÔÏÌØËÏ ÆÁÊÌÙ, 
+       ÉÇÎÏÒÉÒÕÑ ËÁÔÁÌÏÇÉ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á fastcgi_split_path_info.
+
+    *) éÓÐÒÁ×ÌÅÎÉÑ × ÐÏÄÄÅÒÖËÅ ÓÔÒÏËÉ "Expect" × ÚÁÇÏÌÏ×ËÅ ÚÁÐÒÏÓÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÑ × ÇÅÏ-ÄÉÁÐÁÚÏÎÁÈ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÏÔÓÕÔÓÔ×ÉÉ ÏÔ×ÅÔÁ ngx_http_memcached_module 
+       ×ÏÚ×ÒÁÝÁÌ × ÔÅÌÅ ÏÔ×ÅÔÁ ÓÔÒÏËÕ "END" ×ÍÅÓÔÏ 404-ÏÊ ÓÔÒÁÎÉÃÙ ÐÏ 
+       ÕÍÏÌÞÁÎÉÀ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.18.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÐÒÏËÓÉÒÏ×ÁÎÉÉ SMPT nginx ×ÙÄÁ×ÁÌ ÓÏÏÂÝÅÎÉÅ 
+       "250 2.0.0 OK" ×ÍÅÓÔÏ "235 2.0.0 OK"; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.22.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.30                                          24.12.2008
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation fault, ÅÓÌÉ 
+       × ÄÉÒÅËÔÉ×ÁÈ fastcgi_pass ÉÌÉ proxy_pass ÉÓÐÏÌØÚÏ×ÁÌÉÓØ ÐÅÒÅÍÅÎÎÙÅ É 
+       ÉÍÑ ÈÏÓÔÁ ÄÏÌÖÎÏ ÂÙÌÏ ÒÅÚÏÌ×ÉÔØÓÑ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.29.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.29                                          24.12.2008
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù fastcgi_pass É proxy_pass ÎÅ ÐÏÄÄÅÒÖÉ×ÁÌÉ 
+       ÐÅÒÅÍÅÎÎÙÅ ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ unix domain ÓÏËÅÔÏ×.
+
+    *) éÓÐÒÁ×ÌÅÎÉÑ × ÏÂÒÁÂÏÔËÅ ÐÏÄÚÁÐÒÏÓÏ×; ÏÛÉÂËÉ ÐÏÑ×ÉÌÉÓØ × 0.7.25.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÔ×ÅÔ "100 Continue" ×ÙÄÁ×ÁÌÓÑ ÄÌÑ ÚÁÐÒÏÓÏ× ×ÅÒÓÉÉ 
+       HTTP/1.0;
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ×ÙÄÅÌÅÎÉÉ ÐÁÍÑÔÉ × ÍÏÄÕÌÅ ngx_http_gzip_filter_module 
+       ÐÏÄ Cygwin.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.28                                          22.12.2008
+
+    *) éÚÍÅÎÅÎÉÅ: × ×ÙÄÅÌÅÎÉÉ ÐÁÍÑÔÉ × ÍÏÄÕÌÅ ngx_http_gzip_filter_module.
+
+    *) éÚÍÅÎÅÎÉÅ: ÚÎÁÞÅÎÉÑ ÐÏ ÕÍÏÌÞÁÎÉÀ ÄÌÑ ÄÉÒÅËÔÉ×Ù gzip_buffers ÉÚÍÅÎÅÎÙ 
+       Ó 4 4k/8k ÎÁ 32 4k ÉÌÉ 16 8k.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.27                                          15.12.2008
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á try_files.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á fastcgi_pass ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÅÒÅÍÅÎÎÙÅ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÄÉÒÅËÔÉ×Á geo ÍÏÖÅÔ ÂÒÁÔØ ÁÄÒÅÓ ÉÚ ÐÅÒÅÍÅÎÎÏÊ.
+       óÐÁÓÉÂÏ áÎÄÒÅÀ îÉÇÍÁÔÕÌÉÎÕ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÍÏÄÉÆÉËÁÔÏÒ location'Á ÍÏÖÎÏ ÕËÁÚÙ×ÁÔØ ÂÅÚ 
+       ÐÒÏÂÅÌÁ ÐÅÒÅÄ ÎÁÚ×ÁÎÉÅÍ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÁÑ $upstream_response_length.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÄÉÒÅËÔÉ×Á add_header ÎÅ ÄÏÂÁ×ÌÑÅÔ ÐÕÓÔÏÅ 
+       ÚÎÁÞÅÎÉÅ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÚÁÐÒÏÓÅ ÆÁÊÌÁ ÎÕÌÅ×ÏÊ ÄÌÉÎÙ nginx ÚÁËÒÙ×ÁÌ 
+       ÓÏÅÄÉÎÅÎÉÅ, ÎÉÞÅÇÏ ÎÅ ÐÅÒÅÄÁ×; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.25.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÅÔÏÄ MOVE ÎÅ ÍÏÇ ÐÅÒÅÍÅÝÁÔØ ÆÁÊÌ × ÎÅÓÕÝÅÓÔ×ÕÀÝÉÊ 
+       ËÁÔÁÌÏÇ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ × ÓÅÒ×ÅÒÅ ÎÅ ÂÙÌ ÏÐÉÓÁÎ ÎÉ ÏÄÉÎ ÉÍÅÎÏ×ÁÎÎÙÊ 
+       location, ÎÏ ÔÁËÏÊ location ÉÓÐÏÌØÚÏ×ÁÌÓÑ × ÄÉÒÅËÔÉ×Å error_page, ÔÏ 
+       × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ segmentation fault.
+       óÐÁÓÉÂÏ óÅÒÇÅÀ âÏÞÅÎËÏ×Õ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.26                                          08.12.2008
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÏÂÒÁÂÏÔËÅ ÐÏÄÚÁÐÒÏÓÏ×; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.25.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.25                                          08.12.2008
+
+    *) éÚÍÅÎÅÎÉÅ: × ÏÂÒÁÂÏÔËÅ ÐÏÄÚÁÐÒÏÓÏ×.
+
+    *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ ÒÁÚÒÅÛÁÀÔÓÑ POST'Ù ÂÅÚ ÓÔÒÏËÉ "Content-Length" × 
+       ÚÁÇÏÌÏ×ËÅ ÚÁÐÒÏÓÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ ÄÉÒÅËÔÉ×Ù limit_req É limit_conn ÕËÁÚÙ×ÁÀÔ 
+       ÐÒÉÞÉÎÕ ÚÁÐÒÅÔÁ ÚÁÐÒÏÓÁ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: × ÐÁÒÁÍÅÔÒÅ delete ÄÉÒÅËÔÉ×Ù geo.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.24                                          01.12.2008
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á if_modified_since.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÏÂÒÁÂÁÔÙ×ÁÌ ÏÔ×ÅÔ FastCGI-ÓÅÒ×ÅÒÁ, ÅÓÌÉ ÐÅÒÅÄ 
+       ÏÔ×ÅÔÏÍ ÓÅÒ×ÅÒ ÐÅÒÅÄÁ×ÁÌ ÍÎÏÇÏ ÓÏÏÂÝÅÎÉÊ × stderr.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÙÅ "$cookie_..." ÎÅ ÒÁÂÏÔÁÌÉ × SSI and × 
+       ÐÅÒÌÏ×ÏÍ ÍÏÄÕÌÅ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.23                                          27.11.2008
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒÙ delete É ranges × ÄÉÒÅËÔÉ×Å geo.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÕÓËÏÒÅÎÉÅ ÚÁÇÒÕÚËÉ geo-ÂÁÚÙ Ó ÂÏÌØÛÉÍ ÞÉÓÌÏÍ ÚÎÁÞÅÎÉÊ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÕÍÅÎØÛÅÎÉÅ ÐÁÍÑÔÉ, ÎÅÏÂÈÏÄÉÍÏÊ ÄÌÑ ÚÁÇÒÕÚËÉ geo-ÂÁÚÙ.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.22                                          20.11.2008
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒ none × ÄÉÒÅËÔÉ×Å smtp_auth.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÙÅ "$cookie_...".
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á directio ÎÅ ÒÁÂÏÔÁÌÁ Ó ÆÁÊÌÏ×ÏÊ ÓÉÓÔÅÍÏÊ XFS.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: resolver ÎÅ ÐÏÎÉÍÁÌ ÂÏÌØÛÉÅ DNS-ÏÔ×ÅÔÙ.
+       óÐÁÓÉÂÏ Zyb.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.21                                          11.11.2008
+
+    *) éÚÍÅÎÅÎÉÑ × ÍÏÄÕÌÅ ngx_http_limit_req_module.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÐÏÄÄÅÒÖËÁ EXSLT × ÍÏÄÕÌÅ ngx_http_xslt_module.
+       óÐÁÓÉÂÏ äÅÎÉÓÕ ìÁÔÙÐÏ×Õ.
+
+    *) éÚÍÅÎÅÎÉÅ: ÓÏ×ÍÅÓÔÉÍÏÓÔØ Ó glibc 2.3.
+       óÐÁÓÉÂÏ Eric Benson É íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÚÁÐÕÓËÁÌÓÑ ÎÁ MacOSX 10.4 É ÂÏÌÅÅ ÒÁÎÎÉÈ; 
+       ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.6.
+
+
+éÚÍÅÎÅÎÉÑ × nginx 0.7.20                                          10.11.2008
+
+    *) éÚÍÅÎÅÎÉÑ × ÍÏÄÕÌÅ ngx_http_gzip_filter_module.
+
+    *) äÏÂÁ×ÌÅÎÉÅ: ÍÏÄÕÌØ ngx_http_limit_req_module.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÎÁ ÐÌÁÔÆÏÒÍÁÈ sparc É ppc ÒÁÂÏÞÉÅ ÐÒÏÃÅÓÓÙ ÍÏÇÌÉ 
+       ×ÙÈÏÄÉÔØ ÐÏ ÓÉÇÎÁÌÕ SIGBUS; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.3.
+       óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù ×ÉÄÁ "proxy_pass http://host/some:uri" ÎÅ 
+       ÒÁÂÏÔÁÌÉ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.7.12.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ HTTPS ÚÁÐÒÏÓÙ ÍÏÇÌÉ ÚÁ×ÅÒÛÁÔØÓÑ Ó 
+       ÏÛÉÂËÏÊ "bad write retry".
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÏÄÕÌØ ngx_http_secure_link_module ÎÅ ÒÁÂÏÔÁÌ ×ÎÕÔÒÉ 
+       location'Ï× Ó ÉÍÅÎÁÍÉ ÍÅÎØÛÅ 3 ÓÉÍ×ÏÌÏ×.
+
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÁÑ $server_addr ÍÏÇÌÁ ÎÅ ÉÍÅÔØ ÚÎÁÞÅÎÉÑ.
+
+
 éÚÍÅÎÅÎÉÑ × nginx 0.7.19                                          13.10.2008
 
     *) éÓÐÒÁ×ÌÅÎÉÅ: ÏÂÎÏ×ÌÅÎÉÅ ÎÏÍÅÒÁ ×ÅÒÓÉÉ.
@@ -794,7 +1485,7 @@
     *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù open_file_cache, open_file_cache_retest É 
        open_file_cache_errors.
 
-    *) éÓÐÒÁ×ÌÅÎÉÅ: ÕÔÅÞËÁ ÓÏËÅÔÏ×; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.7.
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÕÔÅÞËÉ ÓÏËÅÔÏ×; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.7.
 
     *) éÓÐÒÁ×ÌÅÎÉÅ: ÷ ÓÔÒÏËÕ ÚÁÇÏÌÏ×ËÁ ÏÔ×ÅÔÁ "Content-Type", ÕËÁÚÁÎÎÕÀ × 
        ÍÅÔÏÄÅ $r->send_http_header(), ÎÅ ÄÏÂÁ×ÌÑÌÁÓØ ËÏÄÉÒÏ×ËÁ, ÕËÁÚÁÎÎÁÑ × 
@@ -1763,7 +2454,7 @@
        ÚÁËÏÄÉÒÏ×ÁÎÎÙÅ ÓÉÍ×ÏÌÙ × ×ÉÄÅ "%XX", ÔÏ ÐÒÏËÓÉÒÕÅÍÙÊ ÚÁÐÒÏÓ 
        ÐÅÒÅÄÁ×ÁÌÓÑ ÎÅÚÁËÏÄÉÒÏ×ÁÎÎÙÍ.
 
-    *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÅÔÏÄ $r->headers_in("Cookie") ÍÏÄÕÌÑ 
+    *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÅÔÏÄ $r->header_in("Cookie") ÍÏÄÕÌÑ 
        ngx_http_perl_module ÔÅÐÅÒØ ×ÏÚ×ÒÁÝÁÅÔ ×ÓÅ ÓÔÒÏËÉ "Cookie" × 
        ÚÁÇÏÌÏ×ËÅ ÚÁÐÒÏÓÁ.
 
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2002-2008 Igor Sysoev
+ * Copyright (C) 2002-2009 Igor Sysoev
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -21,5 +21,4 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
  */
--- a/auto/cc/bcc
+++ b/auto/cc/bcc
@@ -24,7 +24,7 @@ esac
 # __stdcall
 #CPU_OPT="$CPU_OPT -ps"
 # __fastcall
-CPU_OPT="$CPU_OPT -pr"
+#CPU_OPT="$CPU_OPT -pr"
 
 CFLAGS="$CFLAGS $CPU_OPT"
 
@@ -46,7 +46,7 @@ NGX_USE_PCH="-Hu -H=$NGX_OBJS/ngx_config
 
 
 # Win32 GUI mode application
-LINK="\$(CC) -laa"
+#LINK="\$(CC) -laa"
 
 
 # the resource file
--- a/auto/cc/gcc
+++ b/auto/cc/gcc
@@ -158,9 +158,10 @@ case "$NGX_GCC_VER" in
     3.* | 4.* )
         # we have a lot of the unused function arguments
         CFLAGS="$CFLAGS -Wno-unused-parameter"
-        CFLAGS="$CFLAGS -Wno-unused-function"
+        CFLAGS="$CFLAGS -Wunused-function"
         CFLAGS="$CFLAGS -Wunused-variable"
         CFLAGS="$CFLAGS -Wunused-value"
+        # 4.2.1 shows the warning in wrong places
         #CFLAGS="$CFLAGS -Wunreachable-code"
     ;;
 
--- a/auto/cc/msvc
+++ b/auto/cc/msvc
@@ -2,7 +2,9 @@
 # Copyright (C) Igor Sysoev
 
 
-# MSVC 6.0 SP2, MSVC Toolkit 2003 (7.1), MSVC 2005 Express Edition SP1 (8.0)
+# MSVC 6.0 SP2
+# MSVC Toolkit 2003 (7.1)
+# MSVC 2005 Express Edition SP1 (8.0)
 
 # optimizations
 
@@ -49,12 +51,12 @@ case $CPU in
     ;;
 esac
 
-# __cdecl, use with OpenSSL, md5 asm, and sha1 asm
+# __cdecl, default, must be used with OpenSSL, md5 asm, and sha1 asm
 #CPU_OPT="$CPU_OPT -Gd"
 # __stdcall
 #CPU_OPT="$CPU_OPT -Gz"
 # __fastcall
-CPU_OPT="$CPU_OPT -Gr"
+#CPU_OPT="$CPU_OPT -Gr"
 
 
 CFLAGS="$CFLAGS $CPU_OPT"
@@ -76,27 +78,34 @@ LINK="\$(CC)"
 # the link flags
 CORE_LINK="$CORE_LINK -link -verbose:lib"
 
-if [ $NGX_CC_NAME = msvc7 ]; then
-    # link with libcmt.lib, multithreaded
-    LIBC="-MT"
-else
-    # link with msvcrt.dll
-    LIBC="-MD"
-fi
+# link with libcmt.lib, multithreaded
+LIBC="-MT"
+# link with msvcrt.dll
+# however, MSVC Toolkit 2003 has no MSVCRT.LIB
+#LIBC="-MD"
 
 CFLAGS="$CFLAGS $LIBC"
 
+CORE_LIBS="$CORE_LIBS kernel32.lib user32.lib"
+
 # Win32 GUI mode application
-CORE_LIBS="$CORE_LIBS kernel32.lib user32.lib"
-CORE_LINK="$CORE_LINK -subsystem:windows -entry:mainCRTStartup"
+#CORE_LINK="$CORE_LINK -subsystem:windows -entry:mainCRTStartup"
 
 # debug
+# msvc8 under Wine issues
+#     Program database manager mismatch; please check your installation
 if [ $NGX_CC_NAME != msvc8 ]; then
    CFLAGS="$CFLAGS -Zi"
    CORE_LINK="$CORE_LINK -debug"
 fi
 
 
+# MSVC 2005 supports C99 variadic macros
+if [ $NGX_CC_NAME = msvc8 ]; then
+   have=NGX_HAVE_C99_VARIADIC_MACROS . auto/have
+fi
+
+
 # precompiled headers
 CORE_DEPS="$CORE_DEPS $NGX_OBJS/ngx_config.pch"
 NGX_PCH="$NGX_OBJS/ngx_config.pch"
@@ -115,24 +124,13 @@ ngx_binout="-Fe"
 ngx_objext="obj"
 ngx_binext=".exe"
 
-if [ "$BMAKE" = nmake ]; then
-    # MS nmake
-
-    ngx_long_start='@<<
-        '
-    ngx_long_end='<<'
-    ngx_long_regex_cont=' \
+ngx_long_start='@<<
 	'
-    ngx_long_cont='
+ngx_long_end='<<'
+ngx_long_regex_cont=' \
 	'
-
-else
-    # Borland make
-
-    ngx_long_start='@&&|
-        '
-    ngx_long_end='|'
-fi
+ngx_long_cont='
+	'
 
 # MSVC understand / in path
 #ngx_regex_dirsep='\\'
--- a/auto/cc/owc
+++ b/auto/cc/owc
@@ -71,7 +71,7 @@ NGX_USE_PCH="-fh=$NGX_OBJS/ngx_config.pc
 
 
 # the link flags, built target is NT GUI mode application
-CORE_LINK="$CORE_LINK -l=nt_win"
+#CORE_LINK="$CORE_LINK -l=nt_win"
 
 
 # the resource file
@@ -87,3 +87,17 @@ ngx_binext=".exe"
 
 ngx_regex_dirsep='\\'
 ngx_dirsep="\\"
+
+ngx_long_start=' '
+ngx_long_end=' '
+ngx_long_regex_cont=' \&\
+	'
+ngx_long_cont=' &
+	'
+
+ngx_regex_cont=' \&\
+	'
+ngx_cont=' &
+	'
+ngx_tab=' &
+		'
--- a/auto/cc/sunc
+++ b/auto/cc/sunc
@@ -45,21 +45,6 @@ fi
 case "$NGX_MACHINE" in
 
     i86pc)
-        ngx_feature="PAUSE hardware capability bug"
-        ngx_feature_name=
-        ngx_feature_run=bug
-        ngx_feature_incs=
-        ngx_feature_path=
-        ngx_feature_libs=
-        ngx_feature_test='__asm ("pause")'
-
-        . auto/feature
-
-        if [ $ngx_found = yes ]; then
-            # disable [ PAUSE ] hwcap for Sun Studio 11
-            CORE_LINK="$CORE_LINK -Msrc/os/unix/ngx_sunpro_x86.map"
-        fi
-
         NGX_AUX=" src/os/unix/ngx_sunpro_x86.il"
     ;;
 
--- a/auto/headers
+++ b/auto/headers
@@ -2,8 +2,11 @@
 # Copyright (C) Igor Sysoev
 
 
-ngx_include="unistd.h";    . auto/include
-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
+ngx_include="unistd.h";      . auto/include
+ngx_include="inttypes.h";    . auto/include
+ngx_include="limits.h";      . auto/include
+ngx_include="sys/filio.h";   . auto/include
+ngx_include="sys/param.h";   . auto/include
+ngx_include="sys/mount.h";   . auto/include
+ngx_include="sys/statvfs.h"; . auto/include
+ngx_include="crypt.h";       . auto/include
--- a/auto/include
+++ b/auto/include
@@ -16,6 +16,7 @@ ngx_found=no
 
 cat << END > $NGX_AUTOTEST.c
 
+$NGX_INCLUDE_SYS_PARAM_H
 #include <$ngx_include>
 
 int main() {
--- a/auto/init
+++ b/auto/init
@@ -43,36 +43,8 @@ fi
 
 cat << END > Makefile
 
-build:
-	\$(MAKE) -f $NGX_MAKEFILE
-
-install:
-	\$(MAKE) -f $NGX_MAKEFILE install
+default:	build
 
 clean:
 	rm -rf Makefile $NGX_OBJS
-
-upgrade:
-	$NGX_SBIN_PATH -t
-
-	kill -USR2 \`cat $NGX_PID_PATH\`
-	sleep 1
-	test -f $NGX_PID_PATH.oldbin
-
-	kill -QUIT \`cat $NGX_PID_PATH.oldbin\`
-
-upgrade1:
-	# upgrade 0.1.x to 0.2+
-
-	$NGX_SBIN_PATH -t
-
-	cp $NGX_PID_PATH $NGX_PID_PATH.oldbin
-
-	kill -USR2 \`cat $NGX_PID_PATH\`
-	sleep 1
-	test -f $NGX_PID_PATH.oldbin
-
-	cp $NGX_PID_PATH $NGX_PID_PATH.newbin
-
-	kill -QUIT \`cat $NGX_PID_PATH.oldbin\`
 END
--- a/auto/install
+++ b/auto/install
@@ -15,6 +15,63 @@ END
 fi
 
 
+case ".$NGX_SBIN_PATH" in
+    ./*)
+    ;;
+
+    .)
+        NGX_SBIN_PATH=$NGX_PREFIX/sbin/nginx
+    ;;
+
+    *)
+        NGX_SBIN_PATH=$NGX_PREFIX/$NGX_SBIN_PATH
+    ;;
+esac
+
+
+case ".$NGX_CONF_PATH" in
+    ./*)
+    ;;
+
+    *)
+        NGX_CONF_PATH=$NGX_PREFIX/$NGX_CONF_PATH
+    ;;
+esac
+
+
+NGX_CONF_PREFIX=`dirname $NGX_CONF_PATH`
+
+
+case ".$NGX_PID_PATH" in
+    ./*)
+    ;;
+
+    *)
+        NGX_PID_PATH=$NGX_PREFIX/$NGX_PID_PATH
+    ;;
+esac
+
+
+case ".$NGX_ERROR_LOG_PATH" in
+    ./*)
+    ;;
+
+    *)
+        NGX_ERROR_LOG_PATH=$NGX_PREFIX/$NGX_ERROR_LOG_PATH
+    ;;
+esac
+
+
+case ".$NGX_HTTP_LOG_PATH" in
+    ./*)
+    ;;
+
+    *)
+        NGX_HTTP_LOG_PATH=$NGX_PREFIX/$NGX_HTTP_LOG_PATH
+    ;;
+esac
+
+
 cat << END                                                    >> $NGX_MAKEFILE
 
 install:	$NGX_OBJS${ngx_dirsep}nginx${ngx_binext} \
@@ -67,3 +124,39 @@ if test -n "\$(DESTDIR)$NGX_ERROR_LOG_PA
 END
 
 fi
+
+
+# create Makefile
+
+cat << END >> Makefile
+
+build:
+	\$(MAKE) -f $NGX_MAKEFILE
+
+install:
+	\$(MAKE) -f $NGX_MAKEFILE install
+
+upgrade:
+	$NGX_SBIN_PATH -t
+
+	kill -USR2 \`cat $NGX_PID_PATH\`
+	sleep 1
+	test -f $NGX_PID_PATH.oldbin
+
+	kill -QUIT \`cat $NGX_PID_PATH.oldbin\`
+
+upgrade1:
+	# upgrade 0.1.x to 0.2+
+
+	$NGX_SBIN_PATH -t
+
+	cp $NGX_PID_PATH $NGX_PID_PATH.oldbin
+
+	kill -USR2 \`cat $NGX_PID_PATH\`
+	sleep 1
+	test -f $NGX_PID_PATH.oldbin
+
+	cp $NGX_PID_PATH $NGX_PID_PATH.newbin
+
+	kill -QUIT \`cat $NGX_PID_PATH.oldbin\`
+END
--- a/auto/lib/conf
+++ b/auto/lib/conf
@@ -4,15 +4,29 @@
 
 if [ $USE_PCRE = YES -o $PCRE != NONE ]; then
     . auto/lib/pcre/conf
+
+else
+    if [ $USE_PCRE = DISABLED -a $HTTP_REWRITE = YES ]; then
+
+cat << END
+
+$0: error: the HTTP rewrite module requires the PCRE library.
+You can either disable the module by using --without-http_rewrite_module
+option or you have to enable the PCRE support.
+
+END
+        exit 1
+    fi
 fi
 
+
 if [ $USE_OPENSSL = YES ]; then
     . auto/lib/openssl/conf
 fi
 
 if [ $USE_MD5 = YES ]; then
 
-    if [ $OPENSSL != NONE -a $OPENSSL != NO ]; then
+    if [ $USE_OPENSSL = YES ]; then
         have=NGX_HAVE_OPENSSL_MD5_H . auto/have
         have=NGX_OPENSSL_MD5 . auto/have
         MD5=YES
@@ -26,7 +40,7 @@ fi
 
 if [ $USE_SHA1 = YES ]; then
 
-    if [ $OPENSSL != NONE -a $OPENSSL != NO ]; then
+    if [ $USE_OPENSSL = YES ]; then
         have=NGX_HAVE_OPENSSL_SHA1_H . auto/have
         SHA1=YES
         SHA1_LIB=OpenSSL
@@ -45,6 +59,10 @@ if [ $USE_LIBXSLT = YES ]; then
     . auto/lib/libxslt/conf
 fi
 
+if [ $USE_LIBGD = YES ]; then
+    . auto/lib/libgd/conf
+fi
+
 if [ $USE_PERL = YES ]; then
     . auto/lib/perl/conf
 fi
--- a/auto/lib/google-perftools/conf
+++ b/auto/lib/google-perftools/conf
@@ -30,4 +30,15 @@ fi
 
 if [ $ngx_found = yes ]; then
     CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+
+else
+
+cat << END
+
+$0: error: the Google perftool module requires the Google perftools
+library. You can either do not enable the module or install the library.
+
+END
+
+    exit 1
 fi
new file mode 100644
--- /dev/null
+++ b/auto/lib/libgd/conf
@@ -0,0 +1,82 @@
+
+# Copyright (C) Igor Sysoev
+
+
+    ngx_feature="GD library"
+    ngx_feature_name=
+    ngx_feature_run=no
+    ngx_feature_incs="#include <gd.h>"
+    ngx_feature_path=
+    ngx_feature_libs="-lgd"
+    ngx_feature_test="gdImagePtr img = gdImageCreateFromGifPtr(1, NULL);"
+    . auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+    # FreeBSD port
+
+    ngx_feature="GD library in /usr/local/"
+    ngx_feature_path="/usr/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lgd"
+    else
+        ngx_feature_libs="-L/usr/local/lib -lgd"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # NetBSD port
+
+    ngx_feature="GD 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 -lgd"
+    else
+        ngx_feature_libs="-L/usr/pkg/lib -lgd"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # MacPorts
+
+    ngx_feature="GD 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 -lgd"
+    else
+        ngx_feature_libs="-L/opt/local/lib -lgd"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+
+    CORE_INCS="$CORE_INCS $ngx_feature_path"
+    CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+
+else
+
+cat << END
+
+$0: error: the HTTP image filter module requires the GD library.
+You can either do not enable the module or install the libraries.
+
+END
+
+    exit 1
+
+fi
--- a/auto/lib/libxslt/conf
+++ b/auto/lib/libxslt/conf
@@ -88,3 +88,68 @@ END
 
     exit 1
 fi
+
+
+    ngx_feature="libexslt"
+    ngx_feature_name=NGX_HAVE_EXSLT
+    ngx_feature_run=no
+    ngx_feature_incs="#include <libexslt/exslt.h>"
+    ngx_feature_path="/usr/include/libxml2"
+    ngx_feature_libs="-lexslt"
+    ngx_feature_test="exsltRegisterAll();"
+    . auto/feature
+
+if [ $ngx_found = no ]; then
+
+    # FreeBSD port
+
+    ngx_feature="libexslt in /usr/local/"
+    ngx_feature_path="/usr/local/include/libxml2 /usr/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lexslt"
+    else
+        ngx_feature_libs="-L/usr/local/lib -lexslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # NetBSD port
+
+    ngx_feature="libexslt in /usr/pkg/"
+    ngx_feature_path="/usr/pkg/include/libxml2 /usr/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lexslt"
+    else
+        ngx_feature_libs="-L/usr/pkg/lib -lexslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # MacPorts
+
+    ngx_feature="libexslt in /opt/local/"
+    ngx_feature_path="/opt/local/include/libxml2 /opt/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lexslt"
+    else
+        ngx_feature_libs="-L/opt/local/lib -lexslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+    CORE_LIBS="$CORE_LIBS -lexslt"
+fi
--- a/auto/lib/md5/conf
+++ b/auto/lib/md5/conf
@@ -4,7 +4,7 @@
 
 if [ $MD5 != NONE ]; then
 
-    if grep MD5_Init $MD5/md5.h >/dev/null; then
+    if grep MD5_Init $MD5/md5.h 2>&1 >/dev/null; then
         # OpenSSL md5
         OPENSSL_MD5=YES
         have=NGX_HAVE_OPENSSL_MD5 . auto/have
@@ -46,30 +46,30 @@ else
 
     if [ "$NGX_PLATFORM" != win32 ]; then
 
-        MD5=NO
+            MD5=NO
 
-        # Solaris 8/9
+            # FreeBSD, Solaris 10
 
-        ngx_feature="rsaref md5 library"
-        ngx_feature_name=
-        ngx_feature_run=no
-        ngx_feature_incs="#include <md5.h>"
-        ngx_feature_path=
-        ngx_feature_libs="-lmd5"
-        ngx_feature_test="MD5_CTX md5; MD5Init(&md5)"
-        . auto/feature
+            ngx_feature="system 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 md5"
+            ngx_md5_lib="system md"
 
         if [ $ngx_found = no ]; then
 
-            # FreeBSD
+            # Solaris 8/9
 
-            ngx_feature="rsaref md library"
-            ngx_feature_libs="-lmd"
+            ngx_feature="system md5 library"
+            ngx_feature_libs="-lmd5"
             . auto/feature
 
-            ngx_md5_lib="system md"
+            ngx_md5_lib="system md5"
         fi
 
         if [ $ngx_found = no ]; then
@@ -94,6 +94,18 @@ else
             CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
             MD5=YES
             MD5_LIB=$ngx_md5_lib
+        else
+
+cat << END
+
+$0: error: the HTTP cache module requires md5 functions
+from OpenSSL library.  You can either disable the module by using
+--without-http-cache option, or install the OpenSSL library into the system,
+or build the OpenSSL library statically from the source with nginx by using
+--with-http_ssl_module --with-openssl=<path> options.
+
+END
+            exit 1
         fi
 
     fi
--- a/auto/lib/md5/make
+++ b/auto/lib/md5/make
@@ -7,16 +7,19 @@ case "$NGX_CC_NAME" in
     msvc*)
         ngx_makefile=makefile.msvc
         ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC MD5_ASM=$MD5_ASM"
+        ngx_md5="MD5=\"$MD5\""
     ;;
 
     owc*)
         ngx_makefile=makefile.owc
         ngx_opt="CPU_OPT=\"$CPU_OPT\""
+        ngx_md5=`echo MD5=\"$MD5\" | sed -e "s/\//$ngx_regex_dirsep/g"`
     ;;
 
     bcc)
         ngx_makefile=makefile.bcc
         ngx_opt="-DCPU_OPT=\"$CPU_OPT\" -DMD5_ASM=$MD5_ASM"
+        ngx_md5=`echo \-DMD5=\"$MD5\" | sed -e "s/\//$ngx_regex_dirsep/g"`
     ;;
 
 esac
@@ -28,14 +31,10 @@ done=NO
 case "$NGX_PLATFORM" in
 
     win32)
-        cp auto/lib/md5/$ngx_makefile $MD5
-
         cat << END                                        >> $NGX_MAKEFILE
 
 `echo "$MD5/md5.lib:	$NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"`
-	cd `echo $MD5 | sed -e "s/\//$ngx_regex_dirsep/g"`
-	\$(MAKE) -f $ngx_makefile $ngx_opt
-	cd ..\\..\\..
+	\$(MAKE) -f auto/lib/md5/$ngx_makefile $ngx_opt $ngx_md5
 
 END
 
--- a/auto/lib/md5/makefile.bcc
+++ b/auto/lib/md5/makefile.bcc
@@ -7,12 +7,14 @@ CFLAGS = -q -O2 -tWM $(CPU_OPT) -DL_ENDI
 !if "$(MD5_ASM)" == "YES"
 
 md5.lib:
+	cd $(MD5)
 	bcc32 -c $(CFLAGS) -DMD5_ASM md5_dgst.c
 	tlib md5.lib +md5_dgst.obj +"asm\m-win32.obj"
 
 !else
 
 md5.lib:
+	cd $(MD5)
 	bcc32 -c $(CFLAGS) md5_dgst.c
 	tlib md5.lib +md5_dgst.obj
 
--- a/auto/lib/md5/makefile.msvc
+++ b/auto/lib/md5/makefile.msvc
@@ -7,12 +7,14 @@ CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC
 !IF "$(MD5_ASM)" == "YES"
 
 md5.lib:
+	cd $(MD5)
 	cl -c $(CFLAGS) -D MD5_ASM md5_dgst.c
 	link -lib -out:md5.lib md5_dgst.obj asm/m-win32.obj
 
 !ELSE
 
 md5.lib:
+	cd $(MD5)
 	cl -c $(CFLAGS) md5_dgst.c
 	link -lib -out:md5.lib md5_dgst.obj
 
--- a/auto/lib/md5/makefile.owc
+++ b/auto/lib/md5/makefile.owc
@@ -5,5 +5,6 @@
 CFLAGS = -zq -bt=nt -bm -ot -op -oi -oe -s $(CPU_OPT)
 
 md5.lib:
+	cd $(MD5)
 	wcl386 -c $(CFLAGS) -dL_ENDIAN md5_dgst.c
 	wlib -n md5.lib md5_dgst.obj
--- a/auto/lib/openssl/conf
+++ b/auto/lib/openssl/conf
@@ -3,24 +3,36 @@
 
 
 if [ $OPENSSL != NONE ]; then
-    CORE_INCS="$CORE_INCS $OPENSSL/include"
 
     case "$CC" in
+
+        cl | bcc32)
+            have=NGX_OPENSSL . auto/have
+            have=NGX_SSL . auto/have
+
+            CFLAGS="$CFLAGS -DNO_SYS_TYPES_H"
+
+            CORE_INCS="$CORE_INCS $OPENSSL/openssl/include"
+            CORE_DEPS="$CORE_DEPS $OPENSSL/openssl/include/openssl/ssl.h"
+            CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/ssleay32.lib"
+            CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libeay32.lib"
+
+            # libeay32.lib requires gdi32.lib
+            CORE_LIBS="$CORE_LIBS gdi32.lib"
+        ;;
+
         *)
             have=NGX_OPENSSL . auto/have
             have=NGX_SSL . auto/have
-            LINK_DEPS="$LINK_DEPS $OPENSSL/libssl.a $OPENSSL/libcrypto.a"
-            CORE_LIBS="$CORE_LIBS $OPENSSL/libssl.a $OPENSSL/libcrypto.a"
+
+            CORE_INCS="$CORE_INCS $OPENSSL/openssl/include"
+            CORE_DEPS="$CORE_DEPS $OPENSSL/openssl/include/openssl/ssl.h"
+            CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libssl.a"
+            CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libcrypto.a"
+            CORE_LIBS="$CORE_LIBS $NGX_LIBDL"
         ;;
     esac
 
-    case "$NGX_SYSTEM" in
-        SunOS|Linux)
-            CORE_LIBS="$CORE_LIBS -ldl"
-        ;;
-    esac
-
-
 else
 
     case "$NGX_PLATFORM" in
@@ -36,8 +48,6 @@ else
 
             # libeay32.lib requires gdi32.lib
             CORE_LIBS="$CORE_LIBS gdi32.lib"
-            # OpenSSL 0.8's libeay32.lib requires advapi32.lib
-            CORE_LIBS="$CORE_LIBS advapi32.lib"
         ;;
 
         *)
@@ -54,14 +64,19 @@ else
 
             if [ $ngx_found = yes ]; then
                 have=NGX_SSL . auto/have
-                CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+                CORE_LIBS="$CORE_LIBS $ngx_feature_libs $NGX_LIBDL"
                 OPENSSL=YES
+            else
+
+cat << END
 
-                case "$NGX_SYSTEM" in
-                    SunOS)
-                        CORE_LIBS="$CORE_LIBS -ldl"
-                    ;;
-                esac
+$0: error: SSL modules require the OpenSSL library.
+You can either do not enable the modules, or install the OpenSSL library
+into the system, or build the OpenSSL library statically from the source
+with nginx by using --with-openssl=<path> option.
+
+END
+                exit 1
             fi
         ;;
 
--- a/auto/lib/openssl/make
+++ b/auto/lib/openssl/make
@@ -2,26 +2,62 @@
 # Copyright (C) Igor Sysoev
 
 
-if test -n "$OPENSSL_OPT"; then
-    NGX_OPENSSL_CONFIG="./Configure \"$OPENSSL_OPT\""
-else
-    NGX_OPENSSL_CONFIG="./config"
-fi
+case "$CC" in
+
+    cl)
+
+        cat << END                                            >> $NGX_MAKEFILE
+
+$OPENSSL/openssl/include/openssl/ssl.h:	$NGX_MAKEFILE
+	\$(MAKE) -f auto/lib/openssl/makefile.msvc			\
+		OPENSSL="$OPENSSL" OPENSSL_OPT="$OPENSSL_OPT"
 
-case $USE_THREADS in
-    NO) NGX_OPENSSL_CONFIG="$NGX_OPENSSL_CONFIG no-threads" ;;
-    *)  NGX_OPENSSL_CONFIG="$NGX_OPENSSL_CONFIG threads" ;;
-esac
+END
+
+    ;;
 
-case "$NGX_PLATFORM" in
-    *)
+    bcc32)
+
+        ngx_opt=`echo "-DOPENSSL=\"$OPENSSL\" -DOPENSSL_OPT=\"$OPENSSL_OPT\"" \
+            | sed -e "s/\//$ngx_regex_dirsep/g"`
+
         cat << END                                            >> $NGX_MAKEFILE
 
-$OPENSSL/libssl.a:
+`echo "$OPENSSL\\openssl\\lib\\libeay32.lib:				\
+	$OPENSSL\\openssl\\include\\openssl\\ssl.h"			\
+	| sed -e "s/\//$ngx_regex_dirsep/g"`
+
+`echo "$OPENSSL\\openssl\\lib\\ssleay32.lib:				\
+	$OPENSSL\\openssl\\include\\openssl\\ssl.h"			\
+	| sed -e "s/\//$ngx_regex_dirsep/g"`
+
+`echo "$OPENSSL\\openssl\\include\\openssl\\ssl.h:	$NGX_MAKEFILE"	\
+	| sed -e "s/\//$ngx_regex_dirsep/g"`
+	\$(MAKE) -f auto/lib/openssl/makefile.bcc $ngx_opt
+
+END
+
+    ;;
+
+    *)
+        case $USE_THREADS in
+            NO) OPENSSL_OPT="$OPENSSL_OPT no-threads" ;;
+            *)  OPENSSL_OPT="$OPENSSL_OPT threads" ;;
+        esac
+
+        case $OPENSSL in
+            /*) ngx_prefix="$OPENSSL/openssl" ;;
+            *)  ngx_prefix="$PWD/$OPENSSL/openssl" ;;
+        esac
+
+        cat << END                                            >> $NGX_MAKEFILE
+
+$OPENSSL/openssl/include/openssl/ssl.h:	$NGX_MAKEFILE
 	cd $OPENSSL \\
 	&& \$(MAKE) clean \\
-	&& $NGX_OPENSSL_CONFIG no-shared \\
-	&& \$(MAKE)
+	&& ./config --prefix=$ngx_prefix no-shared $OPENSSL_OPT \\
+	&& \$(MAKE) \\
+	&& \$(MAKE) install
 
 END
 
new file mode 100644
--- /dev/null
+++ b/auto/lib/openssl/makefile.bcc
@@ -0,0 +1,18 @@
+
+# Copyright (C) Igor Sysoev
+
+
+all:
+	cd $(OPENSSL)
+
+	perl Configure BC-32 no-shared --prefix=openssl -DNO_SYS_TYPES_H \
+		$(OPENSSL_OPT)
+
+	ms\do_nasm
+
+	$(MAKE) -f ms\bcb.mak
+	$(MAKE) -f ms\bcb.mak install
+
+	# Borland's make does not expand "[ch]" in
+	#    copy "inc32\openssl\*.[ch]" "openssl\include\openssl"
+	copy inc32\openssl\*.h openssl\include\openssl
new file mode 100644
--- /dev/null
+++ b/auto/lib/openssl/makefile.msvc
@@ -0,0 +1,14 @@
+
+# Copyright (C) Igor Sysoev
+
+
+all:
+	cd $(OPENSSL)
+
+	perl Configure VC-WIN32 no-shared --prefix=openssl -DNO_SYS_TYPES_H \
+		$(OPENSSL_OPT)
+
+	ms\do_ms
+
+	$(MAKE) -f ms\nt.mak
+	$(MAKE) -f ms\nt.mak install
--- a/auto/lib/pcre/conf
+++ b/auto/lib/pcre/conf
@@ -161,6 +161,18 @@ else
             CORE_INCS="$CORE_INCS $ngx_feature_path"
             CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
             PCRE=YES
+        else
+
+cat << END
+
+$0: error: the HTTP rewrite module requires the PCRE library.
+You can either disable the module by using --without-http_rewrite_module
+option, or install the PCRE library into the system, or build the PCRE library
+statically from the source with nginx by using --with-pcre=<path> option.
+
+END
+            exit 1
+
         fi
 
     fi
--- a/auto/lib/pcre/make
+++ b/auto/lib/pcre/make
@@ -7,16 +7,19 @@ case "$NGX_CC_NAME" in
     msvc*)
         ngx_makefile=makefile.msvc
         ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC"
+        ngx_pcre="PCRE=\"$PCRE\""
     ;;
 
     owc*)
         ngx_makefile=makefile.owc
         ngx_opt="CPU_OPT=\"$CPU_OPT\""
+        ngx_pcre=`echo PCRE=\"$PCRE\" | sed -e "s/\//$ngx_regex_dirsep/g"`
     ;;
 
     bcc)
         ngx_makefile=makefile.bcc
         ngx_opt="-DCPU_OPT=\"$CPU_OPT\""
+        ngx_pcre=`echo \-DPCRE=\"$PCRE\" | sed -e "s/\//$ngx_regex_dirsep/g"`
     ;;
 
 esac
@@ -25,26 +28,14 @@ esac
 case "$NGX_PLATFORM" in
 
     win32)
-        cp auto/lib/pcre/patch.pcre.in $PCRE
-        cp auto/lib/pcre/patch.pcre.in.owc $PCRE
-        cp auto/lib/pcre/patch.config.in $PCRE
-        cp auto/lib/pcre/patch.pcre.c $PCRE
-        cp auto/lib/pcre/$ngx_makefile $PCRE
-
-        ngx_pcre=`echo $PCRE | sed -e "s/\//$ngx_regex_dirsep/g"`
 
         cat << END                                            >> $NGX_MAKEFILE
 
-`echo "$PCRE/pcre.h:	$NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"`
-	cd $ngx_pcre
-	\$(MAKE) -f $ngx_makefile pcre.h
-	cd ..\\..\\..
+`echo "$PCRE/pcre.lib:	$NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"`
+	\$(MAKE) -f auto/lib/pcre/$ngx_makefile $ngx_pcre $ngx_opt
 
-
-`echo "$PCRE/pcre.lib:	$PCRE/pcre.h" | sed -e "s/\//$ngx_regex_dirsep/g"`
-	cd $ngx_pcre
-	\$(MAKE) -f $ngx_makefile $ngx_opt
-	cd ..\\..\\..
+`echo "$PCRE/pcre.h:" | sed -e "s/\//$ngx_regex_dirsep/g"`
+	\$(MAKE) -f auto/lib/pcre/$ngx_makefile $ngx_pcre pcre.h
 
 END
 
@@ -61,7 +52,6 @@ END
 	&& CC="\$(CC)" CFLAGS="$PCRE_OPT" \\
 	./configure --disable-shared
 
-
 $PCRE/.libs/libpcre.a:	$PCRE/Makefile
 	cd $PCRE \\
 	&& \$(MAKE) libpcre.la
--- a/auto/lib/pcre/makefile.bcc
+++ b/auto/lib/pcre/makefile.bcc
@@ -3,19 +3,23 @@
 
 
 CFLAGS =	-q -O2 -tWM -w-8004 $(CPU_OPT)
-PCREFLAGS =	-DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10
+PCREFLAGS =	-DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10
 
 
-pcre.lib:	pcre.h
-	bcc32 -q -edftables dftables.c
+pcre.lib:
+	cd $(PCRE)
+
+	bcc32 -c $(CFLAGS) -I. $(PCREFLAGS) pcre_*.c
 
-	dftables > chartables.c
+	> pcre.lst
+	for %n in (*.obj) do @echo +%n & >> pcre.lst
+	echo + >> pcre.lst
 
-	bcc32 -c $(CFLAGS) $(PCREFLAGS) maketables.c get.c study.c pcre.c
-
-	tlib pcre.lib +maketables.obj +get.obj +study.obj +pcre.obj
+	tlib pcre.lib @pcre.lst
 
 pcre.h:
-	patch -o pcre.h pcre.in patch.pcre.in
-	patch -o config.h config.in patch.config.in
-	patch -o pcre.c pcre.c patch.pcre.c
+	cd $(PCRE)
+
+	copy /y pcre.h.generic pcre.h
+	copy /y config.h.generic config.h
+	copy /y pcre_chartables.c.dist pcre_chartables.c
--- a/auto/lib/pcre/makefile.msvc
+++ b/auto/lib/pcre/makefile.msvc
@@ -3,21 +3,19 @@
 
 
 CFLAGS =	-O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT)
-PCREFLAGS =	-DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10
+PCREFLAGS =	-DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10
 
 
-pcre.lib:	pcre.h
-	cl -Fedftables dftables.c
-
-	dftables > chartables.c
+pcre.lib:
+	cd $(PCRE)
 
-	cl -nologo -c $(CFLAGS) $(PCREFLAGS)				\
-		maketables.c get.c study.c pcre.c
+	cl -nologo -c $(CFLAGS) -I . $(PCREFLAGS) pcre_*.c
 
-	link -lib -out:pcre.lib -verbose:lib				\
-		maketables.obj get.obj study.obj pcre.obj
+	link -lib -out:pcre.lib -verbose:lib pcre_*.obj
 
 pcre.h:
-	patch -o pcre.h pcre.in patch.pcre.in
-	patch -o config.h config.in patch.config.in
-	patch -o pcre.c pcre.c patch.pcre.c
+	cd $(PCRE)
+
+	copy /y pcre.h.generic pcre.h
+	copy /y config.h.generic config.h
+	copy /y pcre_chartables.c.dist pcre_chartables.c
--- a/auto/lib/pcre/makefile.owc
+++ b/auto/lib/pcre/makefile.owc
@@ -3,17 +3,21 @@
 
 
 CFLAGS =	-c -zq -bt=nt -ot -op -oi -oe -s -bm $(CPU_OPT)
-PCREFLAGS =	-DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10
+PCREFLAGS =	-DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10
 
 
-pcre.lib:	pcre.h
-	wcl386 -zq -bt=nt -l=nt -fe=dftables dftables.c
-	dftables > chartables.c
+pcre.lib:
+	cd $(PCRE)
 
-	wcl386 $(CFLAGS) $(PCREFLAGS) maketables.c get.c study.c pcre.c
-	wlib -n pcre.lib maketables.obj get.obj study.obj pcre.obj
+	wcl386 $(CFLAGS) -i=. $(PCREFLAGS) pcre_*.c
 
+	dir /b *.obj > pcre.lst
+
+	wlib -n pcre.lib @pcre.lst
 
 pcre.h:
-	patch -o pcre.h pcre.in patch.pcre.in.owc
-	patch -o config.h config.in patch.config.in
+	cd $(PCRE)
+
+	copy /y pcre.h.generic pcre.h
+	copy /y config.h.generic config.h
+	copy /y pcre_chartables.c.dist pcre_chartables.c
deleted file mode 100644
--- a/auto/lib/pcre/patch.config.in
+++ /dev/null
@@ -1,11 +0,0 @@
---- config.in	Thu Aug 21 14:43:07 2003
-+++ config.in	Sun Mar  7 02:37:24 2004
-@@ -28,7 +28,7 @@
- found. */
- 
- #define HAVE_STRERROR 0
--#define HAVE_MEMMOVE  0
-+#define HAVE_MEMMOVE 1
- 
- /* There are some non-Unix systems that don't even have bcopy(). If this macro
- is false, an emulation is used. If HAVE_MEMMOVE is set to 1, the value of
deleted file mode 100644
--- a/auto/lib/pcre/patch.pcre.c
+++ /dev/null
@@ -1,13 +0,0 @@
---- pcre.c	Thu Aug 21 14:43:07 2003
-+++ pcre.c	Tue Mar 22 12:56:59 2005
-@@ -246,8 +246,8 @@
- extern "C" void  (*pcre_free)(void *) = free;
- extern "C" int   (*pcre_callout)(pcre_callout_block *) = NULL;
- #else
--void *(*pcre_malloc)(size_t) = malloc;
--void  (*pcre_free)(void *) = free;
-+void *(__cdecl *pcre_malloc)(size_t) = malloc;
-+void  (__cdecl *pcre_free)(void *) = free;
- int   (*pcre_callout)(pcre_callout_block *) = NULL;
- #endif
- #endif
deleted file mode 100644
--- a/auto/lib/pcre/patch.pcre.in
+++ /dev/null
@@ -1,26 +0,0 @@
---- pcre.in	Thu Aug 21 14:43:07 2003
-+++ pcre.h	Tue Mar 22 12:56:59 2005
-@@ -10,9 +10,9 @@
- /* The file pcre.h is build by "configure". Do not edit it; instead
- make changes to pcre.in. */
- 
--#define PCRE_MAJOR          @PCRE_MAJOR@
--#define PCRE_MINOR          @PCRE_MINOR@
--#define PCRE_DATE           @PCRE_DATE@
-+#define PCRE_MAJOR          4
-+#define PCRE_MINOR          4
-+#define PCRE_DATE           21-August-2003
- 
- /* Win32 uses DLL by default */
- 
-@@ -143,8 +143,8 @@
- have to be different again. */
- 
- #ifndef VPCOMPAT
--PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t);
--PCRE_DATA_SCOPE void  (*pcre_free)(void *);
-+PCRE_DATA_SCOPE void *(__cdecl *pcre_malloc)(size_t);
-+PCRE_DATA_SCOPE void  (__cdecl *pcre_free)(void *);
- PCRE_DATA_SCOPE int   (*pcre_callout)(pcre_callout_block *);
- #else   /* VPCOMPAT */
- extern void *pcre_malloc(size_t);
deleted file mode 100644
--- a/auto/lib/pcre/patch.pcre.in.owc
+++ /dev/null
@@ -1,15 +0,0 @@
---- pcre.in	Thu Aug 21 14:43:07 2003
-+++ pcre.h	Tue Mar 22 12:56:59 2005
-@@ -10,9 +10,9 @@
- /* The file pcre.h is build by "configure". Do not edit it; instead
- make changes to pcre.in. */
- 
--#define PCRE_MAJOR          @PCRE_MAJOR@
--#define PCRE_MINOR          @PCRE_MINOR@
--#define PCRE_DATE           @PCRE_DATE@
-+#define PCRE_MAJOR          4
-+#define PCRE_MINOR          4
-+#define PCRE_DATE           21-August-2003
- 
- /* Win32 uses DLL by default */
- 
--- a/auto/lib/sha1/make
+++ b/auto/lib/sha1/make
@@ -7,16 +7,19 @@ case "$NGX_CC_NAME" in
     msvc*)
         ngx_makefile=makefile.msvc
         ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC SHA1_ASM=$SHA1_ASM"
+        ngx_sha1="SHA1=\"$SHA1\""
     ;;
 
     owc*)
         ngx_makefile=makefile.owc
         ngx_opt="CPU_OPT=\"$CPU_OPT\""
+        ngx_sha1=`echo SHA1=\"$SHA1\" | sed -e "s/\//$ngx_regex_dirsep/g"`
     ;;
 
     bcc)
         ngx_makefile=makefile.bcc
         ngx_opt="-DCPU_OPT=\"$CPU_OPT\" -DSHA1_ASM=$SHA1_ASM"
+        ngx_sha1=`echo \-DSHA1=\"$SHA1\" | sed -e "s/\//$ngx_regex_dirsep/g"`
     ;;
 
 esac
@@ -28,14 +31,10 @@ done=NO
 case "$NGX_PLATFORM" in
 
     win32)
-        cp auto/lib/sha1/$ngx_makefile $SHA1
-
         cat << END                                        >> $NGX_MAKEFILE
 
 `echo "$SHA1/sha1.lib:	$NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"`
-	cd `echo $SHA1 | sed -e "s/\//$ngx_regex_dirsep/g"`
-	\$(MAKE) -f $ngx_makefile $ngx_opt
-	cd ..\\..\\..
+	\$(MAKE) -f auto/lib/sha1/$ngx_makefile $ngx_opt $ngx_sha1
 
 END
 
--- a/auto/lib/sha1/makefile.bcc
+++ b/auto/lib/sha1/makefile.bcc
@@ -7,12 +7,14 @@ CFLAGS = -q -O2 -tWM $(CPU_OPT) -DL_ENDI
 !if "$(SHA1_ASM)" == "YES"
 
 sha1.lib:
+	cd $(SHA1)
 	bcc32 -c $(CFLAGS) -DSHA1_ASM sha1dgst.c
 	tlib sha1.lib +sha1dgst.obj +"asm\s-win32.obj"
 
 !else
 
 sha1.lib:
+	cd $(SHA1)
 	bcc32 -c $(CFLAGS) sha1dgst.c
 	tlib sha1.lib +sha1dgst.obj
 
--- a/auto/lib/sha1/makefile.msvc
+++ b/auto/lib/sha1/makefile.msvc
@@ -7,12 +7,14 @@ CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC
 !IF "$(SHA1_ASM)" == "YES"
 
 sha1.lib:
+	cd $(SHA1)
 	cl -c $(CFLAGS) -D SHA1_ASM sha1dgst.c
 	link -lib -out:sha1.lib sha1dgst.obj asm/s-win32.obj
 
 !ELSE
 
 sha1.lib:
+	cd $(SHA1)
 	cl -c $(CFLAGS) sha1dgst.c
 	link -lib -out:sha1.lib sha1dgst.obj
 
--- a/auto/lib/sha1/makefile.owc
+++ b/auto/lib/sha1/makefile.owc
@@ -5,5 +5,6 @@
 CFLAGS = -zq -bt=nt -bm -ot -op -oi -oe -s $(CPU_OPT)
 
 sha1.lib:
+	cd $(SHA1)
 	wcl386 -c $(CFLAGS) -dL_ENDIAN sha1dgst.c
 	wlib -n sha1.lib sha1dgst.obj
--- a/auto/lib/zlib/conf
+++ b/auto/lib/zlib/conf
@@ -57,6 +57,17 @@ else
             CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
             ZLIB=YES
             ngx_found=no
+        else
+
+cat << END
+
+$0: error: the HTTP gzip module requires the zlib library.
+You can either disable the module by using --without-http_gzip_module
+option, or install the zlib library into the system, or build the zlib library
+statically from the source with nginx by using --with-zlib=<path> option.
+
+END
+            exit 1
         fi
     fi
 
--- a/auto/lib/zlib/make
+++ b/auto/lib/zlib/make
@@ -7,17 +7,20 @@ case "$NGX_CC_NAME" in
     msvc*)
         ngx_makefile=makefile.msvc
         ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC"
+        ngx_zlib="ZLIB=\"$ZLIB\""
 
     ;;
 
     owc*)
         ngx_makefile=makefile.owc
         ngx_opt="CPU_OPT=\"$CPU_OPT\""
+        ngx_zlib=`echo ZLIB=\"$ZLIB\" | sed -e "s/\//$ngx_regex_dirsep/g"`
     ;;
 
     bcc)
         ngx_makefile=makefile.bcc
         ngx_opt="-DCPU_OPT=\"$CPU_OPT\""
+        ngx_zlib=`echo \-DZLIB=\"$ZLIB\" | sed -e "s/\//$ngx_regex_dirsep/g"`
     ;;
 
 esac
@@ -29,14 +32,10 @@ done=NO
 case "$NGX_PLATFORM" in
 
     win32)
-        cp auto/lib/zlib/$ngx_makefile $ZLIB
-
         cat << END                                            >> $NGX_MAKEFILE
 
 `echo "$ZLIB/zlib.lib:	$NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"`
-	cd `echo $ZLIB | sed -e "s/\//$ngx_regex_dirsep/g"`
-	\$(MAKE) -f $ngx_makefile $ngx_opt
-	cd ..\\..\\..
+	\$(MAKE) -f auto/lib/zlib/$ngx_makefile $ngx_opt $ngx_zlib
 
 END
 
--- a/auto/lib/zlib/makefile.bcc
+++ b/auto/lib/zlib/makefile.bcc
@@ -5,6 +5,8 @@
 CFLAGS = -q -O2 -tWM -w-8004 -w-8012 $(CPU_OPT)
 
 zlib.lib:
+	cd $(ZLIB)
+
 	bcc32 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c \
 		compress.c
 
--- a/auto/lib/zlib/makefile.msvc
+++ b/auto/lib/zlib/makefile.msvc
@@ -5,6 +5,8 @@
 CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT)
 
 zlib.lib:
+	cd $(ZLIB)
+
 	cl -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c compress.c
 
 	link -lib -out:zlib.lib adler32.obj crc32.obj deflate.obj \
--- a/auto/lib/zlib/makefile.owc
+++ b/auto/lib/zlib/makefile.owc
@@ -5,5 +5,9 @@
 CFLAGS = -zq -bt=nt -ot -op -oi -oe -s -bm $(CPU_OPT)
 
 zlib.lib:
-	wcl386 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c compress.c
-	wlib -n zlib.lib adler32.obj crc32.obj deflate.obj trees.obj zutil.obj compress.obj
+	cd $(ZLIB)
+
+	wcl386 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c &
+		compress.c
+	wlib -n zlib.lib adler32.obj crc32.obj deflate.obj trees.obj &
+		zutil.obj compress.obj
--- a/auto/make
+++ b/auto/make
@@ -2,6 +2,8 @@
 # Copyright (C) Igor Sysoev
 
 
+echo "creating $NGX_MAKEFILE"
+
 mkdir -p $NGX_OBJS/src/core $NGX_OBJS/src/event $NGX_OBJS/src/event/modules \
          $NGX_OBJS/src/os/unix $NGX_OBJS/src/os/win32 \
          $NGX_OBJS/src/http $NGX_OBJS/src/http/modules \
@@ -23,22 +25,12 @@ LINK =	$LINK
 
 END
 
+
 if test -n "$NGX_PERL_CFLAGS"; then
     echo NGX_PERL_CFLAGS = $NGX_PERL_CFLAGS                   >> $NGX_MAKEFILE
     echo NGX_PM_CFLAGS = $NGX_PM_CFLAGS                       >> $NGX_MAKEFILE
 fi
 
-if [ "$BMAKE" = wmake ]; then
-    echo MAKE = wmake                                         >> $NGX_MAKEFILE
-
-    ngx_regex_cont=' '
-    ngx_long_regex_cont=' '
-    ngx_cont=' '
-    ngx_long_cont=' '
-    ngx_tab=' '
-
-fi
-
 
 # ALL_INCS, required by the addons and by OpenWatcom C precompiled headers
 
--- a/auto/modules
+++ b/auto/modules
@@ -65,6 +65,13 @@ if [ $HTTP != YES ]; then
 fi
 
 
+if [ $HTTP_CACHE = YES ]; then
+    USE_MD5=YES
+    have=NGX_HTTP_CACHE . auto/have
+    HTTP_SRCS="$HTTP_SRCS $HTTP_FILE_CACHE_SRCS"
+fi
+
+
 if [ $HTTP_SSI = YES ]; then
     HTTP_POSTPONE=YES
 fi
@@ -97,6 +104,7 @@ fi
 #     ngx_http_charset_filter
 #     ngx_http_ssi_filter
 #         ngx_http_xslt_filter
+#         ngx_http_image_filter_filter
 #         ngx_http_sub_filter
 #         ngx_http_addition_filter
 #         ngx_http_userid_filter
@@ -143,6 +151,12 @@ if [ $HTTP_XSLT = YES ]; then
     HTTP_SRCS="$HTTP_SRCS $HTTP_XSLT_SRCS"
 fi
 
+if [ $HTTP_IMAGE_FILTER = YES ]; then
+    USE_LIBGD=YES
+    HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_IMAGE_FILTER_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_IMAGE_SRCS"
+fi
+
 if [ $HTTP_SUB = YES ]; then
     HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SUB_FILTER_MODULE"
     HTTP_SRCS="$HTTP_SRCS $HTTP_SUB_SRCS"
@@ -205,6 +219,11 @@ if [ $HTTP_LIMIT_ZONE = YES ]; then
     HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_ZONE_SRCS"
 fi
 
+if [ $HTTP_LIMIT_REQ = YES ]; then
+    HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_REQ_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_REQ_SRCS"
+fi
+
 if [ $HTTP_REALIP = YES ]; then
     have=NGX_HTTP_REALIP . auto/have
     HTTP_MODULES="$HTTP_MODULES $HTTP_REALIP_MODULE"
@@ -302,11 +321,6 @@ if [ $HTTP_UPSTREAM_IP_HASH = YES ]; the
     HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_IP_HASH_SRCS"
 fi
 
-# STUB
-#USE_MD5=YES
-#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
     HTTP_MODULES="$HTTP_MODULES ngx_http_stub_status_module"
--- a/auto/options
+++ b/auto/options
@@ -43,6 +43,8 @@ EVENT_AIO=NO
 
 USE_THREADS=NO
 
+NGX_IPV6=NO
+
 HTTP=YES
 
 NGX_HTTP_LOG_PATH=
@@ -50,6 +52,7 @@ NGX_HTTP_CLIENT_TEMP_PATH=
 NGX_HTTP_PROXY_TEMP_PATH=
 NGX_HTTP_FASTCGI_TEMP_PATH=
 
+HTTP_CACHE=YES
 HTTP_CHARSET=YES
 HTTP_GZIP=YES
 HTTP_SSL=NO
@@ -57,6 +60,7 @@ HTTP_SSI=YES
 HTTP_POSTPONE=NO
 HTTP_REALIP=NO
 HTTP_XSLT=NO
+HTTP_IMAGE_FILTER=NO
 HTTP_SUB=NO
 HTTP_ADDITION=NO
 HTTP_DAV=NO
@@ -75,6 +79,7 @@ HTTP_FASTCGI=YES
 HTTP_PERL=NO
 HTTP_MEMCACHED=YES
 HTTP_LIMIT_ZONE=YES
+HTTP_LIMIT_REQ=YES
 HTTP_EMPTY_GIF=YES
 HTTP_BROWSER=YES
 HTTP_SECURE_LINK=NO
@@ -119,15 +124,19 @@ USE_PERL=NO
 NGX_PERL=perl
 
 USE_LIBXSLT=NO
+USE_LIBGD=NO
 
 NGX_GOOGLE_PERFTOOLS=NO
 NGX_CPP_TEST=NO
 
 NGX_CPU_CACHE_LINE=
 
+opt=
 
 for option
 do
+    opt="$opt `echo $option | sed -e \"s/\(--[^=]*=\)\(.* .*\)/\1'\2'/\"`"
+
     case "$option" in
         -*=*) value=`echo "$option" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;;
            *) value="" ;;
@@ -136,6 +145,7 @@ do
     case "$option" in
         --help)                          help=yes                   ;;
 
+        --prefix=)                       NGX_PREFIX="!"             ;;
         --prefix=*)                      NGX_PREFIX="$value"        ;;
         --sbin-path=*)                   NGX_SBIN_PATH="$value"     ;;
         --conf-path=*)                   NGX_CONF_PATH="$value"     ;;
@@ -159,7 +169,11 @@ do
         #--with-threads=*)                USE_THREADS="$value"       ;;
         #--with-threads)                  USE_THREADS="pthreads"     ;;
 
+        --with-ipv6)                     NGX_IPV6=YES               ;;
+
         --without-http)                  HTTP=NO                    ;;
+        --without-http-cache)            HTTP_CACHE=NO              ;;
+
         --http-log-path=*)               NGX_HTTP_LOG_PATH="$value" ;;
         --http-client-body-temp-path=*)  NGX_HTTP_CLIENT_TEMP_PATH="$value" ;;
         --http-proxy-temp-path=*)        NGX_HTTP_PROXY_TEMP_PATH="$value" ;;
@@ -169,6 +183,7 @@ do
         --with-http_realip_module)       HTTP_REALIP=YES            ;;
         --with-http_addition_module)     HTTP_ADDITION=YES          ;;
         --with-http_xslt_module)         HTTP_XSLT=YES              ;;
+        --with-http_image_filter_module) HTTP_IMAGE_FILTER=YES      ;;
         --with-http_sub_module)          HTTP_SUB=YES               ;;
         --with-http_dav_module)          HTTP_DAV=YES               ;;
         --with-http_flv_module)          HTTP_FLV=YES               ;;
@@ -192,6 +207,7 @@ do
         --without-http_fastcgi_module)   HTTP_FASTCGI=NO            ;;
         --without-http_memcached_module) HTTP_MEMCACHED=NO          ;;
         --without-http_limit_zone_module) HTTP_LIMIT_ZONE=NO        ;;
+        --without-http_limit_req_module) HTTP_LIMIT_REQ=NO         ;;
         --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO          ;;
         --without-http_browser_module)   HTTP_BROWSER=NO            ;;
         --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;;
@@ -225,6 +241,7 @@ do
         --with-debug)                    NGX_DEBUG=YES              ;;
 
         --without-pcre)                  USE_PCRE=DISABLED          ;;
+        --with-pcre)                     USE_PCRE=YES               ;;
         --with-pcre=*)                   PCRE="$value"              ;;
         --with-pcre-opt=*)               PCRE_OPT="$value"          ;;
 
@@ -257,6 +274,9 @@ do
 done
 
 
+NGX_CONFIGURE="$opt"
+
+
 if [ $help = yes ]; then
 
 cat << END
@@ -283,10 +303,13 @@ cat << END
   --with-poll_module                 enable poll module
   --without-poll_module              disable poll module
 
+  --with-ipv6                        enable ipv6 support
+
   --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_image_filter_module    enable ngx_http_image_filter_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
@@ -310,6 +333,7 @@ cat << END
   --without-http_fastcgi_module      disable ngx_http_fastcgi_module
   --without-http_memcached_module    disable ngx_http_memcached_module
   --without-http_limit_zone_module   disable ngx_http_limit_zone_module
+  --without-http_limit_req_module    disable ngx_http_limit_req_module
   --without-http_empty_gif_module    disable ngx_http_empty_gif_module
   --without-http_browser_module      disable ngx_http_browser_module
   --without-http_upstream_ip_hash_module
@@ -327,6 +351,7 @@ cat << END
                                      files
 
   --without-http                     disable HTTP server
+  --without-http-cache               disable HTTP cache
 
   --with-mail                        enable POP3/IMAP4/SMTP proxy module
   --with-mail_ssl_module             enable ngx_mail_ssl_module
@@ -347,7 +372,8 @@ cat << END
                                      pentium, pentiumpro, pentium3, pentium4,
                                      athlon, opteron, sparc32, sparc64, ppc64
 
-  --without-pcre                     disable PCRE libarary usage
+  --without-pcre                     disable PCRE library usage
+  --with-pcre                        force PCRE library usage
   --with-pcre=DIR                    set path to PCRE library sources
   --with-pcre-opt=OPTIONS            set additional options for PCRE building
 
@@ -394,141 +420,21 @@ if [ ".$NGX_PLATFORM" = ".win32" ]; then
 fi
 
 
-NGX_PREFIX=${NGX_PREFIX:-/usr/local/nginx}
-
-
-case ".$NGX_SBIN_PATH" in
-    ./*)
-    ;;
-
-    .)
-        NGX_SBIN_PATH=$NGX_PREFIX/sbin/nginx
-    ;;
-
-    *)
-        NGX_SBIN_PATH=$NGX_PREFIX/$NGX_SBIN_PATH
-    ;;
-esac
-
-
-case ".$NGX_CONF_PATH" in
-    ./*)
-    ;;
-
-    .)
-        NGX_CONF_PATH=$NGX_PREFIX/conf/nginx.conf
-    ;;
-
-    *)
-        NGX_CONF_PATH=$NGX_PREFIX/$NGX_CONF_PATH
-    ;;
-esac
-
-
+NGX_CONF_PATH=${NGX_CONF_PATH:-conf/nginx.conf}
 NGX_CONF_PREFIX=`dirname $NGX_CONF_PATH`
-
-
-case ".$NGX_PID_PATH" in
-    ./*)
-    ;;
-
-    .)
-        NGX_PID_PATH=$NGX_PREFIX/logs/nginx.pid
-    ;;
-
-    *)
-        NGX_PID_PATH=$NGX_PREFIX/$NGX_PID_PATH
-    ;;
-esac
-
-
-case ".$NGX_LOCK_PATH" in
-    ./*)
-    ;;
-
-    .)
-        NGX_LOCK_PATH=$NGX_PREFIX/logs/nginx.lock
-    ;;
-
-    *)
-        NGX_LOCK_PATH=$NGX_PREFIX/$NGX_LOCK_PATH
-    ;;
-esac
-
-
-case ".$NGX_ERROR_LOG_PATH" in
-    ./*)
-    ;;
+NGX_PID_PATH=${NGX_PID_PATH:-logs/nginx.pid}
+NGX_LOCK_PATH=${NGX_LOCK_PATH:-logs/nginx.lock}
 
-    .)
-        NGX_ERROR_LOG_PATH=$NGX_PREFIX/logs/error.log
-    ;;
-
-    .stderr)
-        NGX_ERROR_LOG_PATH=
-    ;;
-
-    *)
-        NGX_ERROR_LOG_PATH=$NGX_PREFIX/$NGX_ERROR_LOG_PATH
-    ;;
-esac
-
-
-case ".$NGX_HTTP_LOG_PATH" in
-    ./*)
-    ;;
-
-    .)
-        NGX_HTTP_LOG_PATH=$NGX_PREFIX/logs/access.log
-    ;;
-
-    *)
-        NGX_HTTP_LOG_PATH=$NGX_PREFIX/$NGX_HTTP_LOG_PATH
-    ;;
-esac
-
-
-case ".$NGX_HTTP_CLIENT_TEMP_PATH" in
-    ./*)
-    ;;
+if [ ".$NGX_ERROR_LOG_PATH" = ".stderr" ]; then
+    NGX_ERROR_LOG_PATH=
+else
+    NGX_ERROR_LOG_PATH=${NGX_ERROR_LOG_PATH:-logs/error.log}
+fi
 
-    .)
-        NGX_HTTP_CLIENT_TEMP_PATH=$NGX_PREFIX/client_body_temp
-    ;;
-
-    *)
-        NGX_HTTP_CLIENT_TEMP_PATH=$NGX_PREFIX/$NGX_HTTP_CLIENT_TEMP_PATH
-    ;;
-esac
-
-
-case ".$NGX_HTTP_PROXY_TEMP_PATH" in
-    ./*)
-    ;;
-
-    .)
-        NGX_HTTP_PROXY_TEMP_PATH=$NGX_PREFIX/proxy_temp
-    ;;
-
-    *)
-        NGX_HTTP_PROXY_TEMP_PATH=$NGX_PREFIX/$NGX_HTTP_PROXY_TEMP_PATH
-    ;;
-esac
-
-
-case ".$NGX_HTTP_FASTCGI_TEMP_PATH" in
-    ./*)
-    ;;
-
-    .)
-        NGX_HTTP_FASTCGI_TEMP_PATH=$NGX_PREFIX/fastcgi_temp
-    ;;
-
-    *)
-        NGX_HTTP_FASTCGI_TEMP_PATH=$NGX_PREFIX/$NGX_HTTP_FASTCGI_TEMP_PATH
-    ;;
-esac
-
+NGX_HTTP_LOG_PATH=${NGX_HTTP_LOG_PATH:-logs/access.log}
+NGX_HTTP_CLIENT_TEMP_PATH=${NGX_HTTP_CLIENT_TEMP_PATH:-client_body_temp}
+NGX_HTTP_PROXY_TEMP_PATH=${NGX_HTTP_PROXY_TEMP_PATH:-proxy_temp}
+NGX_HTTP_FASTCGI_TEMP_PATH=${NGX_HTTP_FASTCGI_TEMP_PATH:-fastcgi_temp}
 
 case ".$NGX_PERL_MODULES" in
     ./*)
--- a/auto/os/features
+++ b/auto/os/features
@@ -98,7 +98,7 @@ if test -z "$NGX_KQUEUE_CHECKED"; then
         ngx_feature_name="NGX_HAVE_TIMER_EVENT"
         ngx_feature_run=yes
         ngx_feature_incs="#include <sys/event.h>
-#include <sys/time.h>"
+                          #include <sys/time.h>"
         ngx_feature_path=
         ngx_feature_libs=
         ngx_feature_test="int      kq;
@@ -205,3 +205,72 @@ ngx_feature_path=
 ngx_feature_libs=
 ngx_feature_test="directio(0, DIRECTIO_ON);"
 . auto/feature
+
+
+ngx_feature="statfs()"
+ngx_feature_name="NGX_HAVE_STATFS"
+ngx_feature_run=no
+ngx_feature_incs="$NGX_INCLUDE_SYS_PARAM_H
+                  $NGX_INCLUDE_SYS_MOUNT_H
+                  $NGX_INCLUDE_SYS_VFS_H"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct statfs  fs;
+                  statfs(NULL, &fs);"
+. auto/feature
+
+
+ngx_feature="statvfs()"
+ngx_feature_name="NGX_HAVE_STATVFS"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/types.h>
+                  #include <sys/statvfs.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct statvfs  fs;
+                  statvfs(NULL, &fs);"
+. auto/feature
+
+
+ngx_feature="dlopen()"
+ngx_feature_name=
+ngx_feature_run=no
+ngx_feature_incs="#include <dlfcn.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="dlopen(NULL, 0)"
+. auto/feature
+
+
+if [ $ngx_found != yes ]; then
+
+    ngx_feature="dlopen() in libdl"
+    ngx_feature_libs="-ldl"
+    . auto/feature
+
+    if [ $ngx_found = yes ]; then
+        NGX_LIBDL="-ldl"
+    fi
+fi
+
+
+ngx_feature="sched_yield()"
+ngx_feature_name="NGX_HAVE_SCHED_YIELD"
+ngx_feature_run=no
+ngx_feature_incs="#include <sched.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="sched_yield()"
+. auto/feature
+
+
+if [ $ngx_found != yes ]; then
+
+    ngx_feature="sched_yield() in librt"
+    ngx_feature_libs="-lrt"
+    . auto/feature
+
+    if [ $ngx_found = yes ]; then
+        CORE_LIBS="$CORE_LIBS -lrt"
+    fi
+fi
--- a/auto/os/linux
+++ b/auto/os/linux
@@ -11,7 +11,8 @@ CORE_SRCS="$UNIX_SRCS $LINUX_SRCS"
 ngx_spacer='
 '
 
-CC_AUX_FLAGS="$CC_AUX_FLAGS -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
+cc_aux_flags="$CC_AUX_FLAGS"
+CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
 
 
 # Linux kernel version
@@ -60,12 +61,12 @@ fi
 
 # sendfile()
 
-CC_AUX_FLAGS="$CC_AUX_FLAGS -D_GNU_SOURCE"
+CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE"
 ngx_feature="sendfile()"
 ngx_feature_name="NGX_HAVE_SENDFILE"
 ngx_feature_run=yes
 ngx_feature_incs="#include <sys/sendfile.h>
-#include <errno.h>"
+                  #include <errno.h>"
 ngx_feature_path=
 ngx_feature_libs=
 ngx_feature_test="int s = 0, fd = 1;
@@ -81,12 +82,12 @@ fi
 
 # sendfile64()
 
-CC_AUX_FLAGS="$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64"
+CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
 ngx_feature="sendfile64()"
 ngx_feature_name="NGX_HAVE_SENDFILE64"
 ngx_feature_run=yes
 ngx_feature_incs="#include <sys/sendfile.h>
-#include <errno.h>"
+                  #include <errno.h>"
 ngx_feature_path=
 ngx_feature_libs=
 ngx_feature_test="int s = 0, fd = 1;
@@ -121,3 +122,22 @@ ngx_feature_libs=
 ngx_feature_test="long mask = 0;
                   sched_setaffinity(0, 32, (cpu_set_t *) &mask)"
 . auto/feature
+
+
+# crypt_r()
+
+ngx_feature="crypt_r()"
+ngx_feature_name="NGX_HAVE_GNU_CRYPT_R"
+ngx_feature_run=no
+ngx_feature_incs="#include <crypt.h>"
+ngx_feature_path=
+ngx_feature_libs=-lcrypt
+ngx_feature_test="struct crypt_data  cd;
+                  crypt_r(NULL, NULL, &cd);"
+. auto/feature
+
+
+ngx_include="sys/vfs.h";     . auto/include
+
+
+CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
--- a/auto/os/solaris
+++ b/auto/os/solaris
@@ -7,14 +7,14 @@ have=NGX_SOLARIS . auto/have_headers
 CORE_INCS="$UNIX_INCS"
 CORE_DEPS="$UNIX_DEPS $SOLARIS_DEPS"
 CORE_SRCS="$UNIX_SRCS $SOLARIS_SRCS "
-CORE_LIBS="$CORE_LIBS -lsocket -lnsl -lrt"
+CORE_LIBS="$CORE_LIBS -lsocket -lnsl"
 
 NGX_RPATH=YES
 
 # Solaris's make does not support a blank line between target and rules
 ngx_spacer=
 
-CC_AUX_FLAGS="$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64 -lsocket -lnsl -lrt"
+CC_AUX_FLAGS="$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64 -lsocket -lnsl"
 
 
 if [ $ZLIB_ASM != NO ]; then
--- a/auto/os/win32
+++ b/auto/os/win32
@@ -8,8 +8,9 @@ CORE_INCS="$WIN32_INCS"
 CORE_DEPS="$WIN32_DEPS"
 CORE_SRCS="$WIN32_SRCS $IOCP_SRCS"
 OS_CONFIG="$WIN32_CONFIG"
-CORE_LIBS="$CORE_LIBS shell32.lib ws2_32.lib"
+CORE_LIBS="$CORE_LIBS advapi32.lib ws2_32.lib"
 NGX_ICONS="$NGX_WIN32_ICONS"
+SELECT_SRCS=$WIN32_SELECT_SRCS
 
 EVENT_MODULES="$EVENT_MODULES $IOCP_MODULE"
 EVENT_FOUND=YES
@@ -19,5 +20,9 @@ if [ $EVENT_SELECT = NO ]; then
     EVENT_MODULES="$EVENT_MODULES $SELECT_MODULE"
 fi
 
+if [ $NGX_IPV6 = YES ]; then
+    have=NGX_HAVE_INET6 . auto/have
+fi
+
 have=NGX_HAVE_AIO . auto/have
 have=NGX_HAVE_IOCP . auto/have
--- a/auto/sources
+++ b/auto/sources
@@ -33,8 +33,7 @@ CORE_DEPS="src/core/nginx.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"
+           src/core/ngx_open_file_cache.h"
 
 
 CORE_SRCS="src/core/nginx.c \
@@ -62,8 +61,7 @@ CORE_SRCS="src/core/nginx.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"
+           src/core/ngx_open_file_cache.c"
 
 
 REGEX_DEPS=src/core/ngx_regex.h
@@ -97,6 +95,7 @@ EVENT_SRCS="src/event/ngx_event.c \
 
 SELECT_MODULE=ngx_select_module
 SELECT_SRCS=src/event/modules/ngx_select_module.c
+WIN32_SELECT_SRCS=src/event/modules/ngx_win32_select_module.c
 
 POLL_MODULE=ngx_poll_module
 POLL_SRCS=src/event/modules/ngx_poll_module.c
@@ -217,8 +216,6 @@ WIN32_DEPS="$CORE_DEPS $EVENT_DEPS \
             src/os/win32/ngx_socket.h \
             src/os/win32/ngx_os.h \
             src/os/win32/ngx_user.h \
-            src/os/win32/ngx_gui.h \
-            src/os/win32/ngx_gui_resources.h \
             src/os/win32/ngx_process_cycle.h"
 
 WIN32_CONFIG=src/os/win32/ngx_win32_config.h
@@ -235,14 +232,15 @@ WIN32_SRCS="$CORE_SRCS $EVENT_SRCS \
             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.c \
             src/os/win32/ngx_wsasend_chain.c \
             src/os/win32/ngx_win32_init.c \
             src/os/win32/ngx_user.c \
-            src/os/win32/ngx_gui.c \
+            src/os/win32/ngx_event_log.c \
             src/os/win32/ngx_process_cycle.c \
             src/event/ngx_event_acceptex.c"
 
-NGX_WIN32_ICONS="src/os/win32/nginx.ico src/os/win32/nginx_tray.ico"
+NGX_WIN32_ICONS="src/os/win32/nginx.ico"
 NGX_WIN32_RC="src/os/win32/nginx.rc"
 
 
@@ -254,8 +252,6 @@ HTTP_MODULES="ngx_http_module \
               ngx_http_log_module \
               ngx_http_upstream_module"
 
-HTTP_CACHE_MODULE=ngx_http_cache_module
-
 HTTP_WRITE_FILTER_MODULE="ngx_http_write_filter_module"
 HTTP_HEADER_FILTER_MODULE="ngx_http_header_filter_module"
 
@@ -314,7 +310,6 @@ HTTP_SRCS="$HTTP_SRCS src/http/ngx_http_
 
 HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c
 
-HTTP_CACHE_SRCS=src/http/ngx_http_cache.c
 HTTP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c
 
 
@@ -335,6 +330,10 @@ HTTP_XSLT_FILTER_MODULE=ngx_http_xslt_fi
 HTTP_XSLT_SRCS=src/http/modules/ngx_http_xslt_filter_module.c
 
 
+HTTP_IMAGE_FILTER_MODULE=ngx_http_image_filter_module
+HTTP_IMAGE_SRCS=src/http/modules/ngx_http_image_filter_module.c
+
+
 HTTP_SUB_FILTER_MODULE=ngx_http_sub_filter_module
 HTTP_SUB_SRCS=src/http/modules/ngx_http_sub_filter_module.c
 
@@ -418,6 +417,10 @@ HTTP_LIMIT_ZONE_MODULE=ngx_http_limit_zo
 HTTP_LIMIT_ZONE_SRCS=src/http/modules/ngx_http_limit_zone_module.c
 
 
+HTTP_LIMIT_REQ_MODULE=ngx_http_limit_req_module
+HTTP_LIMIT_REQ_SRCS=src/http/modules/ngx_http_limit_req_module.c
+
+
 HTTP_EMPTY_GIF_MODULE=ngx_http_empty_gif_module
 HTTP_EMPTY_GIF_SRCS=src/http/modules/ngx_http_empty_gif_module.c
 
--- a/auto/stubs
+++ b/auto/stubs
@@ -2,7 +2,6 @@
 # Copyright (C) Igor Sysoev
 
 
-have=NGX_USE_HTTP_FILE_CACHE_UNIQ . auto/have
 have=NGX_SUPPRESS_WARN . auto/have
 
 have=NGX_SMP . auto/have
--- a/auto/summary
+++ b/auto/summary
@@ -74,65 +74,6 @@ esac
 echo
 
 
-if [ $HTTP_REWRITE = YES ]; then
-    if [ $USE_PCRE = DISABLED ]; then
-
-cat << END
-$0: error: the HTTP rewrite module requires the PCRE library.
-You can either disable the module by using --without-http_rewrite_module
-option or you have to enable the PCRE support.
-
-END
-        exit 1
-    fi
-
-    if [ $PCRE = NONE -o $PCRE = NO ]; then
-
-cat << END
-$0: error: the HTTP rewrite module requires the PCRE library.
-You can either disable the module by using --without-http_rewrite_module
-option, or install the PCRE library into the system, or build the PCRE library
-statically from the source with nginx by using --with-pcre=<path> option.
-
-END
-
-        exit 1
-    fi
-fi
-
-
-if [ $HTTP_GZIP = YES ]; then
-    if [ $ZLIB = NONE -o $ZLIB = NO ]; then
-
-cat << END
-$0: error: the HTTP gzip module requires the zlib library.
-You can either disable the module by using --without-http_gzip_module
-option, or install the zlib library into the system, or build the zlib library
-statically from the source with nginx by using --with-zlib=<path> option.
-
-END
-
-        exit 1
-    fi
-fi
-
-
-if [ $HTTP_SSL = YES ]; then
-    if [ $OPENSSL = NONE -o $OPENSSL = NO ]; then
-
-cat << END
-$0: error: the HTTP SSL module requires the OpenSSL library.
-You can either do not enable the module, or install the OpenSSL library
-into the system, or build the OpenSSL library statically from the source
-with nginx by using --with-openssl=<path> option.
-
-END
-
-        exit 1
-    fi
-fi
-
-
 cat << END
   nginx path prefix: "$NGX_PREFIX"
   nginx binary file: "$NGX_SBIN_PATH"
--- a/auto/unix
+++ b/auto/unix
@@ -64,6 +64,21 @@ ngx_param=NGX_TIME_T_LEN; ngx_value=$ngx
 # syscalls, libc calls and some features
 
 
+if [ $NGX_IPV6 = YES ]; then
+    ngx_feature="AF_INET6"
+    ngx_feature_name="NGX_HAVE_INET6"
+    ngx_feature_run=no
+    ngx_feature_incs="#include <sys/socket.h>
+                      #include <netinet/in.h>
+                      #include <arpa/inet.h>"
+    ngx_feature_path=
+    ngx_feature_libs=
+    ngx_feature_test="struct sockaddr_in6  sin6;
+                      sin6.sin6_family = AF_INET6;"
+    . auto/feature
+fi
+
+
 ngx_feature="setproctitle()"
 ngx_feature_name="NGX_HAVE_SETPROCTITLE"
 ngx_feature_run=no
@@ -148,16 +163,6 @@ ngx_feature_test="void *p; p = memalign(
 . auto/feature
 
 
-ngx_feature="sched_yield()"
-ngx_feature_name="NGX_HAVE_SCHED_YIELD"
-ngx_feature_run=no
-ngx_feature_incs="#include <sched.h>"
-ngx_feature_path=
-ngx_feature_libs=
-ngx_feature_test="sched_yield()"
-. auto/feature
-
-
 ngx_feature="mmap(MAP_ANON|MAP_SHARED)"
 ngx_feature_name="NGX_HAVE_MAP_ANON"
 ngx_feature_run=yes
@@ -175,8 +180,8 @@ ngx_feature='mmap("/dev/zero", MAP_SHARE
 ngx_feature_name="NGX_HAVE_MAP_DEVZERO"
 ngx_feature_run=yes
 ngx_feature_incs="#include <sys/mman.h>
-#include <sys/stat.h>
-#include <fcntl.h>"
+                  #include <sys/stat.h>
+                  #include <fcntl.h>"
 ngx_feature_path=
 ngx_feature_libs=
 ngx_feature_test='void *p; int  fd;
@@ -190,7 +195,7 @@ ngx_feature="System V shared memory"
 ngx_feature_name="NGX_HAVE_SYSVSHM"
 ngx_feature_run=yes
 ngx_feature_incs="#include <sys/ipc.h>
-#include <sys/shm.h>"
+                  #include <sys/shm.h>"
 ngx_feature_path=
 ngx_feature_libs=
 ngx_feature_test="int  id;
@@ -214,7 +219,7 @@ ngx_feature="ioctl(FIONBIO)"
 ngx_feature_name="NGX_HAVE_FIONBIO"
 ngx_feature_run=no
 ngx_feature_incs="#include <sys/ioctl.h>
-$NGX_INCLUDE_SYS_FILIO_H"
+                  $NGX_INCLUDE_SYS_FILIO_H"
 ngx_feature_path=
 ngx_feature_libs=
 ngx_feature_test="int i; i = FIONBIO"
--- a/conf/nginx.conf
+++ b/conf/nginx.conf
@@ -18,8 +18,8 @@ http {
     include       mime.types;
     default_type  application/octet-stream;
 
-    #log_format  main  '$remote_addr - $remote_user [$time_local] $request '
-    #                  '"$status" $body_bytes_sent "$http_referer" '
+    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
+    #                  '$status $body_bytes_sent "$http_referer" '
     #                  '"$http_user_agent" "$http_x_forwarded_for"';
 
     #access_log  logs/access.log  main;
--- a/configure
+++ b/configure
@@ -3,8 +3,6 @@
 # Copyright (C) Igor Sysoev
 
 
-NGX_CONFIGURE=`echo $@ | sed 's/"/\\\\"/g'`
-
 . auto/options
 . auto/init
 . auto/sources
@@ -33,6 +31,12 @@ if test -z "$NGX_PLATFORM"; then
 
     NGX_PLATFORM="$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE";
 
+    case "$NGX_SYSTEM" in
+        MINGW32_*)
+            NGX_PLATFORM=win32
+        ;;
+    esac
+
 else
     echo "building for $NGX_PLATFORM"
     NGX_SYSTEM=$NGX_PLATFORM
@@ -54,6 +58,39 @@ fi
 . auto/modules
 . auto/lib/conf
 
+case ".$NGX_PREFIX" in
+    .)
+        NGX_PREFIX=${NGX_PREFIX:-/usr/local/nginx}
+        have=NGX_PREFIX value="\"$NGX_PREFIX/\"" . auto/define
+    ;;
+
+    .!)
+        NGX_PREFIX=
+    ;;
+
+    *)
+        have=NGX_PREFIX value="\"$NGX_PREFIX/\"" . auto/define
+    ;;
+esac
+
+if [ ".$NGX_CONF_PREFIX" != "." ]; then
+    have=NGX_CONF_PREFIX value="\"$NGX_CONF_PREFIX/\"" . auto/define
+fi
+
+have=NGX_SBIN_PATH value="\"$NGX_SBIN_PATH\"" . auto/define
+have=NGX_CONF_PATH value="\"$NGX_CONF_PATH\"" . auto/define
+have=NGX_PID_PATH value="\"$NGX_PID_PATH\"" . auto/define
+have=NGX_LOCK_PATH value="\"$NGX_LOCK_PATH\"" . auto/define
+have=NGX_ERROR_LOG_PATH value="\"$NGX_ERROR_LOG_PATH\"" . auto/define
+
+have=NGX_HTTP_LOG_PATH value="\"$NGX_HTTP_LOG_PATH\"" . auto/define
+have=NGX_HTTP_CLIENT_TEMP_PATH value="\"$NGX_HTTP_CLIENT_TEMP_PATH\""
+. auto/define
+have=NGX_HTTP_PROXY_TEMP_PATH value="\"$NGX_HTTP_PROXY_TEMP_PATH\""
+. auto/define
+have=NGX_HTTP_FASTCGI_TEMP_PATH value="\"$NGX_HTTP_FASTCGI_TEMP_PATH\""
+. auto/define
+
 . auto/make
 . auto/lib/make
 . auto/install
@@ -65,24 +102,6 @@ fi
 # STUB
 . auto/stubs
 
-have=NGX_PREFIX value="\"$NGX_PREFIX/\"" . auto/define
-have=NGX_SBIN_PATH value="\"$NGX_SBIN_PATH\"" . auto/define
-have=NGX_CONF_PREFIX value="\"$NGX_CONF_PREFIX/\"" . auto/define
-have=NGX_CONF_PATH value="\"$NGX_CONF_PATH\"" . auto/define
-have=NGX_PID_PATH value="\"$NGX_PID_PATH\"" . auto/define
-have=NGX_LOCK_PATH value="\"$NGX_LOCK_PATH\"" . auto/define
-if test -n "$NGX_ERROR_LOG_PATH"; then
-    have=NGX_ERROR_LOG_PATH value="\"$NGX_ERROR_LOG_PATH\"" . auto/define
-fi
-
-have=NGX_HTTP_LOG_PATH value="\"$NGX_HTTP_LOG_PATH\"" . auto/define
-have=NGX_HTTP_CLIENT_TEMP_PATH value="\"$NGX_HTTP_CLIENT_TEMP_PATH\""
-. auto/define
-have=NGX_HTTP_PROXY_TEMP_PATH value="\"$NGX_HTTP_PROXY_TEMP_PATH\""
-. auto/define
-have=NGX_HTTP_FASTCGI_TEMP_PATH value="\"$NGX_HTTP_FASTCGI_TEMP_PATH\""
-. auto/define
-
 have=NGX_USER value="\"$NGX_USER\"" . auto/define
 have=NGX_GROUP value="\"$NGX_GROUP\"" . auto/define
 
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -10,7 +10,8 @@
 
 
 static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle);
-static ngx_int_t ngx_getopt(ngx_cycle_t *cycle, int argc, char *const *argv);
+static ngx_int_t ngx_get_options(int argc, char *const *argv);
+static ngx_int_t ngx_process_options(ngx_cycle_t *cycle);
 static ngx_int_t ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv);
 static void *ngx_core_module_create_conf(ngx_cycle_t *cycle);
 static char *ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf);
@@ -180,10 +181,16 @@ ngx_module_t  ngx_core_module = {
 };
 
 
-ngx_uint_t  ngx_max_module;
+ngx_uint_t          ngx_max_module;
 
-static ngx_uint_t  ngx_show_version;
-static ngx_uint_t  ngx_show_configure;
+static ngx_uint_t   ngx_show_help;
+static ngx_uint_t   ngx_show_version;
+static ngx_uint_t   ngx_show_configure;
+static u_char      *ngx_prefix;
+static u_char      *ngx_conf_file;
+static u_char      *ngx_conf_params;
+static char        *ngx_signal;
+
 
 static char **ngx_os_environ;
 
@@ -196,6 +203,50 @@ main(int argc, char *const *argv)
     ngx_cycle_t      *cycle, init_cycle;
     ngx_core_conf_t  *ccf;
 
+    if (ngx_get_options(argc, argv) != NGX_OK) {
+        return 1;
+    }
+
+    if (ngx_show_version) {
+        ngx_log_stderr(0, "nginx version: " NGINX_VER);
+
+        if (ngx_show_help) {
+            ngx_log_stderr(0,
+                "Usage: nginx [-?hvVt] [-s signal] [-c filename] "
+                             "[-p prefix] [-g directives]" CRLF CRLF
+                "Options:" CRLF
+                "  -?,-h         : this help" CRLF
+                "  -v            : show version and exit" CRLF
+                "  -V            : show version and configure options then exit"
+                                   CRLF
+                "  -t            : test configuration and exit" CRLF
+                "  -s signal     : send signal to a master process: "
+                                   "stop, quit, reopen, reload" CRLF
+#ifdef NGX_PREFIX
+                "  -p prefix     : set prefix path (default: "
+                                   NGX_PREFIX ")" CRLF
+#else
+                "  -p prefix     : set prefix path (default: NONE)" CRLF
+#endif
+                "  -c filename   : set configuration file (default: "
+                                   NGX_CONF_PATH ")" CRLF
+                "  -g directives : set global directives out of configuration "
+                                   "file" CRLF
+                );
+        }
+
+        if (ngx_show_configure) {
+#ifdef NGX_COMPILER
+            ngx_log_stderr(0, "built by " NGX_COMPILER);
+#endif
+            ngx_log_stderr(0, "configure arguments:" NGX_CONFIGURE);
+        }
+
+        if (!ngx_test_config) {
+            return 0;
+        }
+    }
+
 #if (NGX_FREEBSD)
     ngx_debug_init();
 #endif
@@ -210,7 +261,7 @@ main(int argc, char *const *argv)
 
     ngx_pid = ngx_getpid();
 
-    log = ngx_log_init();
+    log = ngx_log_init(ngx_prefix);
     if (log == NULL) {
         return 1;
     }
@@ -220,7 +271,10 @@ main(int argc, char *const *argv)
     ngx_ssl_init(log);
 #endif
 
-    /* init_cycle->log is required for signal handlers and ngx_getopt() */
+    /*
+     * init_cycle->log is required for signal handlers and
+     * ngx_process_options()
+     */
 
     ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));
     init_cycle.log = log;
@@ -235,39 +289,10 @@ main(int argc, char *const *argv)
         return 1;
     }
 
-    if (ngx_getopt(&init_cycle, argc, ngx_argv) != NGX_OK) {
+    if (ngx_process_options(&init_cycle) != NGX_OK) {
         return 1;
     }
 
-    if (ngx_show_version) {
-        ngx_write_fd(ngx_stderr_fileno, "nginx version: " NGINX_VER CRLF,
-                     sizeof("nginx version: " NGINX_VER CRLF) - 1);
-
-        if (ngx_show_configure) {
-#ifdef NGX_COMPILER
-            ngx_write_fd(ngx_stderr_fileno, "built by " NGX_COMPILER CRLF,
-                         sizeof("built by " NGX_COMPILER CRLF) - 1);
-#endif
-
-#ifndef __WATCOMC__
-
-            /* OpenWatcomC could not build the long NGX_CONFIGURE string */
-
-            ngx_write_fd(ngx_stderr_fileno,
-                        "configure arguments: " NGX_CONFIGURE CRLF,
-                        sizeof("configure arguments :" NGX_CONFIGURE CRLF) - 1);
-#endif
-        }
-
-        if (!ngx_test_config) {
-            return 0;
-        }
-    }
-
-    if (ngx_test_config) {
-        log->log_level = NGX_LOG_INFO;
-    }
-
     if (ngx_os_init(log) != NGX_OK) {
         return 1;
     }
@@ -292,45 +317,34 @@ main(int argc, char *const *argv)
     cycle = ngx_init_cycle(&init_cycle);
     if (cycle == NULL) {
         if (ngx_test_config) {
-            ngx_log_error(NGX_LOG_EMERG, log, 0,
-                          "the configuration file %s test failed",
-                          init_cycle.conf_file.data);
+            ngx_log_stderr(0, "configuration file %s test failed",
+                           init_cycle.conf_file.data);
         }
 
         return 1;
     }
 
     if (ngx_test_config) {
-        ngx_log_error(NGX_LOG_INFO, log, 0,
-                      "the configuration file %s was tested successfully",
-                      cycle->conf_file.data);
+        ngx_log_stderr(0, "configuration file %s test is successful",
+                       cycle->conf_file.data);
         return 0;
     }
 
+    if (ngx_signal) {
+        return ngx_signal_process(cycle, ngx_signal);
+    }
+
     ngx_os_status(cycle->log);
 
     ngx_cycle = cycle;
 
     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
 
-    ngx_process = ccf->master ? NGX_PROCESS_MASTER : NGX_PROCESS_SINGLE;
-
-#if (NGX_WIN32)
-
-#if 0
-
-    TODO:
+    if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) {
+        ngx_process = NGX_PROCESS_MASTER;
+    }
 
-    if (ccf->run_as_service) {
-        if (ngx_service(cycle->log) != NGX_OK) {
-            return 1;
-        }
-
-        return 0;
-    }
-#endif
-
-#else
+#if !(NGX_WIN32)
 
     if (ngx_init_signals(cycle->log) != NGX_OK) {
         return 1;
@@ -344,17 +358,28 @@ main(int argc, char *const *argv)
         ngx_daemonized = 1;
     }
 
+#endif
+
     if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) {
         return 1;
     }
 
-#endif
+    if (cycle->log->file->fd != ngx_stderr) {
 
-    if (ngx_process == NGX_PROCESS_MASTER) {
-        ngx_master_process_cycle(cycle);
+        if (ngx_set_stderr(cycle->log->file->fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+                          ngx_set_stderr_n " failed");
+            return 1;
+        }
+    }
+
+    ngx_use_stderr = 0;
+
+    if (ngx_process == NGX_PROCESS_SINGLE) {
+        ngx_single_process_cycle(cycle);
 
     } else {
-        ngx_single_process_cycle(cycle);
+        ngx_master_process_cycle(cycle);
     }
 
     return 0;
@@ -379,7 +404,7 @@ ngx_add_inherited_sockets(ngx_cycle_t *c
 
     if (ngx_array_init(&cycle->listening, cycle->pool, 10,
                        sizeof(ngx_listening_t))
-        == NGX_ERROR)
+        != NGX_OK)
     {
         return NGX_ERROR;
     }
@@ -524,6 +549,8 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, 
     ngx_core_conf_t   *ccf;
     ngx_listening_t   *ls;
 
+    ngx_memzero(&ctx, sizeof(ngx_exec_ctx_t));
+
     ctx.path = argv[0];
     ctx.name = "new binary process";
     ctx.argv = argv;
@@ -607,64 +634,118 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, 
 
 
 static ngx_int_t
-ngx_getopt(ngx_cycle_t *cycle, int argc, char *const *argv)
+ngx_get_options(int argc, char *const *argv)
 {
-    ngx_int_t  i;
+    u_char     *p;
+    ngx_int_t   i;
 
     for (i = 1; i < argc; i++) {
-        if (argv[i][0] != '-') {
-            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
-                          "invalid option: \"%s\"", argv[i]);
+
+        p = (u_char *) argv[i];
+
+        if (*p++ != '-') {
+            ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]);
             return NGX_ERROR;
         }
 
-        switch (argv[i][1]) {
+        while (*p) {
+
+            switch (*p++) {
+
+            case '?':
+            case 'h':
+                ngx_show_version = 1;
+                ngx_show_help = 1;
+                break;
+
+            case 'v':
+                ngx_show_version = 1;
+                break;
+
+            case 'V':
+                ngx_show_version = 1;
+                ngx_show_configure = 1;
+                break;
 
-        case 'v':
-            ngx_show_version = 1;
-            break;
+            case 't':
+                ngx_test_config = 1;
+                break;
+
+            case 'p':
+                if (*p) {
+                    ngx_prefix = p;
+                    goto next;
+                }
+
+                if (argv[++i]) {
+                    ngx_prefix = (u_char *) argv[i];
+                    goto next;
+                }
+
+                ngx_log_stderr(0, "option \"-p\" requires directory name");
+                return NGX_ERROR;
+
+            case 'c':
+                if (*p) {
+                    ngx_conf_file = p;
+                    goto next;
+                }
 
-        case 'V':
-            ngx_show_version = 1;
-            ngx_show_configure = 1;
-            break;
+                if (argv[++i]) {
+                    ngx_conf_file = (u_char *) argv[i];
+                    goto next;
+                }
+
+                ngx_log_stderr(0, "option \"-c\" requires file name");
+                return NGX_ERROR;
+
+            case 'g':
+                if (*p) {
+                    ngx_conf_params = p;
+                    goto next;
+                }
+
+                if (argv[++i]) {
+                    ngx_conf_params = (u_char *) argv[i];
+                    goto next;
+                }
+
+                ngx_log_stderr(0, "option \"-g\" requires parameter");
+                return NGX_ERROR;
 
-        case 't':
-            ngx_test_config = 1;
-            break;
+            case 's':
+                if (*p) {
+                    ngx_signal = (char *) p;
+
+                } else if (argv[++i]) {
+                    ngx_signal = argv[i];
+
+                } else {
+                    ngx_log_stderr(0, "option \"-s\" requires parameter");
+                    return NGX_ERROR;
+                }
 
-        case 'c':
-            if (argv[i + 1] == NULL) {
-                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
-                              "the option \"-c\" requires file name");
+                if (ngx_strcmp(ngx_signal, "stop") == 0
+                    || ngx_strcmp(ngx_signal, "quit") == 0
+                    || ngx_strcmp(ngx_signal, "reopen") == 0
+                    || ngx_strcmp(ngx_signal, "reload") == 0)
+                {
+                    ngx_process = NGX_PROCESS_SIGNALLER;
+                    goto next;
+                }
+
+                ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal);
+                return NGX_ERROR;
+
+            default:
+                ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1));
                 return NGX_ERROR;
             }
-
-            cycle->conf_file.data = (u_char *) argv[++i];
-            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;
+    next:
 
-        default:
-            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
-                          "invalid option: \"%s\"", argv[i]);
-            return NGX_ERROR;
-        }
-    }
-
-    if (cycle->conf_file.data == NULL) {
-        cycle->conf_file.len = sizeof(NGX_CONF_PATH) - 1;
-        cycle->conf_file.data = (u_char *) NGX_CONF_PATH;
+        continue;
     }
 
     return NGX_OK;
@@ -713,6 +794,106 @@ ngx_save_argv(ngx_cycle_t *cycle, int ar
 }
 
 
+static ngx_int_t
+ngx_process_options(ngx_cycle_t *cycle)
+{
+    u_char  *p;
+    size_t   len;
+
+    if (ngx_prefix) {
+        len = ngx_strlen(ngx_prefix);
+        p = ngx_prefix;
+
+        if (!ngx_path_separator(*p)) {
+            p = ngx_pnalloc(cycle->pool, len + 1);
+            if (p == NULL) {
+                return NGX_ERROR;
+            }
+
+            ngx_memcpy(p, ngx_prefix, len);
+            p[len++] = '/';
+        }
+
+        cycle->conf_prefix.len = len;
+        cycle->conf_prefix.data = p;
+        cycle->prefix.len = len;
+        cycle->prefix.data = p;
+
+    } else {
+
+#ifndef NGX_PREFIX
+
+        p = ngx_pnalloc(cycle->pool, NGX_MAX_PATH);
+        if (p == NULL) {
+            return NGX_ERROR;
+        }
+
+        if (ngx_getcwd(p, NGX_MAX_PATH) == 0) {
+            ngx_log_stderr(ngx_errno, "[emerg]: " ngx_getcwd_n " failed");
+            return NGX_ERROR;
+        }
+
+        len = ngx_strlen(p);
+
+        p[len++] = '/';
+
+        cycle->conf_prefix.len = len;
+        cycle->conf_prefix.data = p;
+        cycle->prefix.len = len;
+        cycle->prefix.data = p;
+
+#else
+
+#ifdef NGX_CONF_PREFIX
+        cycle->conf_prefix.len = sizeof(NGX_CONF_PREFIX) - 1;
+        cycle->conf_prefix.data = (u_char *) NGX_CONF_PREFIX;
+#else
+        cycle->conf_prefix.len = sizeof(NGX_PREFIX) - 1;
+        cycle->conf_prefix.data = (u_char *) NGX_PREFIX;
+#endif
+        cycle->prefix.len = sizeof(NGX_PREFIX) - 1;
+        cycle->prefix.data = (u_char *) NGX_PREFIX;
+
+#endif
+    }
+
+    if (ngx_conf_file) {
+        cycle->conf_file.len = ngx_strlen(ngx_conf_file);
+        cycle->conf_file.data = ngx_conf_file;
+
+    } else {
+        cycle->conf_file.len = sizeof(NGX_CONF_PATH) - 1;
+        cycle->conf_file.data = (u_char *) NGX_CONF_PATH;
+    }
+
+    if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    for (p = cycle->conf_file.data + cycle->conf_file.len - 1;
+         p > cycle->conf_file.data;
+         p--)
+    {
+        if (ngx_path_separator(*p)) {
+            cycle->conf_prefix.len = p - ngx_cycle->conf_file.data + 1;
+            cycle->conf_prefix.data = ngx_cycle->conf_file.data;
+            break;
+        }
+    }
+
+    if (ngx_conf_params) {
+        cycle->conf_param.len = ngx_strlen(ngx_conf_params);
+        cycle->conf_param.data = ngx_conf_params;
+    }
+
+    if (ngx_test_config) {
+        cycle->log->log_level = NGX_LOG_INFO;
+    }
+
+    return NGX_OK;
+}
+
+
 static void *
 ngx_core_module_create_conf(ngx_cycle_t *cycle)
 {
@@ -796,6 +977,27 @@ ngx_core_module_init_conf(ngx_cycle_t *c
 
 #endif
 
+
+    if (ccf->pid.len == 0) {
+        ccf->pid.len = sizeof(NGX_PID_PATH) - 1;
+        ccf->pid.data = (u_char *) NGX_PID_PATH;
+    }
+
+    if (ngx_conf_full_name(cycle, &ccf->pid, 0) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    ccf->oldpid.len = ccf->pid.len + sizeof(NGX_OLDPID_EXT);
+
+    ccf->oldpid.data = ngx_pnalloc(cycle->pool, ccf->oldpid.len);
+    if (ccf->oldpid.data == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    ngx_memcpy(ngx_cpymem(ccf->oldpid.data, ccf->pid.data, ccf->pid.len),
+               NGX_OLDPID_EXT, sizeof(NGX_OLDPID_EXT));
+
+
 #if !(NGX_WIN32)
 
     if (ccf->user == (uid_t) NGX_CONF_UNSET_UINT && geteuid() == 0) {
@@ -824,32 +1026,13 @@ ngx_core_module_init_conf(ngx_cycle_t *c
         ccf->group = grp->gr_gid;
     }
 
-    if (ccf->pid.len == 0) {
-        ccf->pid.len = sizeof(NGX_PID_PATH) - 1;
-        ccf->pid.data = (u_char *) NGX_PID_PATH;
-    }
-
-    if (ngx_conf_full_name(cycle, &ccf->pid, 0) == NGX_ERROR) {
-        return NGX_CONF_ERROR;
-    }
-
-    ccf->oldpid.len = ccf->pid.len + sizeof(NGX_OLDPID_EXT);
-
-    ccf->oldpid.data = ngx_pnalloc(cycle->pool, ccf->oldpid.len);
-    if (ccf->oldpid.data == NULL) {
-        return NGX_CONF_ERROR;
-    }
-
-    ngx_memcpy(ngx_cpymem(ccf->oldpid.data, ccf->pid.data, ccf->pid.len),
-               NGX_OLDPID_EXT, sizeof(NGX_OLDPID_EXT));
-
 
     if (ccf->lock_file.len == 0) {
         ccf->lock_file.len = sizeof(NGX_LOCK_PATH) - 1;
         ccf->lock_file.data = (u_char *) NGX_LOCK_PATH;
     }
 
-    if (ngx_conf_full_name(cycle, &ccf->lock_file, 0) == NGX_ERROR) {
+    if (ngx_conf_full_name(cycle, &ccf->lock_file, 0) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.7.19"
+#define nginx_version         8005
+#define NGINX_VERSION      "0.8.5"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_buf.c
+++ b/src/core/ngx_buf.c
@@ -201,12 +201,6 @@ ngx_chain_update_chains(ngx_chain_t **fr
             break;
         }
 
-#if (NGX_HAVE_WRITE_ZEROCOPY)
-        if ((*busy)->buf->zerocopy_busy) {
-            break;
-        }
-#endif
-
         if ((*busy)->buf->tag != tag) {
             *busy = (*busy)->next;
             continue;
--- a/src/core/ngx_buf.h
+++ b/src/core/ngx_buf.h
@@ -51,8 +51,6 @@ struct ngx_buf_s {
     unsigned         last_shadow:1;
     unsigned         temp_file:1;
 
-    unsigned         zerocopy_busy:1;
-
     /* STUB */ int   num;
 };
 
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -12,6 +12,7 @@
 static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last);
 static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf);
 static char *ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static ngx_int_t ngx_conf_test_full_name(ngx_str_t *name);
 static void ngx_conf_flush_files(ngx_cycle_t *cycle);
 
 
@@ -61,6 +62,7 @@ static ngx_uint_t argument_number[] = {
 char *
 ngx_conf_param(ngx_conf_t *cf)
 {
+    char             *rv;
     ngx_str_t        *param;
     ngx_buf_t         b;
     ngx_conf_file_t   conf_file;
@@ -82,13 +84,17 @@ ngx_conf_param(ngx_conf_t *cf)
     b.temporary = 1;
 
     conf_file.file.fd = NGX_INVALID_FILE;
-    conf_file.file.name.data = (u_char *) "command line";
-    conf_file.line = 1;
+    conf_file.file.name.data = NULL;
+    conf_file.line = 0;
 
     cf->conf_file = &conf_file;
     cf->conf_file->buffer = &b;
 
-    return ngx_conf_parse(cf, NULL);
+    rv = ngx_conf_parse(cf, NULL);
+
+    cf->conf_file = NULL;
+
+    return rv;
 }
 
 
@@ -98,8 +104,8 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t
     char             *rv;
     ngx_fd_t          fd;
     ngx_int_t         rc;
-    ngx_buf_t        *b;
-    ngx_conf_file_t  *prev;
+    ngx_buf_t         buf;
+    ngx_conf_file_t  *prev, conf_file;
     enum {
         parse_file = 0,
         parse_block,
@@ -125,32 +131,24 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t
 
         prev = cf->conf_file;
 
-        cf->conf_file = ngx_palloc(cf->pool, sizeof(ngx_conf_file_t));
-        if (cf->conf_file == NULL) {
-            return NGX_CONF_ERROR;
-        }
+        cf->conf_file = &conf_file;
 
         if (ngx_fd_info(fd, &cf->conf_file->file.info) == -1) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,
                           ngx_fd_info_n " \"%s\" failed", filename->data);
         }
 
-        b = ngx_calloc_buf(cf->pool);
-        if (b == NULL) {
-            return NGX_CONF_ERROR;
+        cf->conf_file->buffer = &buf;
+
+        buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
+        if (buf.start == NULL) {
+            goto failed;
         }
 
-        cf->conf_file->buffer = b;
-
-        b->start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
-        if (b->start == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        b->pos = b->start;
-        b->last = b->start;
-        b->end = b->last + NGX_CONF_BUFFER;
-        b->temporary = 1;
+        buf.pos = buf.start;
+        buf.last = buf.start;
+        buf.end = buf.last + NGX_CONF_BUFFER;
+        buf.temporary = 1;
 
         cf->conf_file->file.fd = fd;
         cf->conf_file->file.name.len = filename->len;
@@ -256,9 +254,9 @@ failed:
 done:
 
     if (filename) {
-        ngx_free(cf->conf_file->buffer->start);
-
-        cf->conf_file = prev;
+        if (cf->conf_file->buffer->start) {
+            ngx_free(cf->conf_file->buffer->start);
+        }
 
         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
@@ -266,6 +264,8 @@ done:
                           cf->conf_file->file.name.data);
             return NGX_CONF_ERROR;
         }
+
+        cf->conf_file = prev;
     }
 
     if (rc == NGX_ERROR) {
@@ -447,7 +447,9 @@ ngx_conf_read_token(ngx_conf_t *cf)
     last_space = 1;
     sharp_comment = 0;
     variable = 0;
-    quoted = s_quoted = d_quoted = 0;
+    quoted = 0;
+    s_quoted = 0;
+    d_quoted = 0;
 
     cf->args->nelts = 0;
     b = cf->conf_file->buffer;
@@ -747,7 +749,7 @@ ngx_conf_include(ngx_conf_t *cf, ngx_com
 
     ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
 
-    if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR) {
+    if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
@@ -801,46 +803,94 @@ ngx_int_t
 ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name, ngx_uint_t conf_prefix)
 {
     size_t      len;
-    u_char     *p, *prefix;
-    ngx_str_t   old;
+    u_char     *p, *n, *prefix;
+    ngx_int_t   rc;
+
+    rc = ngx_conf_test_full_name(name);
+
+    if (rc == NGX_OK) {
+        return rc;
+    }
+
+    if (conf_prefix) {
+        len = cycle->conf_prefix.len;
+        prefix = cycle->conf_prefix.data;
+
+    } else {
+        len = cycle->prefix.len;
+        prefix = cycle->prefix.data;
+    }
+
+#if (NGX_WIN32)
+
+    if (rc == 2) {
+        len = rc;
+    }
+
+#endif
+
+    n = ngx_pnalloc(cycle->pool, len + name->len + 1);
+    if (n == NULL) {
+        return NGX_ERROR;
+    }
+
+    p = ngx_cpymem(n, prefix, len);
+    ngx_cpystrn(p, name->data, name->len + 1);
+
+    name->len += len;
+    name->data = n;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_conf_test_full_name(ngx_str_t *name)
+{
+#if (NGX_WIN32)
+    u_char  c0, c1;
+
+    c0 = name->data[0];
+
+    if (name->len < 2) {
+        if (c0 == '/') {
+            return 2;
+        }
+
+        return NGX_DECLINED;
+    }
+
+    c1 = name->data[1];
+
+    if (c1 == ':') {
+        c0 |= 0x20;
+
+        if ((c0 >= 'a' && c0 <= 'z')) {
+            return NGX_OK;
+        }
+
+        return NGX_DECLINED;
+    }
+
+    if (c1 == '/') {
+        return NGX_OK;
+    }
+
+    if (c0 == '/') {
+        return 2;
+    }
+
+    return NGX_DECLINED;
+
+#else
 
     if (name->data[0] == '/') {
         return NGX_OK;
     }
 
-#if (NGX_WIN32)
-
-    if (name->len > 2
-        && name->data[1] == ':'
-        && ((name->data[0] >= 'a' && name->data[0] <= 'z')
-             || (name->data[0] >= 'A' && name->data[0] <= 'Z')))
-    {
-        return NGX_OK;
-    }
+    return NGX_DECLINED;
 
 #endif
-
-    old = *name;
-
-    if (conf_prefix) {
-        len = sizeof(NGX_CONF_PREFIX) - 1;
-        prefix = (u_char *) NGX_CONF_PREFIX;
-
-    } else {
-        len = cycle->root.len;
-        prefix = cycle->root.data;
-    }
-
-    name->len = len + old.len;
-    name->data = ngx_pnalloc(cycle->pool, name->len + 1);
-    if (name->data == NULL) {
-        return  NGX_ERROR;
-    }
-
-    p = ngx_cpymem(name->data, prefix, len);
-    ngx_cpystrn(p, old.data, old.len + 1);
-
-    return NGX_OK;
 }
 
 
@@ -857,10 +907,10 @@ ngx_conf_open_file(ngx_cycle_t *cycle, n
     full.data = NULL;
 #endif
 
-    if (name) {
+    if (name->len) {
         full = *name;
 
-        if (ngx_conf_full_name(cycle, &full, 0) == NGX_ERROR) {
+        if (ngx_conf_full_name(cycle, &full, 0) != NGX_OK) {
             return NULL;
         }
 
@@ -893,14 +943,13 @@ ngx_conf_open_file(ngx_cycle_t *cycle, n
         return NULL;
     }
 
-    if (name) {
+    if (name->len) {
         file->fd = NGX_INVALID_FILE;
         file->name = full;
 
     } else {
-        file->fd = ngx_stderr_fileno;
-        file->name.len = 0;
-        file->name.data = NULL;
+        file->fd = ngx_stderr;
+        file->name = *name;
     }
 
     file->buffer = NULL;
@@ -912,6 +961,7 @@ ngx_conf_open_file(ngx_cycle_t *cycle, n
 static void
 ngx_conf_flush_files(ngx_cycle_t *cycle)
 {
+    ssize_t           n, len;
     ngx_uint_t        i;
     ngx_list_part_t  *part;
     ngx_open_file_t  *file;
@@ -932,11 +982,24 @@ ngx_conf_flush_files(ngx_cycle_t *cycle)
             i = 0;
         }
 
-        if (file[i].buffer == NULL || file[i].pos - file[i].buffer == 0) {
+        len = file[i].pos - file[i].buffer;
+
+        if (file[i].buffer == NULL || len == 0) {
             continue;
         }
 
-        ngx_write_fd(file[i].fd, file[i].buffer, file[i].pos - file[i].buffer);
+        n = ngx_write_fd(file[i].fd, file[i].buffer, len);
+
+        if (n == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          ngx_write_fd_n " to \"%s\" failed",
+                          file[i].name.data);
+
+        } else if (n != len) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                          ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
+                          file[i].name.data, n, len);
+        }
     }
 }
 
@@ -945,31 +1008,33 @@ void ngx_cdecl
 ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err,
     char *fmt, ...)
 {
-    u_char   errstr[NGX_MAX_CONF_ERRSTR], *buf, *last;
+    u_char   errstr[NGX_MAX_CONF_ERRSTR], *p, *last;
     va_list  args;
 
     last = errstr + NGX_MAX_CONF_ERRSTR;
 
     va_start(args, fmt);
-    buf = ngx_vsnprintf(errstr, last - errstr, fmt, args);
+    p = ngx_vslprintf(errstr, last, fmt, args);
     va_end(args);
 
-    *buf = '\0';
-
     if (err) {
-        buf = ngx_snprintf(buf, last - buf - 1, " (%d: ", err);
-        buf = ngx_strerror_r(err, buf, last - buf - 1);
-        *buf++ = ')';
-        *buf = '\0';
+        p = ngx_log_errno(p, last, err);
     }
 
     if (cf->conf_file == NULL) {
-        ngx_log_error(level, cf->log, 0, "%s", errstr);
+        ngx_log_error(level, cf->log, 0, "%*s", p - errstr, errstr);
         return;
     }
 
-    ngx_log_error(level, cf->log, 0, "%s in %s:%ui",
-                  errstr, cf->conf_file->file.name.data, cf->conf_file->line);
+    if (cf->conf_file->file.fd == NGX_INVALID_FILE) {
+        ngx_log_error(level, cf->log, 0, "%*s in command line",
+                      p - errstr, errstr);
+        return;
+    }
+
+    ngx_log_error(level, cf->log, 0, "%*s in %s:%ui",
+                  p - errstr, errstr,
+                  cf->conf_file->file.name.data, cf->conf_file->line);
 }
 
 
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -71,7 +71,7 @@
 #define NGX_CONF_MODULE      0x464E4F43  /* "CONF" */
 
 
-#define NGX_MAX_CONF_ERRSTR  256
+#define NGX_MAX_CONF_ERRSTR  1024
 
 
 struct ngx_command_s {
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -13,11 +13,11 @@ ngx_os_io_t  ngx_io;
 
 
 ngx_listening_t *
-ngx_listening_inet_stream_socket(ngx_conf_t *cf, in_addr_t addr, in_port_t port)
+ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen)
 {
-    size_t               len;
-    ngx_listening_t     *ls;
-    struct sockaddr_in  *sin;
+    ngx_listening_t  *ls;
+    struct sockaddr  *sa;
+    u_char            text[NGX_SOCKADDR_STRLEN];
 
     ls = ngx_array_push(&cf->cycle->listening);
     if (ls == NULL) {
@@ -26,34 +26,45 @@ ngx_listening_inet_stream_socket(ngx_con
 
     ngx_memzero(ls, sizeof(ngx_listening_t));
 
-    sin = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in));
-    if (sin == NULL) {
+    sa = ngx_palloc(cf->pool, socklen);
+    if (sa == NULL) {
         return NULL;
     }
 
-    sin->sin_family = AF_INET;
-    sin->sin_addr.s_addr = addr;
-    sin->sin_port = htons(port);
+    ngx_memcpy(sa, sockaddr, socklen);
 
+    ls->sockaddr = sa;
+    ls->socklen = socklen;
 
-    ls->addr_text.data = ngx_pnalloc(cf->pool,
-                                    NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1);
+    ls->addr_text.len = ngx_sock_ntop(sa, text, NGX_SOCKADDR_STRLEN, 1);
+
+    ls->addr_text.data = ngx_pnalloc(cf->pool, ls->addr_text.len);
     if (ls->addr_text.data == NULL) {
         return NULL;
     }
 
-    len = ngx_inet_ntop(AF_INET, &addr, ls->addr_text.data,
-                        NGX_INET_ADDRSTRLEN);
-
-    ls->addr_text.len = ngx_sprintf(ls->addr_text.data + len, ":%d", port)
-                        - ls->addr_text.data;
+    ngx_memcpy(ls->addr_text.data, text, ls->addr_text.len);
 
     ls->fd = (ngx_socket_t) -1;
     ls->type = SOCK_STREAM;
-    ls->sockaddr = (struct sockaddr *) sin;
-    ls->socklen = sizeof(struct sockaddr_in);
-    ls->addr = offsetof(struct sockaddr_in, sin_addr);
-    ls->addr_text_max_len = NGX_INET_ADDRSTRLEN;
+
+    switch (ls->sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+         ls->addr_text_max_len = NGX_INET6_ADDRSTRLEN;
+         break;
+#endif
+    case AF_INET:
+         ls->addr_text_max_len = NGX_INET_ADDRSTRLEN;
+         break;
+    default:
+         ls->addr_text_max_len = NGX_SOCKADDR_STRLEN;
+         break;
+    }
+
+    ls->backlog = NGX_LISTEN_BACKLOG;
+    ls->rcvbuf = -1;
+    ls->sndbuf = -1;
 
     return ls;
 }
@@ -65,7 +76,6 @@ ngx_set_inherited_sockets(ngx_cycle_t *c
     size_t                     len;
     ngx_uint_t                 i;
     ngx_listening_t           *ls;
-    struct sockaddr_in        *sin;
     socklen_t                  olen;
 #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
     ngx_err_t                  err;
@@ -94,33 +104,39 @@ ngx_set_inherited_sockets(ngx_cycle_t *c
             continue;
         }
 
-        sin = (struct sockaddr_in *) ls[i].sockaddr;
+        switch (ls[i].sockaddr->sa_family) {
 
-        if (sin->sin_family != AF_INET) {
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+             ls[i].addr_text_max_len = NGX_INET6_ADDRSTRLEN;
+             break;
+#endif
+
+        case AF_INET:
+             ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN;
+             break;
+
+        default:
             ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
                           "the inherited socket #%d has "
-                          "unsupported family", ls[i].fd);
+                          "an unsupported protocol family", ls[i].fd);
             ls[i].ignore = 1;
             continue;
         }
 
-        ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN;
+        len = ls[i].addr_text_max_len + sizeof(":65535") - 1;
 
-        ls[i].addr_text.data = ngx_pnalloc(cycle->pool,
-                                   NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1);
+        ls[i].addr_text.data = ngx_pnalloc(cycle->pool, len);
         if (ls[i].addr_text.data == NULL) {
             return NGX_ERROR;
         }
 
-        len = ngx_sock_ntop(ls[i].sockaddr, ls[i].addr_text.data,
-                            NGX_INET_ADDRSTRLEN);
+        len = ngx_sock_ntop(ls[i].sockaddr, ls[i].addr_text.data, len, 1);
         if (len == 0) {
             return NGX_ERROR;
         }
 
-        ls[i].addr_text.len = ngx_sprintf(ls[i].addr_text.data + len, ":%d",
-                                          ntohs(sin->sin_port))
-                              - ls[i].addr_text.data;
+        ls[i].addr_text.len = len;
 
         ls[i].backlog = NGX_LISTEN_BACKLOG;
 
@@ -278,6 +294,23 @@ ngx_open_listening_sockets(ngx_cycle_t *
                 return NGX_ERROR;
             }
 
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+
+            if (ls[i].sockaddr->sa_family == AF_INET6 && ls[i].ipv6only) {
+                int  ipv6only;
+
+                ipv6only = (ls[i].ipv6only == 1);
+
+                if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+                               (const void *) &ipv6only, sizeof(int))
+                    == -1)
+                {
+                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                                  "setsockopt(IPV6_V6ONLY) %V failed, ignored",
+                                  &ls[i].addr_text);
+                }
+            }
+#endif
             /* TODO: close on exit */
 
             if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) {
@@ -296,7 +329,7 @@ ngx_open_listening_sockets(ngx_cycle_t *
                 }
             }
 
-            ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+            ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0,
                            "bind() %V #%d ", &ls[i].addr_text, s);
 
             if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {
@@ -365,7 +398,7 @@ ngx_open_listening_sockets(ngx_cycle_t *
 
 
 void
-ngx_configure_listening_socket(ngx_cycle_t *cycle)
+ngx_configure_listening_sockets(ngx_cycle_t *cycle)
 {
     ngx_uint_t                 i;
     ngx_listening_t           *ls;
@@ -380,6 +413,8 @@ ngx_configure_listening_socket(ngx_cycle
     ls = cycle->listening.elts;
     for (i = 0; i < cycle->listening.nelts; i++) {
 
+        ls[i].log = *ls[i].logp;
+
         if (ls[i].rcvbuf != -1) {
             if (setsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF,
                            (const void *) &ls[i].rcvbuf, sizeof(int))
@@ -536,29 +571,31 @@ ngx_close_listening_sockets(ngx_cycle_t 
 
         c = ls[i].connection;
 
-        if (c->read->active) {
-            if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
-                ngx_del_conn(c, NGX_CLOSE_EVENT);
+        if (c) {
+            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 (ngx_event_flags & NGX_USE_EPOLL_EVENT) {
 
-                /*
-                 * it seems that Linux-2.6.x OpenVZ sends events
-                 * for closed shared listening sockets unless
-                 * the events was explicity deleted
-                 */
+                    /*
+                     * 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);
+                    ngx_del_event(c->read, NGX_READ_EVENT, 0);
 
-            } else {
-                ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
+                } else {
+                    ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
+                }
             }
+
+            ngx_free_connection(c);
+
+            c->fd = (ngx_socket_t) -1;
         }
 
-        ngx_free_connection(c);
-
-        c->fd = (ngx_socket_t) -1;
-
         ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
                        "close listening %V #%d ", &ls[i].addr_text, ls[i].fd);
 
@@ -566,6 +603,8 @@ ngx_close_listening_sockets(ngx_cycle_t 
             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
                           ngx_close_socket_n " %V failed", &ls[i].addr_text);
         }
+
+        ls[i].fd = (ngx_socket_t) -1;
     }
 }
 
@@ -774,19 +813,92 @@ ngx_close_connection(ngx_connection_t *c
 
 
 ngx_int_t
+ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s,
+    ngx_uint_t port)
+{
+    socklen_t             len;
+    ngx_uint_t            addr;
+    u_char                sa[NGX_SOCKADDRLEN];
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    ngx_uint_t            i;
+    struct sockaddr_in6  *sin6;
+#endif
+
+    switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+
+        for (addr = 0, i = 0; addr == 0 && i < 16; i++) {
+            addr |= sin6->sin6_addr.s6_addr[i];
+        }
+
+        break;
+#endif
+
+    default: /* AF_INET */
+        sin = (struct sockaddr_in *) c->local_sockaddr;
+        addr = sin->sin_addr.s_addr;
+        break;
+    }
+
+    if (addr == 0) {
+
+        len = NGX_SOCKADDRLEN;
+
+        if (getsockname(c->fd, (struct sockaddr *) &sa, &len) == -1) {
+            ngx_connection_error(c, ngx_socket_errno, "getsockname() failed");
+            return NGX_ERROR;
+        }
+
+        c->local_sockaddr = ngx_palloc(c->pool, len);
+        if (c->local_sockaddr == NULL) {
+            return NGX_ERROR;
+        }
+
+        c->local_socklen = len;
+        ngx_memcpy(c->local_sockaddr, &sa, len);
+    }
+
+    if (s == NULL) {
+        return NGX_OK;
+    }
+
+    s->len = ngx_sock_ntop(c->local_sockaddr, s->data, s->len, port);
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text)
 {
     ngx_uint_t  level;
 
-    if (err == NGX_ECONNRESET
-        && c->log_error == NGX_ERROR_IGNORE_ECONNRESET)
+    /* Winsock may return NGX_ECONNABORTED instead of NGX_ECONNRESET */
+
+    if ((err == NGX_ECONNRESET
+#if (NGX_WIN32)
+         || err == NGX_ECONNABORTED
+#endif
+        ) && c->log_error == NGX_ERROR_IGNORE_ECONNRESET)
     {
         return 0;
     }
 
+#if (NGX_SOLARIS)
+    if (err == NGX_EINVAL && c->log_error == NGX_ERROR_IGNORE_EINVAL) {
+        return 0;
+    }
+#endif
+
     if (err == 0
         || err == NGX_ECONNRESET
-#if !(NGX_WIN32)
+#if (NGX_WIN32)
+        || err == NGX_ECONNABORTED
+#else
         || err == NGX_EPIPE
 #endif
         || err == NGX_ENOTCONN
@@ -799,6 +911,7 @@ ngx_connection_error(ngx_connection_t *c
     {
         switch (c->log_error) {
 
+        case NGX_ERROR_IGNORE_EINVAL:
         case NGX_ERROR_IGNORE_ECONNRESET:
         case NGX_ERROR_INFO:
             level = NGX_LOG_INFO;
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -19,7 +19,6 @@ struct ngx_listening_s {
 
     struct sockaddr    *sockaddr;
     socklen_t           socklen;    /* size of sockaddr */
-    size_t              addr;       /* offset to address in sockaddr */
     size_t              addr_text_max_len;
     ngx_str_t           addr_text;
 
@@ -35,6 +34,7 @@ struct ngx_listening_s {
     void               *servers;  /* array of ngx_http_in_addr_t, for example */
 
     ngx_log_t           log;
+    ngx_log_t          *logp;
 
     size_t              pool_size;
     /* should be here because of the AcceptEx() preread */
@@ -57,6 +57,10 @@ struct ngx_listening_s {
     unsigned            shared:1;    /* shared between threads or processes */
     unsigned            addr_ntop:1;
 
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+    unsigned            ipv6only:2;
+#endif
+
 #if (NGX_HAVE_DEFERRED_ACCEPT)
     unsigned            deferred_accept:1;
     unsigned            delete_deferred:1;
@@ -70,10 +74,11 @@ struct ngx_listening_s {
 
 
 typedef enum {
-     NGX_ERROR_CRIT = 0,
+     NGX_ERROR_ALERT = 0,
      NGX_ERROR_ERR,
      NGX_ERROR_INFO,
-     NGX_ERROR_IGNORE_ECONNRESET
+     NGX_ERROR_IGNORE_ECONNRESET,
+     NGX_ERROR_IGNORE_EINVAL
 } ngx_connection_log_error_e;
 
 
@@ -123,18 +128,18 @@ struct ngx_connection_s {
     ngx_ssl_connection_t  *ssl;
 #endif
 
-#if (NGX_HAVE_IOCP)
     struct sockaddr    *local_sockaddr;
     socklen_t           local_socklen;
-#endif
 
     ngx_buf_t          *buffer;
 
     ngx_atomic_uint_t   number;
 
+    ngx_uint_t          requests;
+
     unsigned            buffered:8;
 
-    unsigned            log_error:2;     /* ngx_connection_log_error_e */
+    unsigned            log_error:3;     /* ngx_connection_log_error_e */
 
     unsigned            single_connection:1;
     unsigned            unexpected_eof:1;
@@ -160,18 +165,15 @@ struct ngx_connection_s {
 };
 
 
-#ifndef ngx_ssl_set_nosendshut
-#define ngx_ssl_set_nosendshut(ssl)
-#endif
-
-
-ngx_listening_t *ngx_listening_inet_stream_socket(ngx_conf_t *cf,
-    in_addr_t addr, in_port_t port);
+ngx_listening_t *ngx_create_listening(ngx_conf_t *cf, void *sockaddr,
+    socklen_t socklen);
 ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle);
 ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle);
-void ngx_configure_listening_socket(ngx_cycle_t *cycle);
+void ngx_configure_listening_sockets(ngx_cycle_t *cycle);
 void ngx_close_listening_sockets(ngx_cycle_t *cycle);
 void ngx_close_connection(ngx_connection_t *c);
+ngx_int_t ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s,
+    ngx_uint_t port);
 ngx_int_t ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text);
 
 ngx_connection_t *ngx_get_connection(ngx_socket_t s, ngx_log_t *log);
--- a/src/core/ngx_cpuinfo.c
+++ b/src/core/ngx_cpuinfo.c
@@ -72,7 +72,7 @@ void
 ngx_cpuinfo(void)
 {
     u_char    *vendor;
-    uint32_t   vbuf[5], cpu[4];
+    uint32_t   vbuf[5], cpu[4], model;
 
     vbuf[0] = 0;
     vbuf[1] = 0;
@@ -103,8 +103,10 @@ ngx_cpuinfo(void)
         case 6:
             ngx_cacheline_size = 32;
 
-            if ((cpu[0] & 0xf0) >= 0xd0) {
-                /* Intel Core */
+            model = ((cpu[0] & 0xf0000) >> 8) | (cpu[0] & 0xf0);
+
+            if (model >= 0xd0) {
+                /* Intel Core, Core 2, Atom */
                 ngx_cacheline_size = 64;
             }
 
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -9,9 +9,11 @@
 #include <ngx_event.h>
 
 
-static ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log);
 static void ngx_destroy_cycle_pools(ngx_conf_t *conf);
 static ngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2);
+static ngx_int_t ngx_init_zone_pool(ngx_cycle_t *cycle,
+    ngx_shm_zone_t *shm_zone);
+static ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log);
 static void ngx_clean_old_cycles(ngx_event_t *ev);
 
 
@@ -32,11 +34,7 @@ ngx_tls_key_t          ngx_core_tls_key;
 static ngx_connection_t  dumb;
 /* STUB */
 
-#ifdef NGX_ERROR_LOG_PATH
 static ngx_str_t  error_log = ngx_string(NGX_ERROR_LOG_PATH);
-#else
-static ngx_str_t  error_log = ngx_null_string;
-#endif
 
 
 ngx_cycle_t *
@@ -44,7 +42,6 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
 {
     void                *rv;
     char               **senv, **env;
-    u_char              *lock_file;
     ngx_uint_t           i, n;
     ngx_log_t           *log;
     ngx_time_t          *tp;
@@ -52,7 +49,6 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     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;
@@ -86,10 +82,22 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
 
     cycle->pool = pool;
     cycle->log = log;
+    cycle->new_log.log_level = NGX_LOG_ERR;
     cycle->old_cycle = old_cycle;
-    cycle->root.len = sizeof(NGX_PREFIX) - 1;
-    cycle->root.data = (u_char *) NGX_PREFIX;
 
+    cycle->conf_prefix.len = old_cycle->conf_prefix.len;
+    cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix);
+    if (cycle->conf_prefix.data == NULL) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+
+    cycle->prefix.len = old_cycle->prefix.len;
+    cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix);
+    if (cycle->prefix.data == NULL) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
 
     cycle->conf_file.len = old_cycle->conf_file.len;
     cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1);
@@ -100,15 +108,12 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data,
                 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);
+    cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param);
     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;
@@ -136,7 +141,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     }
 
     if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t))
-        == NGX_ERROR)
+        != NGX_OK)
     {
         ngx_destroy_pool(pool);
         return NULL;
@@ -155,22 +160,12 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     }
 
     if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t))
-        == NGX_ERROR)
+        != NGX_OK)
     {
         ngx_destroy_pool(pool);
         return NULL;
     }
 
-
-    cycle->new_log = ngx_log_create_errlog(cycle, NULL);
-    if (cycle->new_log == NULL) {
-        ngx_destroy_pool(pool);
-        return NULL;
-    }
-
-    cycle->new_log->file->name = error_log;
-
-
     n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10;
 
     cycle->listening.elts = ngx_pcalloc(pool, n * sizeof(ngx_listening_t));
@@ -221,7 +216,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
 
         if (module->create_conf) {
             rv = module->create_conf(cycle);
-            if (rv == NGX_CONF_ERROR) {
+            if (rv == NULL) {
                 ngx_destroy_pool(pool);
                 return NULL;
             }
@@ -270,12 +265,10 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     }
 
     if (ngx_test_config) {
-        ngx_log_error(NGX_LOG_INFO, log, 0,
-                      "the configuration file %s syntax is ok",
-                      cycle->conf_file.data);
+        ngx_log_stderr(0, "the configuration file %s syntax is ok",
+                       cycle->conf_file.data);
     }
 
-
     for (i = 0; ngx_modules[i]; i++) {
         if (ngx_modules[i]->type != NGX_CORE_MODULE) {
             continue;
@@ -293,11 +286,12 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
         }
     }
 
+    if (ngx_process == NGX_PROCESS_SIGNALLER) {
+        return cycle;
+    }
 
     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
 
-#if !(NGX_WIN32)
-
     if (ngx_test_config) {
 
         if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {
@@ -326,8 +320,6 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
         }
     }
 
-#endif
-
 
     if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) {
         goto failed;
@@ -339,6 +331,13 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     }
 
 
+    if (cycle->new_log.file == NULL) {
+        cycle->new_log.file = ngx_conf_open_file(cycle, &error_log);
+        if (cycle->new_log.file == NULL) {
+            goto failed;
+        }
+    }
+
     /* open the new files */
 
     part = &cycle->open_files.part;
@@ -355,12 +354,13 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
             i = 0;
         }
 
-        if (file[i].name.data == NULL) {
+        if (file[i].name.len == 0) {
             continue;
         }
 
-        file[i].fd = ngx_open_file(file[i].name.data, NGX_FILE_RDWR,
-                                   NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND,
+        file[i].fd = ngx_open_file(file[i].name.data,
+                                   NGX_FILE_APPEND,
+                                   NGX_FILE_CREATE_OR_OPEN,
                                    NGX_FILE_DEFAULT_ACCESS);
 
         ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0,
@@ -374,14 +374,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
             goto failed;
         }
 
-#if (NGX_WIN32)
-        if (ngx_file_append_mode(file[i].fd) != NGX_OK) {
-            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
-                          ngx_file_append_mode_n " \"%s\" failed",
-                          file[i].name.data);
-            goto failed;
-        }
-#else
+#if !(NGX_WIN32)
         if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) {
             ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                           "fcntl(FD_CLOEXEC) \"%s\" failed",
@@ -391,12 +384,8 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
 #endif
     }
 
-    cycle->log = cycle->new_log;
-    pool->log = cycle->new_log;
-
-    if (cycle->log->log_level == 0) {
-        cycle->log->log_level = NGX_LOG_ERR;
-    }
+    cycle->log = &cycle->new_log;
+    pool->log = &cycle->new_log;
 
 
     /* create shared memory */
@@ -418,7 +407,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
         if (shm_zone[i].shm.size == 0) {
             ngx_log_error(NGX_LOG_EMERG, log, 0,
                           "zero size shared memory zone \"%V\"",
-                          &shm_zone[i].name);
+                          &shm_zone[i].shm.name);
             goto failed;
         }
 
@@ -443,12 +432,13 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
                 n = 0;
             }
 
-            if (shm_zone[i].name.len != oshm_zone[n].name.len) {
+            if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) {
                 continue;
             }
 
-            if (ngx_strncmp(shm_zone[i].name.data, oshm_zone[n].name.data,
-                            shm_zone[i].name.len)
+            if (ngx_strncmp(shm_zone[i].shm.name.data,
+                            oshm_zone[n].shm.name.data,
+                            shm_zone[i].shm.name.len)
                 != 0)
             {
                 continue;
@@ -475,38 +465,10 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
             goto failed;
         }
 
-        shpool = (ngx_slab_pool_t *) shm_zone[i].shm.addr;
-
-        shpool->end = shm_zone[i].shm.addr + shm_zone[i].shm.size;
-        shpool->min_shift = 3;
-
-#if (NGX_HAVE_ATOMIC_OPS)
-
-        lock_file = NULL;
-
-#else
-
-        lock_file = ngx_pnalloc(cycle->pool,
-                                cycle->lock_file.len + shm_zone[i].name.len);
-
-        if (lock_file == NULL) {
+        if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) {
             goto failed;
         }
 
-        (void) ngx_cpystrn(ngx_cpymem(lock_file, cycle->lock_file.data,
-                                      cycle->lock_file.len),
-                           shm_zone[i].name.data, shm_zone[i].name.len + 1);
-
-#endif
-
-        if (ngx_shmtx_create(&shpool->mutex, (void *) &shpool->lock, lock_file)
-            != NGX_OK)
-        {
-            goto failed;
-        }
-
-        ngx_slab_init(shpool);
-
         if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {
             goto failed;
         }
@@ -609,31 +571,20 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     }
 
     if (!ngx_test_config) {
-        ngx_configure_listening_socket(cycle);
+        ngx_configure_listening_sockets(cycle);
     }
 
 
     /* commit the new cycle configuration */
 
-#if !(NGX_WIN32)
-
-    if (!ngx_test_config && cycle->log->file->fd != STDERR_FILENO) {
+    if (!ngx_use_stderr && cycle->log->file->fd != ngx_stderr) {
 
-        ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0,
-                       "dup2: %p %d \"%s\"",
-                       cycle->log->file,
-                       cycle->log->file->fd, cycle->log->file->name.data);
-
-        if (dup2(cycle->log->file->fd, STDERR_FILENO) == -1) {
-            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
-                          "dup2(STDERR) failed");
-            /* fatal */
-            exit(1);
+        if (ngx_set_stderr(cycle->log->file->fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          ngx_set_stderr_n " failed");
         }
     }
 
-#endif
-
     pool->log = cycle->log;
 
     for (i = 0; ngx_modules[i]; i++) {
@@ -678,10 +629,10 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
                 n = 0;
             }
 
-            if (oshm_zone[i].name.len == shm_zone[n].name.len
-                && ngx_strncmp(oshm_zone[i].name.data,
-                               shm_zone[n].name.data,
-                               oshm_zone[i].name.len)
+            if (oshm_zone[i].shm.name.len == shm_zone[n].shm.name.len
+                && ngx_strncmp(oshm_zone[i].shm.name.data,
+                               shm_zone[n].shm.name.data,
+                               oshm_zone[i].shm.name.len)
                 == 0)
             {
                 goto live_shm_zone;
@@ -702,7 +653,8 @@ old_shm_zone_done:
 
     ls = old_cycle->listening.elts;
     for (i = 0; i < old_cycle->listening.nelts; i++) {
-        if (ls[i].remain) {
+
+        if (ls[i].remain || ls[i].fd == -1) {
             continue;
         }
 
@@ -730,7 +682,7 @@ old_shm_zone_done:
             i = 0;
         }
 
-        if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr_fileno) {
+        if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) {
             continue;
         }
 
@@ -830,7 +782,7 @@ failed:
             i = 0;
         }
 
-        if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr_fileno) {
+        if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) {
             continue;
         }
 
@@ -876,49 +828,123 @@ ngx_destroy_cycle_pools(ngx_conf_t *conf
 static ngx_int_t
 ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2)
 {
-    struct sockaddr_in  *sin1, *sin2;
+    struct sockaddr_in   *sin1, *sin2;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6  *sin61, *sin62;
+#endif
 
-    /* AF_INET only */
-
-    if (sa1->sa_family != AF_INET || sa2->sa_family != AF_INET) {
+    if (sa1->sa_family != sa2->sa_family) {
         return NGX_DECLINED;
     }
 
-    sin1 = (struct sockaddr_in *) sa1;
-    sin2 = (struct sockaddr_in *) sa2;
+    switch (sa1->sa_family) {
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        sin61 = (struct sockaddr_in6 *) sa1;
+        sin62 = (struct sockaddr_in6 *) sa2;
+
+        if (sin61->sin6_port != sin61->sin6_port) {
+            return NGX_DECLINED;
+        }
+
+        if (ngx_memcmp(&sin61->sin6_addr, &sin62->sin6_addr, 16) != 0) {
+            return NGX_DECLINED;
+        }
 
-    if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) {
-        return NGX_DECLINED;
-    }
+        break;
+#endif
+
+    default: /* AF_INET */
+
+        sin1 = (struct sockaddr_in *) sa1;
+        sin2 = (struct sockaddr_in *) sa2;
 
-    if (sin1->sin_port != sin2->sin_port) {
-        return NGX_DECLINED;
+        if (sin1->sin_port != sin2->sin_port) {
+            return NGX_DECLINED;
+        }
+
+        if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) {
+            return NGX_DECLINED;
+        }
+
+        break;
     }
 
     return NGX_OK;
 }
 
 
-#if !(NGX_WIN32)
+static ngx_int_t
+ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn)
+{
+    u_char           *file;
+    ngx_slab_pool_t  *sp;
+
+    sp = (ngx_slab_pool_t *) zn->shm.addr;
+
+    if (zn->shm.exists) {
+
+        if (sp == sp->addr) {
+            return NGX_OK;
+        }
+
+        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+                      "shared zone \"%V\" has no equal addresses: %p vs %p",
+                      &zn->shm.name, sp->addr, sp);
+        return NGX_ERROR;
+    }
+
+    sp->end = zn->shm.addr + zn->shm.size;
+    sp->min_shift = 3;
+    sp->addr = zn->shm.addr;
+
+#if (NGX_HAVE_ATOMIC_OPS)
+
+    file = NULL;
+
+#else
+
+    file = ngx_pnalloc(cycle->pool, cycle->lock_file.len + zn->shm.name.len);
+    if (file == NULL) {
+        return NGX_ERROR;
+    }
+
+    (void) ngx_sprintf(file, "%V%V%Z", &cycle->lock_file, &zn->shm.name);
+
+#endif
+
+    if (ngx_shmtx_create(&sp->mutex, (void *) &sp->lock, file) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    ngx_slab_init(sp);
+
+    return NGX_OK;
+}
+
 
 ngx_int_t
 ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log)
 {
-    size_t            len;
-    ngx_uint_t        trunc;
-    ngx_file_t        file;
-    u_char            pid[NGX_INT64_LEN + 2];
+    size_t      len;
+    ngx_uint_t  create;
+    ngx_file_t  file;
+    u_char      pid[NGX_INT64_LEN + 2];
+
+    if (ngx_process > NGX_PROCESS_MASTER) {
+        return NGX_OK;
+    }
 
     ngx_memzero(&file, sizeof(ngx_file_t));
 
     file.name = *name;
     file.log = log;
 
-    trunc = ngx_test_config ? 0 : NGX_FILE_TRUNCATE;
+    create = ngx_test_config ? NGX_FILE_CREATE_OR_OPEN : NGX_FILE_TRUNCATE;
 
     file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR,
-                            NGX_FILE_CREATE_OR_OPEN|trunc,
-                            NGX_FILE_DEFAULT_ACCESS);
+                            create, NGX_FILE_DEFAULT_ACCESS);
 
     if (file.fd == NGX_INVALID_FILE) {
         ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
@@ -959,7 +985,57 @@ ngx_delete_pidfile(ngx_cycle_t *cycle)
     }
 }
 
-#endif
+
+ngx_int_t
+ngx_signal_process(ngx_cycle_t *cycle, char *sig)
+{
+    ssize_t           n;
+    ngx_int_t         pid;
+    ngx_file_t        file;
+    ngx_core_conf_t  *ccf;
+    u_char            buf[NGX_INT64_LEN + 2];
+
+    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started");
+
+    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+    file.name = ccf->pid;
+    file.log = cycle->log;
+
+    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,
+                            NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS);
+
+    if (file.fd == NGX_INVALID_FILE) {
+        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,
+                      ngx_open_file_n " \"%s\" failed", file.name.data);
+        return 1;
+    }
+
+    n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0);
+
+    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      ngx_close_file_n " \"%s\" failed", file.name.data);
+    }
+
+    if (n == NGX_ERROR) {
+        return 1;
+    }
+
+    while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ }
+
+    pid = ngx_atoi(buf, ++n);
+
+    if (pid == NGX_ERROR) {
+        ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
+                      "invalid PID number \"%*s\" in \"%s\"",
+                      n, buf, file.name.data);
+        return 1;
+    }
+
+    return ngx_os_signal_process(cycle, sig, pid);
+
+}
 
 
 static ngx_int_t
@@ -996,6 +1072,7 @@ ngx_test_lockfile(u_char *file, ngx_log_
 void
 ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user)
 {
+    ssize_t           n, len;
     ngx_fd_t          fd;
     ngx_uint_t        i;
     ngx_list_part_t  *part;
@@ -1015,19 +1092,32 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx
             i = 0;
         }
 
-        if (file[i].name.data == NULL) {
+        if (file[i].name.len == 0) {
             continue;
         }
 
-        if (file[i].buffer && file[i].pos - file[i].buffer != 0) {
-            ngx_write_fd(file[i].fd, file[i].buffer,
-                         file[i].pos - file[i].buffer);
+        len = file[i].pos - file[i].buffer;
+
+        if (file[i].buffer && len != 0) {
+
+            n = ngx_write_fd(file[i].fd, file[i].buffer, len);
+
+            if (n == NGX_FILE_ERROR) {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                              ngx_write_fd_n " to \"%s\" failed",
+                              file[i].name.data);
+
+            } else if (n != len) {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                          ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
+                          file[i].name.data, n, len);
+            }
+
             file[i].pos = file[i].buffer;
         }
 
-        fd = ngx_open_file(file[i].name.data, NGX_FILE_RDWR,
-                           NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND,
-                           NGX_FILE_DEFAULT_ACCESS);
+        fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND,
+                           NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS);
 
         ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                        "reopen file \"%s\", old:%d new:%d",
@@ -1039,25 +1129,13 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx
             continue;
         }
 
-#if (NGX_WIN32)
-        if (ngx_file_append_mode(fd) == NGX_ERROR) {
-            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                          ngx_file_append_mode_n " \"%s\" failed",
-                          file[i].name.data);
-
-            if (ngx_close_file(fd) == NGX_FILE_ERROR) {
-                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                              ngx_close_file_n " \"%s\" failed",
-                              file[i].name.data);
-            }
-
-            continue;
-        }
-#else
+#if !(NGX_WIN32)
         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) {
+            if (ngx_file_info((const char *) file[i].name.data, &fi)
+                == NGX_FILE_ERROR)
+            {
                 ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                               ngx_file_info_n " \"%s\" failed",
                               file[i].name.data);
@@ -1158,27 +1236,29 @@ ngx_shared_memory_add(ngx_conf_t *cf, ng
             i = 0;
         }
 
-        if (name->len != shm_zone[i].name.len) {
+        if (name->len != shm_zone[i].shm.name.len) {
             continue;
         }
 
-        if (ngx_strncmp(name->data, shm_zone[i].name.data, name->len) != 0) {
+        if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len)
+            != 0)
+        {
             continue;
         }
 
         if (size && size != shm_zone[i].shm.size) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "the size %uz of shared memory zone \"%V\" "
-                               "conflicts with already declared size %uz",
-                               size, &shm_zone[i].name, shm_zone[i].shm.size);
+                            "the size %uz of shared memory zone \"%V\" "
+                            "conflicts with already declared size %uz",
+                            size, &shm_zone[i].shm.name, shm_zone[i].shm.size);
             return NULL;
         }
 
         if (tag != shm_zone[i].tag) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "the shared memory zone \"%V\" is "
-                               "already declared for a different use",
-                               &shm_zone[i].name);
+                            "the shared memory zone \"%V\" is "
+                            "already declared for a different use",
+                            &shm_zone[i].shm.name);
             return NULL;
         }
 
@@ -1194,8 +1274,9 @@ ngx_shared_memory_add(ngx_conf_t *cf, ng
     shm_zone->data = NULL;
     shm_zone->shm.log = cf->cycle->log;
     shm_zone->shm.size = size;
+    shm_zone->shm.name = *name;
+    shm_zone->shm.exists = 0;
     shm_zone->init = NULL;
-    shm_zone->name = *name;
     shm_zone->tag = tag;
 
     return shm_zone;
--- a/src/core/ngx_cycle.h
+++ b/src/core/ngx_cycle.h
@@ -29,7 +29,6 @@ struct ngx_shm_zone_s {
     void                     *data;
     ngx_shm_t                 shm;
     ngx_shm_zone_init_pt      init;
-    ngx_str_t                 name;
     void                     *tag;
 };
 
@@ -39,7 +38,7 @@ struct ngx_cycle_s {
     ngx_pool_t               *pool;
 
     ngx_log_t                *log;
-    ngx_log_t                *new_log;
+    ngx_log_t                 new_log;
 
     ngx_connection_t        **files;
     ngx_connection_t         *free_connections;
@@ -61,7 +60,8 @@ struct ngx_cycle_s {
 
     ngx_str_t                 conf_file;
     ngx_str_t                 conf_param;
-    ngx_str_t                 root;
+    ngx_str_t                 conf_prefix;
+    ngx_str_t                 prefix;
     ngx_str_t                 lock_file;
     ngx_str_t                 hostname;
 };
@@ -117,6 +117,7 @@ typedef struct {
 ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle);
 ngx_int_t ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log);
 void ngx_delete_pidfile(ngx_cycle_t *cycle);
+ngx_int_t ngx_signal_process(ngx_cycle_t *cycle, char *sig);
 void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user);
 char **ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last);
 ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv);
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -259,12 +259,12 @@ ngx_conf_set_path_slot(ngx_conf_t *cf, n
         path->name.len--;
     }
 
-    if (ngx_conf_full_name(cf->cycle, &path->name, 0) == NGX_ERROR) {
+    if (ngx_conf_full_name(cf->cycle, &path->name, 0) != NGX_OK) {
         return NULL;
     }
 
     path->len = 0;
-    path->cleaner = (ngx_gc_handler_pt) cmd->post;
+    path->manager = (ngx_path_manager_pt) cmd->post;
     path->conf_file = cf->conf_file->file.name.data;
     path->line = cf->conf_file->line;
 
@@ -293,6 +293,49 @@ ngx_conf_set_path_slot(ngx_conf_t *cf, n
 
 
 char *
+ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev,
+    ngx_path_init_t *init)
+{
+    if (*path) {
+        return NGX_CONF_OK;
+    }
+
+    if (prev) {
+        *path = prev;
+        return NGX_CONF_OK;
+    }
+
+    *path = ngx_palloc(cf->pool, sizeof(ngx_path_t));
+    if (*path == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    (*path)->name = init->name;
+
+    if (ngx_conf_full_name(cf->cycle, &(*path)->name, 0) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    (*path)->level[0] = init->level[0];
+    (*path)->level[1] = init->level[1];
+    (*path)->level[2] = init->level[2];
+
+    (*path)->len = init->level[0] + (init->level[0] ? 1 : 0)
+                   + init->level[1] + (init->level[1] ? 1 : 0)
+                   + init->level[2] + (init->level[2] ? 1 : 0);
+
+    (*path)->manager = NULL;
+    (*path)->conf_file = NULL;
+
+    if (ngx_add_path(cf, path) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+char *
 ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     char  *confp = conf;
@@ -446,7 +489,9 @@ ngx_create_pathes(ngx_cycle_t *cycle, ng
         {
         ngx_file_info_t   fi;
 
-        if (ngx_file_info((const char *) path[i]->name.data, &fi) == -1) {
+        if (ngx_file_info((const char *) path[i]->name.data, &fi)
+            == NGX_FILE_ERROR)
+        {
             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                           ngx_file_info_n " \"%s\" failed", path[i]->name.data);
             return NGX_ERROR;
@@ -487,11 +532,13 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_
 
 #if !(NGX_WIN32)
 
-    if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
-                      ngx_change_file_access_n " \"%s\" failed", src->data);
-        err = 0;
-        goto failed;
+    if (ext->access) {
+        if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+                          ngx_change_file_access_n " \"%s\" failed", src->data);
+            err = 0;
+            goto failed;
+        }
     }
 
 #endif
@@ -511,13 +558,19 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_
 
     err = ngx_errno;
 
-    if (err == NGX_ENOENT) {
-
+    if (err
+#if (NGX_WIN32)
+            == ERROR_PATH_NOT_FOUND
+#else
+            == NGX_ENOENT
+#endif
+       )
+    {
         if (!ext->create_path) {
             goto failed;
         }
 
-        err = ngx_create_full_path(to->data, ngx_dir_access(ext->access));
+        err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access));
 
         if (err) {
             ngx_log_error(NGX_LOG_CRIT, ext->log, err,
@@ -561,12 +614,14 @@ failed:
         }
     }
 
-    if (err) {
+    if (err && ext->log_rename_error) {
         ngx_log_error(NGX_LOG_CRIT, ext->log, err,
                       ngx_rename_file_n " \"%s\" to \"%s\" failed",
                       src->data, to->data);
     }
 
+    ext->rename_error = err;
+
     return NGX_ERROR;
 }
 
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -11,62 +11,73 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 
-typedef struct ngx_path_s  ngx_path_t;
-
-#include <ngx_garbage_collector.h>
-
 
 struct ngx_file_s {
-    ngx_fd_t            fd;
-    ngx_str_t           name;
-    ngx_file_info_t     info;
+    ngx_fd_t                   fd;
+    ngx_str_t                  name;
+    ngx_file_info_t            info;
 
-    off_t               offset;
-    off_t               sys_offset;
+    off_t                      offset;
+    off_t                      sys_offset;
 
-    ngx_log_t          *log;
+    ngx_log_t                 *log;
 
-    unsigned            valid_info:1;
-    unsigned            directio:1;
+    unsigned                   valid_info:1;
+    unsigned                   directio:1;
 };
 
 #define NGX_MAX_PATH_LEVEL  3
 
-struct ngx_path_s {
-    ngx_str_t           name;
-    size_t              len;
-    size_t              level[3];
-    ngx_gc_handler_pt   cleaner;
+
+typedef time_t (*ngx_path_manager_pt) (void *data);
+
 
-    u_char             *conf_file;
-    ngx_uint_t          line;
-};
+typedef struct {
+    ngx_str_t                  name;
+    size_t                     len;
+    size_t                     level[3];
+
+    ngx_path_manager_pt        manager;
+    void                      *data;
+
+    u_char                    *conf_file;
+    ngx_uint_t                 line;
+} ngx_path_t;
 
 
 typedef struct {
-    ngx_file_t          file;
-    off_t               offset;
-    ngx_path_t         *path;
-    ngx_pool_t         *pool;
-    char               *warn;
+    ngx_str_t                  name;
+    size_t                     level[3];
+} ngx_path_init_t;
+
 
-    ngx_uint_t          access;
+typedef struct {
+    ngx_file_t                 file;
+    off_t                      offset;
+    ngx_path_t                *path;
+    ngx_pool_t                *pool;
+    char                      *warn;
 
-    unsigned            log_level:8;
-    unsigned            persistent:1;
-    unsigned            clean:1;
+    ngx_uint_t                 access;
+
+    unsigned                   log_level:8;
+    unsigned                   persistent:1;
+    unsigned                   clean:1;
 } ngx_temp_file_t;
 
 
 typedef struct {
-    ngx_uint_t          access;
-    time_t              time;
-    ngx_fd_t            fd;
+    ngx_uint_t                 access;
+    ngx_uint_t                 path_access;
+    time_t                     time;
+    ngx_fd_t                   fd;
+    ngx_err_t                  rename_error;
 
-    unsigned            create_path:1;
-    unsigned            delete_file:1;
+    unsigned                   create_path:1;
+    unsigned                   delete_file:1;
+    unsigned                   log_rename_error:1;
 
-    ngx_log_t          *log;
+    ngx_log_t                 *log;
 } ngx_ext_rename_file_t;
 
 
@@ -110,40 +121,9 @@ void ngx_init_temp_number(void);
 ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision);
 
 char *ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path,
+    ngx_path_t *prev, ngx_path_init_t *init);
 char *ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 
 
-#define ngx_conf_merge_path_value(curr, prev, path, l1, l2, l3, clean, cf)    \
-    if (curr == NULL) {                                                       \
-        if (prev == NULL) {                                                   \
-            curr = ngx_palloc(cf->pool, sizeof(ngx_path_t));                  \
-            if (curr == NULL) {                                               \
-                return NGX_CONF_ERROR;                                        \
-            }                                                                 \
-                                                                              \
-            curr->name.len = sizeof(path) - 1;                                \
-            curr->name.data = (u_char *) path;                                \
-                                                                              \
-            if (ngx_conf_full_name(cf->cycle, &curr->name, 0) == NGX_ERROR) { \
-                return NGX_CONF_ERROR;                                        \
-            }                                                                 \
-                                                                              \
-            curr->level[0] = l1;                                              \
-            curr->level[1] = l2;                                              \
-            curr->level[2] = l3;                                              \
-            curr->len = l1 + l2 + l3 + (l1 ? 1:0) + (l2 ? 1:0) + (l3 ? 1:0);  \
-            curr->cleaner = clean;                                            \
-            curr->conf_file = NULL;                                           \
-                                                                              \
-            if (ngx_add_path(cf, &curr) == NGX_ERROR) {                       \
-                return NGX_CONF_ERROR;                                        \
-            }                                                                 \
-                                                                              \
-        } else {                                                              \
-            curr = prev;                                                      \
-        }                                                                     \
-    }
-
-
-
 #endif /* _NGX_FILE_H_INCLUDED_ */
deleted file mode 100644
--- a/src/core/ngx_garbage_collector.c
+++ /dev/null
@@ -1,217 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- */
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-
-
-
-ngx_int_t ngx_collect_garbage(ngx_gc_t *ctx, ngx_str_t *dname, ngx_int_t level)
-{
-    int         rc;
-    u_char     *last;
-    size_t      len;
-    ngx_err_t   err;
-    ngx_str_t   fname, buf;
-    ngx_dir_t   dir;
-
-    buf.len = 0;
-#if (NGX_SUPPRESS_WARN)
-    buf.data = NULL;
-    fname.data = NULL;
-#endif
-
-    ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0,
-                   "gc dir \"%s\":%d", dname->data, dname->len);
-
-    if (ngx_open_dir(dname, &dir) == NGX_ERROR) {
-        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
-                      ngx_open_dir_n " \"%s\" failed", dname->data);
-        return NGX_ERROR;
-    }
-
-    for ( ;; ) {
-        ngx_set_errno(0);
-        if (ngx_read_dir(&dir) == NGX_ERROR) {
-            err = ngx_errno;
-
-            if (err != NGX_ENOMOREFILES) {
-                ngx_log_error(NGX_LOG_CRIT, ctx->log, err,
-                              ngx_read_dir_n " \"%s\" failed", dname->data);
-                rc = NGX_ERROR;
-
-            } else {
-                rc = NGX_OK;
-            }
-
-            break;
-        }
-
-        len = ngx_de_namelen(&dir);
-
-        ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0,
-                      "gc name \"%s\":%d", ngx_de_name(&dir), len);
-
-        if (len == 1 && ngx_de_name(&dir)[0] == '.') {
-            continue;
-        }
-
-        if (len == 2
-            && ngx_de_name(&dir)[0] == '.'
-            && ngx_de_name(&dir)[1] == '.')
-        {
-            continue;
-        }
-
-        fname.len = dname->len + 1+ len;
-
-        if (fname.len + NGX_DIR_MASK_LEN > buf.len) {
-
-            if (buf.len) {
-                ngx_free(buf.data);
-            }
-
-            buf.len = dname->len + 1 + len + NGX_DIR_MASK_LEN;
-
-            buf.data = ngx_alloc(buf.len + 1, ctx->log);
-            if (buf.data == NULL) {
-                return NGX_ABORT;
-            }
-        }
-
-        last = ngx_cpymem(buf.data, dname->data, dname->len);
-        *last++ = '/';
-        ngx_memcpy(last, ngx_de_name(&dir), len + 1);
-        fname.data = buf.data;
-
-        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
-                       "gc path: \"%s\"", fname.data);
-
-        if (!dir.valid_info) {
-            if (ngx_de_info(fname.data, &dir) == NGX_FILE_ERROR) {
-                ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
-                              ngx_de_info_n " \"%s\" failed", fname.data);
-                continue;
-            }
-        }
-
-        if (ngx_de_is_dir(&dir)) {
-
-            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
-                           "gc enter dir \"%s\"", fname.data);
-
-            if (level == -1
-                   /* there can not be directory on the last level */
-                || level == NGX_MAX_PATH_LEVEL
-                   /* an directory from the old path hierarchy */
-                || len != ctx->path->level[level])
-            {
-                if (ngx_collect_garbage(ctx, &fname, -1) == NGX_ABORT) {
-                    return NGX_ABORT;
-                }
-
-                fname.data[fname.len] = '\0';
-
-                ngx_log_error(NGX_LOG_NOTICE, ctx->log, 0,
-                              "delete old hierachy directory \"%s\"",
-                              fname.data);
-
-                if (ngx_delete_dir(fname.data) == NGX_FILE_ERROR) {
-                    ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
-                                  ngx_delete_dir_n " \"%s\" failed",
-                                  fname.data);
-                } else {
-                    ctx->deleted++;
-                    ctx->freed += ngx_de_size(&dir);
-                }
-
-                continue;
-            }
-
-            if (ngx_collect_garbage(ctx, &fname, level + 1) == NGX_ABORT) {
-                return NGX_ABORT;
-            }
-
-        } else if (ngx_de_is_file(&dir)) {
-
-            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
-                           "gc file \"%s\"", fname.data);
-
-            if (level == -1
-                || (level < NGX_MAX_PATH_LEVEL && ctx->path->level[level] != 0))
-            {
-                if (ngx_delete_file(fname.data) == NGX_FILE_ERROR) {
-                    ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
-                                  ngx_delete_file_n " \"%s\" failed",
-                                  fname.data);
-                } else {
-                    ctx->deleted++;
-                    ctx->freed += ngx_de_size(&dir);
-                }
-
-                continue;
-            }
-
-            if (ctx->handler(ctx, &fname, &dir) == NGX_ABORT) {
-                return NGX_ABORT;
-            }
-
-        } else {
-            ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
-                          "the file \"%s\" has unknown type, deleting",
-                          fname.data);
-
-            if (ngx_delete_file(fname.data) == NGX_FILE_ERROR) {
-                ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
-                              ngx_delete_file_n " \"%s\" failed", fname.data);
-            } else {
-                ctx->deleted++;
-                ctx->freed += ngx_de_size(&dir);
-            }
-        }
-    }
-
-    if (buf.len) {
-        ngx_free(buf.data);
-    }
-
-    if (ngx_close_dir(&dir) == NGX_ERROR) {
-        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
-                      ngx_close_dir_n " \"%s\" failed", fname.data);
-    }
-
-    return rc;
-}
-
-
-ngx_int_t ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name,
-                                             ngx_dir_t *dir)
-{
-    /*
-     * We use mtime only and do not use atime because:
-     *    on NTFS access time has a resolution of 1 hour,
-     *    on NT FAT access time has a resolution of 1 day,
-     *    Unices have the mount option "noatime".
-     */
-
-    if (ngx_time() - ngx_de_mtime(dir) < 3600) {
-        return NGX_OK;
-    }
-
-    ngx_log_error(NGX_LOG_NOTICE, ctx->log, 0,
-                  "delete the stale temporary file \"%s\"", name->data);
-
-    if (ngx_delete_file(name->data) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
-                      ngx_delete_file_n " \"%s\" failed", name->data);
-        return NGX_ERROR;
-    }
-
-    ctx->deleted++;
-    ctx->freed += ngx_de_size(dir);
-
-    return NGX_OK;
-}
deleted file mode 100644
--- a/src/core/ngx_garbage_collector.h
+++ /dev/null
@@ -1,31 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- */
-
-
-#ifndef _NGX_GARBAGE_COLLECTOR_H_INCLUDED_
-#define _NGX_GARBAGE_COLLECTOR_H_INCLUDED_
-
-
-typedef struct ngx_gc_s  ngx_gc_t;
-
-typedef ngx_int_t (*ngx_gc_handler_pt) (ngx_gc_t *ctx, ngx_str_t *name,
-    ngx_dir_t *dir);
-
-
-struct ngx_gc_s {
-    ngx_path_t         *path;
-    u_int               deleted;
-    off_t               freed;
-    ngx_gc_handler_pt   handler;
-    ngx_log_t          *log;
-};
-
-
-ngx_int_t ngx_collect_garbage(ngx_gc_t *ctx, ngx_str_t *dname, ngx_int_t level);
-ngx_int_t ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name,
-    ngx_dir_t *dir);
-
-
-#endif /* _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ */
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -589,7 +589,7 @@ ngx_hash_wildcard_init(ngx_hash_init_t *
                 wdc->value = names[n].value;
             }
 
-            name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 1));
+            name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2));
 
         } else if (dot) {
             name->value = (void *) ((uintptr_t) name->value | 1);
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -8,11 +8,13 @@
 #include <ngx_core.h>
 
 
+#if (NGX_HAVE_INET6)
+static size_t ngx_inet6_ntop(u_char *p, u_char *text, size_t len);
+#endif
 static ngx_int_t ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u);
 static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u);
-
+static ngx_int_t ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u);
 
-/* AF_INET only */
 
 in_addr_t
 ngx_inet_addr(u_char *text, size_t len)
@@ -57,25 +59,58 @@ ngx_inet_addr(u_char *text, size_t len)
 }
 
 
-/* AF_INET only */
-
 size_t
-ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len)
+ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len, ngx_uint_t port)
 {
-    u_char              *p;
-    struct sockaddr_in  *sin;
+    u_char               *p;
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    size_t                n;
+    struct sockaddr_in6  *sin6;
+#endif
 
-    if (sa->sa_family == AF_INET) {
+    switch (sa->sa_family) {
+
+    case AF_INET:
 
         sin = (struct sockaddr_in *) sa;
         p = (u_char *) &sin->sin_addr;
 
-        return ngx_snprintf(text, len, "%ud.%ud.%ud.%ud",
-                            p[0], p[1], p[2], p[3])
-               - text;
+        if (port) {
+            p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud:%d",
+                             p[0], p[1], p[2], p[3], ntohs(sin->sin_port));
+        } else {
+            p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud",
+                             p[0], p[1], p[2], p[3]);
+        }
+
+        return (p - text);
+
+#if (NGX_HAVE_INET6)
+
+    case AF_INET6:
+
+        sin6 = (struct sockaddr_in6 *) sa;
+
+        n = 0;
+
+        if (port) {
+            text[n++] = '[';
+        }
+
+        n = ngx_inet6_ntop((u_char *) &sin6->sin6_addr, &text[n], len);
+
+        if (port) {
+            n = ngx_sprintf(&text[1 + n], "]:%d",
+                            ntohs(sin6->sin6_port)) - text;
+        }
+
+        return n;
+#endif
+
+    default:
+        return 0;
     }
-
-    return 0;
 }
 
 
@@ -84,42 +119,132 @@ ngx_inet_ntop(int family, void *addr, u_
 {
     u_char  *p;
 
-    if (family == AF_INET) {
+    switch (family) {
 
-        p = (u_char *) addr;
+    case AF_INET:
+
+        p = addr;
 
         return ngx_snprintf(text, len, "%ud.%ud.%ud.%ud",
                             p[0], p[1], p[2], p[3])
                - text;
+
+#if (NGX_HAVE_INET6)
+
+    case AF_INET6:
+        return ngx_inet6_ntop(addr, text, len);
+
+#endif
+
+    default:
+        return 0;
+    }
+}
+
+
+#if (NGX_HAVE_INET6)
+
+static size_t
+ngx_inet6_ntop(u_char *p, u_char *text, size_t len)
+{
+    u_char      *dst;
+    size_t       max, n;
+    ngx_uint_t   i, zero, last;
+
+    if (len < NGX_INET6_ADDRSTRLEN) {
+        return 0;
     }
 
-    return 0;
+    zero = (ngx_uint_t) -1;
+    last = (ngx_uint_t) -1;
+    max = 1;
+    n = 0;
+
+    for (i = 0; i < 16; i += 2) {
+
+        if (p[i] || p[i + 1]) {
+
+            if (max < n) {
+                zero = last;
+                max = n;
+            }
+
+            n = 0;
+            continue;
+        }
+
+        if (n++ == 0) {
+            last = i;
+        }
+    }
+
+    if (max < n) {
+        zero = last;
+        max = n;
+    }
+
+    dst = text;
+    n = 16;
+
+    if (zero == 0) {
+
+        if ((max == 5 && p[10] == 0xff && p[11] == 0xff)
+            || (max == 6)
+            || (max == 7 && p[14] != 0 && p[15] != 1))
+        {
+            n = 12;
+        }
+
+        *dst++ = ':';
+    }
+
+    for (i = 0; i < n; i += 2) {
+
+        if (i == zero) {
+            *dst++ = ':';
+            i += (max - 1) * 2;
+            continue;
+        }
+
+        dst = ngx_sprintf(dst, "%uxi", p[i] * 256 + p[i + 1]);
+
+        if (i < 14) {
+            *dst++ = ':';
+        }
+    }
+
+    if (n == 12) {
+        dst = ngx_sprintf(dst, "%ud.%ud.%ud.%ud", p[12], p[13], p[14], p[15]);
+    }
+
+    return dst - text;
 }
 
+#endif
+
 
 /* AF_INET only */
 
 ngx_int_t
-ngx_ptocidr(ngx_str_t *text, void *cidr)
+ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr)
 {
-    u_char           *addr, *mask, *last;
-    ngx_int_t         shift;
-    ngx_inet_cidr_t  *in_cidr;
+    u_char     *addr, *mask, *last;
+    ngx_int_t   shift;
 
-    in_cidr = cidr;
     addr = text->data;
     last = addr + text->len;
 
     mask = ngx_strlchr(addr, last, '/');
 
-    in_cidr->addr = ngx_inet_addr(addr, (mask ? mask : last) - addr);
+    cidr->u.in.addr = ngx_inet_addr(addr, (mask ? mask : last) - addr);
 
-    if (in_cidr->addr == INADDR_NONE) {
+    if (cidr->u.in.addr == INADDR_NONE) {
         return NGX_ERROR;
     }
 
     if (mask == NULL) {
-        in_cidr->mask = 0xffffffff;
+        cidr->family = AF_INET;
+        cidr->u.in.mask = 0xffffffff;
         return NGX_OK;
     }
 
@@ -130,26 +255,28 @@ ngx_ptocidr(ngx_str_t *text, void *cidr)
         return NGX_ERROR;
     }
 
+    cidr->family = AF_INET;
+
     if (shift == 0) {
 
         /* the x86 compilers use the shl instruction that shifts by modulo 32 */
 
-        in_cidr->mask = 0;
+        cidr->u.in.mask = 0;
 
-        if (in_cidr->addr == 0) {
+        if (cidr->u.in.addr == 0) {
             return NGX_OK;
         }
 
         return NGX_DONE;
     }
 
-    in_cidr->mask = htonl((ngx_uint_t) (0 - (1 << (32 - shift))));
+    cidr->u.in.mask = htonl((ngx_uint_t) (0 - (1 << (32 - shift))));
 
-    if (in_cidr->addr == (in_cidr->addr & in_cidr->mask)) {
+    if (cidr->u.in.addr == (cidr->u.in.addr & cidr->u.in.mask)) {
         return NGX_OK;
     }
 
-    in_cidr->addr &= in_cidr->mask;
+    cidr->u.in.addr &= cidr->u.in.mask;
 
     return NGX_DONE;
 }
@@ -171,6 +298,10 @@ ngx_parse_url(ngx_pool_t *pool, ngx_url_
         return NGX_ERROR;
     }
 
+    if (p[0] == '[') {
+        return ngx_parse_inet6_url(pool, u);
+    }
+
     return ngx_parse_inet_url(pool, u);
 }
 
@@ -209,13 +340,17 @@ ngx_parse_unix_domain_url(ngx_pool_t *po
 
     u->host.len = len++;
     u->host.data = path;
-    u->family = AF_UNIX;
 
     if (len > sizeof(saun->sun_path)) {
         u->err = "too long path in the unix domain socket";
         return NGX_ERROR;
     }
 
+    u->socklen = sizeof(struct sockaddr_un);
+    saun = (struct sockaddr_un *) &u->sockaddr;
+    saun->sun_family = AF_UNIX;
+    (void) ngx_cpystrn((u_char *) saun->sun_path, path, len);
+
     u->addrs = ngx_pcalloc(pool, sizeof(ngx_peer_addr_t));
     if (u->addrs == NULL) {
         return NGX_ERROR;
@@ -226,6 +361,7 @@ ngx_parse_unix_domain_url(ngx_pool_t *po
         return NGX_ERROR;
     }
 
+    u->family = AF_UNIX;
     u->naddrs = 1;
 
     saun->sun_family = AF_UNIX;
@@ -251,10 +387,15 @@ ngx_parse_unix_domain_url(ngx_pool_t *po
 static ngx_int_t
 ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u)
 {
-    u_char          *p, *host, *port, *last, *uri;
-    size_t           len;
-    ngx_int_t        n;
-    struct hostent  *h;
+    u_char              *p, *host, *port, *last, *uri, *args;
+    size_t               len;
+    ngx_int_t            n;
+    struct hostent      *h;
+    struct sockaddr_in  *sin;
+
+    u->socklen = sizeof(struct sockaddr_in);
+    sin = (struct sockaddr_in *) &u->sockaddr;
+    sin->sin_family = AF_INET;
 
     u->family = AF_INET;
 
@@ -264,7 +405,18 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
 
     port = ngx_strlchr(host, last, ':');
 
-    uri = ngx_strlchr(port ? port : host, last, '/');
+    uri = ngx_strlchr(host, last, '/');
+
+    args = ngx_strlchr(host, last, '?');
+
+    if (args) {
+        if (uri == NULL) {
+            uri = args;
+
+        } else if (args < uri) {
+            uri = args;
+        }
+    }
 
     if (uri) {
         if (u->listen || !u->uri_part) {
@@ -276,6 +428,10 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
         u->uri.data = uri;
 
         last = uri;
+
+        if (uri < port) {
+            port = NULL;
+        }
     }
 
     if (port) {
@@ -296,6 +452,7 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
         }
 
         u->port = (in_port_t) n;
+        sin->sin_port = htons((in_port_t) n);
 
         u->port_text.len = len;
         u->port_text.data = port;
@@ -319,10 +476,13 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
                     }
 
                     u->port = (in_port_t) n;
+                    sin->sin_port = htons((in_port_t) n);
 
                     u->port_text.len = last - host;
                     u->port_text.data = host;
 
+                    u->wildcard = 1;
+
                     return NGX_OK;
                 }
             }
@@ -358,9 +518,9 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
 
         (void) ngx_cpystrn(p, host, len);
 
-        u->addr.in_addr = inet_addr((const char *) p);
+        sin->sin_addr.s_addr = inet_addr((const char *) p);
 
-        if (u->addr.in_addr == INADDR_NONE) {
+        if (sin->sin_addr.s_addr == INADDR_NONE) {
             h = gethostbyname((const char *) p);
 
             if (h == NULL || h->h_addr_list[0] == NULL) {
@@ -369,17 +529,23 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
                 return NGX_ERROR;
             }
 
-            u->addr.in_addr = *(in_addr_t *) (h->h_addr_list[0]);
+            sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[0]);
+        }
+
+        if (sin->sin_addr.s_addr == INADDR_ANY) {
+            u->wildcard = 1;
         }
 
         ngx_free(p);
 
     } else {
-        u->addr.in_addr = INADDR_ANY;
+        sin->sin_addr.s_addr = INADDR_ANY;
+        u->wildcard = 1;
     }
 
     if (u->no_port) {
         u->port = u->default_port;
+        sin->sin_port = htons(u->default_port);
     }
 
     if (u->listen) {
@@ -394,11 +560,148 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
 }
 
 
+static ngx_int_t
+ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u)
+{
+#if (NGX_HAVE_INET6)
+    int                   rc;
+    u_char               *p, *host, *port, *last, *uri;
+    size_t                len;
+    ngx_int_t             n;
+    struct sockaddr_in6  *sin6;
+
+    u->socklen = sizeof(struct sockaddr_in6);
+    sin6 = (struct sockaddr_in6 *) &u->sockaddr;
+    sin6->sin6_family = AF_INET6;
+
+    host = u->url.data + 1;
+
+    last = u->url.data + u->url.len;
+
+    p = ngx_strlchr(host, last, ']');
+
+    if (p == NULL) {
+        u->err = "invalid host";
+        return NGX_ERROR;
+    }
+
+    if (last - p) {
+
+        port = p + 1;
+
+        uri = ngx_strlchr(port, last, '/');
+
+        if (uri) {
+            if (u->listen || !u->uri_part) {
+                u->err = "invalid host";
+                return NGX_ERROR;
+            }
+
+            u->uri.len = last - uri;
+            u->uri.data = uri;
+        }
+
+        if (*port == ':') {
+            port++;
+
+            len = last - port;
+
+            if (len == 0) {
+                u->err = "invalid port";
+                return NGX_ERROR;
+            }
+
+            n = ngx_atoi(port, len);
+
+            if (n < 1 || n > 65536) {
+                u->err = "invalid port";
+                return NGX_ERROR;
+            }
+
+            u->port = (in_port_t) n;
+            sin6->sin6_port = htons((in_port_t) n);
+
+            u->port_text.len = len;
+            u->port_text.data = port;
+
+        } else {
+            u->no_port = 1;
+        }
+    }
+
+    len = p - host;
+
+    if (len == 0) {
+        u->err = "no host";
+        return NGX_ERROR;
+    }
+
+    u->host.len = len++;
+    u->host.data = host;
+
+    p = ngx_alloc(len, pool->log);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    (void) ngx_cpystrn(p, host, len);
+
+#if (NGX_WIN32)
+
+    rc = WSAStringToAddress((char *) p, AF_INET6, NULL,
+                            (SOCKADDR *) sin6, &u->socklen);
+    rc = !rc;
+
+    if (u->port) {
+        sin6->sin6_port = htons(u->port);
+    }
+
+#else
+
+    rc = inet_pton(AF_INET6, (const char *) p, &sin6->sin6_addr);
+
+#endif
+
+    ngx_free(p);
+
+    if (rc == 0) {
+        u->err = "invalid IPv6 address";
+        return NGX_ERROR;
+    }
+
+    if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+        u->wildcard = 1;
+    }
+
+    u->family = AF_INET6;
+
+    if (u->no_resolve) {
+        return NGX_OK;
+    }
+
+    if (u->no_port) {
+        u->port = u->default_port;
+        sin6->sin6_port = htons(u->default_port);
+    }
+
+    return NGX_OK;
+
+#else
+
+    u->err = "the INET6 sockets are not supported on this platform";
+
+    return NGX_ERROR;
+
+#endif
+}
+
+
 ngx_int_t
 ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)
 {
     u_char              *p, *host;
     size_t               len;
+    in_port_t            port;
     in_addr_t            in_addr;
     ngx_uint_t           i;
     struct hostent      *h;
@@ -413,6 +716,8 @@ ngx_inet_resolve_host(ngx_pool_t *pool, 
 
     /* AF_INET only */
 
+    port = htons(u->port);
+
     in_addr = inet_addr((char *) host);
 
     if (in_addr == INADDR_NONE) {
@@ -449,22 +754,22 @@ ngx_inet_resolve_host(ngx_pool_t *pool, 
             }
 
             sin->sin_family = AF_INET;
-            sin->sin_port = htons(u->port);
+            sin->sin_port = port;
             sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[i]);
 
             u->addrs[i].sockaddr = (struct sockaddr *) sin;
             u->addrs[i].socklen = sizeof(struct sockaddr_in);
 
-            len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1;
+            len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
 
             p = ngx_pnalloc(pool, len);
             if (p == NULL) {
                 return NGX_ERROR;
             }
 
-            len = ngx_sock_ntop((struct sockaddr *) sin, p, len);
+            len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1);
 
-            u->addrs[i].name.len = ngx_sprintf(&p[len], ":%d", u->port) - p;
+            u->addrs[i].name.len = len;
             u->addrs[i].name.data = p;
         }
 
@@ -487,18 +792,19 @@ ngx_inet_resolve_host(ngx_pool_t *pool, 
         u->naddrs = 1;
 
         sin->sin_family = AF_INET;
-        sin->sin_port = htons(u->port);
+        sin->sin_port = port;
         sin->sin_addr.s_addr = in_addr;
 
         u->addrs[0].sockaddr = (struct sockaddr *) sin;
         u->addrs[0].socklen = sizeof(struct sockaddr_in);
 
-        p = ngx_pnalloc(pool, u->host.len + sizeof(":65536") - 1);
+        p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);
         if (p == NULL) {
             return NGX_ERROR;
         }
 
-        u->addrs[0].name.len = ngx_sprintf(p, "%V:%d", &u->host, u->port) - p;
+        u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",
+                                           &u->host, ntohs(port)) - p;
         u->addrs[0].name.data = p;
     }
 
--- a/src/core/ngx_inet.h
+++ b/src/core/ngx_inet.h
@@ -12,57 +12,95 @@
 #include <ngx_core.h>
 
 
-#define NGX_INET_ADDRSTRLEN  (sizeof("255.255.255.255") - 1)
+#define NGX_INET_ADDRSTRLEN   (sizeof("255.255.255.255") - 1)
+#define NGX_INET6_ADDRSTRLEN                                                 \
+    (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") - 1)
+
+#define NGX_SOCKADDR_STRLEN   (NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1)
+
+
+/*
+ * TODO: autoconfigure NGX_SOCKADDRLEN as
+ *       sizeof(struct sockaddr_storage)
+ *       sizeof(struct sockaddr_un)
+ *       sizeof(struct sockaddr_in6)
+ *       sizeof(struct sockaddr_in)
+ */
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+#define NGX_SOCKADDRLEN       sizeof(struct sockaddr_un)
+#else
+#define NGX_SOCKADDRLEN       512
+#endif
 
 
 typedef struct {
-    in_addr_t         addr;
-    in_addr_t         mask;
-} ngx_inet_cidr_t;
+    in_addr_t                 addr;
+    in_addr_t                 mask;
+} ngx_in_cidr_t;
 
 
-typedef union {
-    in_addr_t         in_addr;
-} ngx_url_addr_t;
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+    struct in6_addr           addr;
+    struct in6_addr           mask;
+} ngx_in6_cidr_t;
+
+#endif
 
 
 typedef struct {
-    struct sockaddr  *sockaddr;
-    socklen_t         socklen;
-    ngx_str_t         name;
+    ngx_uint_t                family;
+    union {
+        ngx_in_cidr_t         in;
+#if (NGX_HAVE_INET6)
+        ngx_in6_cidr_t        in6;
+#endif
+    } u;
+} ngx_cidr_t;
+
+
+typedef struct {
+    struct sockaddr          *sockaddr;
+    socklen_t                 socklen;
+    ngx_str_t                 name;
 } ngx_peer_addr_t;
 
 
 typedef struct {
-    ngx_str_t         url;
-    ngx_str_t         host;
-    ngx_str_t         port_text;
-    ngx_str_t         uri;
+    ngx_str_t                 url;
+    ngx_str_t                 host;
+    ngx_str_t                 port_text;
+    ngx_str_t                 uri;
 
-    in_port_t         port;
-    in_port_t         default_port;
-    int               family;
+    in_port_t                 port;
+    in_port_t                 default_port;
+    int                       family;
 
-    unsigned          listen:1;
-    unsigned          uri_part:1;
-    unsigned          no_resolve:1;
-    unsigned          one_addr:1;
+    unsigned                  listen:1;
+    unsigned                  uri_part:1;
+    unsigned                  no_resolve:1;
+    unsigned                  one_addr:1;
 
-    unsigned          no_port:1;
+    unsigned                  no_port:1;
+    unsigned                  wildcard:1;
 
-    ngx_url_addr_t    addr;
+    socklen_t                 socklen;
+    u_char                    sockaddr[NGX_SOCKADDRLEN];
 
-    ngx_peer_addr_t  *addrs;
-    ngx_uint_t        naddrs;
+    ngx_peer_addr_t          *addrs;
+    ngx_uint_t                naddrs;
 
-    char             *err;
+    char                     *err;
 } ngx_url_t;
 
 
 in_addr_t ngx_inet_addr(u_char *text, size_t len);
-size_t ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len);
+size_t ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len,
+    ngx_uint_t port);
 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_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr);
 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.c
+++ b/src/core/ngx_log.c
@@ -8,14 +8,14 @@
 #include <ngx_core.h>
 
 
-static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 
 
 static ngx_command_t  ngx_errlog_commands[] = {
 
     {ngx_string("error_log"),
      NGX_MAIN_CONF|NGX_CONF_1MORE,
-     ngx_set_error_log,
+     ngx_error_log,
      0,
      0,
      NULL},
@@ -48,12 +48,20 @@ ngx_module_t  ngx_errlog_module = {
 
 
 static ngx_log_t        ngx_log;
-static ngx_open_file_t  ngx_stderr;
+static ngx_open_file_t  ngx_log_file;
+ngx_uint_t              ngx_use_stderr = 1;
 
 
-static const char *err_levels[] = {
-    "stderr", "emerg", "alert", "crit", "error",
-    "warn", "notice", "info", "debug"
+static ngx_str_t err_levels[] = {
+    ngx_null_string,
+    ngx_string("emerg"),
+    ngx_string("alert"),
+    ngx_string("crit"),
+    ngx_string("error"),
+    ngx_string("warn"),
+    ngx_string("notice"),
+    ngx_string("info"),
+    ngx_string("debug")
 };
 
 static const char *debug_levels[] = {
@@ -79,7 +87,8 @@ ngx_log_error_core(ngx_uint_t level, ngx
 #if (NGX_HAVE_VARIADIC_MACROS)
     va_list  args;
 #endif
-    u_char   errstr[NGX_MAX_ERROR_STR], *p, *last;
+    u_char  *p, *last, *msg;
+    u_char   errstr[NGX_MAX_ERROR_STR];
 
     if (log->file->fd == NGX_INVALID_FILE) {
         return;
@@ -92,60 +101,32 @@ ngx_log_error_core(ngx_uint_t level, ngx
 
     p = errstr + ngx_cached_err_log_time.len;
 
-    p = ngx_snprintf(p, last - p, " [%s] ", err_levels[level]);
+    p = ngx_slprintf(p, last, " [%V] ", &err_levels[level]);
 
     /* pid#tid */
-    p = ngx_snprintf(p, last - p, "%P#" NGX_TID_T_FMT ": ",
+    p = ngx_slprintf(p, last, "%P#" NGX_TID_T_FMT ": ",
                     ngx_log_pid, ngx_log_tid);
 
     if (log->connection) {
-        p = ngx_snprintf(p, last - p, "*%uA ", log->connection);
+        p = ngx_slprintf(p, last, "*%uA ", log->connection);
     }
 
+    msg = p;
+
 #if (NGX_HAVE_VARIADIC_MACROS)
 
     va_start(args, fmt);
-    p = ngx_vsnprintf(p, last - p, fmt, args);
+    p = ngx_vslprintf(p, last, fmt, args);
     va_end(args);
 
 #else
 
-    p = ngx_vsnprintf(p, last - p, fmt, args);
+    p = ngx_vslprintf(p, last, fmt, args);
 
 #endif
 
     if (err) {
-
-        if (p > last - 50) {
-
-            /* leave a space for an error code */
-
-            p = last - 50;
-            *p++ = '.';
-            *p++ = '.';
-            *p++ = '.';
-        }
-
-#if (NGX_WIN32)
-
-        if ((unsigned) err >= 0x80000000) {
-            p = ngx_snprintf(p, last - p, " (%Xd: ", err);
-
-        } else {
-            p = ngx_snprintf(p, last - p, " (%d: ", err);
-        }
-
-#else
-
-        p = ngx_snprintf(p, last - p, " (%d: ", err);
-
-#endif
-
-        p = ngx_strerror_r(err, p, last - p);
-
-        if (p < last) {
-            *p++ = ')';
-        }
+        p = ngx_log_errno(p, last, err);
     }
 
     if (level != NGX_LOG_DEBUG && log->handler) {
@@ -158,7 +139,20 @@ ngx_log_error_core(ngx_uint_t level, ngx
 
     ngx_linefeed(p);
 
-    ngx_write_fd(log->file->fd, errstr, p - errstr);
+    (void) ngx_write_fd(log->file->fd, errstr, p - errstr);
+
+    if (!ngx_use_stderr
+        || level > NGX_LOG_WARN
+        || log->file->fd == ngx_stderr)
+    {
+        return;
+    }
+
+    msg -= (err_levels[level].len + 4);
+
+    (void) ngx_sprintf(msg, "[%V]: ", &err_levels[level]);
+
+    (void) ngx_write_console(ngx_stderr, msg, p - msg);
 }
 
 
@@ -191,64 +185,169 @@ ngx_log_debug_core(ngx_log_t *log, ngx_e
 #endif
 
 
-void
-ngx_log_abort(ngx_err_t err, const char *text)
+void ngx_cdecl
+ngx_log_abort(ngx_err_t err, const char *fmt, ...)
+{
+    u_char   *p;
+    va_list   args;
+    u_char    errstr[NGX_MAX_CONF_ERRSTR];
+
+    va_start(args, fmt);
+    p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args);
+    va_end(args);
+
+    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,
+                  "%*s", p - errstr, errstr);
+}
+
+
+void ngx_cdecl
+ngx_log_stderr(ngx_err_t err, const char *fmt, ...)
 {
-    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, text);
+    u_char   *p, *last;
+    va_list   args;
+    u_char    errstr[NGX_MAX_ERROR_STR];
+
+    last = errstr + NGX_MAX_ERROR_STR;
+
+    va_start(args, fmt);
+    p = ngx_vslprintf(errstr, last, fmt, args);
+    va_end(args);
+
+    if (err) {
+        p = ngx_log_errno(p, last, err);
+    }
+
+    if (p > last - NGX_LINEFEED_SIZE) {
+        p = last - NGX_LINEFEED_SIZE;
+    }
+
+    ngx_linefeed(p);
+
+    (void) ngx_write_console(ngx_stderr, errstr, p - errstr);
+}
+
+
+u_char *
+ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err)
+{
+    if (buf > last - 50) {
+
+        /* leave a space for an error code */
+
+        buf = last - 50;
+        *buf++ = '.';
+        *buf++ = '.';
+        *buf++ = '.';
+    }
+
+#if (NGX_WIN32)
+    buf = ngx_slprintf(buf, last, ((unsigned) err < 0x80000000)
+                                       ? " (%d: " : " (%Xd: ", err);
+#else
+    buf = ngx_slprintf(buf, last, " (%d: ", err);
+#endif
+
+    buf = ngx_strerror_r(err, buf, last - buf);
+
+    if (buf < last) {
+        *buf++ = ')';
+    }
+
+    return buf;
 }
 
 
 ngx_log_t *
-ngx_log_init(void)
+ngx_log_init(u_char *prefix)
 {
-    ngx_log.file = &ngx_stderr;
+    u_char  *p, *name;
+    size_t   nlen, plen;
+
+    ngx_log.file = &ngx_log_file;
     ngx_log.log_level = NGX_LOG_NOTICE;
 
+    name = (u_char *) NGX_ERROR_LOG_PATH;
+
+    /*
+     * we use ngx_strlen() here since BCC warns about
+     * condition is always false and unreachable code
+     */
+
+    nlen = ngx_strlen(name);
+
+    if (nlen == 0) {
+        ngx_log_file.fd = ngx_stderr;
+        return &ngx_log;
+    }
+
+    p = NULL;
+
 #if (NGX_WIN32)
+    if (name[1] != ':') {
+#else
+    if (name[0] != '/') {
+#endif
 
-    ngx_stderr_fileno = GetStdHandle(STD_ERROR_HANDLE);
+        if (prefix) {
+            plen = ngx_strlen(prefix);
 
-    ngx_stderr.fd = ngx_open_file(NGX_ERROR_LOG_PATH, NGX_FILE_RDWR,
-                                  NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND, 0);
+        } else {
+#ifdef NGX_PREFIX
+            prefix = (u_char *) NGX_PREFIX;
+            plen = ngx_strlen(prefix);
+#else
+            plen = 0;
+#endif
+        }
 
-    if (ngx_stderr.fd == NGX_INVALID_FILE) {
-        ngx_message_box("nginx", MB_OK, ngx_errno,
-                        "Could not open error log file: "
-                        ngx_open_file_n " \"" NGX_ERROR_LOG_PATH "\" failed");
-        return NULL;
+        if (plen) {
+            name = malloc(plen + nlen + 2);
+            if (name == NULL) {
+                return NULL;
+            }
+
+            p = ngx_cpymem(name, prefix, plen);
+
+            if (!ngx_path_separator(*(p - 1))) {
+                *p++ = '/';
+            }
+
+            ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1);
+
+            p = name;
+        }
     }
 
-    if (ngx_file_append_mode(ngx_stderr.fd) == NGX_ERROR) {
-        ngx_message_box("nginx", MB_OK, ngx_errno,
-                        "Could not open error log file: "
-                        ngx_file_append_mode_n " \"" NGX_ERROR_LOG_PATH
-                        "\" failed");
-        return NULL;
+    ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,
+                                    NGX_FILE_CREATE_OR_OPEN,
+                                    NGX_FILE_DEFAULT_ACCESS);
+
+    if (ngx_log_file.fd == NGX_INVALID_FILE) {
+        ngx_log_stderr(ngx_errno,
+                       "[alert]: could not open error log file: "
+                       ngx_open_file_n " \"%s\" failed", name);
+#if (NGX_WIN32)
+        ngx_event_log(ngx_errno,
+                       "could not open error log file: "
+                       ngx_open_file_n " \"%s\" failed", name);
+#endif
+
+        ngx_log_file.fd = ngx_stderr;
     }
 
-#else
-
-    ngx_stderr.fd = STDERR_FILENO;
-
-#endif
+    if (p) {
+        ngx_free(p);
+    }
 
     return &ngx_log;
 }
 
 
 ngx_log_t *
-ngx_log_create_errlog(ngx_cycle_t *cycle, ngx_array_t *args)
+ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name)
 {
     ngx_log_t  *log;
-    ngx_str_t  *value, *name;
-
-    if (args) {
-        value = args->elts;
-        name = &value[1];
-
-    } else {
-        name = NULL;
-    }
 
     log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t));
     if (log == NULL) {
@@ -265,7 +364,7 @@ ngx_log_create_errlog(ngx_cycle_t *cycle
 
 
 char *
-ngx_set_error_log_levels(ngx_conf_t *cf, ngx_log_t *log)
+ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log)
 {
     ngx_uint_t   i, n, d;
     ngx_str_t   *value;
@@ -275,12 +374,12 @@ ngx_set_error_log_levels(ngx_conf_t *cf,
     for (i = 2; i < cf->args->nelts; i++) {
 
         for (n = 1; n <= NGX_LOG_DEBUG; n++) {
-            if (ngx_strcmp(value[i].data, err_levels[n]) == 0) {
+            if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) {
 
                 if (log->log_level != 0) {
                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                                       "duplicate log level \"%s\"",
-                                       value[i].data);
+                                       "duplicate log level \"%V\"",
+                                       &value[i]);
                     return NGX_CONF_ERROR;
                 }
 
@@ -293,8 +392,8 @@ ngx_set_error_log_levels(ngx_conf_t *cf,
             if (ngx_strcmp(value[i].data, debug_levels[n++]) == 0) {
                 if (log->log_level & ~NGX_LOG_DEBUG_ALL) {
                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                                       "invalid log level \"%s\"",
-                                       value[i].data);
+                                       "invalid log level \"%V\"",
+                                       &value[i]);
                     return NGX_CONF_ERROR;
                 }
 
@@ -305,7 +404,7 @@ ngx_set_error_log_levels(ngx_conf_t *cf,
 
         if (log->log_level == 0) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "invalid log level \"%s\"", value[i].data);
+                               "invalid log level \"%V\"", &value[i]);
             return NGX_CONF_ERROR;
         }
     }
@@ -319,26 +418,35 @@ ngx_set_error_log_levels(ngx_conf_t *cf,
 
 
 static char *
-ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    ngx_str_t  *value;
+    ngx_str_t  *value, name;
+
+    if (cf->cycle->new_log.file) {
+        return "is duplicate";
+    }
 
     value = cf->args->elts;
 
-    if (value[1].len == 6 && ngx_strcmp(value[1].data, "stderr") == 0) {
-        cf->cycle->new_log->file->fd = ngx_stderr.fd;
-        cf->cycle->new_log->file->name.len = 0;
-        cf->cycle->new_log->file->name.data = NULL;
+    if (ngx_strcmp(value[1].data, "stderr") == 0) {
+        name.len = 0;
+        name.data = NULL;
 
     } else {
-        cf->cycle->new_log->file->name = value[1];
+        name = value[1];
+    }
 
-        if (ngx_conf_full_name(cf->cycle, &cf->cycle->new_log->file->name, 0)
-            == NGX_ERROR)
-        {
-            return NGX_CONF_ERROR;
-        }
+    cf->cycle->new_log.file = ngx_conf_open_file(cf->cycle, &name);
+    if (cf->cycle->new_log.file == NULL) {
+        return NULL;
     }
 
-    return ngx_set_error_log_levels(cf, cf->cycle->new_log);
+    if (cf->args->nelts == 2) {
+        cf->cycle->new_log.log_level = NGX_LOG_ERR;
+        return NGX_CONF_OK;
+    }
+
+    cf->cycle->new_log.log_level = 0;
+
+    return ngx_log_set_levels(cf, &cf->cycle->new_log);
 }
--- a/src/core/ngx_log.h
+++ b/src/core/ngx_log.h
@@ -195,13 +195,16 @@ void ngx_cdecl ngx_log_debug_core(ngx_lo
 
 /*********************************/
 
-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);
-void ngx_log_abort(ngx_err_t err, const char *text);
+ngx_log_t *ngx_log_init(u_char *prefix);
+ngx_log_t *ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name);
+char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log);
+void ngx_cdecl ngx_log_abort(ngx_err_t err, const char *fmt, ...);
+void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...);
+u_char *ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err);
 
 
 extern ngx_module_t  ngx_errlog_module;
+extern ngx_uint_t    ngx_use_stderr;
 
 
 #endif /* _NGX_LOG_H_INCLUDED_ */
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -130,6 +130,7 @@ ngx_open_cached_file(ngx_open_file_cache
     time_t                          now;
     uint32_t                        hash;
     ngx_int_t                       rc;
+    ngx_file_info_t                 fi;
     ngx_pool_cleanup_t             *cln;
     ngx_cached_open_file_t         *file;
     ngx_pool_cleanup_file_t        *clnf;
@@ -140,6 +141,25 @@ ngx_open_cached_file(ngx_open_file_cache
 
     if (cache == NULL) {
 
+        if (of->test_only) {
+
+            if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
+                of->err = ngx_errno;
+                of->failed = ngx_file_info_n;
+                return NGX_ERROR;
+            }
+
+            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);
+
+            return NGX_OK;
+        }
+
         cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
         if (cln == NULL) {
             return NGX_ERROR;
@@ -214,6 +234,7 @@ ngx_open_cached_file(ngx_open_file_cache
 
             } else {
                 of->err = file->err;
+                of->failed = ngx_open_file_n;
             }
 
             goto found;
@@ -339,6 +360,7 @@ create:
 
     file->uses = 1;
     file->count = 0;
+    file->use_event = 0;
     file->event = NULL;
 
 add_event:
@@ -443,7 +465,8 @@ ngx_open_and_stat_file(u_char *name, ngx
 
     if (of->fd != NGX_INVALID_FILE) {
 
-        if (ngx_file_info(name, &fi) == -1) {
+        if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
+            of->failed = ngx_file_info_n;
             goto failed;
         }
 
@@ -453,11 +476,12 @@ ngx_open_and_stat_file(u_char *name, ngx
 
     } else if (of->test_dir) {
 
-        if (ngx_file_info(name, &fi) == -1) {
+        if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
+            of->failed = ngx_file_info_n;
             goto failed;
         }
 
-        if (of->is_dir) {
+        if (ngx_is_dir(&fi)) {
             goto done;
         }
     }
@@ -466,12 +490,12 @@ ngx_open_and_stat_file(u_char *name, ngx
         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,
+        fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,
                            NGX_FILE_DEFAULT_ACCESS);
     }
 
     if (fd == NGX_INVALID_FILE) {
+        of->failed = ngx_open_file_n;
         goto failed;
     }
 
--- a/src/core/ngx_open_file_cache.h
+++ b/src/core/ngx_open_file_cache.h
@@ -12,19 +12,25 @@
 #define _NGX_OPEN_FILE_CACHE_H_INCLUDED_
 
 
+#define NGX_OPEN_FILE_DIRECTIO_OFF  NGX_MAX_OFF_T_VALUE
+
+
 typedef struct {
     ngx_fd_t                 fd;
     ngx_file_uniq_t          uniq;
     time_t                   mtime;
     off_t                    size;
     off_t                    directio;
+
     ngx_err_t                err;
+    char                    *failed;
 
     time_t                   valid;
 
     ngx_uint_t               min_uses;
 
     unsigned                 test_dir:1;
+    unsigned                 test_only:1;
     unsigned                 log:1;
     unsigned                 errors:1;
     unsigned                 events:1;
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -13,6 +13,17 @@
 #define NGX_SENDFILE_LIMIT  4096
 #endif
 
+/*
+ * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly
+ * to an application memory from a device if parameters are aligned
+ * to device sector boundary(512 bytes).  They fallback to usual read
+ * operation if the parameters are not aligned.
+ * Linux allows DIRECTIO only if the parameters are aligned to a filesystem
+ * sector boundary, otherwise it returns EINVAL.  The sector size is
+ * usually 512 bytes, however, on XFS it may be 4096 bytes.
+ */
+#define NGX_DIRECTIO_BLOCK  4096
+
 
 #define NGX_NONE            1
 
@@ -303,12 +314,11 @@ ngx_output_chain_add_copy(ngx_pool_t *po
 
 #endif
 
+        cl->next = NULL;
         *ll = cl;
         ll = &cl->next;
     }
 
-    *ll = NULL;
-
     return NGX_OK;
 }
 
@@ -327,7 +337,7 @@ ngx_output_chain_align_file_buf(ngx_outp
 
     ctx->directio = 1;
 
-    size = (size_t) (in->file_pos - (in->file_pos & ~511));
+    size = (size_t) (in->file_pos - (in->file_pos & ~(NGX_DIRECTIO_BLOCK - 1)));
 
     if (size == 0) {
 
@@ -338,7 +348,7 @@ ngx_output_chain_align_file_buf(ngx_outp
         size = (size_t) bsize;
 
     } else {
-        size = 512 - size;
+        size = NGX_DIRECTIO_BLOCK - size;
 
         if ((off_t) size > bsize) {
             size = (size_t) bsize;
@@ -413,7 +423,7 @@ ngx_output_chain_get_buf(ngx_output_chai
          * userland buffer direct usage conjunctly with directio
          */
 
-        b->start = ngx_pmemalign(ctx->pool, size, 512);
+        b->start = ngx_pmemalign(ctx->pool, size, NGX_DIRECTIO_BLOCK);
         if (b->start == NULL) {
             return NGX_ERROR;
         }
@@ -492,6 +502,7 @@ ngx_output_chain_copy_buf(ngx_output_cha
         if (src->pos == src->last) {
             dst->flush = src->flush;
             dst->last_buf = src->last_buf;
+            dst->last_in_chain = src->last_in_chain;
         }
 
     } else {
@@ -566,6 +577,7 @@ ngx_output_chain_copy_buf(ngx_output_cha
         if (src->file_pos == src->file_last) {
             dst->flush = src->flush;
             dst->last_buf = src->last_buf;
+            dst->last_in_chain = src->last_in_chain;
         }
     }
 
--- a/src/core/ngx_palloc.c
+++ b/src/core/ngx_palloc.c
@@ -25,6 +25,7 @@ ngx_create_pool(size_t size, ngx_log_t *
     p->d.last = (u_char *) p + sizeof(ngx_pool_t);
     p->d.end = (u_char *) p + size;
     p->d.next = NULL;
+    p->d.failed = 0;
 
     size = size - sizeof(ngx_pool_t);
     p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
@@ -91,6 +92,26 @@ ngx_destroy_pool(ngx_pool_t *pool)
 }
 
 
+void
+ngx_reset_pool(ngx_pool_t *pool)
+{
+    ngx_pool_t        *p;
+    ngx_pool_large_t  *l;
+
+    for (l = pool->large; l; l = l->next) {
+        if (l->alloc) {
+            ngx_free(l->alloc);
+        }
+    }
+
+    pool->large = NULL;
+
+    for (p = pool; p; p = p->d.next) {
+        p->d.last = (u_char *) p + sizeof(ngx_pool_t);
+    }
+}
+
+
 void *
 ngx_palloc(ngx_pool_t *pool, size_t size)
 {
@@ -169,14 +190,16 @@ ngx_palloc_block(ngx_pool_t *pool, size_
 
     new->d.end = m + psize;
     new->d.next = NULL;
+    new->d.failed = 0;
 
     m += sizeof(ngx_pool_data_t);
+    m = ngx_align_ptr(m, NGX_ALIGNMENT);
     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) {
+        if (p->d.failed++ > 4) {
             current = p->d.next;
         }
     }
@@ -193,6 +216,7 @@ static void *
 ngx_palloc_large(ngx_pool_t *pool, size_t size)
 {
     void              *p;
+    ngx_uint_t         n;
     ngx_pool_large_t  *large;
 
     p = ngx_alloc(size, pool->log);
@@ -200,6 +224,19 @@ ngx_palloc_large(ngx_pool_t *pool, size_
         return NULL;
     }
 
+    n = 0;
+
+    for (large = pool->large; large; large = large->next) {
+        if (large->alloc == NULL) {
+            large->alloc = p;
+            return p;
+        }
+
+        if (n++ > 3) {
+            break;
+        }
+    }
+
     large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
     if (large == NULL) {
         ngx_free(p);
@@ -305,6 +342,27 @@ ngx_pool_cleanup_add(ngx_pool_t *p, size
 
 
 void
+ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)
+{
+    ngx_pool_cleanup_t       *c;
+    ngx_pool_cleanup_file_t  *cf;
+
+    for (c = p->cleanup; c; c = c->next) {
+        if (c->handler == ngx_pool_cleanup_file) {
+
+            cf = c->data;
+
+            if (cf->fd == fd) {
+                c->handler(cf);
+                c->handler = NULL;
+                return;
+            }
+        }
+    }
+}
+
+
+void
 ngx_pool_cleanup_file(void *data)
 {
     ngx_pool_cleanup_file_t  *c = data;
--- a/src/core/ngx_palloc.h
+++ b/src/core/ngx_palloc.h
@@ -14,7 +14,6 @@
 
 /*
  * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
- * On FreeBSD 5.x it allows to use the zero copy sending.
  * On Windows NT it decreases a number of locked pages in a kernel.
  */
 #define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)
@@ -47,6 +46,7 @@ typedef struct {
     u_char               *last;
     u_char               *end;
     ngx_pool_t           *next;
+    ngx_uint_t            failed;
 } ngx_pool_data_t;
 
 
@@ -73,6 +73,7 @@ void *ngx_calloc(size_t size, ngx_log_t 
 
 ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
 void ngx_destroy_pool(ngx_pool_t *pool);
+void ngx_reset_pool(ngx_pool_t *pool);
 
 void *ngx_palloc(ngx_pool_t *pool, size_t size);
 void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
@@ -82,6 +83,7 @@ ngx_int_t ngx_pfree(ngx_pool_t *pool, vo
 
 
 ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);
+void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);
 void ngx_pool_cleanup_file(void *data);
 void ngx_pool_delete_file(void *data);
 
--- a/src/core/ngx_radix_tree.c
+++ b/src/core/ngx_radix_tree.c
@@ -42,13 +42,13 @@ ngx_radix_tree_create(ngx_pool_t *pool, 
     }
 
     /*
-     * The preallocation the first nodes: 0, 1, 00, 01, 10, 11, 000, 001, etc.
-     * increases the TLB hits even if for the first lookup iterations.
-     * On the 32-bit platforms the 7 preallocated bits takes continuous 4K,
-     * 8 - 8K, 9 - 16K, etc.  On the 64-bit platforms the 6 preallocated bits
+     * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc.
+     * increases TLB hits even if for first lookup iterations.
+     * On 32-bit platforms the 7 preallocated bits takes continuous 4K,
+     * 8 - 8K, 9 - 16K, etc.  On 64-bit platforms the 6 preallocated bits
      * takes continuous 4K, 7 - 8K, 8 - 16K, etc.  There is no sense to
      * to preallocate more than one page, because further preallocation
-     * distributes the only bit per page.  Instead, the random insertion
+     * distributes the only bit per page.  Instead, a random insertion
      * may distribute several bits per page.
      *
      * Thus, by default we preallocate maximum
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -131,14 +131,14 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_
 
     r->event->handler = ngx_resolver_resend_handler;
     r->event->data = r;
-    r->event->log = cf->cycle->new_log;
+    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 = &cf->cycle->new_log;
     r->log_level = NGX_LOG_ALERT;
 
     if (addr) {
@@ -152,7 +152,7 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_
         uc->sockaddr = addr->sockaddr;
         uc->socklen = addr->socklen;
         uc->server = addr->name;
-        uc->log = cf->cycle->new_log;
+        uc->log = &cf->cycle->new_log;
     }
 
     return r;
@@ -578,6 +578,7 @@ failed:
 ngx_int_t
 ngx_resolve_addr(ngx_resolver_ctx_t *ctx)
 {
+    u_char               *name;
     ngx_resolver_t       *r;
     ngx_resolver_node_t  *rn;
 
@@ -601,19 +602,21 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx
 
             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) {
+            name = ngx_resolver_dup(r, rn->name, rn->nlen);
+            if (name == NULL) {
                 goto failed;
             }
 
+            ctx->name.len = rn->nlen;
+            ctx->name.data = name;
+
             /* unlock addr mutex */
 
             ctx->state = NGX_OK;
 
             ctx->handler(ctx);
 
-            ngx_resolver_free(r, ctx->name.data);
+            ngx_resolver_free(r, name);
 
             return NGX_OK;
         }
@@ -623,7 +626,9 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx
             ctx->next = rn->waiting;
             rn->waiting = ctx;
 
-            return NGX_AGAIN;
+            /* unlock addr mutex */
+
+            return NGX_OK;
         }
 
         ngx_queue_remove(&rn->queue);
@@ -956,11 +961,14 @@ ngx_resolver_process_response(ngx_resolv
 {
     char                  *err;
     size_t                 len;
-    ngx_uint_t             i, ident, flags, code, nqs, nan, qtype, qclass;
+    ngx_uint_t             i, times, ident, qident, flags, code, nqs, nan,
+                           qtype, qclass;
+    ngx_queue_t           *q;
     ngx_resolver_qs_t     *qs;
+    ngx_resolver_node_t   *rn;
     ngx_resolver_query_t  *query;
 
-    if ((size_t) n < sizeof(ngx_resolver_query_t) + 1) {
+    if ((size_t) n < sizeof(ngx_resolver_query_t)) {
         goto short_response;
     }
 
@@ -985,11 +993,31 @@ ngx_resolver_process_response(ngx_resolv
 
     code = flags & 0x7f;
 
-    if (code == NGX_RESOLVE_FORMERR || code > NGX_RESOLVE_REFUSED) {
-        ngx_log_error(r->log_level, r->log, 0,
-                      "DNS error (%ui: %s), query id:%ui",
-                      code, ngx_resolver_strerror(code), ident);
-        return;
+    if (code == NGX_RESOLVE_FORMERR) {
+
+        times = 0;
+
+        for (q = ngx_queue_head(&r->name_resend_queue);
+             q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100;
+             q = ngx_queue_next(q))
+        {
+            rn = ngx_queue_data(q, ngx_resolver_node_t, queue);
+            qident = (rn->query[0] << 8) + rn->query[1];
+
+            if (qident == ident) {
+                ngx_log_error(r->log_level, r->log, 0,
+                              "DNS error (%ui: %s), query id:%ui, name:\"%*s\"",
+                              code, ngx_resolver_strerror(code), ident,
+                              rn->nlen, rn->name);
+                return;
+            }
+        }
+
+        goto dns_error;
+    }
+
+    if (code > NGX_RESOLVE_REFUSED) {
+        goto dns_error;
     }
 
     if (nqs != 1) {
@@ -1069,6 +1097,13 @@ done:
     ngx_log_error(r->log_level, r->log, 0, err);
 
     return;
+
+dns_error:
+
+    ngx_log_error(r->log_level, r->log, 0,
+                  "DNS error (%ui: %s), query id:%ui",
+                  code, ngx_resolver_strerror(code), ident);
+    return;
 }
 
 
@@ -1306,7 +1341,7 @@ ngx_resolver_process_a(ngx_resolver_t *r
              ctx->handler(ctx);
         }
 
-        if (naddrs) {
+        if (naddrs > 1) {
             ngx_resolver_free(r, addrs);
         }
 
@@ -1483,20 +1518,23 @@ ngx_resolver_process_ptr(ngx_resolver_t 
         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)
+    if (name.len != (size_t) rn->nlen
+        || ngx_strncmp(name.data, rn->name, name.len) != 0)
     {
-        ngx_resolver_free(r, rn->name);
+        if (rn->nlen) {
+            ngx_resolver_free(r, rn->name);
+        }
+
+        rn->nlen = (u_short) name.len;
         rn->name = name.data;
 
-        name.data = ngx_resolver_dup(r, rn->name, len);
+        name.data = ngx_resolver_dup(r, rn->name, name.len);
         if (name.data == NULL) {
             goto failed;
         }
@@ -1836,7 +1874,7 @@ ngx_resolver_copy(ngx_resolver_t *r, ngx
         }
 
         if (n & 0xc0) {
-            n = (n & 0x3f << 8) + *p;
+            n = ((n & 0x3f) << 8) + *p;
             p = &buf[n];
 
         } else {
@@ -1886,7 +1924,7 @@ done:
             }
 
         } else {
-            n = (n & 0x3f << 8) + *src;
+            n = ((n & 0x3f) << 8) + *src;
             src = &buf[n];
 
             n = *src++;
--- a/src/core/ngx_shmtx.h
+++ b/src/core/ngx_shmtx.h
@@ -57,7 +57,15 @@ ngx_shmtx_trylock(ngx_shmtx_t *mtx)
         return 0;
     }
 
-    ngx_log_abort(err, ngx_trylock_fd_n " failed");
+#if __osf__ /* Tru64 UNIX */
+
+    if (err == NGX_EACCESS) {
+        return 0;
+    }
+
+#endif
+
+    ngx_log_abort(err, ngx_trylock_fd_n " %s failed", mtx->name);
 
     return 0;
 }
@@ -74,7 +82,7 @@ ngx_shmtx_lock(ngx_shmtx_t *mtx)
         return;
     }
 
-    ngx_log_abort(err, ngx_lock_fd_n " failed");
+    ngx_log_abort(err, ngx_lock_fd_n " %s failed", mtx->name);
 }
 
 
@@ -89,7 +97,7 @@ ngx_shmtx_unlock(ngx_shmtx_t *mtx)
         return;
     }
 
-    ngx_log_abort(err, ngx_unlock_fd_n " failed");
+    ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name);
 }
 
 
--- a/src/core/ngx_slab.c
+++ b/src/core/ngx_slab.c
@@ -6,23 +6,6 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 
-/*
-
-                         12
-    2048   2             11
-    1024   4             10
-    512    8             9
-    256   16             8
-
-    128   32   4   32    7
-
-    64    64   8   63    6      1
-    32   128  16  127    5      1
-    16   256  32  254    4      2
-    8    512  64  504    3      8
-
- */
-
 
 #define NGX_SLAB_PAGE_MASK   3
 #define NGX_SLAB_PAGE        0
@@ -80,6 +63,8 @@ static ngx_slab_page_t *ngx_slab_alloc_p
     ngx_uint_t pages);
 static void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,
     ngx_uint_t pages);
+static void ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level,
+    char *text);
 
 
 static ngx_uint_t  ngx_slab_max_size;
@@ -147,11 +132,8 @@ ngx_slab_init(ngx_slab_pool_t *pool)
         pool->pages->slab = pages;
     }
 
-#if 0
-    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "slab: %p, %p, %ui, %d",
-                  pool, pool->start, pages,
-                  (pool->end - pool->start) / ngx_pagesize - pages);
-#endif
+    pool->log_ctx = &pool->zero;
+    pool->zero = '\0';
 }
 
 
@@ -438,8 +420,7 @@ ngx_slab_free_locked(ngx_slab_pool_t *po
     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p);
 
     if ((u_char *) p < pool->start || (u_char *) p > pool->end) {
-        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
-                      "ngx_slab_free(): outside of pool");
+        ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_free(): outside of pool");
         goto fail;
     }
 
@@ -587,14 +568,14 @@ ngx_slab_free_locked(ngx_slab_pool_t *po
         }
 
         if (slab == NGX_SLAB_PAGE_FREE) {
-            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
-                          "ngx_slab_free(): page is already free");
+            ngx_slab_error(pool, NGX_LOG_ALERT,
+                           "ngx_slab_free(): page is already free");
             goto fail;
         }
 
         if (slab == NGX_SLAB_PAGE_BUSY) {
-            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
-                          "ngx_slab_free(): pointer to wrong page");
+            ngx_slab_error(pool, NGX_LOG_ALERT,
+                           "ngx_slab_free(): pointer to wrong page");
             goto fail;
         }
 
@@ -603,9 +584,9 @@ ngx_slab_free_locked(ngx_slab_pool_t *po
 
         ngx_slab_free_pages(pool, &pool->pages[n], size);
 
-        size <<= ngx_pagesize_shift;
+        ngx_slab_junk(p, size << ngx_pagesize_shift);
 
-        goto done;
+        return;
     }
 
     /* not reached */
@@ -620,15 +601,15 @@ done:
 
 wrong_chunk:
 
-    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
-                      "ngx_slab_free(): pointer to wrong chunk");
+    ngx_slab_error(pool, NGX_LOG_ALERT,
+                   "ngx_slab_free(): pointer to wrong chunk");
 
     goto fail;
 
 chunk_already_free:
 
-    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
-                      "ngx_slab_free(): chunk is already free");
+    ngx_slab_error(pool, NGX_LOG_ALERT,
+                   "ngx_slab_free(): chunk is already free");
 
 fail:
 
@@ -661,11 +642,8 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *po
             }
 
             page->slab = pages | NGX_SLAB_PAGE_START;
-
-#if (NGX_DEBUG)
             page->next = NULL;
             page->prev = NGX_SLAB_PAGE;
-#endif
 
             if (--pages == 0) {
                 return page;
@@ -673,10 +651,8 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *po
 
             for (p = page + 1; pages; pages--) {
                 p->slab = NGX_SLAB_PAGE_BUSY;
-#if (NGX_DEBUG)
                 p->next = NULL;
                 p->prev = NGX_SLAB_PAGE;
-#endif
                 p++;
             }
 
@@ -684,8 +660,7 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *po
         }
     }
 
-    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, NGX_ENOMEM,
-                      "ngx_slab_alloc(): failed");
+    ngx_slab_error(pool, NGX_LOG_CRIT, "ngx_slab_alloc() failed: no memory");
 
     return NULL;
 }
@@ -716,3 +691,10 @@ ngx_slab_free_pages(ngx_slab_pool_t *poo
 
     pool->free.next = page;
 }
+
+
+static void
+ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, char *text)
+{
+    ngx_log_error(level, ngx_cycle->log, 0, "%s%s", text, pool->log_ctx);
+}
--- a/src/core/ngx_slab.h
+++ b/src/core/ngx_slab.h
@@ -34,6 +34,12 @@ typedef struct {
     u_char           *end;
 
     ngx_shmtx_t       mutex;
+
+    u_char           *log_ctx;
+    u_char            zero;
+
+    void             *data;
+    void             *addr;
 } ngx_slab_pool_t;
 
 
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -8,6 +8,10 @@
 #include <ngx_core.h>
 
 
+static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64,
+    u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width);
+
+
 void
 ngx_strlow(u_char *dst, u_char *src, size_t n)
 {
@@ -26,12 +30,15 @@ ngx_cpystrn(u_char *dst, u_char *src, si
         return dst;
     }
 
-    for ( /* void */ ; --n; dst++, src++) {
+    while (--n) {
         *dst = *src;
 
         if (*dst == '\0') {
             return dst;
         }
+
+        dst++;
+        src++;
     }
 
     *dst = '\0';
@@ -67,6 +74,7 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t 
  *    %[0][width][u][x|X]D      int32_t/uint32_t
  *    %[0][width][u][x|X]L      int64_t/uint64_t
  *    %[0][width|m][u][x|X]A    ngx_atomic_int_t/ngx_atomic_uint_t
+ *    %[0][width][.width]f      float
  *    %P                        ngx_pid_t
  *    %M                        ngx_msec_t
  *    %r                        rlim_t
@@ -94,7 +102,7 @@ ngx_sprintf(u_char *buf, const char *fmt
     va_list   args;
 
     va_start(args, fmt);
-    p = ngx_vsnprintf(buf, /* STUB */ 65536, fmt, args);
+    p = ngx_vslprintf(buf, (void *) -1, fmt, args);
     va_end(args);
 
     return p;
@@ -108,7 +116,21 @@ ngx_snprintf(u_char *buf, size_t max, co
     va_list   args;
 
     va_start(args, fmt);
-    p = ngx_vsnprintf(buf, max, fmt, args);
+    p = ngx_vslprintf(buf, buf + max, fmt, args);
+    va_end(args);
+
+    return p;
+}
+
+
+u_char * ngx_cdecl
+ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...)
+{
+    u_char   *p;
+    va_list   args;
+
+    va_start(args, fmt);
+    p = ngx_vslprintf(buf, last, fmt, args);
     va_end(args);
 
     return p;
@@ -116,30 +138,18 @@ ngx_snprintf(u_char *buf, size_t max, co
 
 
 u_char *
-ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args)
+ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
 {
-    u_char                *p, zero, *last, temp[NGX_INT64_LEN + 1];
-                                    /*
-                                     * really we need temp[NGX_INT64_LEN] only,
-                                     * but icc issues the warning
-                                     */
+    u_char                *p, zero;
     int                    d;
+    float                  f, scale;
     size_t                 len, slen;
-    uint32_t               ui32;
     int64_t                i64;
     uint64_t               ui64;
     ngx_msec_t             ms;
-    ngx_uint_t             width, sign, hexadecimal, max_width;
+    ngx_uint_t             width, sign, hex, max_width, frac_width, i;
     ngx_str_t             *v;
     ngx_variable_value_t  *vv;
-    static u_char          hex[] = "0123456789abcdef";
-    static u_char          HEX[] = "0123456789ABCDEF";
-
-    if (max == 0) {
-        return buf;
-    }
-
-    last = buf + max;
 
     while (*fmt && buf < last) {
 
@@ -156,12 +166,11 @@ ngx_vsnprintf(u_char *buf, size_t max, c
             zero = (u_char) ((*++fmt == '0') ? '0' : ' ');
             width = 0;
             sign = 1;
-            hexadecimal = 0;
+            hex = 0;
             max_width = 0;
+            frac_width = 0;
             slen = (size_t) -1;
 
-            p = temp + NGX_INT64_LEN;
-
             while (*fmt >= '0' && *fmt <= '9') {
                 width = width * 10 + *fmt++ - '0';
             }
@@ -181,17 +190,26 @@ ngx_vsnprintf(u_char *buf, size_t max, c
                     continue;
 
                 case 'X':
-                    hexadecimal = 2;
+                    hex = 2;
                     sign = 0;
                     fmt++;
                     continue;
 
                 case 'x':
-                    hexadecimal = 1;
+                    hex = 1;
                     sign = 0;
                     fmt++;
                     continue;
 
+                case '.':
+                    fmt++;
+
+                    while (*fmt >= '0' && *fmt <= '9') {
+                        frac_width = frac_width * 10 + *fmt++ - '0';
+                    }
+
+                    break;
+
                 case '*':
                     slen = va_arg(args, size_t);
                     fmt++;
@@ -339,6 +357,43 @@ ngx_vsnprintf(u_char *buf, size_t max, c
 
                 break;
 
+            case 'f':
+                f = (float) va_arg(args, double);
+
+                if (f < 0) {
+                    *buf++ = '-';
+                    f = -f;
+                }
+
+                ui64 = (int64_t) f;
+
+                buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width);
+
+                if (frac_width) {
+
+                    if (buf < last) {
+                        *buf++ = '.';
+                    }
+
+                    scale = 1.0;
+
+                    for (i = 0; i < frac_width; i++) {
+                        scale *= 10.0;
+                    }
+
+                    /*
+                     * (int64_t) cast is required for msvc6:
+                     * it can not convert uint64_t to double
+                     */
+                    ui64 = (uint64_t) ((f - (int64_t) ui64) * scale);
+
+                    buf = ngx_sprintf_num(buf, last, ui64, '0', 0, frac_width);
+                }
+
+                fmt++;
+
+                continue;
+
 #if !(NGX_WIN32)
             case 'r':
                 i64 = (int64_t) va_arg(args, rlim_t);
@@ -348,7 +403,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c
 
             case 'p':
                 ui64 = (uintptr_t) va_arg(args, void *);
-                hexadecimal = 2;
+                hex = 2;
                 sign = 0;
                 zero = '0';
                 width = NGX_PTR_SIZE * 2;
@@ -398,63 +453,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c
                 }
             }
 
-            if (hexadecimal == 1) {
-                do {
-
-                    /* the "(uint32_t)" cast disables the BCC's warning */
-                    *--p = hex[(uint32_t) (ui64 & 0xf)];
-
-                } while (ui64 >>= 4);
-
-            } else if (hexadecimal == 2) {
-                do {
-
-                    /* the "(uint32_t)" cast disables the BCC's warning */
-                    *--p = HEX[(uint32_t) (ui64 & 0xf)];
-
-                } while (ui64 >>= 4);
-
-            } else if (ui64 <= NGX_MAX_UINT32_VALUE) {
-
-                /*
-                 * To divide 64-bit number and to find the remainder
-                 * on the x86 platform gcc and icc call the libc functions
-                 * [u]divdi3() and [u]moddi3(), they call another function
-                 * in its turn.  On FreeBSD it is the qdivrem() function,
-                 * its source code is about 170 lines of the code.
-                 * The glibc counterpart is about 150 lines of the code.
-                 *
-                 * For 32-bit numbers and some divisors gcc and icc use
-                 * the inlined multiplication and shifts.  For example,
-                 * unsigned "i32 / 10" is compiled to
-                 *
-                 *     (i32 * 0xCCCCCCCD) >> 35
-                 */
-
-                ui32 = (uint32_t) ui64;
-
-                do {
-                    *--p = (u_char) (ui32 % 10 + '0');
-                } while (ui32 /= 10);
-
-            } else {
-                do {
-                    *--p = (u_char) (ui64 % 10 + '0');
-                } while (ui64 /= 10);
-            }
-
-            len = (temp + NGX_INT64_LEN) - p;
-
-            while (len++ < width && buf < last) {
-                *buf++ = zero;
-            }
-
-            len = (temp + NGX_INT64_LEN) - p;
-            if (buf + len > last) {
-                len = last - buf;
-            }
-
-            buf = ngx_cpymem(buf, p, len);
+            buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);
 
             fmt++;
 
@@ -467,6 +466,92 @@ ngx_vsnprintf(u_char *buf, size_t max, c
 }
 
 
+static u_char *
+ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero,
+    ngx_uint_t hexadecimal, ngx_uint_t width)
+{
+    u_char         *p, temp[NGX_INT64_LEN + 1];
+                       /*
+                        * we need temp[NGX_INT64_LEN] only,
+                        * but icc issues the warning
+                        */
+    size_t          len;
+    uint32_t        ui32;
+    static u_char   hex[] = "0123456789abcdef";
+    static u_char   HEX[] = "0123456789ABCDEF";
+
+    p = temp + NGX_INT64_LEN;
+
+    if (hexadecimal == 0) {
+
+        if (ui64 <= NGX_MAX_UINT32_VALUE) {
+
+            /*
+             * To divide 64-bit numbers and to find remainders
+             * on the x86 platform gcc and icc call the libc functions
+             * [u]divdi3() and [u]moddi3(), they call another function
+             * in its turn.  On FreeBSD it is the qdivrem() function,
+             * its source code is about 170 lines of the code.
+             * The glibc counterpart is about 150 lines of the code.
+             *
+             * For 32-bit numbers and some divisors gcc and icc use
+             * a inlined multiplication and shifts.  For example,
+             * unsigned "i32 / 10" is compiled to
+             *
+             *     (i32 * 0xCCCCCCCD) >> 35
+             */
+
+            ui32 = (uint32_t) ui64;
+
+            do {
+                *--p = (u_char) (ui32 % 10 + '0');
+            } while (ui32 /= 10);
+
+        } else {
+            do {
+                *--p = (u_char) (ui64 % 10 + '0');
+            } while (ui64 /= 10);
+        }
+
+    } else if (hexadecimal == 1) {
+
+        do {
+
+            /* the "(uint32_t)" cast disables the BCC's warning */
+            *--p = hex[(uint32_t) (ui64 & 0xf)];
+
+        } while (ui64 >>= 4);
+
+    } else { /* hexadecimal == 2 */
+
+        do {
+
+            /* the "(uint32_t)" cast disables the BCC's warning */
+            *--p = HEX[(uint32_t) (ui64 & 0xf)];
+
+        } while (ui64 >>= 4);
+    }
+
+    /* zero or space padding */
+
+    len = (temp + NGX_INT64_LEN) - p;
+
+    while (len++ < width && buf < last) {
+        *buf++ = zero;
+    }
+
+    /* number safe copy */
+
+    len = (temp + NGX_INT64_LEN) - p;
+
+    if (buf + len > last) {
+        len = last - buf;
+    }
+
+    return ngx_cpymem(buf, p, len);
+}
+
+
 /*
  * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only,
  * and implement our own ngx_strcasecmp()/ngx_strncasecmp()
@@ -618,6 +703,39 @@ ngx_strcasestrn(u_char *s1, char *s2, si
 }
 
 
+/*
+ * ngx_strlcasestrn() is intended to search for static substring
+ * with known length in string until the argument last. The argument n
+ * must be length of the second substring - 1.
+ */
+
+u_char *
+ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n)
+{
+    ngx_uint_t  c1, c2;
+
+    c2 = (ngx_uint_t) *s2++;
+    c2  = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+    last -= n;
+
+    do {
+        do {
+            if (s1 >= last) {
+                return NULL;
+            }
+
+            c1 = (ngx_uint_t) *s1++;
+
+            c1  = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+
+        } while (c1 != c2);
+
+    } while (ngx_strncasecmp(s1, s2, n) != 0);
+
+    return --s1;
+}
+
+
 ngx_int_t
 ngx_rstrncmp(u_char *s1, u_char *s2, size_t n)
 {
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -140,7 +140,11 @@ u_char *ngx_cpystrn(u_char *dst, u_char 
 u_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src);
 u_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...);
 u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...);
-u_char *ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args);
+u_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt,
+    ...);
+u_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args);
+#define ngx_vsnprintf(buf, max, fmt, args)                                   \
+    ngx_vslprintf(buf, buf + (max), fmt, args)
 
 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);
@@ -149,6 +153,7 @@ u_char *ngx_strnstr(u_char *s1, char *s2
 
 u_char *ngx_strstrn(u_char *s1, char *s2, size_t n);
 u_char *ngx_strcasestrn(u_char *s1, char *s2, size_t n);
+u_char *ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n);
 
 ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n);
 ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n);
--- a/src/event/modules/ngx_aio_module.c
+++ b/src/event/modules/ngx_aio_module.c
@@ -134,12 +134,14 @@ ngx_aio_del_connection(ngx_connection_t 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_cancel: %d", rc);
 
     if (rc == AIO_CANCELED) {
-        c->read->active = c->write->active = 0;
+        c->read->active = 0;
+        c->write->active = 0;
         return NGX_OK;
     }
 
     if (rc == AIO_ALLDONE) {
-        c->read->active = c->write->active = 0;
+        c->read->active = 0;
+        c->write->active = 0;
         ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                       "aio_cancel() returned AIO_ALLDONE");
         return NGX_OK;
--- a/src/event/modules/ngx_devpoll_module.c
+++ b/src/event/modules/ngx_devpoll_module.c
@@ -550,7 +550,7 @@ ngx_devpoll_create_conf(ngx_cycle_t *cyc
 
     dpcf = ngx_palloc(cycle->pool, sizeof(ngx_devpoll_conf_t));
     if (dpcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     dpcf->changes = NGX_CONF_UNSET;
--- a/src/event/modules/ngx_epoll_module.c
+++ b/src/event/modules/ngx_epoll_module.c
@@ -552,7 +552,7 @@ ngx_epoll_create_conf(ngx_cycle_t *cycle
 
     epcf = ngx_palloc(cycle->pool, sizeof(ngx_epoll_conf_t));
     if (epcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     epcf->events = NGX_CONF_UNSET;
--- a/src/event/modules/ngx_eventport_module.c
+++ b/src/event/modules/ngx_eventport_module.c
@@ -581,7 +581,7 @@ ngx_eventport_create_conf(ngx_cycle_t *c
 
     epcf = ngx_palloc(cycle->pool, sizeof(ngx_eventport_conf_t));
     if (epcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     epcf->events = NGX_CONF_UNSET;
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -768,7 +768,7 @@ ngx_kqueue_create_conf(ngx_cycle_t *cycl
 
     kcf = ngx_palloc(cycle->pool, sizeof(ngx_kqueue_conf_t));
     if (kcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     kcf->changes = NGX_CONF_UNSET;
--- a/src/event/modules/ngx_rtsig_module.c
+++ b/src/event/modules/ngx_rtsig_module.c
@@ -691,7 +691,7 @@ ngx_rtsig_create_conf(ngx_cycle_t *cycle
 
     rtscf = ngx_palloc(cycle->pool, sizeof(ngx_rtsig_conf_t));
     if (rtscf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     rtscf->signo = NGX_CONF_UNSET;
--- a/src/event/modules/ngx_select_module.c
+++ b/src/event/modules/ngx_select_module.c
@@ -9,7 +9,6 @@
 #include <ngx_event.h>
 
 
-
 static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer);
 static void ngx_select_done(ngx_cycle_t *cycle);
 static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event,
@@ -18,6 +17,7 @@ static ngx_int_t ngx_select_del_event(ng
     ngx_uint_t flags);
 static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
     ngx_uint_t flags);
+static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);
 static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);
 
 
@@ -26,13 +26,7 @@ static fd_set         master_write_fd_se
 static fd_set         work_read_fd_set;
 static fd_set         work_write_fd_set;
 
-#if (NGX_WIN32)
-static ngx_uint_t     max_read;
-static ngx_uint_t     max_write;
-#else
 static ngx_int_t      max_fd;
-#endif
-
 static ngx_uint_t     nevents;
 
 static ngx_event_t  **event_index;
@@ -111,11 +105,7 @@ ngx_select_init(ngx_cycle_t *cycle, ngx_
 
     ngx_event_flags = NGX_USE_LEVEL_EVENT;
 
-#if (NGX_WIN32)
-    max_read = max_write = 0;
-#else
     max_fd = -1;
-#endif
 
     return NGX_OK;
 }
@@ -146,30 +136,17 @@ ngx_select_add_event(ngx_event_t *ev, ng
         return NGX_OK;
     }
 
-#if (NGX_WIN32)
-
-    if ((event == NGX_READ_EVENT) && (max_read >= FD_SETSIZE)
-        || (event == NGX_WRITE_EVENT) && (max_write >= FD_SETSIZE))
+    if ((event == NGX_READ_EVENT && ev->write)
+        || (event == NGX_WRITE_EVENT && !ev->write))
     {
-        ngx_log_error(NGX_LOG_ERR, ev->log, 0,
-                      "maximum number of descriptors "
-                      "supported by select() is %d", FD_SETSIZE);
+        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+                      "invalid select %s event fd:%d ev:%i",
+                      ev->write ? "write" : "read", c->fd, event);
         return NGX_ERROR;
     }
 
     if (event == NGX_READ_EVENT) {
         FD_SET(c->fd, &master_read_fd_set);
-        max_read++;
-
-    } else if (event == NGX_WRITE_EVENT) {
-        FD_SET(c->fd, &master_write_fd_set);
-        max_write++;
-    }
-
-#else
-
-    if (event == NGX_READ_EVENT) {
-        FD_SET(c->fd, &master_read_fd_set);
 
     } else if (event == NGX_WRITE_EVENT) {
         FD_SET(c->fd, &master_write_fd_set);
@@ -179,8 +156,6 @@ ngx_select_add_event(ngx_event_t *ev, ng
         max_fd = c->fd;
     }
 
-#endif
-
     ev->active = 1;
 
     event_index[nevents] = ev;
@@ -194,6 +169,7 @@ ngx_select_add_event(ngx_event_t *ev, ng
 static ngx_int_t
 ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
 {
+    ngx_event_t       *e;
     ngx_connection_t  *c;
 
     c = ev->data;
@@ -207,19 +183,6 @@ ngx_select_del_event(ngx_event_t *ev, ng
     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                    "select del event fd:%d ev:%i", c->fd, event);
 
-#if (NGX_WIN32)
-
-    if (event == NGX_READ_EVENT) {
-        FD_CLR(c->fd, &master_read_fd_set);
-        max_read--;
-
-    } else if (event == NGX_WRITE_EVENT) {
-        FD_CLR(c->fd, &master_write_fd_set);
-        max_write--;
-    }
-
-#else
-
     if (event == NGX_READ_EVENT) {
         FD_CLR(c->fd, &master_read_fd_set);
 
@@ -231,11 +194,10 @@ ngx_select_del_event(ngx_event_t *ev, ng
         max_fd = -1;
     }
 
-#endif
-
     if (ev->index < --nevents) {
-        event_index[ev->index] = event_index[nevents];
-        event_index[ev->index]->index = ev->index;
+        e = event_index[nevents];
+        event_index[ev->index] = e;
+        e->index = ev->index;
     }
 
     ev->index = NGX_INVALID_INDEX;
@@ -248,14 +210,12 @@ static ngx_int_t
 ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
     ngx_uint_t flags)
 {
-    int                 ready, nready;
-    ngx_uint_t          i, found;
-    ngx_err_t           err;
-    ngx_event_t        *ev, **queue;
-    ngx_connection_t   *c;
-    struct timeval      tv, *tp;
-
-#if !(NGX_WIN32)
+    int                ready, nready;
+    ngx_err_t          err;
+    ngx_uint_t         i, found;
+    ngx_event_t       *ev, **queue;
+    struct timeval     tv, *tp;
+    ngx_connection_t  *c;
 
     if (max_fd == -1) {
         for (i = 0; i < nevents; i++) {
@@ -269,8 +229,6 @@ ngx_select_process_events(ngx_cycle_t *c
                        "change max_fd: %d", max_fd);
     }
 
-#endif
-
 #if (NGX_DEBUG)
     if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
         for (i = 0; i < nevents; i++) {
@@ -280,10 +238,8 @@ ngx_select_process_events(ngx_cycle_t *c
                            "select event: fd:%d wr:%d", c->fd, ev->write);
         }
 
-#if !(NGX_WIN32)
         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                        "max_fd: %d", max_fd);
-#endif
     }
 #endif
 
@@ -302,26 +258,8 @@ ngx_select_process_events(ngx_cycle_t *c
     work_read_fd_set = master_read_fd_set;
     work_write_fd_set = master_write_fd_set;
 
-#if 1
-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
-                   /*
-                    * (void *) disables "dereferencing type-punned
-                    * pointer will break strict-aliasing rules
-                    */
-                   "select read fd_set: %08Xd",
-                   *(int *) (void *) &work_read_fd_set);
-#endif
-
-#if (NGX_WIN32)
-
-    ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp);
-
-#else
-
     ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp);
 
-#endif
-
     if (ready == -1) {
         err = ngx_socket_errno;
     } else {
@@ -335,15 +273,6 @@ ngx_select_process_events(ngx_cycle_t *c
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                    "select ready %d", ready);
 
-#if (NGX_WIN32)
-
-    if (err) {
-        ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed");
-        return NGX_ERROR;
-    }
-
-#else
-
     if (err) {
         ngx_uint_t  level;
 
@@ -361,11 +290,14 @@ ngx_select_process_events(ngx_cycle_t *c
         }
 
         ngx_log_error(level, cycle->log, err, "select() failed");
+
+        if (err == EBADF) {
+            ngx_select_repair_fd_sets(cycle);
+        }
+
         return NGX_ERROR;
     }
 
-#endif
-
     if (ready == 0) {
         if (timer != NGX_TIMER_INFINITE) {
             return NGX_OK;
@@ -414,13 +346,64 @@ ngx_select_process_events(ngx_cycle_t *c
     ngx_mutex_unlock(ngx_posted_events_mutex);
 
     if (ready != nready) {
-        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "select ready != events");
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                      "select ready != events: %d:%d", ready, nready);
+
+        ngx_select_repair_fd_sets(cycle);
     }
 
     return NGX_OK;
 }
 
 
+static void
+ngx_select_repair_fd_sets(ngx_cycle_t *cycle)
+{
+    int           n;
+    socklen_t     len;
+    ngx_err_t     err;
+    ngx_socket_t  s;
+
+    for (s = 0; s <= max_fd; s++) {
+
+        if (FD_ISSET(s, &master_read_fd_set) == 0) {
+            continue;
+        }
+
+        len = sizeof(int);
+
+        if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {
+            err = ngx_socket_errno;
+
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+                          "invalid descriptor #%d in read fd_set", s);
+
+            FD_CLR(s, &master_read_fd_set);
+        }
+    }
+
+    for (s = 0; s <= max_fd; s++) {
+
+        if (FD_ISSET(s, &master_write_fd_set) == 0) {
+            continue;
+        }
+
+        len = sizeof(int);
+
+        if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {
+            err = ngx_socket_errno;
+
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+                          "invalid descriptor #%d in write fd_set", s);
+
+            FD_CLR(s, &master_write_fd_set);
+        }
+    }
+
+    max_fd = -1;
+}
+
+
 static char *
 ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)
 {
@@ -434,8 +417,6 @@ ngx_select_init_conf(ngx_cycle_t *cycle,
 
     /* disable warning: the default FD_SETSIZE is 1024U in FreeBSD 5.x */
 
-#if !(NGX_WIN32)
-
     if (cycle->connection_n > FD_SETSIZE) {
         ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                       "the maximum number of files "
@@ -443,9 +424,7 @@ ngx_select_init_conf(ngx_cycle_t *cycle,
         return NGX_CONF_ERROR;
     }
 
-#endif
-
-#if (NGX_THREADS) && !(NGX_WIN32)
+#if (NGX_THREADS)
 
     ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                   "select() is not supported in the threaded mode");
new file mode 100644
--- /dev/null
+++ b/src/event/modules/ngx_win32_select_module.c
@@ -0,0 +1,403 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer);
+static void ngx_select_done(ngx_cycle_t *cycle);
+static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event,
+    ngx_uint_t flags);
+static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event,
+    ngx_uint_t flags);
+static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+    ngx_uint_t flags);
+static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);
+static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);
+
+
+static fd_set         master_read_fd_set;
+static fd_set         master_write_fd_set;
+static fd_set         work_read_fd_set;
+static fd_set         work_write_fd_set;
+
+static ngx_uint_t     max_read;
+static ngx_uint_t     max_write;
+static ngx_uint_t     nevents;
+
+static ngx_event_t  **event_index;
+
+
+static ngx_str_t    select_name = ngx_string("select");
+
+ngx_event_module_t  ngx_select_module_ctx = {
+    &select_name,
+    NULL,                                  /* create configuration */
+    ngx_select_init_conf,                  /* init configuration */
+
+    {
+        ngx_select_add_event,              /* add an event */
+        ngx_select_del_event,              /* delete an event */
+        ngx_select_add_event,              /* enable an event */
+        ngx_select_del_event,              /* disable an event */
+        NULL,                              /* add an connection */
+        NULL,                              /* delete an connection */
+        NULL,                              /* process the changes */
+        ngx_select_process_events,         /* process the events */
+        ngx_select_init,                   /* init the events */
+        ngx_select_done                    /* done the events */
+    }
+
+};
+
+ngx_module_t  ngx_select_module = {
+    NGX_MODULE_V1,
+    &ngx_select_module_ctx,                /* module context */
+    NULL,                                  /* module directives */
+    NGX_EVENT_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_select_init(ngx_cycle_t *cycle, ngx_msec_t timer)
+{
+    ngx_event_t  **index;
+
+    if (event_index == NULL) {
+        FD_ZERO(&master_read_fd_set);
+        FD_ZERO(&master_write_fd_set);
+        nevents = 0;
+    }
+
+    if (ngx_process == NGX_PROCESS_WORKER
+        || cycle->old_cycle == NULL
+        || cycle->old_cycle->connection_n < cycle->connection_n)
+    {
+        index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n,
+                          cycle->log);
+        if (index == NULL) {
+            return NGX_ERROR;
+        }
+
+        if (event_index) {
+            ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents);
+            ngx_free(event_index);
+        }
+
+        event_index = index;
+    }
+
+    ngx_io = ngx_os_io;
+
+    ngx_event_actions = ngx_select_module_ctx.actions;
+
+    ngx_event_flags = NGX_USE_LEVEL_EVENT;
+
+    max_read = 0;
+    max_write = 0;
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_select_done(ngx_cycle_t *cycle)
+{
+    ngx_free(event_index);
+
+    event_index = NULL;
+}
+
+
+static ngx_int_t
+ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+    ngx_connection_t  *c;
+
+    c = ev->data;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+                   "select add event fd:%d ev:%i", c->fd, event);
+
+    if (ev->index != NGX_INVALID_INDEX) {
+        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+                      "select event fd:%d ev:%i is already set", c->fd, event);
+        return NGX_OK;
+    }
+
+    if ((event == NGX_READ_EVENT && ev->write)
+        || (event == NGX_WRITE_EVENT && !ev->write))
+    {
+        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+                      "invalid select %s event fd:%d ev:%i",
+                      ev->write ? "write" : "read", c->fd, event);
+        return NGX_ERROR;
+    }
+
+    if ((event == NGX_READ_EVENT) && (max_read >= FD_SETSIZE)
+        || (event == NGX_WRITE_EVENT) && (max_write >= FD_SETSIZE))
+    {
+        ngx_log_error(NGX_LOG_ERR, ev->log, 0,
+                      "maximum number of descriptors "
+                      "supported by select() is %d", FD_SETSIZE);
+        return NGX_ERROR;
+    }
+
+    if (event == NGX_READ_EVENT) {
+        FD_SET(c->fd, &master_read_fd_set);
+        max_read++;
+
+    } else if (event == NGX_WRITE_EVENT) {
+        FD_SET(c->fd, &master_write_fd_set);
+        max_write++;
+    }
+
+    ev->active = 1;
+
+    event_index[nevents] = ev;
+    ev->index = nevents;
+    nevents++;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+    ngx_event_t       *e;
+    ngx_connection_t  *c;
+
+    c = ev->data;
+
+    ev->active = 0;
+
+    if (ev->index == NGX_INVALID_INDEX) {
+        return NGX_OK;
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+                   "select del event fd:%d ev:%i", c->fd, event);
+
+    if (event == NGX_READ_EVENT) {
+        FD_CLR(c->fd, &master_read_fd_set);
+        max_read--;
+
+    } else if (event == NGX_WRITE_EVENT) {
+        FD_CLR(c->fd, &master_write_fd_set);
+        max_write--;
+    }
+
+    if (ev->index < --nevents) {
+        e = event_index[nevents];
+        event_index[ev->index] = e;
+        e->index = ev->index;
+    }
+
+    ev->index = NGX_INVALID_INDEX;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+    ngx_uint_t flags)
+{
+    int                ready, nready;
+    ngx_err_t          err;
+    ngx_uint_t         i, found;
+    ngx_event_t       *ev, **queue;
+    struct timeval     tv, *tp;
+    ngx_connection_t  *c;
+
+#if (NGX_DEBUG)
+    if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
+        for (i = 0; i < nevents; i++) {
+            ev = event_index[i];
+            c = ev->data;
+            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                           "select event: fd:%d wr:%d", c->fd, ev->write);
+        }
+    }
+#endif
+
+    if (timer == NGX_TIMER_INFINITE) {
+        tp = NULL;
+
+    } else {
+        tv.tv_sec = (long) (timer / 1000);
+        tv.tv_usec = (long) ((timer % 1000) * 1000);
+        tp = &tv;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                   "select timer: %M", timer);
+
+    work_read_fd_set = master_read_fd_set;
+    work_write_fd_set = master_write_fd_set;
+
+    if (max_read || max_write) {
+        ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp);
+
+    } else {
+
+        /*
+         * Winsock select() requires that at least one descriptor set must be
+         * be non-null, and any non-null descriptor set must contain at least
+         * one handle to a socket.  Otherwise select() returns WSAEINVAL.
+         */
+
+        ngx_msleep(timer);
+
+        ready = 0;
+    }
+
+    if (ready == -1) {
+        err = ngx_socket_errno;
+    } else {
+        err = 0;
+    }
+
+    if (flags & NGX_UPDATE_TIME) {
+        ngx_time_update(0, 0);
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                   "select ready %d", ready);
+
+    if (err) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed");
+
+        if (err == WSAENOTSOCK) {
+            ngx_select_repair_fd_sets(cycle);
+        }
+
+        return NGX_ERROR;
+    }
+
+    if (ready == 0) {
+        if (timer != NGX_TIMER_INFINITE) {
+            return NGX_OK;
+        }
+
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                      "select() returned no events without timeout");
+        return NGX_ERROR;
+    }
+
+    ngx_mutex_lock(ngx_posted_events_mutex);
+
+    nready = 0;
+
+    for (i = 0; i < nevents; i++) {
+        ev = event_index[i];
+        c = ev->data;
+        found = 0;
+
+        if (ev->write) {
+            if (FD_ISSET(c->fd, &work_write_fd_set)) {
+                found = 1;
+                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                               "select write %d", c->fd);
+            }
+
+        } else {
+            if (FD_ISSET(c->fd, &work_read_fd_set)) {
+                found = 1;
+                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                               "select read %d", c->fd);
+            }
+        }
+
+        if (found) {
+            ev->ready = 1;
+
+            queue = (ngx_event_t **) (ev->accept ? &ngx_posted_accept_events:
+                                                   &ngx_posted_events);
+            ngx_locked_post_event(ev, queue);
+
+            nready++;
+        }
+    }
+
+    ngx_mutex_unlock(ngx_posted_events_mutex);
+
+    if (ready != nready) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                      "select ready != events: %d:%d", ready, nready);
+
+        ngx_select_repair_fd_sets(cycle);
+    }
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_select_repair_fd_sets(ngx_cycle_t *cycle)
+{
+    int           n;
+    u_int         i;
+    socklen_t     len;
+    ngx_err_t     err;
+    ngx_socket_t  s;
+
+    for (i = 0; i < master_read_fd_set.fd_count; i++) {
+
+        s = master_read_fd_set.fd_array[i];
+        len = sizeof(int);
+
+        if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {
+            err = ngx_socket_errno;
+
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+                          "invalid descriptor #%d in read fd_set", s);
+
+            FD_CLR(s, &master_read_fd_set);
+        }
+    }
+
+    for (i = 0; i < master_write_fd_set.fd_count; i++) {
+
+        s = master_write_fd_set.fd_array[i];
+        len = sizeof(int);
+
+        if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {
+            err = ngx_socket_errno;
+
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+                          "invalid descriptor #%d in write fd_set", s);
+
+            FD_CLR(s, &master_write_fd_set);
+        }
+    }
+}
+
+
+static char *
+ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+    ngx_event_conf_t  *ecf;
+
+    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
+
+    if (ecf->use != ngx_select_module.ctx_index) {
+        return NGX_CONF_OK;
+    }
+
+    return NGX_CONF_OK;
+}
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -442,7 +442,7 @@ ngx_event_module_init(ngx_cycle_t *cycle
 
     ecf = (*cf)[ngx_event_core_module.ctx_index];
 
-    if (!ngx_test_config) {
+    if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
         ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
                       "using the \"%s\" event method", ecf->name);
     }
@@ -506,6 +506,8 @@ ngx_event_module_init(ngx_cycle_t *cycle
 #endif
 
     shm.size = size;
+    shm.name.len = sizeof("nginx_shared_zone");
+    shm.name.data = (u_char *) "nginx_shared_zone";
     shm.log = cycle->log;
 
     if (ngx_shm_alloc(&shm) != NGX_OK) {
@@ -535,7 +537,7 @@ ngx_event_module_init(ngx_cycle_t *cycle
 
 #endif
 
-    *ngx_connection_counter = 1;
+    (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);
 
     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                    "counter: %p, %d",
@@ -774,6 +776,10 @@ ngx_event_process_init(ngx_cycle_t *cycl
 
             rev->handler = ngx_event_acceptex;
 
+            if (ngx_use_accept_mutex) {
+                continue;
+            }
+
             if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {
                 return NGX_ERROR;
             }
@@ -790,6 +796,10 @@ ngx_event_process_init(ngx_cycle_t *cycl
         } else {
             rev->handler = ngx_event_accept;
 
+            if (ngx_use_accept_mutex) {
+                continue;
+            }
+
             if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
                 return NGX_ERROR;
             }
@@ -1040,18 +1050,16 @@ ngx_event_debug_connection(ngx_conf_t *c
     ngx_str_t          *value;
     ngx_event_debug_t  *dc;
     struct hostent     *h;
-    ngx_inet_cidr_t     in_cidr;
+    ngx_cidr_t          cidr;
 
     value = cf->args->elts;
 
-    /* AF_INET only */
-
     dc = ngx_array_push(&ecf->debug_connection);
     if (dc == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    rc = ngx_ptocidr(&value[1], &in_cidr);
+    rc = ngx_ptocidr(&value[1], &cidr);
 
     if (rc == NGX_DONE) {
         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
@@ -1060,8 +1068,18 @@ ngx_event_debug_connection(ngx_conf_t *c
     }
 
     if (rc == NGX_OK) {
-        dc->mask = in_cidr.mask;
-        dc->addr = in_cidr.addr;
+
+        /* AF_INET only */
+
+        if (cidr.family != AF_INET) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "\"debug_connection\" supports IPv4 only");
+            return NGX_CONF_ERROR;
+        }
+
+        dc->mask = cidr.u.in.mask;
+        dc->addr = cidr.u.in.addr;
+
         return NGX_CONF_OK;
     }
 
@@ -1095,7 +1113,7 @@ ngx_event_create_conf(ngx_cycle_t *cycle
 
     ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
     if (ecf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     ecf->connections = NGX_CONF_UNSET_UINT;
@@ -1110,7 +1128,7 @@ ngx_event_create_conf(ngx_cycle_t *cycle
     if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4,
                        sizeof(ngx_event_debug_t)) == NGX_ERROR)
     {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
 #endif
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -9,10 +9,6 @@
 #include <ngx_event.h>
 
 
-/* the buffer size is enough to hold "struct sockaddr_un" */
-#define NGX_SOCKLEN  512
-
-
 static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle);
 static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle);
 static void ngx_close_accepted_connection(ngx_connection_t *c);
@@ -29,7 +25,7 @@ ngx_event_accept(ngx_event_t *ev)
     ngx_listening_t   *ls;
     ngx_connection_t  *c, *lc;
     ngx_event_conf_t  *ecf;
-    char               sa[NGX_SOCKLEN];
+    u_char             sa[NGX_SOCKADDRLEN];
 
     ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
 
@@ -48,7 +44,7 @@ ngx_event_accept(ngx_event_t *ev)
                    "accept on %V, ready: %d", &ls->addr_text, ev->available);
 
     do {
-        socklen = NGX_SOCKLEN;
+        socklen = NGX_SOCKADDRLEN;
 
         s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
 
@@ -61,8 +57,8 @@ ngx_event_accept(ngx_event_t *ev)
                 return;
             }
 
-            ngx_log_error((err == NGX_ECONNABORTED) ? NGX_LOG_ERR:
-                                                      NGX_LOG_ALERT,
+            ngx_log_error((ngx_uint_t) ((err == NGX_ECONNABORTED) ?
+                                             NGX_LOG_ERR : NGX_LOG_ALERT),
                           ev->log, err, "accept() failed");
 
             if (err == NGX_ECONNABORTED) {
@@ -79,7 +75,7 @@ ngx_event_accept(ngx_event_t *ev)
         }
 
 #if (NGX_STAT_STUB)
-        ngx_atomic_fetch_add(ngx_stat_accepted, 1);
+        (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
 #endif
 
         ngx_accept_disabled = ngx_cycle->connection_n / 8
@@ -97,7 +93,7 @@ ngx_event_accept(ngx_event_t *ev)
         }
 
 #if (NGX_STAT_STUB)
-        ngx_atomic_fetch_add(ngx_stat_active, 1);
+        (void) ngx_atomic_fetch_add(ngx_stat_active, 1);
 #endif
 
         c->pool = ngx_create_pool(ls->pool_size, ev->log);
@@ -153,8 +149,10 @@ ngx_event_accept(ngx_event_t *ev)
         c->log = log;
         c->pool->log = log;
 
+        c->socklen = socklen;
         c->listening = ls;
-        c->socklen = socklen;
+        c->local_sockaddr = ls->sockaddr;
+        c->local_socklen = ls->socklen;
 
         c->unexpected_eof = 1;
 
@@ -190,7 +188,7 @@ ngx_event_accept(ngx_event_t *ev)
         c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
 
 #if (NGX_STAT_STUB)
-        ngx_atomic_fetch_add(ngx_stat_handled, 1);
+        (void) ngx_atomic_fetch_add(ngx_stat_handled, 1);
 #endif
 
 #if (NGX_THREADS)
@@ -208,7 +206,7 @@ ngx_event_accept(ngx_event_t *ev)
             }
 
             c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->addr_text.data,
-                                             ls->addr_text_max_len);
+                                             ls->addr_text_max_len, 0);
             if (c->addr_text.len == 0) {
                 ngx_close_accepted_connection(c);
                 return;
@@ -381,7 +379,7 @@ ngx_close_accepted_connection(ngx_connec
     }
 
 #if (NGX_STAT_STUB)
-    ngx_atomic_fetch_add(ngx_stat_active, -1);
+    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
 #endif
 }
 
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -155,6 +155,7 @@ ngx_event_connect_peer(ngx_peer_connecti
                  */
                 || err == NGX_EAGAIN
 #endif
+                || err == NGX_ECONNRESET
                 || err == NGX_ENETDOWN
                 || err == NGX_ENETUNREACH
                 || err == NGX_EHOSTDOWN
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -10,7 +10,7 @@
 
 
 typedef struct {
-    ngx_str_t  engine;
+    ngx_uint_t  engine;   /* unsigned  engine:1; */
 } ngx_openssl_conf_t;
 
 
@@ -37,26 +37,17 @@ static void ngx_ssl_session_rbtree_inser
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
 
 static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
-static char *ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf);
+static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static void ngx_openssl_exit(ngx_cycle_t *cycle);
 
-#if !(NGX_SSL_ENGINE)
-static char *ngx_openssl_noengine(ngx_conf_t *cf, ngx_command_t *cmd,
-     void *conf);
-#endif
-
 
 static ngx_command_t  ngx_openssl_commands[] = {
 
     { ngx_string("ssl_engine"),
       NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
-#if (NGX_SSL_ENGINE)
-      ngx_conf_set_str_slot,
-#else
-      ngx_openssl_noengine,
-#endif
+      ngx_openssl_engine,
       0,
-      offsetof(ngx_openssl_conf_t, engine),
+      0,
       NULL },
 
       ngx_null_command
@@ -66,7 +57,7 @@ static ngx_command_t  ngx_openssl_comman
 static ngx_core_module_t  ngx_openssl_module_ctx = {
     ngx_string("openssl"),
     ngx_openssl_create_conf,
-    ngx_openssl_init_conf
+    NULL
 };
 
 
@@ -188,13 +179,6 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_
         SSL_CTX_set_options(ssl->ctx, ngx_ssl_protocols[protocols >> 1]);
     }
 
-    /*
-     * we need this option because in ngx_ssl_send_chain()
-     * we may switch to a buffered write and may copy leftover part of
-     * previously unbuffered data to our internal buffer
-     */
-    SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-
     SSL_CTX_set_read_ahead(ssl->ctx, 1);
 
     return NGX_OK;
@@ -205,7 +189,7 @@ ngx_int_t
 ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
     ngx_str_t *key)
 {
-    if (ngx_conf_full_name(cf->cycle, cert, 1) == NGX_ERROR) {
+    if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -218,7 +202,7 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_
         return NGX_ERROR;
     }
 
-    if (ngx_conf_full_name(cf->cycle, key, 1) == NGX_ERROR) {
+    if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -249,7 +233,7 @@ ngx_ssl_client_certificate(ngx_conf_t *c
         return NGX_OK;
     }
 
-    if (ngx_conf_full_name(cf->cycle, cert, 1) == NGX_ERROR) {
+    if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -407,7 +391,7 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_
         return NGX_OK;
     }
 
-    if (ngx_conf_full_name(cf->cycle, file, 1) == NGX_ERROR) {
+    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -505,11 +489,11 @@ ngx_ssl_handshake(ngx_connection_t *c)
 
     if (n == 1) {
 
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
-        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -576,7 +560,7 @@ ngx_ssl_handshake(ngx_connection_t *c)
         c->read->handler = ngx_ssl_handshake_handler;
         c->write->handler = ngx_ssl_handshake_handler;
 
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -588,7 +572,7 @@ ngx_ssl_handshake(ngx_connection_t *c)
         c->read->handler = ngx_ssl_handshake_handler;
         c->write->handler = ngx_ssl_handshake_handler;
 
-        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -774,7 +758,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
             c->ssl->saved_write_handler = NULL;
             c->write->ready = 1;
 
-            if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+            if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
                 return NGX_ERROR;
             }
 
@@ -802,7 +786,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
 
         c->write->ready = 0;
 
-        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -860,14 +844,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, 
     ssize_t      send, size;
     ngx_buf_t   *buf;
 
-    if (!c->ssl->buffer
-        || (in && in->next == NULL && !(c->buffered & NGX_SSL_BUFFERED)))
-    {
-        /*
-         * we avoid a buffer copy if
-         *     we do not need to buffer the output
-         *     or the incoming buf is a single and our buffer is empty
-         */
+    if (!c->ssl->buffer) {
 
         while (in) {
             if (ngx_buf_special(in->buf)) {
@@ -1033,7 +1010,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha
             c->ssl->saved_read_handler = NULL;
             c->read->ready = 1;
 
-            if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
                 return NGX_ERROR;
             }
 
@@ -1061,7 +1038,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha
 
         c->read->ready = 0;
 
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -1161,11 +1138,11 @@ ngx_ssl_shutdown(ngx_connection_t *c)
         c->read->handler = ngx_ssl_shutdown_handler;
         c->write->handler = ngx_ssl_shutdown_handler;
 
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
-        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -1254,23 +1231,37 @@ ngx_ssl_connection_error(ngx_connection_
         n = ERR_GET_REASON(ERR_peek_error());
 
             /* handshake failures */
-        if (n == SSL_R_DIGEST_CHECK_FAILED
-            || n == SSL_R_NO_SHARED_CIPHER
-            || n == SSL_R_UNEXPECTED_MESSAGE
-            || n == SSL_R_WRONG_VERSION_NUMBER
-            || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC
+        if (n == SSL_R_DIGEST_CHECK_FAILED                           /*  149 */
+            || n == SSL_R_NO_CIPHERS_PASSED                          /*  182 */
+            || n == SSL_R_NO_SHARED_CIPHER                           /*  193 */
+            || n == SSL_R_UNEXPECTED_MESSAGE                         /*  244 */
+            || n == SSL_R_UNEXPECTED_RECORD                          /*  245 */
+            || n == SSL_R_WRONG_VERSION_NUMBER                       /*  267 */
+            || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC        /*  281 */
             || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
-            || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE
-            || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC
-            || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE
-            || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE
-            || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE
-            || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE
-            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED
-            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED
-            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN
-            || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER
-            || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA)
+            || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE             /* 1010 */
+            || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC                 /* 1020 */
+            || n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED              /* 1021 */
+            || n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW                /* 1022 */
+            || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE          /* 1030 */
+            || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE              /* 1040 */
+            || n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE                 /* 1041 */
+            || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE                /* 1042 */
+            || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE        /* 1043 */
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED            /* 1044 */
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED            /* 1045 */
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN            /* 1046 */
+            || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER              /* 1047 */
+            || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA                     /* 1048 */
+            || n == SSL_R_TLSV1_ALERT_ACCESS_DENIED                  /* 1049 */
+            || n == SSL_R_TLSV1_ALERT_DECODE_ERROR                   /* 1050 */
+            || n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR                  /* 1051 */
+            || n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION             /* 1060 */
+            || n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION               /* 1070 */
+            || n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY          /* 1071 */
+            || n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR                 /* 1080 */
+            || n == SSL_R_TLSV1_ALERT_USER_CANCELLED                 /* 1090 */
+            || n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION)              /* 1100 */
         {
             switch (c->log_error) {
 
@@ -1315,7 +1306,7 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_
     last = errstr + NGX_MAX_CONF_ERRSTR;
 
     va_start(args, fmt);
-    p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args);
+    p = ngx_vslprintf(errstr, last - 1, fmt, args);
     va_end(args);
 
     p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p);
@@ -1396,7 +1387,7 @@ ngx_ssl_session_cache(ngx_ssl_t *ssl, ng
         }
     }
 
-    SSL_CTX_set_timeout(ssl->ctx, timeout);
+    SSL_CTX_set_timeout(ssl->ctx, (long) timeout);
 
     if (shm_zone) {
         shm_zone->init = ngx_ssl_session_cache_init;
@@ -1421,6 +1412,7 @@ ngx_ssl_session_cache(ngx_ssl_t *ssl, ng
 static ngx_int_t
 ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)
 {
+    size_t                    len;
     ngx_slab_pool_t          *shpool;
     ngx_ssl_session_cache_t  *cache;
 
@@ -1429,6 +1421,11 @@ ngx_ssl_session_cache_init(ngx_shm_zone_
         return NGX_OK;
     }
 
+    if (shm_zone->shm.exists) {
+        shm_zone->data = data;
+        return NGX_OK;
+    }
+
     shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
 
     cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t));
@@ -1436,12 +1433,23 @@ ngx_ssl_session_cache_init(ngx_shm_zone_
         return NGX_ERROR;
     }
 
+    shpool->data = cache;
+    shm_zone->data = cache;
+
     ngx_rbtree_init(&cache->session_rbtree, &cache->sentinel,
                     ngx_ssl_session_rbtree_insert_value);
 
     ngx_queue_init(&cache->expire_queue);
 
-    shm_zone->data = cache;
+    len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len;
+
+    shpool->log_ctx = ngx_slab_alloc(shpool, len);
+    if (shpool->log_ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_sprintf(shpool->log_ctx, " in SSL session shared cache \"%V\"%Z",
+                &shm_zone->shm.name);
 
     return NGX_OK;
 }
@@ -2107,14 +2115,13 @@ ngx_openssl_create_conf(ngx_cycle_t *cyc
 
     oscf = ngx_pcalloc(cycle->pool, sizeof(ngx_openssl_conf_t));
     if (oscf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
      * set by ngx_pcalloc():
      *
-     *     oscf->engine.len = 0;
-     *     oscf->engine.data = NULL;
+     *     oscf->engine = 0;
      */
 
     return oscf;
@@ -2122,53 +2129,54 @@ ngx_openssl_create_conf(ngx_cycle_t *cyc
 
 
 static char *
-ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf)
+ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
 #if (NGX_SSL_ENGINE)
     ngx_openssl_conf_t *oscf = conf;
 
-    ENGINE  *engine;
-
-    if (oscf->engine.len == 0) {
-        return NGX_CONF_OK;
+    ENGINE     *engine;
+    ngx_str_t  *value;
+
+    if (oscf->engine) {
+        return "is duplicate";
     }
 
-    engine = ENGINE_by_id((const char *) oscf->engine.data);
+    oscf->engine = 1;
+
+    value = cf->args->elts;
+
+    engine = ENGINE_by_id((const char *) value[1].data);
 
     if (engine == NULL) {
-        ngx_ssl_error(NGX_LOG_WARN, cycle->log, 0,
-                      "ENGINE_by_id(\"%V\") failed", &oscf->engine);
+        ngx_ssl_error(NGX_LOG_WARN, cf->log, 0,
+                      "ENGINE_by_id(\"%V\") failed", &value[1]);
         return NGX_CONF_ERROR;
     }
 
     if (ENGINE_set_default(engine, ENGINE_METHOD_ALL) == 0) {
-        ngx_ssl_error(NGX_LOG_WARN, cycle->log, 0,
+        ngx_ssl_error(NGX_LOG_WARN, cf->log, 0,
                       "ENGINE_set_default(\"%V\", ENGINE_METHOD_ALL) failed",
-                      &oscf->engine);
+                      &value[1]);
+
+        ENGINE_free(engine);
+
         return NGX_CONF_ERROR;
     }
 
     ENGINE_free(engine);
 
-#endif
-
     return NGX_CONF_OK;
-}
-
-
-#if !(NGX_SSL_ENGINE)
-
-static char *
-ngx_openssl_noengine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
-{
+
+#else
+
     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                        "\"ssl_engine\" directive is available only in "
                        "OpenSSL 0.9.7 and higher,");
 
     return NGX_CONF_ERROR;
-}
 
 #endif
+}
 
 
 static void
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -56,7 +56,7 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_
 
         flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0;
 
-        if (ngx_handle_read_event(rev, flags) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, flags) != NGX_OK) {
             return NGX_ABORT;
         }
 
@@ -70,7 +70,7 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_
 
     if (p->downstream->fd != -1 && p->downstream->data == p->output_ctx) {
         wev = p->downstream->write;
-        if (ngx_handle_write_event(wev, p->send_lowat) == NGX_ERROR) {
+        if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
             return NGX_ABORT;
         }
 
@@ -397,7 +397,7 @@ ngx_event_pipe_read_upstream(ngx_event_p
 
         p->free_raw_bufs = p->free_raw_bufs->next;
 
-        if (p->free_bufs) {
+        if (p->free_bufs && p->buf_to_file == NULL) {
             for (cl = p->free_raw_bufs; cl; cl = cl->next) {
                 if (cl->buf->shadow == NULL) {
                     ngx_pfree(p->pool, cl->buf->start);
@@ -423,7 +423,7 @@ ngx_event_pipe_write_to_downstream(ngx_e
     size_t             bsize;
     ngx_int_t          rc;
     ngx_uint_t         flush, prev_last_shadow;
-    ngx_chain_t       *out, **ll, *cl;
+    ngx_chain_t       *out, **ll, *cl, file;
     ngx_connection_t  *downstream;
 
     downstream = p->downstream;
@@ -488,6 +488,18 @@ ngx_event_pipe_write_to_downstream(ngx_e
                 p->in = NULL;
             }
 
+            if (p->cacheable && p->buf_to_file) {
+
+                file.buf = p->buf_to_file;
+                file.next = NULL;
+
+                if (ngx_write_chain_to_temp_file(p->temp_file, &file)
+                    == NGX_ERROR)
+                {
+                    return NGX_ABORT;
+                }
+            }
+
             ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
                            "pipe write downstream done");
 
--- a/src/http/modules/ngx_http_access_module.c
+++ b/src/http/modules/ngx_http_access_module.c
@@ -9,8 +9,6 @@
 #include <ngx_http.h>
 
 
-/* AF_INET only */
-
 typedef struct {
     in_addr_t     mask;
     in_addr_t     addr;
@@ -103,6 +101,10 @@ ngx_http_access_handler(ngx_http_request
 
     /* AF_INET only */
 
+    if (r->connection->sockaddr->sa_family != AF_INET) {
+        return NGX_DECLINED;
+    }
+
     sin = (struct sockaddr_in *) r->connection->sockaddr;
 
     rule = alcf->rules->elts;
@@ -139,7 +141,7 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx
 
     ngx_int_t                rc;
     ngx_str_t               *value;
-    ngx_inet_cidr_t          in_cidr;
+    ngx_cidr_t               cidr;
     ngx_http_access_rule_t  *rule;
 
     if (alcf->rules == NULL) {
@@ -166,7 +168,7 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx
         return NGX_CONF_OK;
     }
 
-    rc = ngx_ptocidr(&value[1], &in_cidr);
+    rc = ngx_ptocidr(&value[1], &cidr);
 
     if (rc == NGX_ERROR) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
@@ -174,13 +176,19 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx
         return NGX_CONF_ERROR;
     }
 
+    if (cidr.family != AF_INET) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"allow\" supports IPv4 only");
+        return NGX_CONF_ERROR;
+    }
+
     if (rc == NGX_DONE) {
         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                            "low address bits of %V are meaningless", &value[1]);
     }
 
-    rule->mask = in_cidr.mask;
-    rule->addr = in_cidr.addr;
+    rule->mask = cidr.u.in.mask;
+    rule->addr = cidr.u.in.addr;
 
     return NGX_CONF_OK;
 }
@@ -193,7 +201,7 @@ ngx_http_access_create_loc_conf(ngx_conf
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_access_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     return conf;
--- a/src/http/modules/ngx_http_addition_filter_module.c
+++ b/src/http/modules/ngx_http_addition_filter_module.c
@@ -151,10 +151,10 @@ ngx_http_addition_body_filter(ngx_http_r
         ctx->before_body_sent = 1;
 
         if (conf->before_body.len) {
-            rc = ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0);
-
-            if (rc == NGX_ERROR || rc == NGX_DONE) {
-                return rc;
+            if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0)
+                != NGX_OK)
+            {
+                return NGX_ERROR;
             }
         }
     }
@@ -180,10 +180,10 @@ ngx_http_addition_body_filter(ngx_http_r
         return rc;
     }
 
-    rc = ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0);
-
-    if (rc == NGX_ERROR || rc == NGX_DONE) {
-        return rc;
+    if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
     }
 
     ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
@@ -212,7 +212,7 @@ ngx_http_addition_create_conf(ngx_conf_t
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_addition_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
--- a/src/http/modules/ngx_http_auth_basic_module.c
+++ b/src/http/modules/ngx_http_auth_basic_module.c
@@ -13,13 +13,13 @@
 
 
 typedef struct {
-    ngx_str_t  passwd;
+    ngx_str_t                 passwd;
 } ngx_http_auth_basic_ctx_t;
 
 
 typedef struct {
-    ngx_str_t  realm;
-    ngx_str_t  user_file;
+    ngx_str_t                 realm;
+    ngx_http_complex_value_t  user_file;
 } ngx_http_auth_basic_loc_conf_t;
 
 
@@ -34,6 +34,8 @@ static char *ngx_http_auth_basic_merge_l
     void *parent, void *child);
 static ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf);
 static char *ngx_http_auth_basic(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 
 
 static ngx_conf_post_handler_pt  ngx_http_auth_basic_p = ngx_http_auth_basic;
@@ -51,7 +53,7 @@ static ngx_command_t  ngx_http_auth_basi
     { ngx_string("auth_basic_user_file"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
                         |NGX_CONF_TAKE1,
-      ngx_conf_set_str_slot,
+      ngx_http_auth_basic_user_file,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_auth_basic_loc_conf_t, user_file),
       NULL },
@@ -98,8 +100,9 @@ ngx_http_auth_basic_handler(ngx_http_req
     ssize_t                          n;
     ngx_fd_t                         fd;
     ngx_int_t                        rc;
-    ngx_str_t                        pwd;
-    ngx_uint_t                       i, login, left, passwd;
+    ngx_err_t                        err;
+    ngx_str_t                        pwd, user_file;
+    ngx_uint_t                       i, level, login, left, passwd;
     ngx_file_t                       file;
     ngx_http_auth_basic_ctx_t       *ctx;
     ngx_http_auth_basic_loc_conf_t  *alcf;
@@ -112,7 +115,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) {
+    if (alcf->realm.len == 0 || alcf->user_file.value.len == 0) {
         return NGX_DECLINED;
     }
 
@@ -126,6 +129,10 @@ ngx_http_auth_basic_handler(ngx_http_req
     rc = ngx_http_auth_basic_user(r);
 
     if (rc == NGX_DECLINED) {
+
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "no user/password was provided for basic authentication");
+
         return ngx_http_auth_basic_set_realm(r, &alcf->realm);
     }
 
@@ -133,18 +140,34 @@ ngx_http_auth_basic_handler(ngx_http_req
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    fd = ngx_open_file(alcf->user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+    if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
 
     if (fd == NGX_INVALID_FILE) {
-        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
-                      ngx_open_file_n " \"%s\" failed", alcf->user_file.data);
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        err = ngx_errno;
+
+        if (err == NGX_ENOENT) {
+            level = NGX_LOG_ERR;
+            rc = NGX_HTTP_FORBIDDEN;
+
+        } else {
+            level = NGX_LOG_CRIT;
+            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        ngx_log_error(level, r->connection->log, err,
+                      ngx_open_file_n " \"%s\" failed", user_file.data);
+
+        return rc;
     }
 
     ngx_memzero(&file, sizeof(ngx_file_t));
 
     file.fd = fd;
-    file.name = alcf->user_file;
+    file.name = user_file;
     file.log = r->connection->log;
 
     state = sw_login;
@@ -172,9 +195,16 @@ ngx_http_auth_basic_handler(ngx_http_req
             switch (state) {
 
             case sw_login:
-                if (login == 0 && buf[i] == '#') {
-                    state = sw_skip;
-                    break;
+                if (login == 0) {
+
+                    if (buf[i] == '#' || buf[i] == CR) {
+                        state = sw_skip;
+                        break;
+                    }
+
+                    if (buf[i] == LF) {
+                        break;
+                    }
                 }
 
                 if (buf[i] != r->headers_in.user.data[login]) {
@@ -242,6 +272,10 @@ ngx_http_auth_basic_handler(ngx_http_req
         return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd, &alcf->realm);
     }
 
+    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                  "user \"%V\" was not found in \"%V\"",
+                  &r->headers_in.user, &user_file);
+
     return ngx_http_auth_basic_set_realm(r, &alcf->realm);
 }
 
@@ -257,8 +291,8 @@ ngx_http_auth_basic_crypt_handler(ngx_ht
                    &encrypted);
 
     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                  "rc: %d user: \"%V\" salt: \"%s\"",
-                  rc, &r->headers_in.user, passwd->data);
+                   "rc: %d user: \"%V\" salt: \"%s\"",
+                   rc, &r->headers_in.user, passwd->data);
 
     if (rc == NGX_OK) {
         if (ngx_strcmp(encrypted, passwd->data) == 0) {
@@ -268,6 +302,10 @@ ngx_http_auth_basic_crypt_handler(ngx_ht
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "encrypted: \"%s\"", encrypted);
 
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "user \"%V\": password mismatch",
+                      &r->headers_in.user);
+
         return ngx_http_auth_basic_set_realm(r, realm);
     }
 
@@ -334,7 +372,7 @@ ngx_http_auth_basic_create_loc_conf(ngx_
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     return conf;
@@ -351,12 +389,7 @@ ngx_http_auth_basic_merge_loc_conf(ngx_c
         conf->realm = prev->realm;
     }
 
-    if (conf->user_file.data) {
-        if (ngx_conf_full_name(cf->cycle, &conf->user_file, 1) != NGX_OK) {
-            return NGX_CONF_ERROR;
-        }
-
-    } else {
+    if (conf->user_file.value.len == 0) {
         conf->user_file = prev->user_file;
     }
 
@@ -414,3 +447,33 @@ ngx_http_auth_basic(ngx_conf_t *cf, void
 
     return NGX_CONF_OK;
 }
+
+
+static char *
+ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_auth_basic_loc_conf_t *alcf = conf;
+
+    ngx_str_t                         *value;
+    ngx_http_compile_complex_value_t   ccv;
+
+    if (alcf->user_file.value.len) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[1];
+    ccv.complex_value = &alcf->user_file;
+    ccv.zero = 1;
+    ccv.conf_prefix = 1;
+
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -25,8 +25,11 @@ typedef struct {
 typedef struct {
     ngx_str_t      name;
     size_t         utf_len;
-    ngx_uint_t     escape;
-    ngx_uint_t     dir;
+    size_t         escape;
+
+    unsigned       dir:1;
+    unsigned       colon:1;
+
     time_t         mtime;
     off_t          size;
 } ngx_http_autoindex_entry_t;
@@ -142,7 +145,7 @@ ngx_http_autoindex_handler(ngx_http_requ
     ngx_int_t                       rc, size;
     ngx_str_t                       path;
     ngx_dir_t                       dir;
-    ngx_uint_t                      i, level;
+    ngx_uint_t                      i, level, utf8;
     ngx_pool_t                     *pool;
     ngx_time_t                     *tp;
     ngx_chain_t                     out;
@@ -157,7 +160,6 @@ ngx_http_autoindex_handler(ngx_http_requ
         return NGX_DECLINED;
     }
 
-    /* TODO: Win32 */
     if (r->zero_in_uri) {
         return NGX_DECLINED;
     }
@@ -250,6 +252,16 @@ ngx_http_autoindex_handler(ngx_http_requ
     filename = path.data;
     filename[path.len] = '/';
 
+    if (r->headers_out.charset.len == 5
+        && ngx_strncasecmp(r->headers_out.charset.data, (u_char *) "utf-8", 5)
+           == 0)
+    {
+        utf8 = 1;
+
+    } else {
+        utf8 = 0;
+    }
+
     for ( ;; ) {
         ngx_set_errno(0);
 
@@ -299,6 +311,11 @@ ngx_http_autoindex_handler(ngx_http_requ
                 if (err != NGX_ENOENT) {
                     ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                                   ngx_de_info_n " \"%s\" failed", filename);
+
+                    if (err == NGX_EACCES) {
+                        continue;
+                    }
+
                     return ngx_http_autoindex_error(r, &dir, &path);
                 }
 
@@ -328,12 +345,14 @@ ngx_http_autoindex_handler(ngx_http_requ
         entry->escape = 2 * ngx_escape_uri(NULL, ngx_de_name(&dir), len,
                                            NGX_ESCAPE_HTML);
 
-        if (r->utf8) {
+        if (utf8) {
             entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len);
         } else {
             entry->utf_len = len;
         }
 
+        entry->colon = (ngx_strchr(entry->name.data, ':') != NULL);
+
         entry->dir = ngx_de_is_dir(&dir);
         entry->mtime = ngx_de_mtime(&dir);
         entry->size = ngx_de_size(&dir);
@@ -359,7 +378,7 @@ ngx_http_autoindex_handler(ngx_http_requ
             + entry[i].name.len + entry[i].escape
             + 1                                          /* 1 is for "/" */
             + sizeof("\">") - 1
-            + entry[i].name.len - entry[i].utf_len
+            + entry[i].name.len - entry[i].utf_len + entry[i].colon * 2
             + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof("&gt;") - 2
             + sizeof("</a>") - 1
             + sizeof(" 28-Sep-1970 12:00 ") - 1
@@ -392,6 +411,11 @@ ngx_http_autoindex_handler(ngx_http_requ
     for (i = 0; i < entries.nelts; i++) {
         b->last = ngx_cpymem(b->last, "<a href=\"", sizeof("<a href=\"") - 1);
 
+        if (entry[i].colon) {
+            *b->last++ = '.';
+            *b->last++ = '/';
+        }
+
         if (entry[i].escape) {
             ngx_escape_uri(b->last, entry[i].name.data, entry[i].name.len,
                            NGX_ESCAPE_HTML);
@@ -608,7 +632,7 @@ ngx_http_autoindex_create_loc_conf(ngx_c
 
     conf = ngx_palloc(cf->pool, sizeof(ngx_http_autoindex_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     conf->enable = NGX_CONF_UNSET;
--- a/src/http/modules/ngx_http_browser_module.c
+++ b/src/http/modules/ngx_http_browser_module.c
@@ -318,6 +318,10 @@ ngx_http_browser(ngx_http_request_t *r, 
                 if (c == '.') {
                     version += ver * scale;
 
+                    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                                   "version: \"%ui\" \"%ui\"",
+                                   modern[i].version, version);
+
                     if (version > modern[i].version) {
                         return NGX_HTTP_MODERN_BROWSER;
                     }
@@ -339,6 +343,8 @@ ngx_http_browser(ngx_http_request_t *r, 
             if (version >= modern[i].version) {
                 return NGX_HTTP_MODERN_BROWSER;
             }
+
+            return NGX_HTTP_ANCIENT_BROWSER;
         }
 
         if (!cf->modern_unlisted_browsers) {
@@ -417,7 +423,7 @@ ngx_http_browser_create_conf(ngx_conf_t 
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
--- a/src/http/modules/ngx_http_charset_filter_module.c
+++ b/src/http/modules/ngx_http_charset_filter_module.c
@@ -9,8 +9,9 @@
 #include <ngx_http.h>
 
 
-#define NGX_HTTP_NO_CHARSET    -2
-#define NGX_HTTP_CHARSET_VAR   0x10000
+#define NGX_HTTP_CHARSET_OFF    -2
+#define NGX_HTTP_NO_CHARSET     -3
+#define NGX_HTTP_CHARSET_VAR    0x10000
 
 /* 1 byte length and up to 3 bytes for the UTF-8 encoding of the UCS-2 */
 #define NGX_UTF_LEN             4
@@ -61,6 +62,7 @@ typedef struct {
 typedef struct {
     u_char                     *table;
     ngx_int_t                   charset;
+    ngx_str_t                   charset_name;
 
     ngx_chain_t                *busy;
     ngx_chain_t                *free_bufs;
@@ -82,9 +84,16 @@ typedef struct {
 } ngx_http_charset_conf_ctx_t;
 
 
-static ngx_int_t ngx_http_charset_get_charset(ngx_http_charset_t *charsets,
-    ngx_uint_t n, ngx_str_t *charset);
-static ngx_int_t ngx_http_charset_set_charset(ngx_http_request_t *r,
+static ngx_int_t ngx_http_destination_charset(ngx_http_request_t *r,
+    ngx_str_t *name);
+static ngx_int_t ngx_http_main_request_charset(ngx_http_request_t *r,
+    ngx_str_t *name);
+static ngx_int_t ngx_http_source_charset(ngx_http_request_t *r,
+    ngx_str_t *name);
+static ngx_int_t ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name);
+static ngx_inline void ngx_http_set_charset(ngx_http_request_t *r,
+    ngx_str_t *charset);
+static ngx_int_t ngx_http_charset_ctx(ngx_http_request_t *r,
     ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset);
 static ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table);
 static ngx_chain_t *ngx_http_charset_recode_from_utf8(ngx_pool_t *pool,
@@ -207,206 +216,264 @@ static ngx_int_t
 ngx_http_charset_header_filter(ngx_http_request_t *r)
 {
     ngx_int_t                      charset, source_charset;
-    ngx_str_t                     *mc, *from, *to, s;
-    ngx_uint_t                     n;
+    ngx_str_t                      dst, src;
     ngx_http_charset_t            *charsets;
-    ngx_http_charset_ctx_t        *ctx;
-    ngx_http_variable_value_t     *vv;
-    ngx_http_charset_loc_conf_t   *lcf, *mlcf;
     ngx_http_charset_main_conf_t  *mcf;
 
-    mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
-
-    charsets = mcf->charsets.elts;
-    n = mcf->charsets.nelts;
-
-    /* destination charset */
-
     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);
-        }
-
-        if (r->headers_out.override_charset
-            && r->headers_out.override_charset->len)
-        {
-            charset = ngx_http_charset_get_charset(charsets, n,
-                                              r->headers_out.override_charset);
-
-            if (charset == NGX_HTTP_NO_CHARSET) {
-                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                              "unknown charset \"%V\" to override",
-                              r->headers_out.override_charset);
-
-                return ngx_http_next_header_filter(r);
-            }
-
-        } else {
-            mlcf = ngx_http_get_module_loc_conf(r,
-                                                ngx_http_charset_filter_module);
-            charset = mlcf->charset;
-
-            if (charset == NGX_HTTP_NO_CHARSET) {
-                return ngx_http_next_header_filter(r);
-            }
-
-            if (r->headers_out.charset.len) {
-                if (mlcf->override_charset == 0) {
-                    return ngx_http_next_header_filter(r);
-                }
-
-            } else {
-                if (ngx_http_test_content_type(r, &mlcf->types) == NULL) {
-                    return ngx_http_next_header_filter(r);
-                }
-            }
-
-            if (charset >= NGX_HTTP_CHARSET_VAR) {
-                vv = ngx_http_get_indexed_variable(r,
-                                               charset - NGX_HTTP_CHARSET_VAR);
-
-                if (vv == NULL || vv->not_found) {
-                    return NGX_ERROR;
-                }
-
-                s.len = vv->len;
-                s.data = vv->data;
-
-                charset = ngx_http_charset_get_charset(charsets, n, &s);
-            }
-        }
+        charset = ngx_http_destination_charset(r, &dst);
 
     } else {
-        ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module);
-
-        if (ctx == NULL) {
-
-            mc = &r->main->headers_out.charset;
-
-            if (mc->len == 0) {
-                return ngx_http_next_header_filter(r);
-            }
+        charset = ngx_http_main_request_charset(r, &dst);
+    }
 
-            ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
-            if (ctx == NULL) {
-                return NGX_ERROR;
-            }
-
-            ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module);
+    if (charset == NGX_ERROR) {
+        return NGX_ERROR;
+    }
 
-            charset = ngx_http_charset_get_charset(charsets, n, mc);
-
-            ctx->charset = charset;
-
-        } else {
-            charset = ctx->charset;
-        }
+    if (charset == NGX_DECLINED) {
+        return ngx_http_next_header_filter(r);
     }
 
-    /* source charset */
-
-    if (r->headers_out.charset.len == 0) {
-        lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
-
-        source_charset = lcf->source_charset;
-
-        if (source_charset >= NGX_HTTP_CHARSET_VAR) {
-            vv = ngx_http_get_indexed_variable(r,
-                                        source_charset - NGX_HTTP_CHARSET_VAR);
-
-            if (vv == NULL || vv->not_found) {
-                return NGX_ERROR;
-            }
+    /* charset: charset index or NGX_HTTP_NO_CHARSET */
 
-            s.len = vv->len;
-            s.data = vv->data;
-
-            source_charset = ngx_http_charset_get_charset(charsets, n, &s);
-        }
+    source_charset = ngx_http_source_charset(r, &src);
 
-        if (charset != NGX_HTTP_NO_CHARSET) {
-            return ngx_http_charset_set_charset(r, mcf->charsets.elts, charset,
-                                                source_charset);
-        }
-
-        if (source_charset == NGX_CONF_UNSET) {
-            return ngx_http_next_header_filter(r);
-        }
-
-        from = &charsets[source_charset].name;
-        to = &r->main->headers_out.charset;
-
-        goto no_charset_map;
+    if (source_charset == NGX_ERROR) {
+        return NGX_ERROR;
     }
 
-    source_charset = ngx_http_charset_get_charset(charsets, n,
-                                                  &r->headers_out.charset);
+    /*
+     * source_charset: charset index, NGX_HTTP_NO_CHARSET,
+     *                 or NGX_HTTP_CHARSET_OFF
+     */
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "charset: \"%V\" > \"%V\"", &src, &dst);
+
+    if (source_charset == NGX_HTTP_CHARSET_OFF) {
+        ngx_http_set_charset(r, &dst);
+
+        return ngx_http_next_header_filter(r);
+    }
 
     if (charset == NGX_HTTP_NO_CHARSET
         || source_charset == NGX_HTTP_NO_CHARSET)
     {
-        if (charset != source_charset
-            || ngx_strcasecmp(r->main->headers_out.charset.data,
-                              r->headers_out.charset.data)
-                != 0)
+        if (source_charset != charset
+            || ngx_strncasecmp(dst.data, src.data, dst.len) != 0)
         {
-            from = &r->headers_out.charset;
-            to = (charset == NGX_HTTP_NO_CHARSET) ?
-                                           &r->main->headers_out.charset:
-                                           &charsets[charset].name;
-
             goto no_charset_map;
         }
 
+        ngx_http_set_charset(r, &dst);
+
         return ngx_http_next_header_filter(r);
     }
 
+    mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
+    charsets = mcf->charsets.elts;
+
     if (source_charset != charset
         && (charsets[source_charset].tables == NULL
             || charsets[source_charset].tables[charset] == NULL))
     {
-        from = &charsets[source_charset].name;
-        to = &charsets[charset].name;
-
         goto no_charset_map;
     }
 
     r->headers_out.content_type.len = r->headers_out.content_type_len;
 
-    return ngx_http_charset_set_charset(r, mcf->charsets.elts, charset,
-                                        source_charset);
+    ngx_http_set_charset(r, &dst);
+
+    if (source_charset != charset) {
+        return ngx_http_charset_ctx(r, charsets, charset, source_charset);
+    }
+
+    return ngx_http_next_header_filter(r);
 
 no_charset_map:
 
     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   "no \"charset_map\" between the charsets \"%V\" and \"%V\"",
-                  from, to);
+                  &src, &dst);
 
     return ngx_http_next_header_filter(r);
 }
 
 
 static ngx_int_t
-ngx_http_charset_get_charset(ngx_http_charset_t *charsets, ngx_uint_t n,
-    ngx_str_t *charset)
+ngx_http_destination_charset(ngx_http_request_t *r, ngx_str_t *name)
+{
+    ngx_int_t                      charset;
+    ngx_http_charset_t            *charsets;
+    ngx_http_variable_value_t     *vv;
+    ngx_http_charset_loc_conf_t   *mlcf;
+    ngx_http_charset_main_conf_t  *mcf;
+
+    if (!r->ignore_content_encoding
+        && r->headers_out.content_encoding
+        && r->headers_out.content_encoding->value.len)
+    {
+        return NGX_DECLINED;
+    }
+
+    if (r->headers_out.content_type.len == 0) {
+        return NGX_DECLINED;
+    }
+
+    if (r->headers_out.override_charset
+        && r->headers_out.override_charset->len)
+    {
+        *name = *r->headers_out.override_charset;
+
+        charset = ngx_http_get_charset(r, name);
+
+        if (charset != NGX_HTTP_NO_CHARSET) {
+            return charset;
+        }
+
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "unknown charset \"%V\" to override", name);
+
+        return NGX_DECLINED;
+    }
+
+    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
+    charset = mlcf->charset;
+
+    if (charset == NGX_HTTP_CHARSET_OFF) {
+        return NGX_DECLINED;
+    }
+
+    if (r->headers_out.charset.len) {
+        if (mlcf->override_charset == 0) {
+            return NGX_DECLINED;
+        }
+
+    } else {
+        if (ngx_http_test_content_type(r, &mlcf->types) == NULL) {
+            return NGX_DECLINED;
+        }
+    }
+
+    if (charset < NGX_HTTP_CHARSET_VAR) {
+        mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
+        charsets = mcf->charsets.elts;
+        *name = charsets[charset].name;
+        return charset;
+    }
+
+    vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);
+
+    if (vv == NULL || vv->not_found) {
+        return NGX_ERROR;
+    }
+
+    name->len = vv->len;
+    name->data = vv->data;
+
+    return ngx_http_get_charset(r, name);
+}
+
+
+static ngx_int_t
+ngx_http_main_request_charset(ngx_http_request_t *r, ngx_str_t *src)
 {
-    ngx_uint_t  i;
+    ngx_int_t                charset;
+    ngx_str_t               *main_charset;
+    ngx_http_charset_ctx_t  *ctx;
+
+    ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module);
+
+    if (ctx) {
+        *src = ctx->charset_name;
+        return ctx->charset;
+    }
+
+    main_charset = &r->main->headers_out.charset;
+
+    if (main_charset->len == 0) {
+        return NGX_DECLINED;
+    }
+
+    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module);
+
+    charset = ngx_http_get_charset(r, main_charset);
+
+    ctx->charset = charset;
+    ctx->charset_name = *main_charset;
+    *src = *main_charset;
+
+    return charset;
+}
+
+
+static ngx_int_t
+ngx_http_source_charset(ngx_http_request_t *r, ngx_str_t *name)
+{
+    ngx_int_t                      charset;
+    ngx_http_charset_t            *charsets;
+    ngx_http_variable_value_t     *vv;
+    ngx_http_charset_loc_conf_t   *lcf;
+    ngx_http_charset_main_conf_t  *mcf;
+
+    if (r->headers_out.charset.len) {
+        *name = r->headers_out.charset;
+        return ngx_http_get_charset(r, name);
+    }
+
+    lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
+
+    charset = lcf->source_charset;
+
+    if (charset == NGX_HTTP_CHARSET_OFF) {
+        name->len = 0;
+        return charset;
+    }
+
+    if (charset < NGX_HTTP_CHARSET_VAR) {
+        mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
+        charsets = mcf->charsets.elts;
+        *name = charsets[charset].name;
+        return charset;
+    }
+
+    vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);
+
+    if (vv == NULL || vv->not_found) {
+        return NGX_ERROR;
+    }
+
+    name->len = vv->len;
+    name->data = vv->data;
+
+    return ngx_http_get_charset(r, name);
+}
+
+
+static ngx_int_t
+ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name)
+{
+    ngx_uint_t                     i, n;
+    ngx_http_charset_t            *charset;
+    ngx_http_charset_main_conf_t  *mcf;
+
+    mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
+
+    charset = mcf->charsets.elts;
+    n = mcf->charsets.nelts;
 
     for (i = 0; i < n; i++) {
-        if (charsets[i].name.len != charset->len) {
+        if (charset[i].name.len != name->len) {
             continue;
         }
 
-        if (ngx_strncasecmp(charsets[i].name.data, charset->data, charset->len)
-            == 0)
-        {
+        if (ngx_strncasecmp(charset[i].name.data, name->data, name->len) == 0) {
             return i;
         }
     }
@@ -415,11 +482,12 @@ ngx_http_charset_get_charset(ngx_http_ch
 }
 
 
-static ngx_int_t
-ngx_http_charset_set_charset(ngx_http_request_t *r,
-    ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset)
+static ngx_inline void
+ngx_http_set_charset(ngx_http_request_t *r, ngx_str_t *charset)
 {
-    ngx_http_charset_ctx_t  *ctx;
+    if (r != r->main) {
+        return;
+    }
 
     if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY
         || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY)
@@ -430,16 +498,18 @@ ngx_http_charset_set_charset(ngx_http_re
          */
 
         r->headers_out.charset.len = 0;
-
-        return ngx_http_next_header_filter(r);
+        return;
     }
 
-    r->headers_out.charset = charsets[charset].name;
-    r->utf8 = charsets[charset].utf8;
+    r->headers_out.charset = *charset;
+}
+
 
-    if (source_charset == NGX_CONF_UNSET || source_charset == charset) {
-        return ngx_http_next_header_filter(r);
-    }
+static ngx_int_t
+ngx_http_charset_ctx(ngx_http_request_t *r, ngx_http_charset_t *charsets,
+    ngx_int_t charset, ngx_int_t source_charset)
+{
+    ngx_http_charset_ctx_t  *ctx;
 
     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
     if (ctx == NULL) {
@@ -450,6 +520,7 @@ ngx_http_charset_set_charset(ngx_http_re
 
     ctx->table = charsets[source_charset].tables[charset];
     ctx->charset = charset;
+    ctx->charset_name = charsets[charset].name;
     ctx->length = charsets[charset].length;
     ctx->from_utf8 = charsets[source_charset].utf8;
     ctx->to_utf8 = charsets[charset].utf8;
@@ -541,12 +612,6 @@ ngx_http_charset_body_filter(ngx_http_re
                 break;
             }
 
-#if (NGX_HAVE_WRITE_ZEROCOPY)
-            if (b->zerocopy_busy) {
-                break;
-            }
-#endif
-
             ctx->busy = cl->next;
 
             if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) {
@@ -1343,7 +1408,7 @@ ngx_http_set_charset_slot(ngx_conf_t *cf
     if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, charset)
         && ngx_strcmp(value[1].data, "off") == 0)
     {
-        *cp = NGX_HTTP_NO_CHARSET;
+        *cp = NGX_HTTP_CHARSET_OFF;
         return NGX_CONF_OK;
     }
 
@@ -1423,25 +1488,27 @@ ngx_http_charset_create_main_conf(ngx_co
 
     mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t));
     if (mcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t))
-        == NGX_ERROR)
+        != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     if (ngx_array_init(&mcf->tables, cf->pool, 1,
-                       sizeof(ngx_http_charset_tables_t)) == NGX_ERROR)
+                       sizeof(ngx_http_charset_tables_t))
+        != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     if (ngx_array_init(&mcf->recodes, cf->pool, 2,
-                       sizeof(ngx_http_charset_recode_t)) == NGX_ERROR)
+                       sizeof(ngx_http_charset_recode_t))
+        != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     return mcf;
@@ -1455,7 +1522,7 @@ ngx_http_charset_create_loc_conf(ngx_con
 
     lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t));
     if (lcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -1492,14 +1559,12 @@ ngx_http_charset_merge_loc_conf(ngx_conf
     }
 
     ngx_conf_merge_value(conf->override_charset, prev->override_charset, 0);
-    ngx_conf_merge_value(conf->charset, prev->charset, NGX_HTTP_NO_CHARSET);
+    ngx_conf_merge_value(conf->charset, prev->charset, NGX_HTTP_CHARSET_OFF);
+    ngx_conf_merge_value(conf->source_charset, prev->source_charset,
+                         NGX_HTTP_CHARSET_OFF);
 
-    if (conf->source_charset == NGX_CONF_UNSET) {
-        conf->source_charset = prev->source_charset;
-    }
-
-    if (conf->charset == NGX_HTTP_NO_CHARSET
-        || conf->source_charset == NGX_CONF_UNSET
+    if (conf->charset == NGX_HTTP_CHARSET_OFF
+        || conf->source_charset == NGX_HTTP_CHARSET_OFF
         || conf->charset == conf->source_charset)
     {
         return NGX_CONF_OK;
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -148,7 +148,6 @@ ngx_http_dav_handler(ngx_http_request_t 
     ngx_int_t                 rc;
     ngx_http_dav_loc_conf_t  *dlcf;
 
-    /* TODO: Win32 */
     if (r->zero_in_uri) {
         return NGX_DECLINED;
     }
@@ -222,7 +221,7 @@ ngx_http_dav_put_handler(ngx_http_reques
 
     temp = &r->request_body->temp_file->file.name;
 
-    if (ngx_file_info(path.data, &fi) == -1) {
+    if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
         status = NGX_HTTP_CREATED;
 
     } else {
@@ -246,9 +245,11 @@ ngx_http_dav_put_handler(ngx_http_reques
     dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
 
     ext.access = dlcf->access;
+    ext.path_access = dlcf->access;
     ext.time = -1;
     ext.create_path = dlcf->create_full_put_path;
     ext.delete_file = 1;
+    ext.log_rename_error = 1;
     ext.log = r->connection->log;
 
     if (r->headers_in.date) {
@@ -325,7 +326,7 @@ ok:
     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) {
+    if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
         err = ngx_errno;
 
         rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;
@@ -521,6 +522,7 @@ ngx_http_dav_copy_move_handler(ngx_http_
     ngx_tree_ctx_t            tree;
     ngx_file_info_t           fi;
     ngx_table_elt_t          *dest, *over;
+    ngx_ext_rename_file_t     ext;
     ngx_http_dav_copy_ctx_t   copy;
     ngx_http_dav_loc_conf_t  *dlcf;
 
@@ -676,7 +678,7 @@ overwrite_done:
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http copy to: \"%s\"", copy.path.data);
 
-    if (ngx_file_info(copy.path.data, &fi) == -1) {
+    if (ngx_file_info(copy.path.data, &fi) == NGX_FILE_ERROR) {
         err = ngx_errno;
 
         if (err != NGX_ENOENT) {
@@ -710,7 +712,7 @@ overwrite_done:
         dir = ngx_is_dir(&fi);
     }
 
-    if (ngx_file_info(path.data, &fi) == -1) {
+    if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
         return ngx_http_dav_error(r->connection->log, ngx_errno,
                                   NGX_HTTP_NOT_FOUND, ngx_file_info_n,
                                   path.data);
@@ -781,9 +783,32 @@ overwrite_done:
     } else {
 
         if (r->method == NGX_HTTP_MOVE) {
-            if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {
+
+            dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
+
+            ext.access = 0;
+            ext.path_access = dlcf->access;
+            ext.time = -1;
+            ext.create_path = 1;
+            ext.delete_file = 0;
+            ext.log_rename_error = 0;
+            ext.log = r->connection->log;
+
+            if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {
                 return NGX_HTTP_NO_CONTENT;
             }
+
+            if (ext.rename_error != NGX_EXDEV) {
+
+                if (ext.rename_error) {
+                    ngx_log_error(NGX_LOG_CRIT, r->connection->log,
+                                  ext.rename_error,
+                                  ngx_rename_file_n " \"%s\" to \"%s\" failed",
+                                  path.data, copy.path.data);
+                }
+
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
         }
 
         dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
@@ -1129,7 +1154,7 @@ ngx_http_dav_create_loc_conf(ngx_conf_t 
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
--- a/src/http/modules/ngx_http_empty_gif_module.c
+++ b/src/http/modules/ngx_http_empty_gif_module.c
@@ -122,6 +122,7 @@ ngx_http_empty_gif_handler(ngx_http_requ
         return rc;
     }
 
+    r->headers_out.content_type_len = sizeof("image/gif") - 1;
     r->headers_out.content_type.len = sizeof("image/gif") - 1;
     r->headers_out.content_type.data = (u_char *) "image/gif";
 
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -19,6 +19,18 @@ typedef struct {
     ngx_array_t                   *params;
     ngx_array_t                   *params_source;
     ngx_array_t                   *catch_stderr;
+
+    ngx_array_t                   *fastcgi_lengths;
+    ngx_array_t                   *fastcgi_values;
+
+#if (NGX_HTTP_CACHE)
+    ngx_http_complex_value_t       cache_key;
+#endif
+
+#if (NGX_PCRE)
+    ngx_regex_t                   *split_regex;
+    ngx_str_t                      split_name;
+#endif
 } ngx_http_fastcgi_loc_conf_t;
 
 
@@ -53,6 +65,9 @@ typedef struct {
     ngx_uint_t                     fastcgi_stdout; /* unsigned :1 */
 
     ngx_array_t                   *split_parts;
+
+    ngx_str_t                      script_name;
+    ngx_str_t                      path_info;
 } ngx_http_fastcgi_ctx_t;
 
 
@@ -103,6 +118,11 @@ typedef struct {
 } ngx_http_fastcgi_request_start_t;
 
 
+static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r,
+    ngx_http_fastcgi_loc_conf_t *flcf);
+#if (NGX_HTTP_CACHE)
+static ngx_int_t ngx_http_fastcgi_create_key(ngx_http_request_t *r);
+#endif
 static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);
 static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);
 static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);
@@ -120,11 +140,24 @@ static char *ngx_http_fastcgi_merge_loc_
     void *parent, void *child);
 static ngx_int_t ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_http_fastcgi_ctx_t *ngx_http_fastcgi_split(ngx_http_request_t *r,
+    ngx_http_fastcgi_loc_conf_t *flcf);
 
 static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_fastcgi_split_path_info(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf);
 static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+#if (NGX_HTTP_CACHE)
+static char *ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char *ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+#endif
+
 static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post,
     void *data);
 
@@ -145,11 +178,24 @@ static ngx_conf_bitmask_t  ngx_http_fast
     { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
     { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
     { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
+    { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },
     { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
     { ngx_null_string, 0 }
 };
 
 
+static ngx_conf_bitmask_t  ngx_http_fastcgi_ignore_headers_masks[] = {
+    { ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT },
+    { ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES },
+    { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES },
+    { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL },
+    { ngx_null_string, 0 }
+};
+
+
+ngx_module_t  ngx_http_fastcgi_module;
+
+
 static ngx_command_t  ngx_http_fastcgi_commands[] = {
 
     { ngx_string("fastcgi_pass"),
@@ -166,6 +212,13 @@ static ngx_command_t  ngx_http_fastcgi_c
       offsetof(ngx_http_fastcgi_loc_conf_t, index),
       NULL },
 
+    { ngx_string("fastcgi_split_path_info"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_http_fastcgi_split_path_info,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("fastcgi_store"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_http_fastcgi_store,
@@ -257,12 +310,65 @@ static ngx_command_t  ngx_http_fastcgi_c
       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf),
       NULL },
 
+#if (NGX_HTTP_CACHE)
+
+    { ngx_string("fastcgi_cache"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_http_fastcgi_cache,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("fastcgi_cache_key"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_http_fastcgi_cache_key,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("fastcgi_cache_path"),
+      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
+      ngx_http_file_cache_set_slot,
+      0,
+      0,
+      &ngx_http_fastcgi_module },
+
+    { ngx_string("fastcgi_cache_valid"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+      ngx_http_file_cache_valid_set_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_valid),
+      NULL },
+
+    { ngx_string("fastcgi_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_fastcgi_loc_conf_t, upstream.cache_min_uses),
+      NULL },
+
+    { ngx_string("fastcgi_cache_use_stale"),
+      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_fastcgi_loc_conf_t, upstream.cache_use_stale),
+      &ngx_http_fastcgi_next_upstream_masks },
+
+    { ngx_string("fastcgi_cache_methods"),
+      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_fastcgi_loc_conf_t, upstream.cache_methods),
+      &ngx_http_upstream_cache_method_mask },
+
+#endif
+
     { ngx_string("fastcgi_temp_path"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
       ngx_conf_set_path_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path),
-      (void *) ngx_garbage_collector_temp_handler },
+      NULL },
 
     { ngx_string("fastcgi_max_temp_file_size"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
@@ -320,6 +426,13 @@ static ngx_command_t  ngx_http_fastcgi_c
       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers),
       NULL },
 
+    { ngx_string("fastcgi_ignore_headers"),
+      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_fastcgi_loc_conf_t, upstream.ignore_headers),
+      &ngx_http_fastcgi_ignore_headers_masks },
+
     { ngx_string("fastcgi_catch_stderr"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_str_array_slot,
@@ -385,8 +498,18 @@ static ngx_http_fastcgi_request_start_t 
 };
 
 
-static ngx_str_t  ngx_http_fastcgi_script_name =
-    ngx_string("fastcgi_script_name");
+static ngx_http_variable_t  ngx_http_fastcgi_vars[] = {
+
+    { ngx_string("fastcgi_script_name"), NULL,
+      ngx_http_fastcgi_script_name_variable, 0,
+      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+    { ngx_string("fastcgi_path_info"), NULL,
+      ngx_http_fastcgi_path_info_variable, 0,
+      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+    { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
 
 
 static ngx_str_t  ngx_http_fastcgi_hide_headers[] = {
@@ -400,11 +523,17 @@ static ngx_str_t  ngx_http_fastcgi_hide_
 };
 
 
+static ngx_path_init_t  ngx_http_fastcgi_temp_path = {
+    ngx_string(NGX_HTTP_FASTCGI_TEMP_PATH), { 1, 2, 0 }
+};
+
+
 static ngx_int_t
 ngx_http_fastcgi_handler(ngx_http_request_t *r)
 {
     ngx_int_t                     rc;
     ngx_http_upstream_t          *u;
+    ngx_http_fastcgi_ctx_t       *f;
     ngx_http_fastcgi_loc_conf_t  *flcf;
 
     if (r->subrequest_in_memory) {
@@ -414,14 +543,30 @@ ngx_http_fastcgi_handler(ngx_http_reques
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+    f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
+    if (f == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
 
     u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
     if (u == NULL) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    u->schema = flcf->upstream.schema;
+    r->upstream = u;
+
+    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+    if (flcf->fastcgi_lengths) {
+        if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    u->schema.len = sizeof("fastcgi://") - 1;
+    u->schema.data = (u_char *) "fastcgi://";
 
     u->peer.log = r->connection->log;
     u->peer.log_error = NGX_ERROR_ERR;
@@ -433,6 +578,9 @@ ngx_http_fastcgi_handler(ngx_http_reques
 
     u->conf = &flcf->upstream;
 
+#if (NGX_HTTP_CACHE)
+    u->create_key = ngx_http_fastcgi_create_key;
+#endif
     u->create_request = ngx_http_fastcgi_create_request;
     u->reinit_request = ngx_http_fastcgi_reinit_request;
     u->process_header = ngx_http_fastcgi_process_header;
@@ -449,8 +597,6 @@ ngx_http_fastcgi_handler(ngx_http_reques
     u->pipe->input_filter = ngx_http_fastcgi_input_filter;
     u->pipe->input_ctx = r;
 
-    r->upstream = u;
-
     rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
 
     if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
@@ -462,6 +608,83 @@ ngx_http_fastcgi_handler(ngx_http_reques
 
 
 static ngx_int_t
+ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
+{
+    ngx_url_t  u;
+
+    ngx_memzero(&u, sizeof(ngx_url_t));
+
+    if (ngx_http_script_run(r, &u.url, flcf->fastcgi_lengths->elts, 0,
+                            flcf->fastcgi_values->elts)
+        == NULL)
+    {
+        return NGX_ERROR;
+    }
+
+    u.no_resolve = 1;
+
+    if (ngx_parse_url(r->pool, &u) != NGX_OK) {
+         if (u.err) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "%s in upstream \"%V\"", u.err, &u.url);
+        }
+
+        return NGX_ERROR;
+    }
+
+    if (u.no_port) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "no port in upstream \"%V\"", &u.url);
+        return NGX_ERROR;
+    }
+
+    r->upstream->resolved = ngx_pcalloc(r->pool,
+                                        sizeof(ngx_http_upstream_resolved_t));
+    if (r->upstream->resolved == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (u.addrs && u.addrs[0].sockaddr) {
+        r->upstream->resolved->sockaddr = u.addrs[0].sockaddr;
+        r->upstream->resolved->socklen = u.addrs[0].socklen;
+        r->upstream->resolved->naddrs = 1;
+        r->upstream->resolved->host = u.addrs[0].name;
+
+    } else {
+        r->upstream->resolved->host = u.host;
+        r->upstream->resolved->port = u.port;
+    }
+
+    return NGX_OK;
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_fastcgi_create_key(ngx_http_request_t *r)
+{
+    ngx_str_t                    *key;
+    ngx_http_fastcgi_loc_conf_t  *flcf;
+
+    key = ngx_array_push(&r->cache->keys);
+    if (key == NULL) {
+        return NGX_ERROR;
+    }
+
+    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+    if (ngx_http_complex_value(r, &flcf->cache_key, key) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
 ngx_http_fastcgi_create_request(ngx_http_request_t *r)
 {
     off_t                         file_pos;
@@ -867,15 +1090,6 @@ ngx_http_fastcgi_process_header(ngx_http
 
     umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
 
-    if (f == NULL) {
-        f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
-        if (f == NULL) {
-            return NGX_ERROR;
-        }
-
-        ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
-    }
-
     u = r->upstream;
 
     for ( ;; ) {
@@ -1177,12 +1391,9 @@ ngx_http_fastcgi_process_header(ngx_http
                     u->headers_in.status_line.data = (u_char *) "200 OK";
                 }
 
-                u->state->status = u->headers_in.status_n;
-#if 0
-                if (u->cacheable) {
-                    u->cacheable = ngx_http_upstream_is_cacheable(r);
+                if (u->state) {
+                    u->state->status = u->headers_in.status_n;
                 }
-#endif
 
                 break;
             }
@@ -1600,16 +1811,18 @@ ngx_http_fastcgi_finalize_request(ngx_ht
 static ngx_int_t
 ngx_http_fastcgi_add_variables(ngx_conf_t *cf)
 {
-    ngx_http_variable_t  *var;
-
-    var = ngx_http_add_variable(cf, &ngx_http_fastcgi_script_name,
-                                NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE);
-    if (var == NULL) {
-        return NGX_ERROR;
+   ngx_http_variable_t  *var, *v;
+
+    for (v = ngx_http_fastcgi_vars; v->name.len; v++) {
+        var = ngx_http_add_variable(cf, &v->name, v->flags);
+        if (var == NULL) {
+            return NGX_ERROR;
+        }
+
+        var->get_handler = v->get_handler;
+        var->data = v->data;
     }
 
-    var->get_handler = ngx_http_fastcgi_script_name_variable;
-
     return NGX_OK;
 }
 
@@ -1621,17 +1834,19 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
      * set by ngx_pcalloc():
      *
      *     conf->upstream.bufs.num = 0;
+     *     conf->upstream.ignore_headers = 0;
      *     conf->upstream.next_upstream = 0;
+     *     conf->upstream.cache_use_stale = 0;
+     *     conf->upstream.cache_methods = 0;
      *     conf->upstream.temp_path = NULL;
      *     conf->upstream.hide_headers_hash = { NULL, 0 };
-     *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
      *     conf->upstream.store_lengths = NULL;
@@ -1660,6 +1875,12 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
     conf->upstream.pass_request_headers = NGX_CONF_UNSET;
     conf->upstream.pass_request_body = NGX_CONF_UNSET;
 
+#if (NGX_HTTP_CACHE)
+    conf->upstream.cache = NGX_CONF_UNSET_PTR;
+    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
+    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
+#endif
+
     conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
     conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
 
@@ -1691,7 +1912,7 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
 
     if (conf->upstream.store != 0) {
         ngx_conf_merge_value(conf->upstream.store,
-                                  prev->upstream.store, 0);
+                              prev->upstream.store, 0);
 
         if (conf->upstream.store_lengths == NULL) {
             conf->upstream.store_lengths = prev->upstream.store_lengths;
@@ -1817,6 +2038,11 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
     }
 
 
+    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
+                              prev->upstream.ignore_headers,
+                              NGX_CONF_BITMASK_SET);
+
+
     ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
                               prev->upstream.next_upstream,
                               (NGX_CONF_BITMASK_SET
@@ -1828,10 +2054,58 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
                                        |NGX_HTTP_UPSTREAM_FT_OFF;
     }
 
-    ngx_conf_merge_path_value(conf->upstream.temp_path,
+    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
                               prev->upstream.temp_path,
-                              NGX_HTTP_FASTCGI_TEMP_PATH, 1, 2, 0,
-                              ngx_garbage_collector_temp_handler, cf);
+                              &ngx_http_fastcgi_temp_path)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+#if (NGX_HTTP_CACHE)
+
+    ngx_conf_merge_ptr_value(conf->upstream.cache,
+                              prev->upstream.cache, NULL);
+
+    if (conf->upstream.cache && conf->upstream.cache->data == NULL) {
+        ngx_shm_zone_t  *shm_zone;
+
+        shm_zone = conf->upstream.cache;
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"fastcgi_cache\" zone \"%V\" is unknown",
+                           &shm_zone->shm.name);
+
+        return NGX_CONF_ERROR;
+    }
+
+    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
+                              prev->upstream.cache_min_uses, 1);
+
+    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
+                              prev->upstream.cache_use_stale,
+                              (NGX_CONF_BITMASK_SET
+                               |NGX_HTTP_UPSTREAM_FT_OFF));
+
+    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
+        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
+                                         |NGX_HTTP_UPSTREAM_FT_OFF;
+    }
+
+    if (conf->upstream.cache_methods == 0) {
+        conf->upstream.cache_methods = prev->upstream.cache_methods;
+    }
+
+    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
+
+    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
+                             prev->upstream.cache_valid, NULL);
+
+    if (conf->cache_key.value.data == NULL) {
+        conf->cache_key = prev->cache_key;
+    }
+
+#endif
 
     ngx_conf_merge_value(conf->upstream.pass_request_headers,
                               prev->upstream.pass_request_headers, 1);
@@ -1861,9 +2135,20 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
 
     if (conf->upstream.upstream == NULL) {
         conf->upstream.upstream = prev->upstream.upstream;
-        conf->upstream.schema = prev->upstream.schema;
+    }
+
+    if (conf->fastcgi_lengths == NULL) {
+        conf->fastcgi_lengths = prev->fastcgi_lengths;
+        conf->fastcgi_values = prev->fastcgi_values;
     }
 
+#if (NGX_PCRE)
+    if (conf->split_regex == NULL) {
+        conf->split_regex = prev->split_regex;
+        conf->split_name = prev->split_name;
+    }
+#endif
+
     if (conf->params_source == NULL) {
         conf->flushes = prev->flushes;
         conf->params_len = prev->params_len;
@@ -2002,77 +2287,194 @@ ngx_http_fastcgi_script_name_variable(ng
     ngx_http_variable_value_t *v, uintptr_t data)
 {
     u_char                       *p;
+    ngx_http_fastcgi_ctx_t       *f;
     ngx_http_fastcgi_loc_conf_t  *flcf;
 
-    if (r->uri.len) {
+    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+    f = ngx_http_fastcgi_split(r, flcf);
+
+    if (f == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (f->script_name.len == 0
+        || f->script_name.data[f->script_name.len - 1] != '/')
+    {
+        v->len = f->script_name.len;
         v->valid = 1;
         v->no_cacheable = 0;
         v->not_found = 0;
-
-        flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
-
-        if (r->uri.data[r->uri.len - 1] != '/') {
-            v->len = r->uri.len;
-            v->data = r->uri.data;
-            return NGX_OK;
-        }
-
-        v->len = r->uri.len + flcf->index.len;
-
-        v->data = ngx_pnalloc(r->pool, v->len);
-        if (v->data == NULL) {
-            return NGX_ERROR;
-        }
-
-        p = ngx_copy(v->data, r->uri.data, r->uri.len);
-        ngx_memcpy(p, flcf->index.data, flcf->index.len);
-
-    } else {
-        v->len = 0;
-        v->valid = 1;
-        v->no_cacheable = 0;
-        v->not_found = 0;
-        v->data = NULL;
+        v->data = f->script_name.data;
 
         return NGX_OK;
     }
 
+    v->len = f->script_name.len + flcf->index.len;
+
+    v->data = ngx_pnalloc(r->pool, v->len);
+    if (v->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    p = ngx_copy(v->data, f->script_name.data, f->script_name.len);
+    ngx_memcpy(p, flcf->index.data, flcf->index.len);
+
     return NGX_OK;
 }
 
 
+static ngx_int_t
+ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_http_fastcgi_ctx_t       *f;
+    ngx_http_fastcgi_loc_conf_t  *flcf;
+
+    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+    f = ngx_http_fastcgi_split(r, flcf);
+
+    if (f == NULL) {
+        return NGX_ERROR;
+    }
+
+    v->len = f->path_info.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = f->path_info.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_http_fastcgi_ctx_t *
+ngx_http_fastcgi_split(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
+{
+    ngx_http_fastcgi_ctx_t       *f;
+#if (NGX_PCRE)
+    ngx_int_t                     n;
+    int                           captures[(1 + 2) * 3];
+
+    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
+
+    if (f == NULL) {
+        f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
+        if (f == NULL) {
+            return NULL;
+        }
+
+        ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
+    }
+
+    if (f->script_name.len) {
+        return f;
+    }
+
+    if (flcf->split_regex == NULL) {
+        f->script_name = r->uri;
+        return f;
+    }
+
+    n = ngx_regex_exec(flcf->split_regex, &r->uri, captures, (1 + 2) * 3);
+
+    if (n == NGX_REGEX_NO_MATCHED) {
+        f->script_name = r->uri;
+        return f;
+    }
+
+    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, &flcf->split_name);
+        return NULL;
+    }
+
+    /* match */
+
+    f->script_name.len = captures[3] - captures[2];
+    f->script_name.data = r->uri.data;
+
+    f->path_info.len = captures[5] - captures[4];
+    f->path_info.data = r->uri.data + f->script_name.len;
+
+    return f;
+
+#else
+
+    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
+
+    if (f == NULL) {
+        f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
+        if (f == NULL) {
+            return NULL;
+        }
+
+        ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
+    }
+
+    f->script_name = r->uri;
+
+    return f;
+
+#endif
+}
+
+
 static char *
 ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    ngx_http_fastcgi_loc_conf_t *lcf = conf;
-
-    ngx_url_t                    u;
-    ngx_str_t                   *value;
-    ngx_http_core_loc_conf_t    *clcf;
-
-    if (lcf->upstream.schema.len) {
+    ngx_http_fastcgi_loc_conf_t *flcf = conf;
+
+    ngx_url_t                   u;
+    ngx_str_t                  *value, *url;
+    ngx_uint_t                  n;
+    ngx_http_core_loc_conf_t   *clcf;
+    ngx_http_script_compile_t   sc;
+
+    if (flcf->upstream.upstream || flcf->fastcgi_lengths) {
         return "is duplicate";
     }
 
+    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+    clcf->handler = ngx_http_fastcgi_handler;
+
     value = cf->args->elts;
 
+    url = &value[1];
+
+    n = ngx_http_script_variables_count(url);
+
+    if (n) {
+
+        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+        sc.cf = cf;
+        sc.source = url;
+        sc.lengths = &flcf->fastcgi_lengths;
+        sc.values = &flcf->fastcgi_values;
+        sc.variables = n;
+        sc.complete_lengths = 1;
+        sc.complete_values = 1;
+
+        if (ngx_http_script_compile(&sc) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+        return NGX_CONF_OK;
+    }
+
     ngx_memzero(&u, sizeof(ngx_url_t));
 
     u.url = value[1];
     u.no_resolve = 1;
 
-    lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
-    if (lcf->upstream.upstream == NULL) {
+    flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+    if (flcf->upstream.upstream == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    lcf->upstream.schema.len = sizeof("fastcgi://") - 1;
-    lcf->upstream.schema.data = (u_char *) "fastcgi://";
-
-    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
-
-    clcf->handler = ngx_http_fastcgi_handler;
-
     if (clcf->name.data[clcf->name.len - 1] == '/') {
         clcf->auto_redirect = 1;
     }
@@ -2082,6 +2484,57 @@ ngx_http_fastcgi_pass(ngx_conf_t *cf, ng
 
 
 static char *
+ngx_http_fastcgi_split_path_info(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#if (NGX_PCRE)
+    ngx_http_fastcgi_loc_conf_t *flcf = conf;
+
+    ngx_int_t   n;
+    ngx_str_t  *value, err;
+    u_char      errstr[NGX_MAX_CONF_ERRSTR];
+
+    value = cf->args->elts;
+
+    flcf->split_name = value[1];
+
+    err.len = NGX_MAX_CONF_ERRSTR;
+    err.data = errstr;
+
+    flcf->split_regex = ngx_regex_compile(&value[1], 0, cf->pool, &err);
+
+    if (flcf->split_regex == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+        return NGX_CONF_ERROR;
+    }
+
+    n = ngx_regex_capture_count(flcf->split_regex);
+
+    if (n < 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           ngx_regex_capture_count_n " failed for "
+                           "pattern \"%V\"", &value[1]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (n != 2) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "pattern \"%V\" must have 2 captures", &value[1]);
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+
+#else
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "\"%V\" requires PCRE library", &cmd->name);
+    return NGX_CONF_ERROR;
+
+#endif
+}
+
+
+static char *
 ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_fastcgi_loc_conf_t *flcf = conf;
@@ -2089,20 +2542,31 @@ ngx_http_fastcgi_store(ngx_conf_t *cf, n
     ngx_str_t                  *value;
     ngx_http_script_compile_t   sc;
 
-    if (flcf->upstream.store != NGX_CONF_UNSET || flcf->upstream.store_lengths)
+    if (flcf->upstream.store != NGX_CONF_UNSET
+        || flcf->upstream.store_lengths)
     {
         return "is duplicate";
     }
 
     value = cf->args->elts;
 
-    if (ngx_strcmp(value[1].data, "on") == 0) {
-        flcf->upstream.store = 1;
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        flcf->upstream.store = 0;
         return NGX_CONF_OK;
     }
 
-    if (ngx_strcmp(value[1].data, "off") == 0) {
-        flcf->upstream.store = 0;
+#if (NGX_HTTP_CACHE)
+
+    if (flcf->upstream.cache != NGX_CONF_UNSET_PTR
+        && flcf->upstream.cache != NULL)
+    {
+        return "is incompatible with \"fastcgi_cache\"";
+    }
+
+#endif
+
+    if (ngx_strcmp(value[1].data, "on") == 0) {
+        flcf->upstream.store = 1;
         return NGX_CONF_OK;
     }
 
@@ -2127,6 +2591,70 @@ ngx_http_fastcgi_store(ngx_conf_t *cf, n
 }
 
 
+#if (NGX_HTTP_CACHE)
+
+static char *
+ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_fastcgi_loc_conf_t *flcf = conf;
+
+    ngx_str_t  *value;
+
+    value = cf->args->elts;
+
+    if (flcf->upstream.cache != NGX_CONF_UNSET_PTR) {
+        return "is duplicate";
+    }
+
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        flcf->upstream.cache = NULL;
+        return NGX_CONF_OK;
+    }
+
+    if (flcf->upstream.store > 0 || flcf->upstream.store_lengths) {
+        return "is incompatible with \"fastcgi_store\"";
+    }
+
+    flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
+                                                 &ngx_http_fastcgi_module);
+    if (flcf->upstream.cache == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_fastcgi_loc_conf_t *flcf = conf;
+
+    ngx_str_t                         *value;
+    ngx_http_compile_complex_value_t   ccv;
+
+    value = cf->args->elts;
+
+    if (flcf->cache_key.value.len) {
+        return "is duplicate";
+    }
+
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[1];
+    ccv.complex_value = &flcf->cache_key;
+
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+#endif
+
+
 static char *
 ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data)
 {
--- a/src/http/modules/ngx_http_flv_module.c
+++ b/src/http/modules/ngx_http_flv_module.c
@@ -60,12 +60,12 @@ ngx_module_t  ngx_http_flv_module = {
 static ngx_int_t
 ngx_http_flv_handler(ngx_http_request_t *r)
 {
-    u_char                    *p, *n, *last;
+    u_char                    *last;
     off_t                      start, len;
     size_t                     root;
     ngx_int_t                  rc;
     ngx_uint_t                 level, i;
-    ngx_str_t                  path;
+    ngx_str_t                  path, value;
     ngx_log_t                 *log;
     ngx_buf_t                 *b;
     ngx_chain_t                out[2];
@@ -80,7 +80,6 @@ ngx_http_flv_handler(ngx_http_request_t 
         return NGX_DECLINED;
     }
 
-    /* TODO: Win32 */
     if (r->zero_in_uri) {
         return NGX_DECLINED;
     }
@@ -144,7 +143,7 @@ ngx_http_flv_handler(ngx_http_request_t 
 
         if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
             ngx_log_error(level, log, of.err,
-                          ngx_open_file_n " \"%s\" failed", path.data);
+                          "%s \"%s\" failed", of.failed, path.data);
         }
 
         return rc;
@@ -160,25 +159,17 @@ ngx_http_flv_handler(ngx_http_request_t 
         return NGX_DECLINED;
     }
 
-    r->root_tested = 1;
+    r->root_tested = !r->error_page;
 
     start = 0;
     len = of.size;
     i = 1;
 
     if (r->args.len) {
-        p = (u_char *) ngx_strnstr(r->args.data, "start=", r->args.len);
-
-        if (p) {
-            p += 6;
 
-            for (n = p; n < r->args.data + r->args.len; n++) {
-                if (*n == '&') {
-                    break;
-                }
-            }
+        if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
 
-            start = ngx_atoof(p, n - p);
+            start = ngx_atoof(value.data, value.len);
 
             if (start == NGX_ERROR || start >= len) {
                 start = 0;
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -10,20 +10,66 @@
 
 
 typedef struct {
-    ngx_radix_tree_t  *tree;
-    ngx_pool_t        *pool;
-    ngx_array_t        values;
+    u_short                          start;
+    u_short                          end;
+    ngx_http_variable_value_t       *value;
+} ngx_http_geo_range_t;
+
+
+typedef struct {
+    ngx_http_geo_range_t            *ranges;
+    ngx_uint_t                       n;
+} ngx_http_geo_low_ranges_t;
+
+
+typedef struct {
+    ngx_http_geo_low_ranges_t        low[0x10000];
+    ngx_http_variable_value_t       *default_value;
+} ngx_http_geo_high_ranges_t;
+
+
+typedef struct {
+    ngx_http_variable_value_t       *value;
+    ngx_str_t                       *net;
+    ngx_http_geo_high_ranges_t      *high;
+    ngx_radix_tree_t                *tree;
+    ngx_rbtree_t                     rbtree;
+    ngx_rbtree_node_t                sentinel;
+    ngx_pool_t                      *pool;
+    ngx_pool_t                      *temp_pool;
 } ngx_http_geo_conf_ctx_t;
 
 
+typedef struct {
+    union {
+        ngx_radix_tree_t            *tree;
+        ngx_http_geo_high_ranges_t  *high;
+    } u;
+
+    ngx_int_t                        index;
+} ngx_http_geo_ctx_t;
+
+
+static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r,
+    ngx_http_geo_ctx_t *ctx);
 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
+static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *value);
+static char *ngx_http_geo_add_range(ngx_conf_t *cf,
+    ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
+static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
+    ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
+static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *value);
+static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
+    ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
 
 
 static ngx_command_t  ngx_http_geo_commands[] = {
 
     { ngx_string("geo"),
-      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
+      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
       ngx_http_geo_block,
       NGX_HTTP_MAIN_CONF_OFFSET,
       0,
@@ -67,53 +113,130 @@ ngx_module_t  ngx_http_geo_module = {
 /* AF_INET only */
 
 static ngx_int_t
-ngx_http_geo_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
     uintptr_t data)
 {
-    ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data;
+    ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
 
-    struct sockaddr_in         *sin;
     ngx_http_variable_value_t  *vv;
 
-    sin = (struct sockaddr_in *) r->connection->sockaddr;
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http geo started");
-
     vv = (ngx_http_variable_value_t *)
-                       ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr));
+              ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx));
 
     *v = *vv;
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http geo: %V %v", &r->connection->addr_text, v);
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http geo: %v", v);
 
     return NGX_OK;
 }
 
 
+static ngx_int_t
+ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
+
+    in_addr_t              addr;
+    ngx_uint_t             i, n;
+    ngx_http_geo_range_t  *range;
+
+    *v = *ctx->u.high->default_value;
+
+    addr = ngx_http_geo_addr(r, ctx);
+
+    range = ctx->u.high->low[addr >> 16].ranges;
+
+    n = addr & 0xffff;
+
+    for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) {
+        if (n >= (ngx_uint_t) range[i].start
+            && n <= (ngx_uint_t) range[i].end)
+        {
+            *v = *range[i].value;
+        }
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http geo: %v", v);
+
+    return NGX_OK;
+}
+
+
+static in_addr_t
+ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx)
+{
+    struct sockaddr_in         *sin;
+    ngx_http_variable_value_t  *v;
+
+    if (ctx->index == -1) {
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http geo started: %V", &r->connection->addr_text);
+
+        if (r->connection->sockaddr->sa_family != AF_INET) {
+            return 0;
+        }
+
+        sin = (struct sockaddr_in *) r->connection->sockaddr;
+        return ntohl(sin->sin_addr.s_addr);
+    }
+
+    v = ngx_http_get_flushed_variable(r, ctx->index);
+
+    if (v == NULL || v->not_found) {
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http geo not found");
+
+        return 0;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http geo started: %v", v);
+
+    return ntohl(ngx_inet_addr(v->data, v->len));
+}
+
+
 static char *
 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     char                     *rv;
+    size_t                    len;
     ngx_str_t                *value, name;
+    ngx_uint_t                i;
     ngx_conf_t                save;
     ngx_pool_t               *pool;
-    ngx_radix_tree_t         *tree;
+    ngx_array_t              *a;
     ngx_http_variable_t      *var;
+    ngx_http_geo_ctx_t       *geo;
     ngx_http_geo_conf_ctx_t   ctx;
 
     value = cf->args->elts;
 
+    geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
+    if (geo == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
     name = value[1];
+    name.len--;
+    name.data++;
 
-    if (name.data[0] != '$') {
-        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                           "\"%V\" variable name should start with '$'",
-                           &value[1]);
-    } else {
+    if (cf->args->nelts == 3) {
+
+        geo->index = ngx_http_get_variable_index(cf, &name);
+        if (geo->index == NGX_ERROR) {
+            return NGX_CONF_ERROR;
+        }
+
+        name = value[2];
         name.len--;
         name.data++;
+
+    } else {
+        geo->index = -1;
     }
 
     var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
@@ -121,29 +244,21 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
         return NGX_CONF_ERROR;
     }
 
-    tree = ngx_radix_tree_create(cf->pool, -1);
-
-    if (tree == NULL) {
-        return NGX_CONF_ERROR;
-    }
-
-    var->get_handler = ngx_http_geo_variable;
-    var->data = (uintptr_t) tree;
-
     pool = ngx_create_pool(16384, cf->log);
     if (pool == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    if (ngx_array_init(&ctx.values, pool, 512,
-                       sizeof(ngx_http_variable_value_t *))
-        != NGX_OK)
-    {
-        ngx_destroy_pool(pool);
+    ctx.temp_pool = ngx_create_pool(16384, cf->log);
+    if (ctx.temp_pool == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    ctx.tree = tree;
+    ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel,
+                    ngx_http_variable_value_rbtree_insert);
+
+    ctx.high = NULL;
+    ctx.tree = NULL;
     ctx.pool = cf->pool;
 
     save = *cf;
@@ -156,121 +271,589 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
 
     *cf = save;
 
-    ngx_destroy_pool(pool);
+    if (ctx.high) {
+
+        for (i = 0; i < 0x10000; i++) {
+            a = (ngx_array_t *) ctx.high->low[i].ranges;
+
+            if (a == NULL || a->nelts == 0) {
+                continue;
+            }
+
+            ctx.high->low[i].n = a->nelts;
+
+            len = a->nelts * sizeof(ngx_http_geo_range_t);
 
-    if (ngx_radix32tree_find(tree, 0) != NGX_RADIX_NO_VALUE) {
-        return rv;
-    }
+            ctx.high->low[i].ranges = ngx_palloc(cf->pool, len);
+            if (ctx.high->low[i].ranges == NULL ){
+                return NGX_CONF_ERROR;
+            }
+
+            ngx_memcpy(ctx.high->low[i].ranges, a->elts, len);
+        }
+
+        geo->u.high = ctx.high;
+
+        var->get_handler = ngx_http_geo_range_variable;
+        var->data = (uintptr_t) geo;
+
+        ngx_destroy_pool(ctx.temp_pool);
+        ngx_destroy_pool(pool);
 
-    if (ngx_radix32tree_insert(tree, 0, 0,
-                               (uintptr_t) &ngx_http_variable_null_value)
-        == NGX_ERROR)
-    {
-        return NGX_CONF_ERROR;
+        if (ctx.high->default_value == NULL) {
+            ctx.high->default_value = &ngx_http_variable_null_value;
+        }
+
+    } else {
+        if (ctx.tree == NULL) {
+            ctx.tree = ngx_radix_tree_create(cf->pool, -1);
+            if (ctx.tree == NULL) {
+                return NGX_CONF_ERROR;
+            }
+        }
+
+        geo->u.tree = ctx.tree;
+
+        var->get_handler = ngx_http_geo_cidr_variable;
+        var->data = (uintptr_t) geo;
+
+        ngx_destroy_pool(ctx.temp_pool);
+        ngx_destroy_pool(pool);
+
+        if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) {
+            return rv;
+        }
+
+        if (ngx_radix32tree_insert(ctx.tree, 0, 0,
+                                   (uintptr_t) &ngx_http_variable_null_value)
+            == NGX_ERROR)
+        {
+            return NGX_CONF_ERROR;
+        }
     }
 
     return rv;
 }
 
 
-/* AF_INET only */
-
 static char *
 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
 {
-    ngx_int_t                   rc;
-    ngx_str_t                  *value, file;
-    ngx_uint_t                  i;
-    ngx_inet_cidr_t             cidrin;
-    ngx_http_geo_conf_ctx_t    *ctx;
-    ngx_http_variable_value_t  *var, *old, **v;
+    char                     *rv;
+    ngx_str_t                *value, file;
+    ngx_http_geo_conf_ctx_t  *ctx;
 
     ctx = cf->ctx;
 
+    value = cf->args->elts;
+
+    if (cf->args->nelts == 1) {
+
+        if (ngx_strcmp(value[0].data, "ranges") == 0) {
+
+            if (ctx->tree) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "the \"ranges\" directive must be "
+                                   "the first directive inside \"geo\" block");
+                goto failed;
+            }
+
+            ctx->high = ngx_pcalloc(ctx->pool,
+                                    sizeof(ngx_http_geo_high_ranges_t));
+            if (ctx->high == NULL) {
+                goto failed;
+            }
+
+            rv = NGX_CONF_OK;
+
+            goto done;
+        }
+    }
+
     if (cf->args->nelts != 2) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid number of the geo parameters");
-        return NGX_CONF_ERROR;
+        goto failed;
     }
 
-    value = cf->args->elts;
+    if (ngx_strcmp(value[0].data, "include") == 0) {
+
+        file.len = value[1].len++;
 
-    if (ngx_strcmp(value[0].data, "include") == 0) {
-        file = value[1];
+        file.data = ngx_pstrdup(ctx->temp_pool, &value[1]);
+        if (file.data == NULL) {
+            goto failed;
+        }
 
-        if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){
-            return NGX_CONF_ERROR;
+        if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){
+            goto failed;
         }
 
         ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
 
-        return ngx_conf_parse(cf, &file);
+        rv = ngx_conf_parse(cf, &file);
+
+        goto done;
+    }
+
+    if (ctx->high) {
+        rv = ngx_http_geo_range(cf, ctx, value);
+
+    } else {
+        rv = ngx_http_geo_cidr(cf, ctx, value);
     }
 
+done:
+
+    ngx_reset_pool(cf->pool);
+
+    return rv;
+
+failed:
+
+    ngx_reset_pool(cf->pool);
+
+    return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *value)
+{
+    u_char                     *p, *last;
+    in_addr_t                   start, end;
+    ngx_str_t                  *net;
+    ngx_uint_t                  del;
+    ngx_http_variable_value_t  *old;
+
     if (ngx_strcmp(value[0].data, "default") == 0) {
-        cidrin.addr = 0;
-        cidrin.mask = 0;
+
+        old = ctx->high->default_value;
 
-    } else {
-        rc = ngx_ptocidr(&value[0], &cidrin);
-
-        if (rc == NGX_ERROR) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "invalid parameter \"%V\"", &value[0]);
+        ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]);
+        if (ctx->high->default_value == NULL) {
             return NGX_CONF_ERROR;
         }
 
-        if (rc == NGX_DONE) {
+        if (old) {
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                    "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
+                    &value[0], ctx->high->default_value, old);
+        }
+
+        return NGX_CONF_OK;
+    }
+
+    if (ngx_strcmp(value[0].data, "delete") == 0) {
+        net = &value[1];
+        del = 1;
+
+    } else {
+        net = &value[0];
+        del = 0;
+    }
+
+    last = net->data + net->len;
+
+    p = ngx_strlchr(net->data, last, '-');
+
+    if (p == NULL) {
+        goto invalid;
+    }
+
+    start = ngx_inet_addr(net->data, p - net->data);
+
+    if (start == INADDR_NONE) {
+        goto invalid;
+    }
+
+    start = ntohl(start);
+
+    p++;
+
+    end = ngx_inet_addr(p, last - p);
+
+    if (end == INADDR_NONE) {
+        goto invalid;
+    }
+
+    end = ntohl(end);
+
+    if (start > end) {
+        goto invalid;
+    }
+
+    if (del) {
+        if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                               "low address bits of %V are meaningless",
-                               &value[0]);
+                               "no address range \"%V\" to delete", net);
+        }
+
+        return NGX_CONF_OK;
+    }
+
+    ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);
+
+    if (ctx->value == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    ctx->net = net;
+
+    return ngx_http_geo_add_range(cf, ctx, start, end);
+
+invalid:
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);
+
+    return NGX_CONF_ERROR;
+}
+
+
+/* the add procedure is optimized to add a growing up sequence */
+
+static char *
+ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    in_addr_t start, in_addr_t end)
+{
+    in_addr_t              n;
+    ngx_uint_t             h, i, s, e;
+    ngx_array_t           *a;
+    ngx_http_geo_range_t  *range;
+
+    for (n = start; n <= end; n += 0x10000) {
+
+        h = n >> 16;
+
+        if (n == start) {
+            s = n & 0xffff;
+        } else {
+            s = 0;
+        }
+
+        if ((n | 0xffff) > end) {
+            e = end & 0xffff;
+
+        } else {
+            e = 0xffff;
+        }
+
+        a = (ngx_array_t *) ctx->high->low[h].ranges;
+
+        if (a == NULL) {
+            a = ngx_array_create(ctx->temp_pool, 64,
+                                 sizeof(ngx_http_geo_range_t));
+            if (a == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a;
         }
 
-        cidrin.addr = ntohl(cidrin.addr);
-        cidrin.mask = ntohl(cidrin.mask);
+        i = a->nelts;
+        range = a->elts;
+
+        while (i) {
+
+            i--;
+
+            if (e < (ngx_uint_t) range[i].start) {
+                continue;
+            }
+
+            if (s > (ngx_uint_t) range[i].end) {
+
+                /* add after the range */
+
+                range = ngx_array_push(a);
+                if (range == NULL) {
+                    return NGX_CONF_ERROR;
+                }
+
+                range = a->elts;
+
+                ngx_memcpy(&range[i + 2], &range[i + 1],
+                           (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
+
+                range[i + 1].start = (u_short) s;
+                range[i + 1].end = (u_short) e;
+                range[i + 1].value = ctx->value;
+
+                goto next;
+            }
+
+            if (s == (ngx_uint_t) range[i].start
+                && e == (ngx_uint_t) range[i].end)
+            {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                    "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
+                    ctx->net, ctx->value, range[i].value);
+
+                range[i].value = ctx->value;
+
+                goto next;
+            }
+
+            if (s > (ngx_uint_t) range[i].start
+                && e < (ngx_uint_t) range[i].end)
+            {
+                /* split the range and insert the new one */
+
+                range = ngx_array_push(a);
+                if (range == NULL) {
+                    return NGX_CONF_ERROR;
+                }
+
+                range = ngx_array_push(a);
+                if (range == NULL) {
+                    return NGX_CONF_ERROR;
+                }
+
+                range = a->elts;
+
+                ngx_memcpy(&range[i + 3], &range[i + 1],
+                           (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t));
+
+                range[i + 2].start = (u_short) (e + 1);
+                range[i + 2].end = range[i].end;
+                range[i + 2].value = range[i].value;
+
+                range[i + 1].start = (u_short) s;
+                range[i + 1].end = (u_short) e;
+                range[i + 1].value = ctx->value;
+
+                range[i].end = (u_short) (s - 1);
+
+                goto next;
+            }
+
+            if (s == (ngx_uint_t) range[i].start
+                && e < (ngx_uint_t) range[i].end)
+            {
+                /* shift the range start and insert the new range */
+
+                range = ngx_array_push(a);
+                if (range == NULL) {
+                    return NGX_CONF_ERROR;
+                }
+
+                range = a->elts;
+
+                ngx_memcpy(&range[i + 1], &range[i],
+                           (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
+
+                range[i + 1].start = (u_short) (e + 1);
+
+                range[i].start = (u_short) s;
+                range[i].end = (u_short) e;
+                range[i].value = ctx->value;
+
+                goto next;
+            }
+
+            if (s > (ngx_uint_t) range[i].start
+                && e == (ngx_uint_t) range[i].end)
+            {
+                /* shift the range end and insert the new range */
+
+                range = ngx_array_push(a);
+                if (range == NULL) {
+                    return NGX_CONF_ERROR;
+                }
+
+                range = a->elts;
+
+                ngx_memcpy(&range[i + 2], &range[i + 1],
+                           (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
+
+                range[i + 1].start = (u_short) s;
+                range[i + 1].end = (u_short) e;
+                range[i + 1].value = ctx->value;
+
+                range[i].end = (u_short) (s - 1);
+
+                goto next;
+            }
+
+            s = (ngx_uint_t) range[i].start;
+            e = (ngx_uint_t) range[i].end;
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                         "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
+                         ctx->net,
+                         h >> 8, h & 0xff, s >> 8, s & 0xff,
+                         h >> 8, h & 0xff, e >> 8, e & 0xff);
+
+            return NGX_CONF_ERROR;
+        }
+
+        /* add the first range */
+
+        range = ngx_array_push(a);
+        if (range == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        range->start = (u_short) s;
+        range->end = (u_short) e;
+        range->value = ctx->value;
+
+    next:
+
+        continue;
     }
 
-    var = NULL;
-    v = ctx->values.elts;
+    return NGX_CONF_OK;
+}
+
+
+static ngx_uint_t
+ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    in_addr_t start, in_addr_t end)
+{
+    in_addr_t              n;
+    ngx_uint_t             h, i, s, e, warn;
+    ngx_array_t           *a;
+    ngx_http_geo_range_t  *range;
+
+    warn = 0;
+
+    for (n = start; n <= end; n += 0x10000) {
 
-    for (i = 0; i < ctx->values.nelts; i++) {
-        if ((size_t) v[i]->len != value[1].len) {
+        h = n >> 16;
+
+        if (n == start) {
+            s = n & 0xffff;
+        } else {
+            s = 0;
+        }
+
+        if ((n | 0xffff) > end) {
+            e = end & 0xffff;
+
+        } else {
+            e = 0xffff;
+        }
+
+        a = (ngx_array_t *) ctx->high->low[h].ranges;
+
+        if (a == NULL) {
+            warn = 1;
             continue;
         }
 
-        if (ngx_strncmp(value[1].data, v[i]->data, value[1].len) == 0) {
-            var = v[i];
-            break;
+        range = a->elts;
+        for (i = 0; i < a->nelts; i++) {
+
+            if (s == (ngx_uint_t) range[i].start
+                && e == (ngx_uint_t) range[i].end)
+            {
+                ngx_memcpy(&range[i], &range[i + 1],
+                           (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
+
+                a->nelts--;
+
+                break;
+            }
+
+            if (s != (ngx_uint_t) range[i].start
+                && e != (ngx_uint_t) range[i].end)
+            {
+                continue;
+            }
+
+            warn = 1;
+        }
+    }
+
+    return warn;
+}
+
+
+static char *
+ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *value)
+{
+    ngx_int_t                        rc, del;
+    ngx_str_t                       *net;
+    ngx_uint_t                       i;
+    ngx_cidr_t                       cidr;
+    ngx_http_variable_value_t       *val, *old;
+
+    if (ctx->tree == NULL) {
+        ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
+        if (ctx->tree == NULL) {
+            return NGX_CONF_ERROR;
         }
     }
 
-    if (var == NULL) {
-        var = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
-        if (var == NULL) {
-            return NGX_CONF_ERROR;
+    if (ngx_strcmp(value[0].data, "default") == 0) {
+        cidr.u.in.addr = 0;
+        cidr.u.in.mask = 0;
+        net = &value[0];
+
+    } else {
+        if (ngx_strcmp(value[0].data, "delete") == 0) {
+            net = &value[1];
+            del = 1;
+
+        } else {
+            net = &value[0];
+            del = 0;
         }
 
-        var->len = value[1].len;
-        var->data = ngx_pstrdup(ctx->pool, &value[1]);
-        if (var->data == NULL) {
-            return NGX_CONF_ERROR;
+        if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
+            cidr.u.in.addr = 0xffffffff;
+            cidr.u.in.mask = 0xffffffff;
+
+        } else {
+            rc = ngx_ptocidr(net, &cidr);
+
+            if (rc == NGX_ERROR) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid network \"%V\"", net);
+                return NGX_CONF_ERROR;
+            }
+
+            if (cidr.family != AF_INET) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "\"geo\" supports IPv4 only");
+                return NGX_CONF_ERROR;
+            }
+
+            if (rc == NGX_DONE) {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                                   "low address bits of %V are meaningless",
+                                   net);
+            }
+
+            cidr.u.in.addr = ntohl(cidr.u.in.addr);
+            cidr.u.in.mask = ntohl(cidr.u.in.mask);
         }
 
-        var->valid = 1;
-        var->no_cacheable = 0;
-        var->not_found = 0;
+        if (del) {
+            if (ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
+                                       cidr.u.in.mask)
+                != NGX_OK)
+            {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                                   "no network \"%V\" to delete", net);
+            }
 
-        v = ngx_array_push(&ctx->values);
-        if (v == NULL) {
-            return NGX_CONF_ERROR;
+            return NGX_CONF_OK;
         }
+    }
 
-        *v = var;
+    val = ngx_http_geo_value(cf, ctx, &value[1]);
+
+    if (val == NULL) {
+        return NGX_CONF_ERROR;
     }
 
     for (i = 2; i; i--) {
-        rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask,
-                                    (uintptr_t) var);
+        rc = ngx_radix32tree_insert(ctx->tree, cidr.u.in.addr, cidr.u.in.mask,
+                                    (uintptr_t) val);
         if (rc == NGX_OK) {
             return NGX_CONF_OK;
         }
@@ -282,18 +865,65 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
         /* rc == NGX_BUSY */
 
         old  = (ngx_http_variable_value_t *)
-                    ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask);
+              ngx_radix32tree_find(ctx->tree, cidr.u.in.addr & cidr.u.in.mask);
 
         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                "duplicate parameter \"%V\", value: \"%v\", old value: \"%v\"",
-                &value[0], var, old);
+                "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
+                net, val, old);
 
-        rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask);
+        rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, cidr.u.in.mask);
 
         if (rc == NGX_ERROR) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
             return NGX_CONF_ERROR;
         }
     }
 
     return NGX_CONF_ERROR;
 }
+
+
+static ngx_http_variable_value_t *
+ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *value)
+{
+    uint32_t                         hash;
+    ngx_http_variable_value_t       *val;
+    ngx_http_variable_value_node_t  *vvn;
+
+    hash = ngx_crc32_long(value->data, value->len);
+
+    val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash);
+
+    if (val) {
+        return val;
+    }
+
+    val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
+    if (val == NULL) {
+        return NULL;
+    }
+
+    val->len = value->len;
+    val->data = ngx_pstrdup(ctx->pool, value);
+    if (val->data == NULL) {
+        return NULL;
+    }
+
+    val->valid = 1;
+    val->no_cacheable = 0;
+    val->not_found = 0;
+
+    vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t));
+    if (vvn == NULL) {
+        return NULL;
+    }
+
+    vvn->node.key = hash;
+    vvn->len = val->len;
+    vvn->value = val;
+
+    ngx_rbtree_insert(&ctx->rbtree, &vvn->node);
+
+    return val;
+}
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -19,6 +19,7 @@ typedef struct {
 
     ngx_bufs_t           bufs;
 
+    size_t               postpone_gzipping;
     ngx_int_t            level;
     size_t               wbits;
     size_t               memlevel;
@@ -34,19 +35,27 @@ typedef struct {
     ngx_chain_t         *busy;
     ngx_chain_t         *out;
     ngx_chain_t        **last_out;
+
+    ngx_chain_t         *copied;
+    ngx_chain_t         *copy_buf;
+
     ngx_buf_t           *in_buf;
     ngx_buf_t           *out_buf;
     ngx_int_t            bufs;
 
-    off_t                length;
-
     void                *preallocated;
     char                *free_mem;
     ngx_uint_t           allocated;
 
+    int                  wbits;
+    int                  memlevel;
+
     unsigned             flush:4;
     unsigned             redo:1;
     unsigned             done:1;
+    unsigned             nomem:1;
+    unsigned             gzheader:1;
+    unsigned             buffering:1;
 
     size_t               zin;
     size_t               zout;
@@ -57,10 +66,45 @@ typedef struct {
 } ngx_http_gzip_ctx_t;
 
 
+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+
+struct gztrailer {
+    uint32_t  crc32;
+    uint32_t  zlen;
+};
+
+#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */
+
+struct gztrailer {
+    u_char  crc32[4];
+    u_char  zlen[4];
+};
+
+#endif
+
+
+static void ngx_http_gzip_filter_memory(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx,
+    ngx_chain_t *in);
+static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+
 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
     u_int size);
 static void ngx_http_gzip_filter_free(void *opaque, void *address);
-static void ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx);
+static void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
 
 static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf);
 static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
@@ -127,6 +171,13 @@ static ngx_command_t  ngx_http_gzip_filt
       offsetof(ngx_http_gzip_conf_t, memlevel),
       &ngx_http_gzip_hash_p },
 
+    { ngx_string("postpone_gzipping"),
+      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_gzip_conf_t, postpone_gzipping),
+      NULL },
+
     { ngx_string("gzip_no_buffer"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -176,25 +227,6 @@ ngx_module_t  ngx_http_gzip_filter_modul
 };
 
 
-static u_char  gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
-
-#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
-
-struct gztrailer {
-    uint32_t  crc32;
-    uint32_t  zlen;
-};
-
-#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */
-
-struct gztrailer {
-    u_char  crc32[4];
-    u_char  zlen[4];
-};
-
-#endif
-
-
 static ngx_str_t  ngx_http_gzip_ratio = ngx_string("gzip_ratio");
 
 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
@@ -233,6 +265,9 @@ ngx_http_gzip_header_filter(ngx_http_req
     ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
 
     ctx->request = r;
+    ctx->buffering = (conf->postpone_gzipping != 0);
+
+    ngx_http_gzip_filter_memory(r, ctx);
 
     h = ngx_list_push(&r->headers_out.headers);
     if (h == NULL) {
@@ -247,8 +282,6 @@ ngx_http_gzip_header_filter(ngx_http_req
 
     r->headers_out.content_encoding = h;
 
-    ctx->length = r->headers_out.content_length_n;
-
     r->main_filter_need_in_memory = 1;
 
     ngx_http_clear_content_length(r);
@@ -261,13 +294,9 @@ ngx_http_gzip_header_filter(ngx_http_req
 static ngx_int_t
 ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    int                    rc, wbits, memlevel;
-    ngx_int_t              last;
-    struct gztrailer      *trailer;
-    ngx_buf_t             *b;
-    ngx_chain_t           *cl, out;
-    ngx_http_gzip_ctx_t   *ctx;
-    ngx_http_gzip_conf_t  *conf;
+    int                   rc;
+    ngx_chain_t          *cl;
+    ngx_http_gzip_ctx_t  *ctx;
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
 
@@ -275,392 +304,633 @@ ngx_http_gzip_body_filter(ngx_http_reque
         return ngx_http_next_body_filter(r, in);
     }
 
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
-
-    if (ctx->preallocated == NULL) {
-        wbits = conf->wbits;
-        memlevel = conf->memlevel;
-
-        if (ctx->length > 0) {
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http gzip filter");
 
-            /* the actual zlib window size is smaller by 262 bytes */
-
-            while (ctx->length < ((1 << (wbits - 1)) - 262)) {
-                wbits--;
-                memlevel--;
-            }
-        }
+    if (ctx->buffering) {
 
         /*
-         * We preallocate a memory for zlib in one buffer (200K-400K), this
-         * decreases a number of malloc() and free() calls and also probably
-         * decreases a number of syscalls (sbrk() and so on).
-         * Besides we free this memory as soon as the gzipping will complete
-         * and do not wait while a whole response will be sent to a client.
-         *
-         * 8K is for zlib deflate_state, it takes
-         *  *) 5816 bytes on i386 and sparc64 (32-bit mode)
-         *  *) 5920 bytes on amd64 and sparc64
+         * With default memory settings zlib starts to output gzipped data
+         * only after it has got about 90K, so it makes sense to allocate
+         * zlib memory (200-400K) only after we have enough data to compress.
+         * Although we copy buffers, nevertheless for not big responses
+         * this allows to allocate zlib memory, to compress and to output
+         * the response in one step using hot CPU cache.
          */
 
-        ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
-
-        ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
-        if (ctx->preallocated == NULL) {
-            return NGX_ERROR;
-        }
-
-        ctx->free_mem = ctx->preallocated;
+        if (in) {
+            switch (ngx_http_gzip_filter_buffer(ctx, in)) {
 
-        ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
-        ctx->zstream.zfree = ngx_http_gzip_filter_free;
-        ctx->zstream.opaque = ctx;
-
-        rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
-                          -wbits, memlevel, Z_DEFAULT_STRATEGY);
+            case NGX_OK:
+                return NGX_OK;
 
-        if (rc != Z_OK) {
-            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                          "deflateInit2() failed: %d", rc);
-            ngx_http_gzip_error(ctx);
-            return NGX_ERROR;
-        }
+            case NGX_DONE:
+                in = NULL;
+                break;
 
-        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
-        if (b == NULL) {
-            ngx_http_gzip_error(ctx);
-            return NGX_ERROR;
-        }
-
-        b->memory = 1;
-        b->pos = gzheader;
-        b->last = b->pos + 10;
-
-        out.buf = b;
-        out.next = NULL;
+            default:  /* NGX_ERROR */
+                goto failed;
+            }
 
-        /*
-         * We pass the gzheader to the next filter now to avoid its linking
-         * to the ctx->busy chain.  zlib does not usually output the compressed
-         * data in the initial iterations, so the gzheader that was linked
-         * to the ctx->busy chain would be flushed by ngx_http_write_filter().
-         */
+        } else {
+            ctx->buffering = 0;
+        }
+    }
 
-        if (ngx_http_next_body_filter(r, &out) == NGX_ERROR) {
-            ngx_http_gzip_error(ctx);
-            return NGX_ERROR;
+    if (ctx->preallocated == NULL) {
+        if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) {
+            goto failed;
         }
-
-        r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
-
-        ctx->last_out = &ctx->out;
-
-        ctx->crc32 = crc32(0L, Z_NULL, 0);
-        ctx->flush = Z_NO_FLUSH;
     }
 
     if (in) {
-        if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
-            ngx_http_gzip_error(ctx);
-            return NGX_ERROR;
+        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
+            goto failed;
         }
     }
 
-    last = NGX_NONE;
+    if (ctx->nomem) {
+
+        /* flush busy buffers */
+
+        if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
+            goto failed;
+        }
+
+        cl = NULL;
+
+        ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl,
+                                (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
+        ctx->nomem = 0;
+    }
 
     for ( ;; ) {
 
+        /* cycle while we can write to a client */
+
         for ( ;; ) {
 
-            /* does zlib need a new data ? */
-
-            if (ctx->zstream.avail_in == 0
-                && ctx->flush == Z_NO_FLUSH
-                && !ctx->redo)
-            {
-                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                               "gzip in: %p", ctx->in);
+            /* cycle while there is data to feed zlib and ... */
 
-                if (ctx->in == NULL) {
-                    break;
-                }
-
-                ctx->in_buf = ctx->in->buf;
-                ctx->in = ctx->in->next;
-
-                ctx->zstream.next_in = ctx->in_buf->pos;
-                ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
-
-                ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                               "gzip in_buf:%p ni:%p ai:%ud",
-                               ctx->in_buf,
-                               ctx->zstream.next_in, ctx->zstream.avail_in);
+            rc = ngx_http_gzip_filter_add_data(r, ctx);
 
-                /* STUB */
-                if (ctx->in_buf->last < ctx->in_buf->pos) {
-                    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                                  "zstream.avail_in is huge");
-                    ctx->done = 1;
-                    return NGX_ERROR;
-                }
-                /**/
-
-                if (ctx->in_buf->last_buf) {
-                    ctx->flush = Z_FINISH;
+            if (rc == NGX_DECLINED) {
+                break;
+            }
 
-                } else if (ctx->in_buf->flush) {
-                    ctx->flush = Z_SYNC_FLUSH;
-                }
-
-                if (ctx->zstream.avail_in == 0) {
-                    if (ctx->flush == Z_NO_FLUSH) {
-                        continue;
-                    }
-
-                } else {
-                    ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
-                                       ctx->zstream.avail_in);
-                }
+            if (rc == NGX_AGAIN) {
+                continue;
             }
 
 
-            /* is there a space for the gzipped data ? */
-
-            if (ctx->zstream.avail_out == 0) {
-
-                if (ctx->free) {
-                    ctx->out_buf = ctx->free->buf;
-                    ctx->free = ctx->free->next;
-
-                } else if (ctx->bufs < conf->bufs.num) {
-                    ctx->out_buf = ngx_create_temp_buf(r->pool,
-                                                       conf->bufs.size);
-                    if (ctx->out_buf == NULL) {
-                        ngx_http_gzip_error(ctx);
-                        return NGX_ERROR;
-                    }
-
-                    ctx->out_buf->tag = (ngx_buf_tag_t)
-                                                  &ngx_http_gzip_filter_module;
-                    ctx->out_buf->recycled = 1;
-                    ctx->bufs++;
+            /* ... there are buffers to write zlib output */
 
-                } else {
-                    break;
-                }
-
-                ctx->zstream.next_out = ctx->out_buf->pos;
-                ctx->zstream.avail_out = conf->bufs.size;
-            }
+            rc = ngx_http_gzip_filter_get_buf(r, ctx);
 
-            ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                         "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
-                         ctx->zstream.next_in, ctx->zstream.next_out,
-                         ctx->zstream.avail_in, ctx->zstream.avail_out,
-                         ctx->flush, ctx->redo);
-
-            rc = deflate(&ctx->zstream, ctx->flush);
-
-            if (rc != Z_OK && rc != Z_STREAM_END) {
-                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                              "deflate() failed: %d, %d", ctx->flush, rc);
-                ngx_http_gzip_error(ctx);
-                return NGX_ERROR;
+            if (rc == NGX_DECLINED) {
+                break;
             }
 
-            ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
-                           ctx->zstream.next_in, ctx->zstream.next_out,
-                           ctx->zstream.avail_in, ctx->zstream.avail_out,
-                           rc);
-
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "gzip in_buf:%p pos:%p",
-                           ctx->in_buf, ctx->in_buf->pos);
-
-
-            if (ctx->zstream.next_in) {
-                ctx->in_buf->pos = ctx->zstream.next_in;
-
-                if (ctx->zstream.avail_in == 0) {
-                    ctx->zstream.next_in = NULL;
-                }
+            if (rc == NGX_ERROR) {
+                goto failed;
             }
 
-            ctx->out_buf->last = ctx->zstream.next_out;
-
-            if (ctx->zstream.avail_out == 0) {
-
-                /* zlib wants to output some more gzipped data */
-
-                cl = ngx_alloc_chain_link(r->pool);
-                if (cl == NULL) {
-                    ngx_http_gzip_error(ctx);
-                    return NGX_ERROR;
-                }
-
-                cl->buf = ctx->out_buf;
-                cl->next = NULL;
-                *ctx->last_out = cl;
-                ctx->last_out = &cl->next;
-
-                ctx->redo = 1;
 
-                continue;
-            }
-
-            ctx->redo = 0;
-
-            if (ctx->flush == Z_SYNC_FLUSH) {
-
-                ctx->zstream.avail_out = 0;
-                ctx->out_buf->flush = 1;
-                ctx->flush = Z_NO_FLUSH;
+            rc = ngx_http_gzip_filter_deflate(r, ctx);
 
-                cl = ngx_alloc_chain_link(r->pool);
-                if (cl == NULL) {
-                    ngx_http_gzip_error(ctx);
-                    return NGX_ERROR;
-                }
-
-                cl->buf = ctx->out_buf;
-                cl->next = NULL;
-                *ctx->last_out = cl;
-                ctx->last_out = &cl->next;
-
+            if (rc == NGX_OK) {
                 break;
             }
 
-            if (rc == Z_STREAM_END) {
-
-                ctx->zin = ctx->zstream.total_in;
-                ctx->zout = 10 + ctx->zstream.total_out + 8;
-
-                rc = deflateEnd(&ctx->zstream);
-
-                if (rc != Z_OK) {
-                    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                                  "deflateEnd() failed: %d", rc);
-                    ngx_http_gzip_error(ctx);
-                    return NGX_ERROR;
-                }
-
-                ngx_pfree(r->pool, ctx->preallocated);
-
-                cl = ngx_alloc_chain_link(r->pool);
-                if (cl == NULL) {
-                    ngx_http_gzip_error(ctx);
-                    return NGX_ERROR;
-                }
-
-                cl->buf = ctx->out_buf;
-                cl->next = NULL;
-                *ctx->last_out = cl;
-                ctx->last_out = &cl->next;
-
-                if (ctx->zstream.avail_out >= 8) {
-                    trailer = (struct gztrailer *) ctx->out_buf->last;
-                    ctx->out_buf->last += 8;
-                    ctx->out_buf->last_buf = 1;
-
-                } else {
-                    b = ngx_create_temp_buf(r->pool, 8);
-                    if (b == NULL) {
-                        ngx_http_gzip_error(ctx);
-                        return NGX_ERROR;
-                    }
-
-                    b->last_buf = 1;
-
-                    cl = ngx_alloc_chain_link(r->pool);
-                    if (cl == NULL) {
-                        ngx_http_gzip_error(ctx);
-                        return NGX_ERROR;
-                    }
-
-                    cl->buf = b;
-                    cl->next = NULL;
-                    *ctx->last_out = cl;
-                    ctx->last_out = &cl->next;
-                    trailer = (struct gztrailer *) b->pos;
-                    b->last += 8;
-                }
-
-#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
-
-                trailer->crc32 = ctx->crc32;
-                trailer->zlen = ctx->zin;
-
-#else
-                trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
-                trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);
-                trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
-                trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);
-
-                trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
-                trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);
-                trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
-                trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);
-#endif
-
-                ctx->zstream.avail_in = 0;
-                ctx->zstream.avail_out = 0;
-
-                ctx->done = 1;
-
-                r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
-
-                break;
+            if (rc == NGX_ERROR) {
+                goto failed;
             }
 
-            if (conf->no_buffer && ctx->in == NULL) {
+            /* rc == NGX_AGAIN */
+        }
+
+        if (ctx->out == NULL) {
+            ngx_http_gzip_filter_free_copy_buf(r, ctx);
 
-                cl = ngx_alloc_chain_link(r->pool);
-                if (cl == NULL) {
-                    ngx_http_gzip_error(ctx);
-                    return NGX_ERROR;
-                }
+            return ctx->busy ? NGX_AGAIN : NGX_OK;
+        }
 
-                cl->buf = ctx->out_buf;
-                cl->next = NULL;
-                *ctx->last_out = cl;
-                ctx->last_out = &cl->next;
-
-                break;
+        if (!ctx->gzheader) {
+            if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) {
+                goto failed;
             }
         }
 
-        if (ctx->out == NULL) {
+        rc = ngx_http_next_body_filter(r, ctx->out);
 
-            if (last == NGX_AGAIN) {
-                return NGX_AGAIN;
-            }
-
-            if (ctx->busy == NULL) {
-                return NGX_OK;
-            }
+        if (rc == NGX_ERROR) {
+            goto failed;
         }
 
-        last = ngx_http_next_body_filter(r, ctx->out);
-
-        /*
-         * we do not check NGX_AGAIN here because the downstream filters
-         * may free some buffers and zlib may compress some data into them
-         */
-
-        if (last == NGX_ERROR) {
-            ngx_http_gzip_error(ctx);
-            return NGX_ERROR;
-        }
+        ngx_http_gzip_filter_free_copy_buf(r, ctx);
 
         ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out,
                                 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
         ctx->last_out = &ctx->out;
 
+        ctx->nomem = 0;
+
         if (ctx->done) {
-            return last;
+            return rc;
+        }
+    }
+
+    /* unreachable */
+
+failed:
+
+    ctx->done = 1;
+
+    if (ctx->preallocated) {
+        deflateEnd(&ctx->zstream);
+
+        ngx_pfree(r->pool, ctx->preallocated);
+    }
+
+    ngx_http_gzip_filter_free_copy_buf(r, ctx);
+
+    return NGX_ERROR;
+}
+
+
+static void
+ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+    int                    wbits, memlevel;
+    ngx_http_gzip_conf_t  *conf;
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+    wbits = conf->wbits;
+    memlevel = conf->memlevel;
+
+    if (r->headers_out.content_length_n > 0) {
+
+        /* the actual zlib window size is smaller by 262 bytes */
+
+        while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) {
+            wbits--;
+            memlevel--;
         }
     }
+
+    ctx->wbits = wbits;
+    ctx->memlevel = memlevel;
+
+    /*
+     * We preallocate a memory for zlib in one buffer (200K-400K), this
+     * decreases a number of malloc() and free() calls and also probably
+     * decreases a number of syscalls (sbrk()/mmap() and so on).
+     * Besides we free the memory as soon as a gzipping will complete
+     * and do not wait while a whole response will be sent to a client.
+     *
+     * 8K is for zlib deflate_state, it takes
+     *  *) 5816 bytes on i386 and sparc64 (32-bit mode)
+     *  *) 5920 bytes on amd64 and sparc64
+     */
+
+    ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in)
+{
+    size_t                 size, buffered;
+    ngx_buf_t             *b, *buf;
+    ngx_chain_t           *cl, **ll;
+    ngx_http_request_t    *r;
+    ngx_http_gzip_conf_t  *conf;
+
+    r = ctx->request;
+
+    r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
+
+    buffered = 0;
+    ll = &ctx->in;
+
+    for (cl = ctx->in; cl; cl = cl->next) {
+        buffered += cl->buf->last - cl->buf->pos;
+        ll = &cl->next;
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+    while (in) {
+        cl = ngx_alloc_chain_link(r->pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        b = in->buf;
+
+        size = b->last - b->pos;
+        buffered += size;
+
+        if (b->flush || b->last_buf || buffered > conf->postpone_gzipping) {
+            ctx->buffering = 0;
+        }
+
+        if (ctx->buffering && size) {
+
+            buf = ngx_create_temp_buf(r->pool, size);
+            if (buf == NULL) {
+                return NGX_ERROR;
+            }
+
+            buf->last = ngx_cpymem(buf->pos, b->pos, size);
+            b->pos = b->last;
+
+            buf->last_buf = b->last_buf;
+            buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
+
+            cl->buf = buf;
+
+        } else {
+            cl->buf = b;
+        }
+
+        *ll = cl;
+        ll = &cl->next;
+        in = in->next;
+    }
+
+    *ll = NULL;
+
+    return ctx->buffering ? NGX_OK : NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx)
+{
+    int                    rc;
+    ngx_http_gzip_conf_t  *conf;
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+    ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
+    if (ctx->preallocated == NULL) {
+        return NGX_ERROR;
+    }
+
+    ctx->free_mem = ctx->preallocated;
+
+    ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
+    ctx->zstream.zfree = ngx_http_gzip_filter_free;
+    ctx->zstream.opaque = ctx;
+
+    rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
+                      - ctx->wbits, ctx->memlevel, Z_DEFAULT_STRATEGY);
+
+    if (rc != Z_OK) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                      "deflateInit2() failed: %d", rc);
+        return NGX_ERROR;
+    }
+
+    ctx->last_out = &ctx->out;
+    ctx->crc32 = crc32(0L, Z_NULL, 0);
+    ctx->flush = Z_NO_FLUSH;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+    ngx_buf_t      *b;
+    ngx_chain_t    *cl;
+    static u_char  gzheader[10] =
+                               { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
+
+    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+    if (b == NULL) {
+        return NGX_ERROR;
+    }
+
+    b->memory = 1;
+    b->pos = gzheader;
+    b->last = b->pos + 10;
+
+    cl = ngx_alloc_chain_link(r->pool);
+    if (cl == NULL) {
+        return NGX_ERROR;
+    }
+
+    cl->buf = b;
+    cl->next = ctx->out;
+    ctx->out = cl;
+
+    ctx->gzheader = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+    if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
+        return NGX_OK;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "gzip in: %p", ctx->in);
+
+    if (ctx->in == NULL) {
+        return NGX_DECLINED;
+    }
+
+    if (ctx->copy_buf) {
+
+        /*
+         * to avoid CPU cache trashing we do not free() just quit buf,
+         * but postpone free()ing after zlib compressing and data output
+         */
+
+        ctx->copy_buf->next = ctx->copied;
+        ctx->copied = ctx->copy_buf;
+        ctx->copy_buf = NULL;
+    }
+
+    ctx->in_buf = ctx->in->buf;
+
+    if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) {
+        ctx->copy_buf = ctx->in;
+    }
+
+    ctx->in = ctx->in->next;
+
+    ctx->zstream.next_in = ctx->in_buf->pos;
+    ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
+
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "gzip in_buf:%p ni:%p ai:%ud",
+                   ctx->in_buf,
+                   ctx->zstream.next_in, ctx->zstream.avail_in);
+
+    if (ctx->in_buf->last_buf) {
+        ctx->flush = Z_FINISH;
+
+    } else if (ctx->in_buf->flush) {
+        ctx->flush = Z_SYNC_FLUSH;
+    }
+
+    if (ctx->zstream.avail_in) {
+
+        ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
+                           ctx->zstream.avail_in);
+
+    } else if (ctx->flush == Z_NO_FLUSH) {
+        return NGX_AGAIN;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+    ngx_http_gzip_conf_t  *conf;
+
+    if (ctx->zstream.avail_out) {
+        return NGX_OK;
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+    if (ctx->free) {
+        ctx->out_buf = ctx->free->buf;
+        ctx->free = ctx->free->next;
+
+    } else if (ctx->bufs < conf->bufs.num) {
+
+        ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
+        if (ctx->out_buf == NULL) {
+            return NGX_ERROR;
+        }
+
+        ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
+        ctx->out_buf->recycled = 1;
+        ctx->bufs++;
+
+    } else {
+        ctx->nomem = 1;
+        return NGX_DECLINED;
+    }
+
+    ctx->zstream.next_out = ctx->out_buf->pos;
+    ctx->zstream.avail_out = conf->bufs.size;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+    int                    rc;
+    ngx_chain_t           *cl;
+    ngx_http_gzip_conf_t  *conf;
+
+    ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                 "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
+                 ctx->zstream.next_in, ctx->zstream.next_out,
+                 ctx->zstream.avail_in, ctx->zstream.avail_out,
+                 ctx->flush, ctx->redo);
+
+    rc = deflate(&ctx->zstream, ctx->flush);
+
+    if (rc != Z_OK && rc != Z_STREAM_END) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                      "deflate() failed: %d, %d", ctx->flush, rc);
+        return NGX_ERROR;
+    }
+
+    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
+                   ctx->zstream.next_in, ctx->zstream.next_out,
+                   ctx->zstream.avail_in, ctx->zstream.avail_out,
+                   rc);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "gzip in_buf:%p pos:%p",
+                   ctx->in_buf, ctx->in_buf->pos);
+
+    if (ctx->zstream.next_in) {
+        ctx->in_buf->pos = ctx->zstream.next_in;
+
+        if (ctx->zstream.avail_in == 0) {
+            ctx->zstream.next_in = NULL;
+        }
+    }
+
+    ctx->out_buf->last = ctx->zstream.next_out;
+
+    if (ctx->zstream.avail_out == 0) {
+
+        /* zlib wants to output some more gzipped data */
+
+        cl = ngx_alloc_chain_link(r->pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        cl->buf = ctx->out_buf;
+        cl->next = NULL;
+        *ctx->last_out = cl;
+        ctx->last_out = &cl->next;
+
+        ctx->redo = 1;
+
+        return NGX_AGAIN;
+    }
+
+    ctx->redo = 0;
+
+    if (ctx->flush == Z_SYNC_FLUSH) {
+
+        ctx->zstream.avail_out = 0;
+        ctx->out_buf->flush = 1;
+        ctx->flush = Z_NO_FLUSH;
+
+        cl = ngx_alloc_chain_link(r->pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        cl->buf = ctx->out_buf;
+        cl->next = NULL;
+        *ctx->last_out = cl;
+        ctx->last_out = &cl->next;
+
+        return NGX_OK;
+    }
+
+    if (rc == Z_STREAM_END) {
+
+        if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        return NGX_OK;
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+    if (conf->no_buffer && ctx->in == NULL) {
+
+        cl = ngx_alloc_chain_link(r->pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        cl->buf = ctx->out_buf;
+        cl->next = NULL;
+        *ctx->last_out = cl;
+        ctx->last_out = &cl->next;
+
+        return NGX_OK;
+    }
+
+    return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx)
+{
+    int                rc;
+    ngx_buf_t         *b;
+    ngx_chain_t       *cl;
+    struct gztrailer  *trailer;
+
+    ctx->zin = ctx->zstream.total_in;
+    ctx->zout = 10 + ctx->zstream.total_out + 8;
+
+    rc = deflateEnd(&ctx->zstream);
+
+    if (rc != Z_OK) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                      "deflateEnd() failed: %d", rc);
+        return NGX_ERROR;
+    }
+
+    ngx_pfree(r->pool, ctx->preallocated);
+
+    cl = ngx_alloc_chain_link(r->pool);
+    if (cl == NULL) {
+        return NGX_ERROR;
+    }
+
+    cl->buf = ctx->out_buf;
+    cl->next = NULL;
+    *ctx->last_out = cl;
+    ctx->last_out = &cl->next;
+
+    if (ctx->zstream.avail_out >= 8) {
+        trailer = (struct gztrailer *) ctx->out_buf->last;
+        ctx->out_buf->last += 8;
+        ctx->out_buf->last_buf = 1;
+
+    } else {
+        b = ngx_create_temp_buf(r->pool, 8);
+        if (b == NULL) {
+            return NGX_ERROR;
+        }
+
+        b->last_buf = 1;
+
+        cl = ngx_alloc_chain_link(r->pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        cl->buf = b;
+        cl->next = NULL;
+        *ctx->last_out = cl;
+        ctx->last_out = &cl->next;
+        trailer = (struct gztrailer *) b->pos;
+        b->last += 8;
+    }
+
+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+
+    trailer->crc32 = ctx->crc32;
+    trailer->zlen = ctx->zin;
+
+#else
+
+    trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
+    trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);
+    trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
+    trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);
+
+    trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
+    trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);
+    trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
+    trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);
+
+#endif
+
+    ctx->zstream.avail_in = 0;
+    ctx->zstream.avail_out = 0;
+
+    ctx->done = 1;
+
+    r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
+
+    return NGX_OK;
 }
 
 
@@ -674,14 +944,14 @@ ngx_http_gzip_filter_alloc(void *opaque,
 
     alloc = items * size;
 
-    if (alloc % 512 != 0) {
+    if (alloc % 512 != 0 && alloc < 8192) {
 
         /*
          * The zlib deflate_state allocation, it takes about 6K,
          * we allocate 8K.  Other allocations are divisible by 512.
          */
 
-        alloc = (alloc + ngx_pagesize - 1) & ~(ngx_pagesize - 1);
+        alloc = 8192;
     }
 
     if (alloc <= ctx->allocated) {
@@ -719,20 +989,16 @@ ngx_http_gzip_filter_free(void *opaque, 
 
 
 static void
-ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx)
+ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx)
 {
-    deflateEnd(&ctx->zstream);
+    ngx_chain_t  *cl;
 
-    if (ctx->preallocated) {
-        ngx_pfree(ctx->request->pool, ctx->preallocated);
+    for (cl = ctx->copied; cl; cl = cl->next) {
+        ngx_pfree(r->pool, cl->buf->start);
     }
 
-    ctx->zstream.avail_in = 0;
-    ctx->zstream.avail_out = 0;
-
-    ctx->done = 1;
-
-    return;
+    ctx->copied = NULL;
 }
 
 
@@ -803,7 +1069,7 @@ ngx_http_gzip_create_conf(ngx_conf_t *cf
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -817,9 +1083,10 @@ ngx_http_gzip_create_conf(ngx_conf_t *cf
     conf->enable = NGX_CONF_UNSET;
     conf->no_buffer = NGX_CONF_UNSET;
 
+    conf->postpone_gzipping = NGX_CONF_UNSET_SIZE;
     conf->level = NGX_CONF_UNSET;
-    conf->wbits = (size_t) NGX_CONF_UNSET;
-    conf->memlevel = (size_t) NGX_CONF_UNSET;
+    conf->wbits = NGX_CONF_UNSET_SIZE;
+    conf->memlevel = NGX_CONF_UNSET_SIZE;
     conf->min_length = NGX_CONF_UNSET;
 
     return conf;
@@ -833,15 +1100,18 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf,
     ngx_http_gzip_conf_t *conf = child;
 
     ngx_conf_merge_value(conf->enable, prev->enable, 0);
+    ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);
 
-    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize);
+    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
+                              (128 * 1024) / ngx_pagesize, ngx_pagesize);
 
+    ngx_conf_merge_size_value(conf->postpone_gzipping, prev->postpone_gzipping,
+                              0);
     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,
                               MAX_MEM_LEVEL - 1);
     ngx_conf_merge_value(conf->min_length, prev->min_length, 20);
-    ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);
 
     if (ngx_http_merge_types(cf, conf->types_keys, &conf->types,
                              prev->types_keys, &prev->types,
@@ -871,9 +1141,9 @@ ngx_http_gzip_filter_init(ngx_conf_t *cf
 static char *
 ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data)
 {
-    int *np = data;
+    size_t *np = data;
 
-    int  wbits, wsize;
+    size_t  wbits, wsize;
 
     wbits = 15;
 
@@ -895,9 +1165,9 @@ ngx_http_gzip_window(ngx_conf_t *cf, voi
 static char *
 ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data)
 {
-    int *np = data;
+    size_t *np = data;
 
-    int  memlevel, hsize;
+    size_t  memlevel, hsize;
 
     memlevel = 9;
 
--- a/src/http/modules/ngx_http_gzip_static_module.c
+++ b/src/http/modules/ngx_http_gzip_static_module.c
@@ -89,7 +89,6 @@ ngx_http_gzip_static_handler(ngx_http_re
         return NGX_DECLINED;
     }
 
-    /* TODO: Win32 */
     if (r->zero_in_uri) {
         return NGX_DECLINED;
     }
@@ -153,7 +152,7 @@ ngx_http_gzip_static_handler(ngx_http_re
         }
 
         ngx_log_error(level, log, of.err,
-                      ngx_open_file_n " \"%s\" failed", path.data);
+                      "%s \"%s\" failed", of.failed, path.data);
 
         return NGX_DECLINED;
     }
@@ -176,7 +175,7 @@ ngx_http_gzip_static_handler(ngx_http_re
 
 #endif
 
-    r->root_tested = 1;
+    r->root_tested = !r->error_page;
 
     rc = ngx_http_discard_request_body(r);
 
@@ -206,6 +205,7 @@ ngx_http_gzip_static_handler(ngx_http_re
     h->value.data = (u_char *) "gzip";
 
     r->headers_out.content_encoding = h;
+    r->ignore_content_encoding = 1;
 
     /* we need to allocate all before the header would be sent */
 
@@ -251,7 +251,7 @@ ngx_http_gzip_static_create_conf(ngx_con
 
     conf = ngx_palloc(cf->pool, sizeof(ngx_http_gzip_static_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     conf->enable = NGX_CONF_UNSET;
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -16,18 +16,18 @@ typedef ngx_int_t (*ngx_http_set_header_
 
 
 typedef struct {
-    ngx_str_t                name;
-    ngx_uint_t               offset;
-    ngx_http_set_header_pt   handler;
+    ngx_str_t                  name;
+    ngx_uint_t                 offset;
+    ngx_http_set_header_pt     handler;
 } ngx_http_set_header_t;
 
 
 struct ngx_http_header_val_s {
-    ngx_table_elt_t          value;
-    ngx_uint_t               offset;
-    ngx_http_set_header_pt   handler;
-    ngx_array_t             *lengths;
-    ngx_array_t             *values;
+    ngx_http_complex_value_t   value;
+    ngx_uint_t                 hash;
+    ngx_str_t                  key;
+    ngx_http_set_header_pt     handler;
+    ngx_uint_t                 offset;
 };
 
 
@@ -162,16 +162,8 @@ ngx_http_headers_filter(ngx_http_request
         h = conf->headers->elts;
         for (i = 0; i < conf->headers->nelts; i++) {
 
-            if (h[i].lengths == NULL) {
-                value = h[i].value.value;
-
-            } else {
-                if (ngx_http_script_run(r, &value, h[i].lengths->elts, 0,
-                                        h[i].values->elts)
-                    == NULL)
-                {
-                    return NGX_ERROR;
-                }
+            if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
+                return NGX_ERROR;
             }
 
             if (h[i].handler(r, &h[i], &value) != NGX_OK) {
@@ -325,15 +317,17 @@ ngx_http_add_header(ngx_http_request_t *
 {
     ngx_table_elt_t  *h;
 
-    h = ngx_list_push(&r->headers_out.headers);
-    if (h == NULL) {
-        return NGX_ERROR;
+    if (value->len) {
+        h = ngx_list_push(&r->headers_out.headers);
+        if (h == NULL) {
+            return NGX_ERROR;
+        }
+
+        h->hash = hv->hash;
+        h->key = hv->key;
+        h->value = *value;
     }
 
-    h->hash = hv->value.hash;
-    h->key = hv->value.key;
-    h->value = *value;
-
     return NGX_OK;
 }
 
@@ -412,8 +406,8 @@ ngx_http_set_last_modified(ngx_http_requ
         }
     }
 
-    h->hash = hv->value.hash;
-    h->key = hv->value.key;
+    h->hash = hv->hash;
+    h->key = hv->key;
     h->value = *value;
 
     return NGX_OK;
@@ -427,7 +421,7 @@ ngx_http_headers_create_conf(ngx_conf_t 
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -576,12 +570,11 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx
 {
     ngx_http_headers_conf_t *hcf = conf;
 
-    ngx_int_t                   n;
-    ngx_str_t                  *value;
-    ngx_uint_t                  i;
-    ngx_http_header_val_t      *h;
-    ngx_http_set_header_t      *sh;
-    ngx_http_script_compile_t   sc;
+    ngx_str_t                         *value;
+    ngx_uint_t                         i;
+    ngx_http_header_val_t             *hv;
+    ngx_http_set_header_t             *set;
+    ngx_http_compile_complex_value_t   ccv;
 
     value = cf->args->elts;
 
@@ -593,47 +586,40 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx
         }
     }
 
-    h = ngx_array_push(hcf->headers);
-    if (h == NULL) {
+    hv = ngx_array_push(hcf->headers);
+    if (hv == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    h->value.hash = 1;
-    h->value.key = value[1];
-    h->value.value = value[2];
-    h->offset = 0;
-    h->handler = ngx_http_add_header;
-    h->lengths = NULL;
-    h->values = NULL;
+    hv->hash = 1;
+    hv->key = value[1];
+    hv->handler = ngx_http_add_header;
+    hv->offset = 0;
 
-    sh = ngx_http_set_headers;
-    for (i = 0; sh[i].name.len; i++) {
-        if (ngx_strcasecmp(value[1].data, sh[i].name.data) != 0) {
+    set = ngx_http_set_headers;
+    for (i = 0; set[i].name.len; i++) {
+        if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) {
             continue;
         }
 
-        h->offset = sh[i].offset;
-        h->handler = sh[i].handler;
+        hv->offset = set[i].offset;
+        hv->handler = set[i].handler;
+
         break;
     }
 
-    n = ngx_http_script_variables_count(&value[2]);
-
-    if (n == 0) {
+    if (value[2].len == 0) {
+        ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));
         return NGX_CONF_OK;
     }
 
-    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
 
-    sc.cf = cf;
-    sc.source = &value[2];
-    sc.lengths = &h->lengths;
-    sc.values = &h->values;
-    sc.variables = n;
-    sc.complete_lengths = 1;
-    sc.complete_values = 1;
+    ccv.cf = cf;
+    ccv.value = &value[2];
+    ccv.complex_value = &hv->value;
 
-    if (ngx_http_script_compile(&sc) != NGX_OK) {
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_image_filter_module.c
@@ -0,0 +1,1079 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <gd.h>
+
+
+#define NGX_HTTP_IMAGE_OFF       0
+#define NGX_HTTP_IMAGE_TEST      1
+#define NGX_HTTP_IMAGE_SIZE      2
+#define NGX_HTTP_IMAGE_RESIZE    3
+#define NGX_HTTP_IMAGE_CROP      4
+
+
+#define NGX_HTTP_IMAGE_START     0
+#define NGX_HTTP_IMAGE_READ      1
+#define NGX_HTTP_IMAGE_PROCESS   2
+#define NGX_HTTP_IMAGE_PASS      3
+#define NGX_HTTP_IMAGE_DONE      4
+
+
+#define NGX_HTTP_IMAGE_NONE      0
+#define NGX_HTTP_IMAGE_JPEG      1
+#define NGX_HTTP_IMAGE_GIF       2
+#define NGX_HTTP_IMAGE_PNG       3
+
+
+#define NGX_HTTP_IMAGE_BUFFERED  0x08
+
+
+typedef struct {
+    ngx_uint_t                   filter;
+    ngx_uint_t                   width;
+    ngx_uint_t                   height;
+    ngx_int_t                    jpeg_quality;
+
+    size_t                       buffer_size;
+} ngx_http_image_filter_conf_t;
+
+
+typedef struct {
+    u_char                      *image;
+    u_char                      *last;
+
+    size_t                       length;
+
+    ngx_uint_t                   width;
+    ngx_uint_t                   height;
+
+    ngx_uint_t                   phase;
+    ngx_uint_t                   type;
+} ngx_http_image_filter_ctx_t;
+
+
+static ngx_int_t ngx_http_image_send(ngx_http_request_t *r,
+    ngx_http_image_filter_ctx_t *ctx, ngx_chain_t *in);
+static ngx_uint_t ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in);
+static ngx_int_t ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in);
+static ngx_buf_t *ngx_http_image_process(ngx_http_request_t *r);
+static ngx_buf_t *ngx_http_image_json(ngx_http_request_t *r,
+    ngx_http_image_filter_ctx_t *ctx);
+static ngx_buf_t *ngx_http_image_asis(ngx_http_request_t *r,
+    ngx_http_image_filter_ctx_t *ctx);
+static void ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b);
+static ngx_int_t ngx_http_image_size(ngx_http_request_t *r,
+    ngx_http_image_filter_ctx_t *ctx);
+
+static ngx_buf_t *ngx_http_image_resize(ngx_http_request_t *r,
+    ngx_http_image_filter_ctx_t *ctx);
+static gdImagePtr ngx_http_image_source(ngx_http_request_t *r,
+    ngx_http_image_filter_ctx_t *ctx);
+static gdImagePtr ngx_http_image_new(ngx_http_request_t *r, int w, int h,
+    int colors);
+static u_char *ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type,
+    gdImagePtr img, int *size);
+static void ngx_http_image_cleanup(void *data);
+
+
+static void *ngx_http_image_filter_create_conf(ngx_conf_t *cf);
+static char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent,
+    void *child);
+static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);
+
+
+static ngx_command_t  ngx_http_image_filter_commands[] = {
+
+    { ngx_string("image_filter"),
+      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13,
+      ngx_http_image_filter,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("image_filter_jpeg_quality"),
+      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_image_filter_conf_t, jpeg_quality),
+      NULL },
+
+    { ngx_string("image_filter_buffer"),
+      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_image_filter_conf_t, buffer_size),
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_image_filter_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    ngx_http_image_filter_init,            /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_image_filter_create_conf,     /* create location configuration */
+    ngx_http_image_filter_merge_conf       /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_image_filter_module = {
+    NGX_MODULE_V1,
+    &ngx_http_image_filter_module_ctx,     /* module context */
+    ngx_http_image_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_str_t  ngx_http_image_types[] = {
+    ngx_string("image/jpeg"),
+    ngx_string("image/gif"),
+    ngx_string("image/png")
+};
+
+
+static ngx_int_t
+ngx_http_image_header_filter(ngx_http_request_t *r)
+{
+    off_t                          len;
+    ngx_http_image_filter_ctx_t   *ctx;
+    ngx_http_image_filter_conf_t  *conf;
+
+    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
+        return ngx_http_next_header_filter(r);
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
+
+    if (ctx) {
+        ngx_http_set_ctx(r, NULL, ngx_http_image_filter_module);
+        return ngx_http_next_header_filter(r);
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+    if (conf->filter == NGX_HTTP_IMAGE_OFF) {
+        return ngx_http_next_header_filter(r);
+    }
+
+    if (r->headers_out.content_type.len
+            >= sizeof("multipart/x-mixed-replace") - 1
+        && ngx_strncasecmp(r->headers_out.content_type.data,
+                           (u_char *) "multipart/x-mixed-replace",
+                           sizeof("multipart/x-mixed-replace") - 1)
+           == 0)
+    {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "image filter: multipart/x-mixed-replace response");
+
+        return NGX_ERROR;
+    }
+
+    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_image_filter_ctx_t));
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_http_set_ctx(r, ctx, ngx_http_image_filter_module);
+
+    len = r->headers_out.content_length_n;
+
+    if (len != -1 && len > (off_t) conf->buffer_size) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "image filter: too big response: %O", len);
+
+        return NGX_ERROR;
+    }
+
+    if (len == -1) {
+        ctx->length = conf->buffer_size;
+
+    } else {
+        ctx->length = (size_t) len;
+    }
+
+    if (r->headers_out.refresh) {
+        r->headers_out.refresh->hash = 0;
+    }
+
+    r->main_filter_need_in_memory = 1;
+    r->allow_ranges = 0;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    ngx_int_t                      rc;
+    ngx_str_t                     *ct;
+    ngx_chain_t                    out;
+    ngx_http_image_filter_ctx_t   *ctx;
+    ngx_http_image_filter_conf_t  *conf;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "image filter");
+
+    if (in == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
+
+    if (ctx == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    switch (ctx->phase) {
+
+    case NGX_HTTP_IMAGE_START:
+
+        ctx->type = ngx_http_image_test(r, in);
+
+        conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+        if (ctx->type == NGX_HTTP_IMAGE_NONE) {
+
+            if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
+                out.buf = ngx_http_image_json(r, NULL);
+
+                if (out.buf) {
+                    out.next = NULL;
+                    ctx->phase = NGX_HTTP_IMAGE_DONE;
+
+                    return ngx_http_image_send(r, ctx, &out);
+                }
+            }
+
+            return ngx_http_filter_finalize_request(r,
+                                              &ngx_http_image_filter_module,
+                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
+        }
+
+        /* override content type */
+
+        ct = &ngx_http_image_types[ctx->type - 1];
+        r->headers_out.content_type_len = ct->len;
+        r->headers_out.content_type = *ct;
+        r->headers_out.content_type_lowcase = NULL;
+
+        if (conf->filter == NGX_HTTP_IMAGE_TEST) {
+            ctx->phase = NGX_HTTP_IMAGE_PASS;
+
+            return ngx_http_image_send(r, ctx, in);
+        }
+
+        ctx->phase = NGX_HTTP_IMAGE_READ;
+
+        /* fall through */
+
+    case NGX_HTTP_IMAGE_READ:
+
+        rc = ngx_http_image_read(r, in);
+
+        if (rc == NGX_AGAIN) {
+            return NGX_OK;
+        }
+
+        if (rc == NGX_ERROR) {
+            return ngx_http_filter_finalize_request(r,
+                                              &ngx_http_image_filter_module,
+                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
+        }
+
+        /* fall through */
+
+    case NGX_HTTP_IMAGE_PROCESS:
+
+        out.buf = ngx_http_image_process(r);
+
+        if (out.buf == NULL) {
+            return ngx_http_filter_finalize_request(r,
+                                              &ngx_http_image_filter_module,
+                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
+        }
+
+        out.next = NULL;
+        ctx->phase = NGX_HTTP_IMAGE_PASS;
+
+        return ngx_http_image_send(r, ctx, &out);
+
+    case NGX_HTTP_IMAGE_PASS:
+
+        return ngx_http_next_body_filter(r, in);
+
+    default: /* NGX_HTTP_IMAGE_DONE */
+
+        rc = ngx_http_next_body_filter(r, NULL);
+
+        /* NGX_ERROR resets any pending data */
+        return (rc == NGX_OK) ? NGX_ERROR : rc;
+    }
+}
+
+
+static ngx_int_t
+ngx_http_image_send(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx,
+    ngx_chain_t *in)
+{
+    ngx_int_t  rc;
+
+    rc = ngx_http_next_header_filter(r);
+
+    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+        return NGX_ERROR;
+    }
+
+    rc = ngx_http_next_body_filter(r, in);
+
+    if (ctx->phase == NGX_HTTP_IMAGE_DONE) {
+        /* NGX_ERROR resets any pending data */
+        return (rc == NGX_OK) ? NGX_ERROR : rc;
+    }
+
+    return rc;
+}
+
+
+static ngx_uint_t
+ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    u_char  *p;
+
+    p = in->buf->pos;
+
+    if (in->buf->last - p < 16) {
+        return NGX_HTTP_IMAGE_NONE;
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "image filter: \"%c%c\"", p[0], p[1]);
+
+    if (p[0] == 0xff && p[1] == 0xd8) {
+
+        /* JPEG */
+
+        return NGX_HTTP_IMAGE_JPEG;
+
+    } else if (p[0] == 'G' && p[1] == 'I' && p[2] == 'F' && p[3] == '8'
+               && p[5] == 'a')
+    {
+        if (p[4] == '9' || p[4] == '7') {
+            /* GIF */
+            return NGX_HTTP_IMAGE_GIF;
+        }
+
+    } else if (p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G'
+               && p[4] == 0x0d && p[5] == 0x0a && p[6] == 0x1a && p[7] == 0x0a)
+    {
+        /* PNG */
+
+        return NGX_HTTP_IMAGE_PNG;
+    }
+
+    return NGX_HTTP_IMAGE_NONE;
+}
+
+
+static ngx_int_t
+ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    u_char                       *p;
+    size_t                        size, rest;
+    ngx_buf_t                    *b;
+    ngx_chain_t                  *cl;
+    ngx_http_image_filter_ctx_t  *ctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
+
+    if (ctx->image == NULL) {
+        ctx->image = ngx_palloc(r->pool, ctx->length);
+        if (ctx->image == NULL) {
+            return NGX_ERROR;
+        }
+
+        ctx->last = ctx->image;
+    }
+
+    p = ctx->last;
+
+    for (cl = in; cl; cl = cl->next) {
+
+        b = cl->buf;
+        size = b->last - b->pos;
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "image buf: %uz", size);
+
+        rest = ctx->image + ctx->length - p;
+        size = (rest < size) ? rest : size;
+
+        p = ngx_cpymem(p, b->pos, size);
+        b->pos += size;
+
+        if (b->last_buf) {
+            ctx->last = p;
+            return NGX_OK;
+        }
+    }
+
+    ctx->last = p;
+    r->connection->buffered |= NGX_HTTP_IMAGE_BUFFERED;
+
+    return NGX_AGAIN;
+}
+
+
+static ngx_buf_t *
+ngx_http_image_process(ngx_http_request_t *r)
+{
+    ngx_buf_t                     *b;
+    ngx_int_t                      rc;
+    ngx_http_image_filter_ctx_t   *ctx;
+    ngx_http_image_filter_conf_t  *conf;
+
+    r->connection->buffered &= ~NGX_HTTP_IMAGE_BUFFERED;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
+
+    rc = ngx_http_image_size(r, ctx);
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+    if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
+
+        b = ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);
+
+    } else if (rc == NGX_OK
+               && ctx->width <= conf->width
+               && ctx->height <= conf->height)
+    {
+        b = ngx_http_image_asis(r, ctx);
+
+    } else {
+        b = ngx_http_image_resize(r, ctx);
+    }
+
+    return b;
+}
+
+
+static ngx_buf_t *
+ngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+    size_t      len;
+    ngx_buf_t  *b;
+
+    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+    if (b == NULL) {
+        return NULL;
+    }
+
+    b->memory = 1;
+    b->last_buf = 1;
+
+    ngx_http_clean_header(r);
+
+    r->headers_out.status = NGX_HTTP_OK;
+    r->headers_out.content_type.len = sizeof("text/plain") - 1;
+    r->headers_out.content_type.data = (u_char *) "text/plain";
+    r->headers_out.content_type_lowcase = NULL;
+
+    if (ctx == NULL) {
+        b->pos = (u_char *) "{}" CRLF;
+        b->last = b->pos + sizeof("{}" CRLF) - 1;
+
+        ngx_http_image_length(r, b);
+
+        return b;
+    }
+
+    len = sizeof("{ \"img\" : "
+                 "{ \"width\": , \"height\": , \"type\": \"jpeg\" } }" CRLF) - 1
+          + 2 * NGX_SIZE_T_LEN;
+
+    b->pos = ngx_pnalloc(r->pool, len);
+    if (b->pos == NULL) {
+        return NULL;
+    }
+
+    b->last = ngx_sprintf(b->pos,
+                          "{ \"img\" : "
+                                       "{ \"width\": %uz,"
+                                        " \"height\": %uz,"
+                                        " \"type\": \"%s\" } }" CRLF,
+                          ctx->width, ctx->height,
+                          ngx_http_image_types[ctx->type - 1].data + 6);
+
+    ngx_http_image_length(r, b);
+
+    return b;
+}
+
+
+static ngx_buf_t *
+ngx_http_image_asis(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+    ngx_buf_t  *b;
+
+    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+    if (b == NULL) {
+        return NULL;
+    }
+
+    b->pos = ctx->image;
+    b->last = ctx->last;
+    b->memory = 1;
+    b->last_buf = 1;
+
+    ngx_http_image_length(r, b);
+
+    return b;
+}
+
+
+static void
+ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b)
+{
+    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;
+}
+
+
+static ngx_int_t
+ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+    u_char      *p, *last;
+    ngx_uint_t   width, height;
+
+    p = ctx->image;
+
+    switch (ctx->type) {
+
+    case NGX_HTTP_IMAGE_JPEG:
+
+        p += 2;
+        last = ctx->image + ctx->length - 10;
+
+        while (p < last) {
+
+            if (p[0] == 0xff && p[1] != 0xff) {
+
+                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                               "JPEG: %02xd %02xd", *p, *(p + 1));
+
+                p++;
+
+                if (*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3
+                    || *p == 0xc9 || *p == 0xca || *p == 0xcb)
+                {
+                    goto found;
+                }
+
+                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                               "JPEG: %02xd %02xd", p[1], p[2]);
+
+                p += p[1] * 256 + p[2];
+
+                continue;
+            }
+
+            p++;
+        }
+
+        return NGX_DECLINED;
+
+    found:
+
+        width = p[6] * 256 + p[7];
+        height = p[4] * 256 + p[5];
+
+        break;
+
+    case NGX_HTTP_IMAGE_GIF:
+
+        if (ctx->length < 10) {
+            return NGX_DECLINED;
+        }
+
+        width = p[7] * 256 + p[6];
+        height = p[9] * 256 + p[8];
+
+        break;
+
+    case NGX_HTTP_IMAGE_PNG:
+
+        if (ctx->length < 24) {
+            return NGX_DECLINED;
+        }
+
+        width = p[18] * 256 + p[19];
+        height = p[22] * 256 + p[23];
+
+        break;
+
+    default:
+
+        return NGX_DECLINED;
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "image size: %d x %d", width, height);
+
+    ctx->width = width;
+    ctx->height = height;
+
+    return NGX_OK;
+}
+
+
+static ngx_buf_t *
+ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+    int                            sx, sy, dx, dy, ox, oy,
+                                   colors, transparent, size;
+    u_char                        *out;
+    ngx_buf_t                     *b;
+    ngx_uint_t                     resize;
+    gdImagePtr                     src, dst;
+    ngx_pool_cleanup_t            *cln;
+    ngx_http_image_filter_conf_t  *conf;
+
+    src = ngx_http_image_source(r, ctx);
+
+    if (src == NULL) {
+        return NULL;
+    }
+
+    sx = gdImageSX(src);
+    sy = gdImageSY(src);
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+    if ((ngx_uint_t) sx <= conf->width && (ngx_uint_t) sy <= conf->height) {
+        gdImageDestroy(src);
+        return ngx_http_image_asis(r, ctx);
+    }
+
+    colors = gdImageColorsTotal(src);
+    transparent = gdImageGetTransparent(src);
+
+    dx = sx;
+    dy = sy;
+
+    if (conf->filter == NGX_HTTP_IMAGE_RESIZE) {
+
+        if ((ngx_uint_t) dx > conf->width) {
+            dy = dy * conf->width / dx;
+            dy = dy ? dy : 1;
+            dx = conf->width;
+        }
+
+        if ((ngx_uint_t) dy > conf->height) {
+            dx = dx * conf->height / dy;
+            dx = dx ? dx : 1;
+            dy = conf->height;
+        }
+
+        resize = 1;
+
+    } else { /* NGX_HTTP_IMAGE_CROP */
+
+        resize = 0;
+
+        if ((ngx_uint_t) (dx * 100 / dy) < conf->width * 100 / conf->height) {
+
+            if ((ngx_uint_t) dx > conf->width) {
+                dy = dy * conf->width / dx;
+                dy = dy ? dy : 1;
+                dx = conf->width;
+                resize = 1;
+            }
+
+        } else {
+            if ((ngx_uint_t) dy > conf->height) {
+                dx = dx * conf->height / dy;
+                dx = dx ? dx : 1;
+                dy = conf->height;
+                resize = 1;
+            }
+        }
+    }
+
+    if (resize) {
+        dst = ngx_http_image_new(r, dx, dy, colors);
+        if (dst == NULL) {
+            gdImageDestroy(src);
+            return NULL;
+        }
+
+        gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy);
+
+        gdImageDestroy(src);
+
+    } else {
+        dst = src;
+    }
+
+    if (conf->filter == NGX_HTTP_IMAGE_CROP) {
+
+        src = dst;
+
+        if ((ngx_uint_t) dx > conf->width) {
+            ox = dx - conf->width;
+
+        } else {
+            ox = 0;
+        }
+
+        if ((ngx_uint_t) dy > conf->height) {
+            oy = dy - conf->height;
+
+        } else {
+            oy = 0;
+        }
+
+        if (ox || oy) {
+
+            dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);
+
+            if (dst == NULL) {
+                gdImageDestroy(src);
+                return NULL;
+            }
+
+            ox /= 2;
+            oy /= 2;
+
+            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "image crop: %d x %d @ %d x %d",
+                           dx, dy, ox, oy);
+
+            gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, dy - oy);
+
+            gdImageDestroy(src);
+        }
+    }
+
+    gdImageColorTransparent(dst, transparent);
+
+    out = ngx_http_image_out(r, ctx->type, dst, &size);
+
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "image: %d x %d %d", sx, sy, colors);
+
+    gdImageDestroy(dst);
+    ngx_pfree(r->pool, ctx->image);
+
+    if (out == NULL) {
+        return NULL;
+    }
+
+    cln = ngx_pool_cleanup_add(r->pool, 0);
+    if (cln == NULL) {
+        gdFree(out);
+        return NULL;
+    }
+
+    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+    if (b == NULL) {
+        gdFree(out);
+        return NULL;
+    }
+
+    cln->handler = ngx_http_image_cleanup;
+    cln->data = out;
+
+    b->pos = out;
+    b->last = out + size;
+    b->memory = 1;
+    b->last_buf = 1;
+
+    ngx_http_image_length(r, b);
+
+    return b;
+}
+
+
+static gdImagePtr
+ngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+    char        *failed;
+    gdImagePtr   img;
+
+    img = NULL;
+
+    switch (ctx->type) {
+
+    case NGX_HTTP_IMAGE_JPEG:
+        img = gdImageCreateFromJpegPtr(ctx->length, ctx->image);
+        failed = "gdImageCreateFromJpegPtr() failed";
+        break;
+
+    case NGX_HTTP_IMAGE_GIF:
+        img = gdImageCreateFromGifPtr(ctx->length, ctx->image);
+        failed = "gdImageCreateFromGifPtr() failed";
+        break;
+
+    case NGX_HTTP_IMAGE_PNG:
+        img = gdImageCreateFromPngPtr(ctx->length, ctx->image);
+        failed = "gdImageCreateFromPngPtr() failed";
+        break;
+
+    default:
+        failed = "unknown image type";
+        break;
+    }
+
+    if (img == NULL) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
+    }
+
+    return img;
+}
+
+
+static gdImagePtr
+ngx_http_image_new(ngx_http_request_t *r, int w, int h, int colors)
+{
+    gdImagePtr  img;
+
+    if (colors == 0) {
+        img = gdImageCreateTrueColor(w, h);
+
+        if (img == NULL) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "gdImageCreateTrueColor() failed");
+            return NULL;
+        }
+
+    } else {
+        img = gdImageCreate(w, h);
+
+        if (img == NULL) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "gdImageCreate() failed");
+            return NULL;
+        }
+    }
+
+    return img;
+}
+
+
+static u_char *
+ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,
+    int *size)
+{
+    char                          *failed;
+    u_char                        *out;
+    ngx_http_image_filter_conf_t  *conf;
+
+    out = NULL;
+
+    switch (type) {
+
+    case NGX_HTTP_IMAGE_JPEG:
+        conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+        out = gdImageJpegPtr(img, size, conf->jpeg_quality);
+        failed = "gdImageJpegPtr() failed";
+        break;
+
+    case NGX_HTTP_IMAGE_GIF:
+        out = gdImageGifPtr(img, size);
+        failed = "gdImageGifPtr() failed";
+        break;
+
+    case NGX_HTTP_IMAGE_PNG:
+        out = gdImagePngPtr(img, size);
+        failed = "gdImagePngPtr() failed";
+        break;
+
+    default:
+        failed = "unknown image type";
+        break;
+    }
+
+    if (out == NULL) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
+    }
+
+    return out;
+}
+
+
+static void
+ngx_http_image_cleanup(void *data)
+{
+    gdFree(data);
+}
+
+
+static void *
+ngx_http_image_filter_create_conf(ngx_conf_t *cf)
+{
+    ngx_http_image_filter_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_image_filter_conf_t));
+    if (conf == NULL) {
+        return NULL;
+    }
+
+    conf->filter = NGX_CONF_UNSET_UINT;
+    conf->jpeg_quality = NGX_CONF_UNSET;
+    conf->buffer_size = NGX_CONF_UNSET_SIZE;
+
+    return conf;
+}
+
+
+static char *
+ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_image_filter_conf_t *prev = parent;
+    ngx_http_image_filter_conf_t *conf = child;
+
+    if (conf->filter == NGX_CONF_UNSET_UINT) {
+
+        if (prev->filter == NGX_CONF_UNSET_UINT) {
+            conf->filter = NGX_HTTP_IMAGE_OFF;
+
+        } else {
+            conf->filter = prev->filter;
+            conf->width = prev->width;
+            conf->height = prev->height;
+        }
+    }
+
+    /* 75 is libjpeg default quality */
+    ngx_conf_merge_value(conf->jpeg_quality, prev->jpeg_quality, 75);
+
+    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
+                              1 * 1024 * 1024);
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_image_filter_conf_t *imcf = conf;
+
+    ngx_str_t   *value;
+    ngx_int_t    n;
+    ngx_uint_t   i;
+
+    value = cf->args->elts;
+
+    i = 1;
+
+    if (cf->args->nelts == 2) {
+        if (ngx_strcmp(value[i].data, "off") == 0) {
+            imcf->filter = NGX_HTTP_IMAGE_OFF;
+
+        } else if (ngx_strcmp(value[i].data, "test") == 0) {
+            imcf->filter = NGX_HTTP_IMAGE_TEST;
+
+        } else if (ngx_strcmp(value[i].data, "size") == 0) {
+            imcf->filter = NGX_HTTP_IMAGE_SIZE;
+
+        } else {
+            goto failed;
+        }
+
+        return NGX_CONF_OK;
+    }
+
+    if (ngx_strcmp(value[i].data, "resize") == 0) {
+        imcf->filter = NGX_HTTP_IMAGE_RESIZE;
+
+    } else if (ngx_strcmp(value[i].data, "crop") == 0) {
+        imcf->filter = NGX_HTTP_IMAGE_CROP;
+
+    } else {
+        goto failed;
+    }
+
+    i++;
+
+    if (value[i].len == 1 && value[i].data[0] == '-') {
+        imcf->width = (ngx_uint_t) -1;
+
+    } else {
+        n = ngx_atoi(value[i].data, value[i].len);
+        if (n == NGX_ERROR) {
+            goto failed;
+        }
+
+        imcf->width = (ngx_uint_t) n;
+    }
+
+    i++;
+
+    if (value[i].len == 1 && value[i].data[0] == '-') {
+        imcf->height = (ngx_uint_t) -1;
+
+    } else {
+        n = ngx_atoi(value[i].data, value[i].len);
+        if (n == NGX_ERROR) {
+            goto failed;
+        }
+
+        imcf->height = (ngx_uint_t) n;
+    }
+
+    return NGX_CONF_OK;
+
+failed:
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
+                       &value[i]);
+
+    return NGX_CONF_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_image_filter_init(ngx_conf_t *cf)
+{
+    ngx_http_next_header_filter = ngx_http_top_header_filter;
+    ngx_http_top_header_filter = ngx_http_image_header_filter;
+
+    ngx_http_next_body_filter = ngx_http_top_body_filter;
+    ngx_http_top_body_filter = ngx_http_image_body_filter;
+
+    return NGX_OK;
+}
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -83,23 +83,22 @@ ngx_module_t  ngx_http_index_module = {
 
 
 /*
- * Try to open the first index file before the test of the directory existence
- * because the valid requests should be many more than invalid ones.
- * If open() would fail, then stat() should be more quickly because some data
- * is already cached in the kernel.
- * Besides, Win32 has ERROR_PATH_NOT_FOUND (NGX_ENOTDIR).
- * Unix has ENOTDIR error, although it less helpfull - it points only
- * that path contains the usual file in place of the directory.
+ * Try to open/test the first index file before the test of directory
+ * existence because valid requests should be much more than invalid ones.
+ * If the file open()/stat() would fail, then the directory stat() should
+ * be more quickly because some data is already cached in the kernel.
+ * Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once.
+ * Unix has ENOTDIR error, however, it's less helpful than Win32's one:
+ * it only indicates that path contains an usual file in place of directory.
  */
 
 static ngx_int_t
 ngx_http_index_handler(ngx_http_request_t *r)
 {
     u_char                       *p, *name;
-    size_t                        len, nlen, root, allocated;
+    size_t                        len, root, reserve, allocated;
     ngx_int_t                     rc;
     ngx_str_t                     path, uri;
-    ngx_log_t                    *log;
     ngx_uint_t                    i, dir_tested;
     ngx_http_index_t             *index;
     ngx_open_file_info_t          of;
@@ -117,13 +116,10 @@ ngx_http_index_handler(ngx_http_request_
         return NGX_DECLINED;
     }
 
-    /* TODO: Win32 */
     if (r->zero_in_uri) {
         return NGX_DECLINED;
     }
 
-    log = r->connection->log;
-
     ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
@@ -131,6 +127,7 @@ ngx_http_index_handler(ngx_http_request_
     root = 0;
     dir_tested = 0;
     name = NULL;
+    /* suppress MSVC warning */
     path.data = NULL;
 
     index = ilcf->indices->elts;
@@ -142,8 +139,8 @@ ngx_http_index_handler(ngx_http_request_
                 return ngx_http_internal_redirect(r, &index[i].name, &r->args);
             }
 
-            len = ilcf->max_index_len;
-            nlen = index[i].name.len;
+            reserve = ilcf->max_index_len;
+            len = index[i].name.len;
 
         } else {
             ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
@@ -152,8 +149,7 @@ ngx_http_index_handler(ngx_http_request_
             e.request = r;
             e.flushed = 1;
 
-            /* 1 byte for terminating '\0' */
-
+            /* 1 is for terminating '\0' as in static names */
             len = 1;
 
             while (*(uintptr_t *) e.ip) {
@@ -161,21 +157,19 @@ ngx_http_index_handler(ngx_http_request_
                 len += lcode(&e);
             }
 
-            nlen = len;
-
             /* 16 bytes are preallocation */
 
-            len += 16;
+            reserve = len + 16;
         }
 
-        if (len > (size_t) (path.data + allocated - name)) {
+        if (reserve > allocated) {
 
-            name = ngx_http_map_uri_to_path(r, &path, &root, len);
+            name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
             if (name == NULL) {
                 return NGX_ERROR;
             }
 
-            allocated = path.len;
+            allocated = path.data + path.len - name;
         }
 
         if (index[i].values == NULL) {
@@ -196,31 +190,33 @@ ngx_http_index_handler(ngx_http_request_
             }
 
             if (*name == '/') {
-                uri.len = nlen - 1;
+                uri.len = len - 1;
                 uri.data = name;
                 return ngx_http_internal_redirect(r, &uri, &r->args);
             }
 
             path.len = e.pos - path.data;
 
-            *e.pos++ = '\0';
+            *e.pos = '\0';
         }
 
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "open index \"%V\"", &path);
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "open index \"%V\"", &path);
 
         ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
         of.directio = clcf->directio;
         of.valid = clcf->open_file_cache_valid;
         of.min_uses = clcf->open_file_cache_min_uses;
+        of.test_only = 1;
         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)
         {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, of.err,
-                           ngx_open_file_n " \"%s\" failed", path.data);
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
+                           "%s \"%s\" failed", of.failed, path.data);
 
             if (of.err == 0) {
                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -244,13 +240,13 @@ ngx_http_index_handler(ngx_http_request_
                 continue;
             }
 
-            ngx_log_error(NGX_LOG_ERR, log, of.err,
-                          ngx_open_file_n " \"%s\" failed", path.data);
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
+                          "%s \"%s\" failed", of.failed, path.data);
 
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
-        uri.len = r->uri.len + nlen - 1;
+        uri.len = r->uri.len + len - 1;
 
         if (!clcf->alias) {
             uri.data = path.data + root;
@@ -262,7 +258,7 @@ ngx_http_index_handler(ngx_http_request_
             }
 
             p = ngx_copy(uri.data, r->uri.data, r->uri.len);
-            ngx_memcpy(p, name, nlen - 1);
+            ngx_memcpy(p, name, len - 1);
         }
 
         return ngx_http_internal_redirect(r, &uri, &r->args);
@@ -296,6 +292,7 @@ ngx_http_index_test_dir(ngx_http_request
     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
     of.test_dir = 1;
+    of.test_only = 1;
     of.valid = clcf->open_file_cache_valid;
     of.errors = clcf->open_file_cache_errors;
 
@@ -323,7 +320,7 @@ ngx_http_index_test_dir(ngx_http_request
             }
 
             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
-                          ngx_open_file_n " \"%s\" failed", dir.data);
+                          "%s \"%s\" failed", of.failed, dir.data);
         }
 
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -369,7 +366,7 @@ ngx_http_index_create_loc_conf(ngx_conf_
 
     conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     conf->indices = NULL;
@@ -493,7 +490,7 @@ ngx_http_index_set_index(ngx_conf_t *cf,
                 continue;
             }
 
-            /* include the terminating '\0' to the length to use ngx_copy() */
+            /* include the terminating '\0' to the length to use ngx_memcpy() */
             index->name.len++;
 
             continue;
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -0,0 +1,795 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    u_char                       color;
+    u_char                       dummy;
+    u_short                      len;
+    ngx_queue_t                  queue;
+    ngx_msec_t                   last;
+    /* integer value, 1 corresponds to 0.001 r/s */
+    ngx_uint_t                   excess;
+    u_char                       data[1];
+} ngx_http_limit_req_node_t;
+
+
+typedef struct {
+    ngx_rbtree_t                  rbtree;
+    ngx_rbtree_node_t             sentinel;
+    ngx_queue_t                   queue;
+} ngx_http_limit_req_shctx_t;
+
+
+typedef struct {
+    ngx_http_limit_req_shctx_t  *sh;
+    ngx_slab_pool_t             *shpool;
+    /* integer value, 1 corresponds to 0.001 r/s */
+    ngx_uint_t                   rate;
+    ngx_int_t                    index;
+    ngx_str_t                    var;
+} ngx_http_limit_req_ctx_t;
+
+
+typedef struct {
+    ngx_shm_zone_t              *shm_zone;
+    /* integer value, 1 corresponds to 0.001 r/s */
+    ngx_uint_t                   burst;
+    ngx_uint_t                   nodelay;/* unsigned  nodelay:1 */
+} ngx_http_limit_req_conf_t;
+
+
+static void ngx_http_limit_req_delay(ngx_http_request_t *r);
+static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf,
+    ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lrp);
+static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
+    ngx_uint_t n);
+
+static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf);
+static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,
+    void *child);
+static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);
+
+
+static ngx_command_t  ngx_http_limit_req_commands[] = {
+
+    { ngx_string("limit_req_zone"),
+      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
+      ngx_http_limit_req_zone,
+      0,
+      0,
+      NULL },
+
+    { ngx_string("limit_req"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
+      ngx_http_limit_req,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_limit_req_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    ngx_http_limit_req_init,               /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_limit_req_create_conf,        /* create location configration */
+    ngx_http_limit_req_merge_conf          /* merge location configration */
+};
+
+
+ngx_module_t  ngx_http_limit_req_module = {
+    NGX_MODULE_V1,
+    &ngx_http_limit_req_module_ctx,        /* module context */
+    ngx_http_limit_req_commands,           /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_limit_req_handler(ngx_http_request_t *r)
+{
+    size_t                      len, n;
+    uint32_t                    hash;
+    ngx_int_t                   rc;
+    ngx_uint_t                  excess;
+    ngx_time_t                 *tp;
+    ngx_rbtree_node_t          *node;
+    ngx_http_variable_value_t  *vv;
+    ngx_http_limit_req_ctx_t   *ctx;
+    ngx_http_limit_req_node_t  *lr;
+    ngx_http_limit_req_conf_t  *lrcf;
+
+    if (r->main->limit_req_set) {
+        return NGX_DECLINED;
+    }
+
+    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
+
+    if (lrcf->shm_zone == NULL) {
+        return NGX_DECLINED;
+    }
+
+    ctx = lrcf->shm_zone->data;
+
+    vv = ngx_http_get_indexed_variable(r, ctx->index);
+
+    if (vv == NULL || vv->not_found) {
+        return NGX_DECLINED;
+    }
+
+    len = vv->len;
+
+    if (len == 0) {
+        return NGX_DECLINED;
+    }
+
+    if (len > 65535) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "the value of the \"%V\" variable "
+                      "is more than 65535 bytes: \"%v\"",
+                      &ctx->var, vv);
+        return NGX_DECLINED;
+    }
+
+    r->main->limit_req_set = 1;
+
+    hash = ngx_crc32_short(vv->data, len);
+
+    ngx_shmtx_lock(&ctx->shpool->mutex);
+
+    ngx_http_limit_req_expire(ctx, 1);
+
+    rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &lr);
+
+    if (lr) {
+        ngx_queue_remove(&lr->queue);
+
+        ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
+
+        excess = lr->excess;
+
+    } else {
+        excess = 0;
+    }
+
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000);
+
+    if (rc == NGX_BUSY) {
+        ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "limiting requests, excess: %ui.%03ui by zone \"%V\"",
+                      excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name);
+
+        return NGX_HTTP_SERVICE_UNAVAILABLE;
+    }
+
+    if (rc == NGX_AGAIN) {
+        ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+        if (lrcf->nodelay) {
+            return NGX_DECLINED;
+        }
+
+        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+                      "delaying request, excess: %ui.%03ui, by zone \"%V\"",
+                      excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name);
+
+        if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        r->read_event_handler = ngx_http_test_reading;
+        r->write_event_handler = ngx_http_limit_req_delay;
+        ngx_add_timer(r->connection->write, (ngx_msec_t) excess);
+
+        return NGX_AGAIN;
+    }
+
+    if (rc == NGX_OK) {
+        goto done;
+    }
+
+    /* rc == NGX_DECLINED */
+
+    n = offsetof(ngx_rbtree_node_t, color)
+        + offsetof(ngx_http_limit_req_node_t, data)
+        + len;
+
+    node = ngx_slab_alloc_locked(ctx->shpool, n);
+    if (node == NULL) {
+
+        ngx_http_limit_req_expire(ctx, 0);
+
+        node = ngx_slab_alloc_locked(ctx->shpool, n);
+        if (node == NULL) {
+            ngx_shmtx_unlock(&ctx->shpool->mutex);
+            return NGX_HTTP_SERVICE_UNAVAILABLE;
+        }
+    }
+
+    lr = (ngx_http_limit_req_node_t *) &node->color;
+
+    node->key = hash;
+    lr->len = (u_char) len;
+
+    tp = ngx_timeofday();
+    lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+
+    lr->excess = 0;
+    ngx_memcpy(lr->data, vv->data, len);
+
+    ngx_rbtree_insert(&ctx->sh->rbtree, node);
+
+    ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
+
+done:
+
+    ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+    return NGX_DECLINED;
+}
+
+
+static void
+ngx_http_limit_req_delay(ngx_http_request_t *r)
+{
+    ngx_event_t  *wev;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "limit_req delay");
+
+    wev = r->connection->write;
+
+    if (!wev->timedout) {
+
+        if (ngx_handle_write_event(wev, 0) != NGX_OK) {
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        }
+
+        return;
+    }
+
+    wev->timedout = 0;
+
+    if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
+    r->read_event_handler = ngx_http_block_reading;
+    r->write_event_handler = ngx_http_core_run_phases;
+
+    ngx_http_core_run_phases(r);
+}
+
+
+static void
+ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_rbtree_node_t          **p;
+    ngx_http_limit_req_node_t   *lrn, *lrnt;
+
+    for ( ;; ) {
+
+        if (node->key < temp->key) {
+
+            p = &temp->left;
+
+        } else if (node->key > temp->key) {
+
+            p = &temp->right;
+
+        } else { /* node->key == temp->key */
+
+            lrn = (ngx_http_limit_req_node_t *) &node->color;
+            lrnt = (ngx_http_limit_req_node_t *) &temp->color;
+
+            p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)
+                ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}
+
+
+static ngx_int_t
+ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash,
+    u_char *data, size_t len, ngx_http_limit_req_node_t **lrp)
+{
+    ngx_int_t                   rc, excess;
+    ngx_time_t                 *tp;
+    ngx_msec_t                  now;
+    ngx_msec_int_t              ms;
+    ngx_rbtree_node_t          *node, *sentinel;
+    ngx_http_limit_req_ctx_t   *ctx;
+    ngx_http_limit_req_node_t  *lr;
+
+    ctx = lrcf->shm_zone->data;
+
+    node = ctx->sh->rbtree.root;
+    sentinel = ctx->sh->rbtree.sentinel;
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        /* hash == node->key */
+
+        do {
+            lr = (ngx_http_limit_req_node_t *) &node->color;
+
+            rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len);
+
+            if (rc == 0) {
+
+                tp = ngx_timeofday();
+
+                now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+                ms = (ngx_msec_int_t) (now - lr->last);
+
+                excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
+
+                if (excess < 0) {
+                    excess = 0;
+                }
+
+                lr->excess = excess;
+                lr->last = now;
+
+                *lrp = lr;
+
+                if ((ngx_uint_t) excess > lrcf->burst) {
+                    return NGX_BUSY;
+                }
+
+                if (excess) {
+                    return NGX_AGAIN;
+                }
+
+                return NGX_OK;
+            }
+
+            node = (rc < 0) ? node->left : node->right;
+
+        } while (node != sentinel && hash == node->key);
+
+        break;
+    }
+
+    *lrp = NULL;
+
+    return NGX_DECLINED;
+}
+
+
+static void
+ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
+{
+    ngx_int_t                   excess;
+    ngx_time_t                 *tp;
+    ngx_msec_t                  now;
+    ngx_queue_t                *q;
+    ngx_msec_int_t              ms;
+    ngx_rbtree_node_t          *node;
+    ngx_http_limit_req_node_t  *lr;
+
+    tp = ngx_timeofday();
+
+    now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+
+    /*
+     * n == 1 deletes one or two zero rate entries
+     * n == 0 deletes oldest entry by force
+     *        and one or two zero rate entries
+     */
+
+    while (n < 3) {
+
+        if (ngx_queue_empty(&ctx->sh->queue)) {
+            return;
+        }
+
+        q = ngx_queue_last(&ctx->sh->queue);
+
+        lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
+
+        if (n++ != 0) {
+
+            ms = (ngx_msec_int_t) (now - lr->last);
+            ms = ngx_abs(ms);
+
+            if (ms < 60000) {
+                return;
+            }
+
+            excess = lr->excess - ctx->rate * ms / 1000;
+
+            if (excess > 0) {
+                return;
+            }
+        }
+
+        ngx_queue_remove(q);
+
+        node = (ngx_rbtree_node_t *)
+                   ((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
+
+        ngx_rbtree_delete(&ctx->sh->rbtree, node);
+
+        ngx_slab_free_locked(ctx->shpool, node);
+    }
+}
+
+
+static ngx_int_t
+ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
+{
+    ngx_http_limit_req_ctx_t  *octx = data;
+
+    size_t                     len;
+    ngx_http_limit_req_ctx_t  *ctx;
+
+    ctx = shm_zone->data;
+
+    if (octx) {
+        if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) {
+            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
+                          "limit_req \"%V\" uses the \"%V\" variable "
+                          "while previously it used the \"%V\" variable",
+                          &shm_zone->shm.name, &ctx->var, &octx->var);
+            return NGX_ERROR;
+        }
+
+        ctx->sh = octx->sh;
+        ctx->shpool = octx->shpool;
+
+        return NGX_OK;
+    }
+
+    ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+    if (shm_zone->shm.exists) {
+        ctx->sh = ctx->shpool->data;
+
+        return NGX_OK;
+    }
+
+    ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t));
+    if (ctx->sh == NULL) {
+        return NGX_ERROR;
+    }
+
+    ctx->shpool->data = ctx->sh;
+
+    ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
+                    ngx_http_limit_req_rbtree_insert_value);
+
+    ngx_queue_init(&ctx->sh->queue);
+
+    len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len;
+
+    ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
+    if (ctx->shpool->log_ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
+                &shm_zone->shm.name);
+
+    return NGX_OK;
+}
+
+
+static void *
+ngx_http_limit_req_create_conf(ngx_conf_t *cf)
+{
+    ngx_http_limit_req_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));
+    if (conf == NULL) {
+        return NULL;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     conf->shm_zone = NULL;
+     *     conf->burst = 0;
+     *     conf->nodelay = 0;
+     */
+
+    return conf;
+}
+
+
+static char *
+ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_limit_req_conf_t *prev = parent;
+    ngx_http_limit_req_conf_t *conf = child;
+
+    if (conf->shm_zone == NULL) {
+        *conf = *prev;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    u_char                    *p;
+    size_t                     size, len;
+    ngx_str_t                 *value, name, s;
+    ngx_int_t                  rate, scale;
+    ngx_uint_t                 i;
+    ngx_shm_zone_t            *shm_zone;
+    ngx_http_limit_req_ctx_t  *ctx;
+
+    value = cf->args->elts;
+
+    ctx = NULL;
+    size = 0;
+    rate = 1;
+    scale = 1;
+    name.len = 0;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
+
+            name.data = value[i].data + 5;
+
+            p = (u_char *) ngx_strchr(name.data, ':');
+
+            if (p) {
+                *p = '\0';
+
+                name.len = p - name.data;
+
+                p++;
+
+                s.len = value[i].data + value[i].len - p;
+                s.data = p;
+
+                size = ngx_parse_size(&s);
+                if (size > 8191) {
+                    continue;
+                }
+            }
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid zone size \"%V\"", &value[i]);
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
+
+            len = value[i].len;
+            p = value[i].data + len - 3;
+
+            if (ngx_strncmp(p, "r/s", 3) == 0) {
+                scale = 1;
+                len -= 3;
+
+            } else if (ngx_strncmp(p, "r/m", 3) == 0) {
+                scale = 60;
+                len -= 3;
+            }
+
+            rate = ngx_atoi(value[i].data + 5, len - 5);
+            if (rate <= NGX_ERROR) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid rate \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (value[i].data[0] == '$') {
+
+            value[i].len--;
+            value[i].data++;
+
+            ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));
+            if (ctx == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            ctx->index = ngx_http_get_variable_index(cf, &value[i]);
+            if (ctx->index == NGX_ERROR) {
+                return NGX_CONF_ERROR;
+            }
+
+            ctx->var = value[i];
+
+            continue;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[i]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (name.len == 0 || size == 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"%V\" must have \"zone\" parameter",
+                           &cmd->name);
+        return NGX_CONF_ERROR;
+    }
+
+    if (ctx == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "no variable is defined for limit_req_zone \"%V\"",
+                           &cmd->name);
+        return NGX_CONF_ERROR;
+    }
+
+    ctx->rate = rate * 1000 / scale;
+
+    shm_zone = ngx_shared_memory_add(cf, &name, size,
+                                     &ngx_http_limit_req_module);
+    if (shm_zone == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (shm_zone->data) {
+        ctx = shm_zone->data;
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                   "limit_req_zone \"%V\" is already bound to variable \"%V\"",
+                   &value[1], &ctx->var);
+        return NGX_CONF_ERROR;
+    }
+
+    shm_zone->init = ngx_http_limit_req_init_zone;
+    shm_zone->data = ctx;
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_limit_req_conf_t  *lrcf = conf;
+
+    ngx_int_t    burst;
+    ngx_str_t   *value, s;
+    ngx_uint_t   i;
+
+    if (lrcf->shm_zone) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    burst = 0;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
+
+            s.len = value[i].len - 5;
+            s.data = value[i].data + 5;
+
+            lrcf->shm_zone = ngx_shared_memory_add(cf, &s, 0,
+                                                   &ngx_http_limit_req_module);
+            if (lrcf->shm_zone == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {
+
+            burst = ngx_atoi(value[i].data + 6, value[i].len - 6);
+            if (burst <= 0) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid burst rate \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) {
+            lrcf->nodelay = 1;
+            continue;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[i]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (lrcf->shm_zone == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"%V\" must have \"zone\" parameter",
+                           &cmd->name);
+        return NGX_CONF_ERROR;
+    }
+
+    if (lrcf->shm_zone->data == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "unknown limit_req_zone \"%V\"",
+                           &lrcf->shm_zone->shm.name);
+        return NGX_CONF_ERROR;
+    }
+
+    lrcf->burst = burst * 1000;
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_limit_req_init(ngx_conf_t *cf)
+{
+    ngx_http_handler_pt        *h;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    *h = ngx_http_limit_req_handler;
+
+    return NGX_OK;
+}
--- a/src/http/modules/ngx_http_limit_zone_module.c
+++ b/src/http/modules/ngx_http_limit_zone_module.c
@@ -189,6 +189,10 @@ ngx_http_limit_zone_handler(ngx_http_req
 
                 ngx_shmtx_unlock(&shpool->mutex);
 
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "limiting connections by zone \"%V\"",
+                              &lzcf->shm_zone->shm.name);
+
                 return NGX_HTTP_SERVICE_UNAVAILABLE;
             }
 
@@ -312,6 +316,7 @@ ngx_http_limit_zone_init_zone(ngx_shm_zo
 {
     ngx_http_limit_zone_ctx_t  *octx = data;
 
+    size_t                      len;
     ngx_slab_pool_t            *shpool;
     ngx_rbtree_node_t          *sentinel;
     ngx_http_limit_zone_ctx_t  *ctx;
@@ -323,7 +328,7 @@ ngx_http_limit_zone_init_zone(ngx_shm_zo
             ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
                           "limit_zone \"%V\" uses the \"%V\" variable "
                           "while previously it used the \"%V\" variable",
-                          &shm_zone->name, &ctx->var, &octx->var);
+                          &shm_zone->shm.name, &ctx->var, &octx->var);
             return NGX_ERROR;
         }
 
@@ -334,11 +339,19 @@ ngx_http_limit_zone_init_zone(ngx_shm_zo
 
     shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
 
+    if (shm_zone->shm.exists) {
+        ctx->rbtree = shpool->data;
+
+        return NGX_OK;
+    }
+
     ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));
     if (ctx->rbtree == NULL) {
         return NGX_ERROR;
     }
 
+    shpool->data = ctx->rbtree;
+
     sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));
     if (sentinel == NULL) {
         return NGX_ERROR;
@@ -347,6 +360,16 @@ ngx_http_limit_zone_init_zone(ngx_shm_zo
     ngx_rbtree_init(ctx->rbtree, sentinel,
                     ngx_http_limit_zone_rbtree_insert_value);
 
+    len = sizeof(" in limit_zone \"\"") + shm_zone->shm.name.len;
+
+    shpool->log_ctx = ngx_slab_alloc(shpool, len);
+    if (shpool->log_ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_sprintf(shpool->log_ctx, " in limit_zone \"%V\"%Z",
+                &shm_zone->shm.name);
+
     return NGX_OK;
 }
 
@@ -358,7 +381,7 @@ ngx_http_limit_zone_create_conf(ngx_conf
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_zone_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -462,6 +485,10 @@ ngx_http_limit_conn(ngx_conf_t *cf, ngx_
     ngx_int_t   n;
     ngx_str_t  *value;
 
+    if (lzcf->shm_zone) {
+        return "is duplicate";
+    }
+
     value = cf->args->elts;
 
     lzcf->shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -385,6 +385,8 @@ ngx_http_log_script_write(ngx_http_reque
 
         of.valid = clcf->open_file_cache_valid;
         of.min_uses = clcf->open_file_cache_min_uses;
+        of.test_dir = 1;
+        of.test_only = 1;
         of.errors = clcf->open_file_cache_errors;
         of.events = clcf->open_file_cache_events;
 
@@ -433,13 +435,13 @@ ngx_http_log_script_write(ngx_http_reque
     of.log = 1;
     of.valid = llcf->open_file_cache_valid;
     of.min_uses = llcf->open_file_cache_min_uses;
-    of.directio = NGX_MAX_OFF_T_VALUE;
+    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
 
     if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool)
         != NGX_OK)
     {
         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
-                      ngx_open_file_n " \"%s\" failed", log.data);
+                      "%s \"%s\" failed", of.failed, log.data);
         /* simulate successfull logging */
         return len;
     }
@@ -712,18 +714,18 @@ ngx_http_log_create_main_conf(ngx_conf_t
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     if (ngx_array_init(&conf->formats, cf->pool, 4, sizeof(ngx_http_log_fmt_t))
         != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     fmt = ngx_array_push(&conf->formats);
     if (fmt == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     fmt->name.len = sizeof("combined") - 1;
@@ -733,7 +735,7 @@ ngx_http_log_create_main_conf(ngx_conf_t
 
     fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t));
     if (fmt->ops == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     return conf;
@@ -747,7 +749,7 @@ ngx_http_log_create_loc_conf(ngx_conf_t 
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     conf->open_file_cache = NGX_CONF_UNSET_PTR;
@@ -863,7 +865,7 @@ ngx_http_log_set_log(ngx_conf_t *cf, ngx
         }
 
     } else {
-        if (ngx_conf_full_name(cf->cycle, &value[1], 0) == NGX_ERROR) {
+        if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
             return NGX_CONF_ERROR;
         }
 
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -160,7 +160,7 @@ ngx_http_map_create_conf(ngx_conf_t *cf)
 
     mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t));
     if (mcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     mcf->hash_max_size = NGX_CONF_UNSET_UINT;
@@ -374,7 +374,7 @@ ngx_http_map(ngx_conf_t *cf, ngx_command
     if (ngx_strcmp(value[0].data, "include") == 0) {
         file = value[1];
 
-        if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){
+        if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){
             return NGX_CONF_ERROR;
         }
 
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -183,7 +183,8 @@ ngx_http_memcached_handler(ngx_http_requ
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    u->schema = mlcf->upstream.schema;
+    u->schema.len = sizeof("memcached://") - 1;
+    u->schema.data = (u_char *) "memcached://";
 
     u->peer.log = r->connection->log;
     u->peer.log_error = NGX_ERROR_ERR;
@@ -512,7 +513,7 @@ ngx_http_memcached_create_loc_conf(ngx_c
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -521,7 +522,6 @@ ngx_http_memcached_create_loc_conf(ngx_c
      *     conf->upstream.bufs.num = 0;
      *     conf->upstream.next_upstream = 0;
      *     conf->upstream.temp_path = NULL;
-     *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
      */
@@ -584,7 +584,6 @@ ngx_http_memcached_merge_loc_conf(ngx_co
 
     if (conf->upstream.upstream == NULL) {
         conf->upstream.upstream = prev->upstream.upstream;
-        conf->upstream.schema = prev->upstream.schema;
     }
 
     if (conf->index == NGX_CONF_UNSET) {
@@ -598,13 +597,13 @@ ngx_http_memcached_merge_loc_conf(ngx_co
 static char *
 ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    ngx_http_memcached_loc_conf_t *lcf = conf;
+    ngx_http_memcached_loc_conf_t *mlcf = conf;
 
     ngx_str_t                 *value;
     ngx_url_t                  u;
     ngx_http_core_loc_conf_t  *clcf;
 
-    if (lcf->upstream.schema.len) {
+    if (mlcf->upstream.upstream) {
         return "is duplicate";
     }
 
@@ -615,14 +614,11 @@ ngx_http_memcached_pass(ngx_conf_t *cf, 
     u.url = value[1];
     u.no_resolve = 1;
 
-    lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
-    if (lcf->upstream.upstream == NULL) {
+    mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+    if (mlcf->upstream.upstream == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    lcf->upstream.schema.len = sizeof("memcached://") - 1;
-    lcf->upstream.schema.data = (u_char *) "memcached://";
-
     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
 
     clcf->handler = ngx_http_memcached_handler;
@@ -631,9 +627,9 @@ ngx_http_memcached_pass(ngx_conf_t *cf, 
         clcf->auto_redirect = 1;
     }
 
-    lcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);
+    mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);
 
-    if (lcf->index == NGX_ERROR) {
+    if (mlcf->index == NGX_ERROR) {
         return NGX_CONF_ERROR;
     }
 
--- a/src/http/modules/ngx_http_not_modified_filter_module.c
+++ b/src/http/modules/ngx_http_not_modified_filter_module.c
@@ -47,10 +47,11 @@ ngx_module_t  ngx_http_not_modified_filt
 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
 
 
-static
-ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r)
+static ngx_int_t
+ngx_http_not_modified_header_filter(ngx_http_request_t *r)
 {
-    time_t  ims;
+    time_t                     ims;
+    ngx_http_core_loc_conf_t  *clcf;
 
     if (r->headers_out.status != NGX_HTTP_OK
         || r != r->main
@@ -60,29 +61,39 @@ ngx_int_t ngx_http_not_modified_header_f
         return ngx_http_next_header_filter(r);
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
+        return ngx_http_next_header_filter(r);
+    }
+
     ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
                               r->headers_in.if_modified_since->value.len);
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http ims:%d lm:%d", ims, r->headers_out.last_modified_time);
 
-    /*
-     * I think that the equality of the dates is correcter
-     */
+    if (ims != r->headers_out.last_modified_time) {
 
-    if (ims == r->headers_out.last_modified_time) {
-        r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
-        r->headers_out.content_type.len = 0;
-        ngx_http_clear_content_length(r);
-        ngx_http_clear_accept_ranges(r);
+        if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
+            || ims < r->headers_out.last_modified_time)
+        {
+            return ngx_http_next_header_filter(r);
+        }
     }
 
+    r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
+    r->headers_out.status_line.len = 0;
+    r->headers_out.content_type.len = 0;
+    ngx_http_clear_content_length(r);
+    ngx_http_clear_accept_ranges(r);
+
     return ngx_http_next_header_filter(r);
 }
 
 
-static
-ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf)
+static ngx_int_t
+ngx_http_not_modified_filter_init(ngx_conf_t *cf)
 {
     ngx_http_next_header_filter = ngx_http_top_header_filter;
     ngx_http_top_header_filter = ngx_http_not_modified_header_filter;
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -32,6 +32,8 @@ struct ngx_http_proxy_redirect_s {
 
 
 typedef struct {
+    ngx_str_t                      key_start;
+    ngx_str_t                      schema;
     ngx_str_t                      host_header;
     ngx_str_t                      port;
     ngx_str_t                      uri;
@@ -62,6 +64,10 @@ typedef struct {
     ngx_str_t                      location;
     ngx_str_t                      url;
 
+#if (NGX_HTTP_CACHE)
+    ngx_http_complex_value_t       cache_key;
+#endif
+
     ngx_http_proxy_vars_t          vars;
 
     ngx_flag_t                     redirect;
@@ -88,6 +94,9 @@ typedef struct {
 
 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);
+#if (NGX_HTTP_CACHE)
+static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r);
+#endif
 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);
@@ -115,6 +124,8 @@ static ngx_int_t ngx_http_proxy_add_vari
 static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf);
 static char *ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf,
     void *parent, void *child);
+static ngx_int_t ngx_http_proxy_merge_headers(ngx_conf_t *cf,
+    ngx_http_proxy_loc_conf_t *conf, ngx_http_proxy_loc_conf_t *prev);
 
 static char *ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
@@ -122,6 +133,12 @@ static char *ngx_http_proxy_redirect(ngx
     void *conf);
 static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+#if (NGX_HTTP_CACHE)
+static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char *ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+#endif
 
 static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data);
 
@@ -134,8 +151,7 @@ static char *ngx_http_proxy_upstream_fai
 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 void ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v);
 
 
 static ngx_conf_post_t  ngx_http_proxy_lowat_post =
@@ -151,11 +167,24 @@ static ngx_conf_bitmask_t  ngx_http_prox
     { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
     { ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 },
     { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
+    { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },
     { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
     { ngx_null_string, 0 }
 };
 
 
+static ngx_conf_bitmask_t  ngx_http_proxy_ignore_headers_masks[] = {
+    { ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT },
+    { ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES },
+    { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES },
+    { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL },
+    { ngx_null_string, 0 }
+};
+
+
+ngx_module_t  ngx_http_proxy_module;
+
+
 static ngx_command_t  ngx_http_proxy_commands[] = {
 
     { ngx_string("proxy_pass"),
@@ -305,12 +334,65 @@ static ngx_command_t  ngx_http_proxy_com
       offsetof(ngx_http_proxy_loc_conf_t, upstream.busy_buffers_size_conf),
       NULL },
 
+#if (NGX_HTTP_CACHE)
+
+    { ngx_string("proxy_cache"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_http_proxy_cache,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("proxy_cache_key"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_http_proxy_cache_key,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("proxy_cache_path"),
+      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
+      ngx_http_file_cache_set_slot,
+      0,
+      0,
+      &ngx_http_proxy_module },
+
+    { ngx_string("proxy_cache_valid"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+      ngx_http_file_cache_valid_set_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_valid),
+      NULL },
+
+    { ngx_string("proxy_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_proxy_loc_conf_t, upstream.cache_min_uses),
+      NULL },
+
+    { ngx_string("proxy_cache_use_stale"),
+      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_proxy_loc_conf_t, upstream.cache_use_stale),
+      &ngx_http_proxy_next_upstream_masks },
+
+    { ngx_string("proxy_cache_methods"),
+      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_proxy_loc_conf_t, upstream.cache_methods),
+      &ngx_http_upstream_cache_method_mask },
+
+#endif
+
     { ngx_string("proxy_temp_path"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
       ngx_conf_set_path_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_path),
-      (void *) ngx_garbage_collector_temp_handler },
+      NULL },
 
     { ngx_string("proxy_max_temp_file_size"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
@@ -361,6 +443,13 @@ static ngx_command_t  ngx_http_proxy_com
       offsetof(ngx_http_proxy_loc_conf_t, upstream.hide_headers),
       NULL },
 
+    { ngx_string("proxy_ignore_headers"),
+      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_proxy_loc_conf_t, upstream.ignore_headers),
+      &ngx_http_proxy_ignore_headers_masks },
+
 #if (NGX_HTTP_SSL)
 
     { ngx_string("proxy_ssl_session_reuse"),
@@ -432,6 +521,40 @@ static ngx_str_t  ngx_http_proxy_hide_he
 };
 
 
+#if (NGX_HTTP_CACHE)
+
+static ngx_keyval_t  ngx_http_proxy_cache_headers[] = {
+    { 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_string("If-Modified-Since"), ngx_string("") },
+    { ngx_string("If-Unmodified-Since"), ngx_string("") },
+    { ngx_string("If-Match-None"), ngx_string("") },
+    { ngx_string("If-Match"), ngx_string("") },
+    { ngx_string("Range"), ngx_string("") },
+    { ngx_string("If-Range"), ngx_string("") },
+    { ngx_null_string, ngx_null_string }
+};
+
+
+static ngx_str_t  ngx_http_proxy_hide_cache_headers[] = {
+    ngx_string("Date"),
+    ngx_string("Server"),
+    ngx_string("X-Pad"),
+    ngx_string("X-Accel-Expires"),
+    ngx_string("X-Accel-Redirect"),
+    ngx_string("X-Accel-Limit-Rate"),
+    ngx_string("X-Accel-Buffering"),
+    ngx_string("X-Accel-Charset"),
+    ngx_string("Set-Cookie"),
+    ngx_string("P3P"),
+    ngx_null_string
+};
+
+#endif
+
+
 static ngx_http_variable_t  ngx_http_proxy_vars[] = {
 
     { ngx_string("proxy_host"), NULL, ngx_http_proxy_host_variable, 0,
@@ -454,6 +577,11 @@ static ngx_http_variable_t  ngx_http_pro
 };
 
 
+static ngx_path_init_t  ngx_http_proxy_temp_path = {
+    ngx_string(NGX_HTTP_PROXY_TEMP_PATH), { 1, 2, 0 }
+};
+
+
 static ngx_int_t
 ngx_http_proxy_handler(ngx_http_request_t *r)
 {
@@ -480,7 +608,7 @@ ngx_http_proxy_handler(ngx_http_request_
 
     if (plcf->proxy_lengths == 0) {
         ctx->vars = plcf->vars;
-        u->schema = plcf->upstream.schema;
+        u->schema = plcf->vars.schema;
 #if (NGX_HTTP_SSL)
         u->ssl = (plcf->upstream.ssl != NULL);
 #endif
@@ -501,6 +629,9 @@ ngx_http_proxy_handler(ngx_http_request_
 
     u->conf = &plcf->upstream;
 
+#if (NGX_HTTP_CACHE)
+    u->create_key = ngx_http_proxy_create_key;
+#endif
     u->create_request = ngx_http_proxy_create_request;
     u->reinit_request = ngx_http_proxy_reinit_request;
     u->process_header = ngx_http_proxy_process_status_line;
@@ -536,10 +667,12 @@ static ngx_int_t
 ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
     ngx_http_proxy_loc_conf_t *plcf)
 {
-    size_t     add;
-    u_short    port;
-    ngx_str_t  proxy;
-    ngx_url_t  u;
+    u_char               *p;
+    size_t                add;
+    u_short               port;
+    ngx_str_t             proxy;
+    ngx_url_t             url;
+    ngx_http_upstream_t  *u;
 
     if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0,
                             plcf->proxy_values->elts)
@@ -569,49 +702,171 @@ ngx_http_proxy_eval(ngx_http_request_t *
         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) {
+    u = r->upstream;
+
+    u->schema.len = add;
+    u->schema.data = proxy.data;
+
+    ngx_memzero(&url, sizeof(ngx_url_t));
+
+    url.url.len = proxy.len - add;
+    url.url.data = proxy.data + add;
+    url.default_port = port;
+    url.uri_part = 1;
+    url.no_resolve = 1;
+
+    if (ngx_parse_url(r->pool, &url) != NGX_OK) {
+        if (url.err) {
             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "%s in upstream \"%V\"", u.err, &u.url);
+                          "%s in upstream \"%V\"", url.err, &url.url);
         }
 
         return NGX_ERROR;
     }
 
-    if (ngx_http_proxy_set_vars(r->pool, &u, &ctx->vars) != NGX_OK) {
+    if (url.uri.len && url.uri.data[0] == '?') {
+        p = ngx_pnalloc(r->pool, url.uri.len + 1);
+        if (p == NULL) {
+            return NGX_ERROR;
+        }
+
+        *p++ = '/';
+        ngx_memcpy(p, url.uri.data, url.uri.len);
+
+        url.uri.len++;
+        url.uri.data = p - 1;
+    }
+
+    ctx->vars.key_start = u->schema;
+
+    ngx_http_proxy_set_vars(&url, &ctx->vars);
+
+    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
+    if (u->resolved == NULL) {
         return NGX_ERROR;
     }
 
-    r->upstream->resolved = ngx_pcalloc(r->pool,
-                                        sizeof(ngx_http_upstream_resolved_t));
-    if (r->upstream->resolved == NULL) {
-        return NGX_ERROR;
+    if (url.addrs && url.addrs[0].sockaddr) {
+        u->resolved->sockaddr = url.addrs[0].sockaddr;
+        u->resolved->socklen = url.addrs[0].socklen;
+        u->resolved->naddrs = 1;
+        u->resolved->host = url.addrs[0].name;
+
+    } else {
+        u->resolved->host = url.host;
+        u->resolved->port = (in_port_t) (url.no_port ? port : url.port);
+        u->resolved->no_port = url.no_port;
     }
 
-    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;
 }
 
 
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_proxy_create_key(ngx_http_request_t *r)
+{
+    size_t                      len, loc_len;
+    u_char                     *p;
+    uintptr_t                   escape;
+    ngx_str_t                  *key;
+    ngx_http_upstream_t        *u;
+    ngx_http_proxy_ctx_t       *ctx;
+    ngx_http_proxy_loc_conf_t  *plcf;
+
+    u = r->upstream;
+
+    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+    key = ngx_array_push(&r->cache->keys);
+    if (key == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (plcf->cache_key.value.len) {
+
+        if (ngx_http_complex_value(r, &plcf->cache_key, key) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        return NGX_OK;
+    }
+
+    *key = ctx->vars.key_start;
+
+    key = ngx_array_push(&r->cache->keys);
+    if (key == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (plcf->proxy_lengths) {
+
+        *key = ctx->vars.uri;
+        u->uri = ctx->vars.uri;
+
+        return NGX_OK;
+
+    } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main)
+    {
+        *key = r->unparsed_uri;
+        u->uri = r->unparsed_uri;
+
+        return NGX_OK;
+    }
+
+    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);
+    } else {
+        escape = 0;
+    }
+
+    len = ctx->vars.uri.len + r->uri.len - loc_len + escape
+          + sizeof("?") - 1 + r->args.len;
+
+    p = ngx_pnalloc(r->pool, len);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    key->data = p;
+
+    if (r->valid_location) {
+        p = ngx_copy(p, ctx->vars.uri.data, ctx->vars.uri.len);
+    }
+
+    if (escape) {
+        ngx_escape_uri(p, r->uri.data + loc_len,
+                       r->uri.len - loc_len, NGX_ESCAPE_URI);
+        p += r->uri.len - loc_len + escape;
+
+    } else {
+        p = ngx_copy(p, r->uri.data + loc_len, r->uri.len - loc_len);
+    }
+
+    if (r->args.len > 0) {
+        *p++ = '?';
+        p = ngx_copy(p, r->args.data, r->args.len);
+    }
+
+    key->len = p - key->data;
+    u->uri = *key;
+
+    return NGX_OK;
+}
+
+#endif
+
+
 static ngx_int_t
 ngx_http_proxy_create_request(ngx_http_request_t *r)
 {
-    size_t                        len, loc_len, body_len;
+    size_t                        len, uri_len, loc_len, body_len;
     uintptr_t                     escape;
     ngx_buf_t                    *b;
     ngx_str_t                     method;
@@ -652,12 +907,12 @@ ngx_http_proxy_create_request(ngx_http_r
     ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
 
     if (plcf->proxy_lengths) {
-        len += ctx->vars.uri.len;
+        uri_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;
+        uri_len = r->unparsed_uri.len;
 
     } else {
         loc_len = (r->valid_location && ctx->vars.uri.len) ?
@@ -668,10 +923,18 @@ ngx_http_proxy_create_request(ngx_http_r
                                         r->uri.len - loc_len, NGX_ESCAPE_URI);
         }
 
-        len += ctx->vars.uri.len + r->uri.len - loc_len + escape
-               + sizeof("?") - 1 + r->args.len;
+        uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape
+                  + sizeof("?") - 1 + r->args.len;
     }
 
+    if (uri_len == 0) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "zero length URI to proxy");
+        return NGX_ERROR;
+    }
+
+    len += uri_len;
+
     ngx_http_script_flush_no_cacheable_variables(r, plcf->flushes);
 
     if (plcf->body_set_len) {
@@ -957,6 +1220,17 @@ ngx_http_proxy_process_status_line(ngx_h
     u = r->upstream;
 
     if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) {
+
+#if (NGX_HTTP_CACHE)
+
+        if (r->cache) {
+            r->http_version = NGX_HTTP_VERSION_9;
+            u->headers_in.status_n = NGX_HTTP_OK;
+            return NGX_OK;
+        }
+
+#endif
+
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "upstream sent no valid HTTP/1.0 header");
 
@@ -973,8 +1247,11 @@ ngx_http_proxy_process_status_line(ngx_h
         return NGX_OK;
     }
 
+    if (u->state) {
+        u->state->status = ctx->status;
+    }
+
     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,
@@ -1607,17 +1884,19 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
      * set by ngx_pcalloc():
      *
      *     conf->upstream.bufs.num = 0;
+     *     conf->upstream.ignore_headers = 0;
      *     conf->upstream.next_upstream = 0;
+     *     conf->upstream.cache_use_stale = 0;
+     *     conf->upstream.cache_methods = 0;
      *     conf->upstream.temp_path = NULL;
      *     conf->upstream.hide_headers_hash = { NULL, 0 };
-     *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
      *     conf->upstream.store_lengths = NULL;
@@ -1653,6 +1932,12 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
     conf->upstream.pass_request_headers = NGX_CONF_UNSET;
     conf->upstream.pass_request_body = NGX_CONF_UNSET;
 
+#if (NGX_HTTP_CACHE)
+    conf->upstream.cache = NGX_CONF_UNSET_PTR;
+    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
+    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
+#endif
+
     conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
     conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
 
@@ -1680,20 +1965,16 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     ngx_http_proxy_loc_conf_t *prev = parent;
     ngx_http_proxy_loc_conf_t *conf = child;
 
-    u_char                       *p;
-    size_t                        size;
-    uintptr_t                    *code;
-    ngx_uint_t                    i;
-    ngx_keyval_t                 *src, *s, *h;
-    ngx_hash_key_t               *hk;
-    ngx_hash_init_t               hash;
-    ngx_http_proxy_redirect_t    *pr;
-    ngx_http_script_compile_t     sc;
-    ngx_http_script_copy_code_t  *copy;
+    size_t                      size;
+    ngx_str_t                  *h;
+    ngx_keyval_t               *s;
+    ngx_hash_init_t             hash;
+    ngx_http_proxy_redirect_t  *pr;
+    ngx_http_script_compile_t   sc;
 
     if (conf->upstream.store != 0) {
         ngx_conf_merge_value(conf->upstream.store,
-                                  prev->upstream.store, 0);
+                              prev->upstream.store, 0);
 
         if (conf->upstream.store_lengths == NULL) {
             conf->upstream.store_lengths = prev->upstream.store_lengths;
@@ -1817,6 +2098,11 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     }
 
 
+    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
+                              prev->upstream.ignore_headers,
+                              NGX_CONF_BITMASK_SET);
+
+
     ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
                               prev->upstream.next_upstream,
                               (NGX_CONF_BITMASK_SET
@@ -1828,10 +2114,59 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
                                        |NGX_HTTP_UPSTREAM_FT_OFF;
     }
 
-    ngx_conf_merge_path_value(conf->upstream.temp_path,
+    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
                               prev->upstream.temp_path,
-                              NGX_HTTP_PROXY_TEMP_PATH, 1, 2, 0,
-                              ngx_garbage_collector_temp_handler, cf);
+                              &ngx_http_proxy_temp_path)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+
+#if (NGX_HTTP_CACHE)
+
+    ngx_conf_merge_ptr_value(conf->upstream.cache,
+                              prev->upstream.cache, NULL);
+
+    if (conf->upstream.cache && conf->upstream.cache->data == NULL) {
+        ngx_shm_zone_t  *shm_zone;
+
+        shm_zone = conf->upstream.cache;
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"proxy_cache\" zone \"%V\" is unknown",
+                           &shm_zone->shm.name);
+
+        return NGX_CONF_ERROR;
+    }
+
+    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
+                              prev->upstream.cache_min_uses, 1);
+
+    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
+                              prev->upstream.cache_use_stale,
+                              (NGX_CONF_BITMASK_SET
+                               |NGX_HTTP_UPSTREAM_FT_OFF));
+
+    if (conf->upstream.cache_methods == 0) {
+        conf->upstream.cache_methods = prev->upstream.cache_methods;
+    }
+
+    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
+
+    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
+        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
+                                         |NGX_HTTP_UPSTREAM_FT_OFF;
+    }
+
+    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
+                             prev->upstream.cache_valid, NULL);
+
+    if (conf->cache_key.value.data == NULL) {
+        conf->cache_key = prev->cache_key;
+    }
+
+#endif
 
     if (conf->method.len == 0) {
         conf->method = prev->method;
@@ -1894,6 +2229,12 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
         conf->proxy_values = prev->proxy_values;
     }
 
+#if (NGX_HTTP_SSL)
+    if (conf->upstream.ssl == NULL) {
+        conf->upstream.ssl = prev->upstream.ssl;
+    }
+#endif
+
     ngx_conf_merge_uint_value(conf->headers_hash_max_size,
                               prev->headers_hash_max_size, 512);
 
@@ -1907,9 +2248,18 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     hash.bucket_size = conf->headers_hash_bucket_size;
     hash.name = "proxy_headers_hash";
 
+#if (NGX_HTTP_CACHE)
+
+    h = conf->upstream.cache ? ngx_http_proxy_hide_cache_headers:
+                               ngx_http_proxy_hide_headers;
+#else
+
+    h = ngx_http_proxy_hide_headers;
+
+#endif
+
     if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
-                                            &prev->upstream,
-                                            ngx_http_proxy_hide_headers, &hash)
+                                            &prev->upstream, h, &hash)
         != NGX_OK)
     {
         return NGX_CONF_ERROR;
@@ -1917,9 +2267,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
     if (conf->upstream.upstream == NULL) {
         conf->upstream.upstream = prev->upstream.upstream;
-
         conf->vars = prev->vars;
-        conf->upstream.schema = prev->upstream.schema;
     }
 
 
@@ -1964,6 +2312,27 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
         s->value.data = (u_char *) "$proxy_internal_body_length";
     }
 
+    if (ngx_http_proxy_merge_headers(cf, conf, prev) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_merge_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,
+    ngx_http_proxy_loc_conf_t *prev)
+{
+    u_char                       *p;
+    size_t                        size;
+    uintptr_t                    *code;
+    ngx_uint_t                    i;
+    ngx_keyval_t                 *src, *s, *h;
+    ngx_hash_key_t               *hk;
+    ngx_hash_init_t               hash;
+    ngx_http_script_compile_t     sc;
+    ngx_http_script_copy_code_t  *copy;
 
     if (conf->headers_source == NULL) {
         conf->flushes = prev->flushes;
@@ -1973,38 +2342,53 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
         conf->headers_source = prev->headers_source;
     }
 
-    if (conf->headers_set_hash.buckets) {
-        return NGX_CONF_OK;
+    if (conf->headers_set_hash.buckets
+#if (NGX_HTTP_CACHE)
+        && ((conf->upstream.cache == NULL) == (prev->upstream.cache == NULL))
+#endif
+       )
+    {
+        return NGX_OK;
     }
 
 
     conf->headers_names = ngx_array_create(cf->pool, 4, sizeof(ngx_hash_key_t));
     if (conf->headers_names == NULL) {
-        return NGX_CONF_ERROR;
+        return NGX_ERROR;
     }
 
     if (conf->headers_source == NULL) {
         conf->headers_source = ngx_array_create(cf->pool, 4,
                                                 sizeof(ngx_keyval_t));
         if (conf->headers_source == NULL) {
-            return NGX_CONF_ERROR;
+            return NGX_ERROR;
         }
     }
 
     conf->headers_set_len = ngx_array_create(cf->pool, 64, 1);
     if (conf->headers_set_len == NULL) {
-        return NGX_CONF_ERROR;
+        return NGX_ERROR;
     }
 
     conf->headers_set = ngx_array_create(cf->pool, 512, 1);
     if (conf->headers_set == NULL) {
-        return NGX_CONF_ERROR;
+        return NGX_ERROR;
     }
 
 
     src = conf->headers_source->elts;
 
-    for (h = ngx_http_proxy_headers; h->key.len; h++) {
+#if (NGX_HTTP_CACHE)
+
+    h = conf->upstream.cache ? ngx_http_proxy_cache_headers:
+                               ngx_http_proxy_headers;
+#else
+
+    h = ngx_http_proxy_headers;
+
+#endif
+
+    while (h->key.len) {
 
         for (i = 0; i < conf->headers_source->nelts; i++) {
             if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
@@ -2014,7 +2398,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
         s = ngx_array_push(conf->headers_source);
         if (s == NULL) {
-            return NGX_CONF_ERROR;
+            return NGX_ERROR;
         }
 
         *s = *h;
@@ -2023,7 +2407,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
     next:
 
-        continue;
+        h++;
     }
 
 
@@ -2032,7 +2416,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
         hk = ngx_array_push(conf->headers_names);
         if (hk == NULL) {
-            return NGX_CONF_ERROR;
+            return NGX_ERROR;
         }
 
         hk->key = src[i].key;
@@ -2047,7 +2431,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
             copy = ngx_array_push_n(conf->headers_set_len,
                                     sizeof(ngx_http_script_copy_code_t));
             if (copy == NULL) {
-                return NGX_CONF_ERROR;
+                return NGX_ERROR;
             }
 
             copy->code = (ngx_http_script_code_pt)
@@ -2064,7 +2448,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
             copy = ngx_array_push_n(conf->headers_set, size);
             if (copy == NULL) {
-                return NGX_CONF_ERROR;
+                return NGX_ERROR;
             }
 
             copy->code = ngx_http_script_copy_code;
@@ -2082,7 +2466,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
             copy = ngx_array_push_n(conf->headers_set_len,
                                     sizeof(ngx_http_script_copy_code_t));
             if (copy == NULL) {
-                return NGX_CONF_ERROR;
+                return NGX_ERROR;
             }
 
             copy->code = (ngx_http_script_code_pt)
@@ -2096,7 +2480,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
             copy = ngx_array_push_n(conf->headers_set, size);
             if (copy == NULL) {
-                return NGX_CONF_ERROR;
+                return NGX_ERROR;
             }
 
             copy->code = ngx_http_script_copy_code;
@@ -2116,14 +2500,14 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
             sc.values = &conf->headers_set;
 
             if (ngx_http_script_compile(&sc) != NGX_OK) {
-                return NGX_CONF_ERROR;
+                return NGX_ERROR;
             }
 
 
             copy = ngx_array_push_n(conf->headers_set_len,
                                     sizeof(ngx_http_script_copy_code_t));
             if (copy == NULL) {
-                return NGX_CONF_ERROR;
+                return NGX_ERROR;
             }
 
             copy->code = (ngx_http_script_code_pt)
@@ -2137,7 +2521,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
             copy = ngx_array_push_n(conf->headers_set, size);
             if (copy == NULL) {
-                return NGX_CONF_ERROR;
+                return NGX_ERROR;
             }
 
             copy->code = ngx_http_script_copy_code;
@@ -2149,14 +2533,14 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
         code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t));
         if (code == NULL) {
-            return NGX_CONF_ERROR;
+            return NGX_ERROR;
         }
 
         *code = (uintptr_t) NULL;
 
         code = ngx_array_push_n(conf->headers_set, sizeof(uintptr_t));
         if (code == NULL) {
-            return NGX_CONF_ERROR;
+            return NGX_ERROR;
         }
 
         *code = (uintptr_t) NULL;
@@ -2164,7 +2548,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
     code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t));
     if (code == NULL) {
-        return NGX_CONF_ERROR;
+        return NGX_ERROR;
     }
 
     *code = (uintptr_t) NULL;
@@ -2182,10 +2566,10 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
                       conf->headers_names->nelts)
         != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NGX_ERROR;
     }
 
-    return NGX_CONF_OK;
+    return NGX_OK;
 }
 
 
@@ -2202,7 +2586,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
     ngx_http_core_loc_conf_t   *clcf;
     ngx_http_script_compile_t   sc;
 
-    if (plcf->upstream.schema.len) {
+    if (plcf->upstream.upstream || plcf->proxy_lengths) {
         return "is duplicate";
     }
 
@@ -2278,16 +2662,16 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
         return NGX_CONF_ERROR;
     }
 
-    if (ngx_http_proxy_set_vars(cf->pool, &u, &plcf->vars) != NGX_OK) {
-        return NGX_CONF_ERROR;
-    }
-
-    plcf->upstream.schema.len = add;
-    plcf->upstream.schema.data = url->data;
-    plcf->location = clcf->name;
+    plcf->vars.schema.len = add;
+    plcf->vars.schema.data = url->data;
+    plcf->vars.key_start = plcf->vars.schema;
+
+    ngx_http_proxy_set_vars(&u, &plcf->vars);
 
     clcf->handler = ngx_http_proxy_handler;
 
+    plcf->location = clcf->name;
+
     if (clcf->named
 #if (NGX_PCRE)
         || clcf->regex
@@ -2333,10 +2717,26 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, 
 
     value = cf->args->elts;
 
-    if (ngx_strcmp(value[1].data, "off") == 0) {
-        plcf->redirect = 0;
-        plcf->redirects = NULL;
-        return NGX_CONF_OK;
+    if (cf->args->nelts == 2) {
+        if (ngx_strcmp(value[1].data, "off") == 0) {
+            plcf->redirect = 0;
+            plcf->redirects = NULL;
+            return NGX_CONF_OK;
+        }
+
+        if (ngx_strcmp(value[1].data, "false") == 0) {
+            ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+                           "invalid parameter \"false\", use \"off\" instead");
+            plcf->redirect = 0;
+            plcf->redirects = NULL;
+            return NGX_CONF_OK;
+        }
+
+        if (ngx_strcmp(value[1].data, "default") != 0) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid parameter \"%V\"", &value[1]);
+            return NGX_CONF_ERROR;
+        }
     }
 
     if (plcf->redirects == NULL) {
@@ -2352,7 +2752,7 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, 
         return NGX_CONF_ERROR;
     }
 
-    if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "default") == 0) {
+    if (ngx_strcmp(value[1].data, "default") == 0) {
         if (plcf->url.data == NULL) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "\"proxy_rewrite_location default\" must go "
@@ -2415,20 +2815,31 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx
     ngx_str_t                  *value;
     ngx_http_script_compile_t   sc;
 
-    if (plcf->upstream.store != NGX_CONF_UNSET || plcf->upstream.store_lengths)
+    if (plcf->upstream.store != NGX_CONF_UNSET
+        || plcf->upstream.store_lengths)
     {
         return "is duplicate";
     }
 
     value = cf->args->elts;
 
-    if (ngx_strcmp(value[1].data, "on") == 0) {
-        plcf->upstream.store = 1;
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        plcf->upstream.store = 0;
         return NGX_CONF_OK;
     }
 
-    if (ngx_strcmp(value[1].data, "off") == 0) {
-        plcf->upstream.store = 0;
+#if (NGX_HTTP_CACHE)
+
+    if (plcf->upstream.cache != NGX_CONF_UNSET_PTR
+        && plcf->upstream.cache != NULL)
+    {
+        return "is incompatible with \"proxy_cache\"";
+    }
+
+#endif
+
+    if (ngx_strcmp(value[1].data, "on") == 0) {
+        plcf->upstream.store = 1;
         return NGX_CONF_OK;
     }
 
@@ -2453,6 +2864,70 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx
 }
 
 
+#if (NGX_HTTP_CACHE)
+
+static char *
+ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_proxy_loc_conf_t *plcf = conf;
+
+    ngx_str_t  *value;
+
+    value = cf->args->elts;
+
+    if (plcf->upstream.cache != NGX_CONF_UNSET_PTR) {
+        return "is duplicate";
+    }
+
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        plcf->upstream.cache = NULL;
+        return NGX_CONF_OK;
+    }
+
+    if (plcf->upstream.store > 0 || plcf->upstream.store_lengths) {
+        return "is incompatible with \"proxy_store\"";
+    }
+
+    plcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
+                                                 &ngx_http_proxy_module);
+    if (plcf->upstream.cache == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_proxy_loc_conf_t *plcf = conf;
+
+    ngx_str_t                         *value;
+    ngx_http_compile_complex_value_t   ccv;
+
+    value = cf->args->elts;
+
+    if (plcf->cache_key.value.len) {
+        return "is duplicate";
+    }
+
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[1];
+    ccv.complex_value = &plcf->cache_key;
+
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+#endif
+
+
 static char *
 ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data)
 {
@@ -2543,12 +3018,13 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, n
 #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 void
+ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v)
 {
     if (u->family != AF_UNIX) {
+
         if (u->no_port || u->port == u->default_port) {
+
             v->host_header = u->host;
 
             if (u->default_port == 80) {
@@ -2566,14 +3042,15 @@ ngx_http_proxy_set_vars(ngx_pool_t *pool
             v->port = u->port_text;
         }
 
+        v->key_start.len += v->host_header.len;
+
     } 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->key_start.len += sizeof("unix:") - 1 + u->host.len + 1;
     }
 
     v->uri = u->uri;
-
-    return NGX_OK;
 }
--- a/src/http/modules/ngx_http_random_index_module.c
+++ b/src/http/modules/ngx_http_random_index_module.c
@@ -86,7 +86,6 @@ ngx_http_random_index_handler(ngx_http_r
         return NGX_DECLINED;
     }
 
-    /* TODO: Win32 */
     if (r->zero_in_uri) {
         return NGX_DECLINED;
     }
@@ -281,7 +280,7 @@ ngx_http_random_index_create_loc_conf(ng
 
     conf = ngx_palloc(cf->pool, sizeof(ngx_http_random_index_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     conf->enable = NGX_CONF_UNSET;
--- a/src/http/modules/ngx_http_range_filter_module.c
+++ b/src/http/modules/ngx_http_range_filter_module.c
@@ -222,7 +222,7 @@ ngx_http_range_header_filter(ngx_http_re
     }
 
     if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))
-        == NGX_ERROR)
+        != NGX_OK)
     {
         return NGX_ERROR;
     }
@@ -234,6 +234,7 @@ ngx_http_range_header_filter(ngx_http_re
         ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
 
         r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
+        r->headers_out.status_line.len = 0;
 
         if (ctx->ranges.nelts == 1) {
             return ngx_http_range_singlepart_header(r, ctx);
@@ -490,6 +491,8 @@ ngx_http_range_multipart_header(ngx_http
         return NGX_ERROR;
     }
 
+    r->headers_out.content_type_lowcase = NULL;
+
     /* "Content-Type: multipart/byteranges; boundary=0123456789" */
 
     r->headers_out.content_type.len =
@@ -498,6 +501,7 @@ ngx_http_range_multipart_header(ngx_http
                                        boundary)
                            - r->headers_out.content_type.data;
 
+    r->headers_out.content_type_len = r->headers_out.content_type.len;
 
     /* the size of the last boundary CRLF "--0123456789--" CRLF */
 
@@ -748,6 +752,7 @@ ngx_http_range_multipart_body(ngx_http_r
     ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in,
     ngx_http_output_body_filter_pt ngx_http_next_body_filter)
 {
+    off_t              body_start;
     ngx_buf_t         *b, *buf;
     ngx_uint_t         i;
     ngx_chain_t       *out, *hcl, *rcl, *dcl, **ll;
@@ -757,6 +762,12 @@ ngx_http_range_multipart_body(ngx_http_r
     buf = in->buf;
     range = ctx->ranges.elts;
 
+#if (NGX_HTTP_CACHE)
+    body_start = r->cached ? r->cache->body_start : 0;
+#else
+    body_start = 0;
+#endif
+
     for (i = 0; i < ctx->ranges.nelts; i++) {
 
         /*
@@ -817,8 +828,8 @@ ngx_http_range_multipart_body(ngx_http_r
         b->file = buf->file;
 
         if (buf->in_file) {
-            b->file_pos = range[i].start;
-            b->file_last = range[i].end;
+            b->file_pos = body_start + range[i].start;
+            b->file_last = body_start + range[i].end;
         }
 
         if (ngx_buf_in_memory(buf)) {
--- a/src/http/modules/ngx_http_realip_module.c
+++ b/src/http/modules/ngx_http_realip_module.c
@@ -14,8 +14,6 @@
 #define NGX_HTTP_REALIP_HEADER   2
 
 
-/* AF_INET only */
-
 typedef struct {
     in_addr_t          mask;
     in_addr_t          addr;
@@ -209,6 +207,10 @@ found:
 
     /* AF_INET only */
 
+    if (r->connection->sockaddr->sa_family != AF_INET) {
+        return NGX_DECLINED;
+    }
+
     sin = (struct sockaddr_in *) c->sockaddr;
 
     from = rlcf->from->elts;
@@ -280,7 +282,7 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx
 
     ngx_int_t                rc;
     ngx_str_t               *value;
-    ngx_inet_cidr_t          in_cidr;
+    ngx_cidr_t               cidr;
     ngx_http_realip_from_t  *from;
 
     if (rlcf->from == NULL) {
@@ -298,7 +300,7 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx
 
     value = cf->args->elts;
 
-    rc = ngx_ptocidr(&value[1], &in_cidr);
+    rc = ngx_ptocidr(&value[1], &cidr);
 
     if (rc == NGX_ERROR) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
@@ -306,13 +308,19 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx
         return NGX_CONF_ERROR;
     }
 
+    if (cidr.family != AF_INET) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"realip_from\" supports IPv4 only");
+        return NGX_CONF_ERROR;
+    }
+
     if (rc == NGX_DONE) {
         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                            "low address bits of %V are meaningless", &value[1]);
     }
 
-    from->mask = in_cidr.mask;
-    from->addr = in_cidr.addr;
+    from->mask = cidr.u.in.mask;
+    from->addr = cidr.u.in.addr;
 
     return NGX_CONF_OK;
 }
@@ -352,7 +360,7 @@ ngx_http_realip_create_loc_conf(ngx_conf
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
--- a/src/http/modules/ngx_http_referer_module.c
+++ b/src/http/modules/ngx_http_referer_module.c
@@ -221,7 +221,7 @@ ngx_http_referer_create_conf(ngx_conf_t 
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
 #if (NGX_PCRE)
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -12,7 +12,6 @@
 typedef struct {
     ngx_array_t  *codes;        /* uintptr_t */
 
-    ngx_uint_t    captures;
     ngx_uint_t    stack_size;
 
     ngx_flag_t    log;
@@ -157,16 +156,6 @@ ngx_http_rewrite_handler(ngx_http_reques
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    if (rlcf->captures) {
-        e->captures = ngx_palloc(r->pool, rlcf->captures * sizeof(int));
-        if (e->captures == NULL) {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
-        }
-
-    } else {
-        e->captures = NULL;
-    }
-
     e->ip = rlcf->codes->elts;
     e->request = r;
     e->quote = 1;
@@ -231,7 +220,7 @@ ngx_http_rewrite_create_loc_conf(ngx_con
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     conf->stack_size = NGX_CONF_UNSET_UINT;
@@ -436,10 +425,6 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com
 
     if (regex->ncaptures) {
         regex->ncaptures = (regex->ncaptures + 1) * 3;
-
-        if (lcf->captures < regex->ncaptures) {
-            lcf->captures = regex->ncaptures;
-        }
     }
 
     regex_end = ngx_http_script_add_code(lcf->codes,
@@ -618,11 +603,6 @@ ngx_http_rewrite_if(ngx_conf_t *cf, ngx_
     }
 
 
-    if (lcf->captures < nlcf->captures) {
-        lcf->captures = nlcf->captures;
-    }
-
-
     if (elts != lcf->codes->elts) {
         if_code = (ngx_http_script_if_code_t *)
                    ((u_char *) if_code + ((u_char *) lcf->codes->elts - elts));
@@ -777,10 +757,6 @@ ngx_http_rewrite_if_condition(ngx_conf_t
 
             if (n) {
                 regex->ncaptures = (n + 1) * 3;
-
-                if (lcf->captures < regex->ncaptures) {
-                    lcf->captures = regex->ncaptures;
-                }
             }
 
             return NGX_CONF_OK;
--- a/src/http/modules/ngx_http_secure_link_module.c
+++ b/src/http/modules/ngx_http_secure_link_module.c
@@ -113,7 +113,7 @@ url_start:
 
     len = last - p;
 
-    if (end - start != 32 || len < 3) {
+    if (end - start != 32 || len == 0) {
         goto not_found;
     }
 
@@ -152,7 +152,7 @@ ngx_http_secure_link_create_conf(ngx_con
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_secure_link_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -375,7 +375,6 @@ ngx_http_ssi_body_filter(ngx_http_reques
     ngx_uint_t                 i, index;
     ngx_chain_t               *cl, **ll;
     ngx_table_elt_t           *param;
-    ngx_http_request_t        *pr;
     ngx_http_ssi_ctx_t        *ctx, *mctx;
     ngx_http_ssi_block_t      *bl;
     ngx_http_ssi_param_t      *prm;
@@ -398,49 +397,42 @@ ngx_http_ssi_body_filter(ngx_http_reques
     /* add the incoming chain to the chain ctx->in */
 
     if (in) {
-        if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
+        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
             return NGX_ERROR;
         }
     }
 
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http ssi filter \"%V?%V\"", &r->uri, &r->args);
+
     if (ctx->wait) {
-        if (r->connection->data != r) {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http ssi filter \"%V\" wait", &r->uri);
+
+        if (r != r->connection->data) {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http ssi filter wait \"%V?%V\" non-active",
+                           &ctx->wait->uri, &ctx->wait->args);
+
             return NGX_AGAIN;
         }
 
-        for (pr = ctx->wait->parent; pr; pr = pr->parent) {
-            if (pr == r) {
-                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                               "http ssi filter \"%V\" flush", &r->uri);
-
-                rc = ngx_http_next_body_filter(r, NULL);
-
-                if (ctx->wait->done) {
-                    ctx->wait = NULL;
-                }
-
-                if (rc == NGX_ERROR || rc == NGX_AGAIN) {
-                    return rc;
-                }
-
-                break;
-            }
-        }
-
-        if (ctx->wait == r) {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http ssi filter \"%V\" continue", &r->uri);
+        if (ctx->wait->done) {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http ssi filter wait \"%V?%V\" done",
+                           &ctx->wait->uri, &ctx->wait->args);
+
             ctx->wait = NULL;
+
+        } else {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http ssi filter wait \"%V?%V\"",
+                           &ctx->wait->uri, &ctx->wait->args);
+
+            return ngx_http_next_body_filter(r, NULL);
         }
     }
 
     slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http ssi filter \"%V\"", &r->uri);
-
     while (ctx->in || ctx->buf) {
 
         if (ctx->buf == NULL ){
@@ -788,16 +780,12 @@ ngx_http_ssi_body_filter(ngx_http_reques
                     }
                 }
 
-                if (cmd->flush) {
-
-                    if (ctx->out) {
-                        rc = ngx_http_ssi_output(r, ctx);
-
-                    } else {
-                        rc = ngx_http_next_body_filter(r, NULL);
-                    }
-
-                    if (rc == NGX_ERROR) {
+                if (cmd->flush && ctx->out) {
+
+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                                   "ssi flush");
+
+                    if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) {
                         return NGX_ERROR;
                     }
                 }
@@ -947,12 +935,6 @@ ngx_http_ssi_output(ngx_http_request_t *
             break;
         }
 
-#if (NGX_HAVE_WRITE_ZEROCOPY)
-        if (b->zerocopy_busy) {
-            break;
-        }
-#endif
-
         if (b->shadow) {
             b->shadow->pos = b->shadow->last;
         }
@@ -1899,6 +1881,7 @@ ngx_http_ssi_include(ngx_http_request_t 
 
     if (uri == NULL) {
         uri = file;
+        wait = (ngx_str_t *) -1;
     }
 
     rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX);
@@ -2001,6 +1984,10 @@ ngx_http_ssi_include(ngx_http_request_t 
         }
     }
 
+    if (wait) {
+        flags |= NGX_HTTP_SUBREQUEST_WAITED;
+    }
+
     if (set) {
         key = ngx_hash_strlow(set->data, set->data, set->len);
 
@@ -2033,16 +2020,10 @@ ngx_http_ssi_include(ngx_http_request_t 
             psr->data = &var->value;
         }
 
-        flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY;
+        flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED;
     }
 
-    rc = ngx_http_subrequest(r, uri, &args, &sr, psr, flags);
-
-    if (rc == NGX_DONE) {
-        return NGX_DONE;
-    }
-
-    if (rc == NGX_ERROR) {
+    if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) {
         return NGX_HTTP_SSI_ERROR;
     }
 
@@ -2050,17 +2031,17 @@ ngx_http_ssi_include(ngx_http_request_t 
         return NGX_OK;
     }
 
-    if (rc == NGX_AGAIN) {
-        if (ctx->wait == NULL) {
-            ctx->wait = sr;
-
-        } else {
-            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "only one subrequest may be waited at the same time");
-        }
+    if (ctx->wait == NULL) {
+        ctx->wait = sr;
+
+        return NGX_AGAIN;
+
+    } else {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "only one subrequest may be waited at the same time");
     }
 
-    return rc;
+    return NGX_OK;
 }
 
 
@@ -2079,7 +2060,7 @@ ngx_http_ssi_stub_output(ngx_http_reques
     out = data;
 
     if (!r->header_sent) {
-        if (ngx_http_set_content_type(r) == NGX_ERROR) {
+        if (ngx_http_set_content_type(r) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -2708,14 +2689,14 @@ ngx_http_ssi_create_main_conf(ngx_conf_t
 
     smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t));
     if (smcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     smcf->commands.pool = cf->pool;
     smcf->commands.temp_pool = cf->temp_pool;
 
     if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     return smcf;
@@ -2755,7 +2736,7 @@ ngx_http_ssi_create_loc_conf(ngx_conf_t 
 
     slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t));
     if (slcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -302,7 +302,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t 
 
     sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t));
     if (sscf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -320,8 +320,8 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t 
 
     sscf->enable = NGX_CONF_UNSET;
     sscf->prefer_server_ciphers = NGX_CONF_UNSET;
-    sscf->verify = NGX_CONF_UNSET;
-    sscf->verify_depth = NGX_CONF_UNSET;
+    sscf->verify = NGX_CONF_UNSET_UINT;
+    sscf->verify_depth = NGX_CONF_UNSET_UINT;
     sscf->builtin_session_cache = NGX_CONF_UNSET;
     sscf->session_timeout = NGX_CONF_UNSET;
 
@@ -564,6 +564,7 @@ ngx_http_ssl_session_cache(ngx_conf_t *c
 
             for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
                 if (value[i].data[j] == ':') {
+                    value[i].data[j] = '\0';
                     break;
                 }
 
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -66,7 +66,6 @@ ngx_http_static_handler(ngx_http_request
         return NGX_DECLINED;
     }
 
-    /* TODO: Win32 */
     if (r->zero_in_uri) {
         return NGX_DECLINED;
     }
@@ -129,13 +128,13 @@ ngx_http_static_handler(ngx_http_request
 
         if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
             ngx_log_error(level, log, of.err,
-                          ngx_open_file_n " \"%s\" failed", path.data);
+                          "%s \"%s\" failed", of.failed, path.data);
         }
 
         return rc;
     }
 
-    r->root_tested = 1;
+    r->root_tested = !r->error_page;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
 
--- a/src/http/modules/ngx_http_sub_filter_module.c
+++ b/src/http/modules/ngx_http_sub_filter_module.c
@@ -10,17 +10,14 @@
 
 
 typedef struct {
-    ngx_str_t      match;
-    ngx_str_t      sub;
-
-    ngx_hash_t     types;
+    ngx_str_t                  match;
+    ngx_http_complex_value_t   value;
 
-    ngx_array_t   *sub_lengths;
-    ngx_array_t   *sub_values;
+    ngx_hash_t                 types;
 
-    ngx_flag_t     once;
+    ngx_flag_t                 once;
 
-    ngx_array_t   *types_keys;
+    ngx_array_t               *types_keys;
 } ngx_http_sub_loc_conf_t;
 
 
@@ -31,27 +28,27 @@ typedef enum {
 
 
 typedef struct {
-    ngx_str_t      match;
+    ngx_str_t                  match;
 
-    ngx_uint_t     once;   /* unsigned  once:1 */
+    ngx_uint_t                 once;   /* unsigned  once:1 */
 
-    ngx_buf_t     *buf;
+    ngx_buf_t                 *buf;
 
-    u_char        *pos;
-    u_char        *copy_start;
-    u_char        *copy_end;
+    u_char                    *pos;
+    u_char                    *copy_start;
+    u_char                    *copy_end;
 
-    ngx_chain_t   *in;
-    ngx_chain_t   *out;
-    ngx_chain_t  **last_out;
-    ngx_chain_t   *busy;
-    ngx_chain_t   *free;
+    ngx_chain_t               *in;
+    ngx_chain_t               *out;
+    ngx_chain_t              **last_out;
+    ngx_chain_t               *busy;
+    ngx_chain_t               *free;
 
-    ngx_str_t      sub;
+    ngx_str_t                  sub;
 
-    ngx_uint_t     state;
-    size_t         saved;
-    size_t         looked;
+    ngx_uint_t                 state;
+    size_t                     saved;
+    size_t                     looked;
 } ngx_http_sub_ctx_t;
 
 
@@ -154,7 +151,6 @@ ngx_http_sub_header_filter(ngx_http_requ
 
     ctx->match = slcf->match;
     ctx->last_out = &ctx->out;
-    ctx->sub = slcf->sub;
 
     r->filter_need_in_memory = 1;
 
@@ -204,7 +200,7 @@ ngx_http_sub_body_filter(ngx_http_reques
     /* add the incoming chain to the chain ctx->in */
 
     if (in) {
-        if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
+        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
             return NGX_ERROR;
         }
     }
@@ -346,9 +342,8 @@ ngx_http_sub_body_filter(ngx_http_reques
 
             if (ctx->sub.data == NULL) {
 
-                if (ngx_http_script_run(r, &ctx->sub, slcf->sub_lengths->elts,
-                                        0, slcf->sub_values->elts)
-                    == NULL)
+                if (ngx_http_complex_value(r, &slcf->value, &ctx->sub)
+                    != NGX_OK)
                 {
                     return NGX_ERROR;
                 }
@@ -465,12 +460,6 @@ ngx_http_sub_output(ngx_http_request_t *
             break;
         }
 
-#if (NGX_HAVE_WRITE_ZEROCOPY)
-        if (b->zerocopy_busy) {
-            break;
-        }
-#endif
-
         if (b->shadow) {
             b->shadow->pos = b->shadow->last;
         }
@@ -615,9 +604,8 @@ ngx_http_sub_filter(ngx_conf_t *cf, ngx_
 {
     ngx_http_sub_loc_conf_t *slcf = conf;
 
-    ngx_str_t                  *value;
-    ngx_int_t                   n;
-    ngx_http_script_compile_t   sc;
+    ngx_str_t                         *value;
+    ngx_http_compile_complex_value_t   ccv;
 
     if (slcf->match.len) {
         return "is duplicate";
@@ -629,24 +617,13 @@ ngx_http_sub_filter(ngx_conf_t *cf, ngx_
 
     slcf->match = value[1];
 
-    n = ngx_http_script_variables_count(&value[2]);
-
-    if (n == 0) {
-        slcf->sub = value[2];
-        return NGX_CONF_OK;
-    }
-
-    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
 
-    sc.cf = cf;
-    sc.source = &value[2];
-    sc.lengths = &slcf->sub_lengths;
-    sc.values = &slcf->sub_values;
-    sc.variables = n;
-    sc.complete_lengths = 1;
-    sc.complete_values = 1;
+    ccv.cf = cf;
+    ccv.value = &value[2];
+    ccv.complex_value = &slcf->value;
 
-    if (ngx_http_script_compile(&sc) != NGX_OK) {
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
@@ -661,7 +638,7 @@ ngx_http_sub_create_conf(ngx_conf_t *cf)
 
     slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t));
     if (slcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -690,10 +667,8 @@ ngx_http_sub_merge_conf(ngx_conf_t *cf, 
     ngx_conf_merge_value(conf->once, prev->once, 1);
     ngx_conf_merge_str_value(conf->match, prev->match, "");
 
-    if (conf->sub.data == NULL && conf->sub_lengths == NULL) {
-        conf->sub = prev->sub;
-        conf->sub_lengths = prev->sub_lengths;
-        conf->sub_values = prev->sub_values;
+    if (conf->value.value.len == 0) {
+        conf->value = prev->value;
     }
 
     if (ngx_http_merge_types(cf, conf->types_keys, &conf->types,
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
@@ -15,7 +15,6 @@ typedef struct {
 
     ngx_uint_t                         hash;
 
-    /* AF_INET only */
     u_char                             addr[3];
 
     u_char                             tries;
@@ -111,11 +110,20 @@ ngx_http_upstream_init_ip_hash_peer(ngx_
     r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;
 
     /* AF_INET only */
-    sin = (struct sockaddr_in *) r->connection->sockaddr;
-    p = (u_char *) &sin->sin_addr.s_addr;
-    iphp->addr[0] = p[0];
-    iphp->addr[1] = p[1];
-    iphp->addr[2] = p[2];
+
+    if (r->connection->sockaddr->sa_family == AF_INET) {
+
+        sin = (struct sockaddr_in *) r->connection->sockaddr;
+        p = (u_char *) &sin->sin_addr.s_addr;
+        iphp->addr[0] = p[0];
+        iphp->addr[1] = p[1];
+        iphp->addr[2] = p[2];
+
+    } else {
+        iphp->addr[0] = 0;
+        iphp->addr[1] = 0;
+        iphp->addr[2] = 0;
+    }
 
     iphp->hash = 89;
     iphp->tries = 0;
--- a/src/http/modules/ngx_http_userid_filter_module.c
+++ b/src/http/modules/ngx_http_userid_filter_module.c
@@ -360,10 +360,16 @@ 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;
-    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;
+    ngx_connection_t     *c;
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6  *sin6;
+#endif
+
     /*
      * TODO: in the threaded mode the sequencers should be in TLS and their
      * ranges should be divided between threads
@@ -384,11 +390,33 @@ ngx_http_userid_set_uid(ngx_http_request
 
         } else {
             if (conf->service == NGX_CONF_UNSET) {
-                if (ngx_http_server_addr(r, NULL) != NGX_OK) {
+
+                c = r->connection;
+
+                if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
                     return NGX_ERROR;
                 }
 
-                ctx->uid_set[0] = htonl(r->in_addr);
+                switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+                case AF_INET6:
+                    sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+
+                    p = (u_char *) &ctx->uid_set[0];
+
+                    *p++ = sin6->sin6_addr.s6_addr[12];
+                    *p++ = sin6->sin6_addr.s6_addr[13];
+                    *p++ = sin6->sin6_addr.s6_addr[14];
+                    *p = sin6->sin6_addr.s6_addr[15];
+
+                    break;
+#endif
+                default: /* AF_INET */
+                    sin = (struct sockaddr_in *) c->local_sockaddr;
+                    ctx->uid_set[0] = sin->sin_addr.s_addr;
+                    break;
+                }
 
             } else {
                 ctx->uid_set[0] = htonl(conf->service);
@@ -542,7 +570,7 @@ ngx_http_userid_create_conf(ngx_conf_t *
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_userid_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
--- a/src/http/modules/ngx_http_xslt_filter_module.c
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -15,6 +15,10 @@
 #include <libxslt/transform.h>
 #include <libxslt/xsltutils.h>
 
+#if (NGX_HAVE_EXSLT)
+#include <libexslt/exslt.h>
+#endif
+
 
 #ifndef NGX_HTTP_XSLT_REUSE_DTD
 #define NGX_HTTP_XSLT_REUSE_DTD  1
@@ -34,14 +38,8 @@ typedef struct {
 
 
 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_array_t          params;       /* ngx_http_complex_value_t */
 } ngx_http_xslt_sheet_t;
 
 
@@ -66,7 +64,6 @@ typedef struct {
 
 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);
 
@@ -283,7 +280,7 @@ ngx_http_xslt_body_filter(ngx_http_reque
             return ngx_http_xslt_send(r, ctx, NULL);
         }
 
-        if (cl->buf->last_buf) {
+        if (cl->buf->last_buf || cl->buf->last_in_chain) {
 
             ctx->doc = ctx->ctxt->myDoc;
 
@@ -322,14 +319,15 @@ ngx_http_xslt_send(ngx_http_request_t *r
     ctx->done = 1;
 
     if (b == NULL) {
-        return ngx_http_xslt_filter_internal_error(r);
+        return ngx_http_filter_finalize_request(r, NULL,
+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
     }
 
     cln = ngx_pool_cleanup_add(r->pool, 0);
 
     if (cln == NULL) {
         ngx_free(b->pos);
-        return ngx_http_special_response_handler(r,
+        return ngx_http_filter_finalize_request(r, NULL,
                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
     }
 
@@ -362,22 +360,6 @@ ngx_http_xslt_send(ngx_http_request_t *r
 
 
 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)
 {
@@ -445,8 +427,8 @@ ngx_http_xslt_add_chunk(ngx_http_request
         ctx->request = r;
     }
 
-    err = xmlParseChunk(ctx->ctxt, (char *) b->pos,
-                        (int) (b->last - b->pos), b->last_buf);
+    err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos),
+                        (b->last_buf) || (b->last_in_chain));
 
     if (err == 0) {
         b->pos = b->last;
@@ -830,7 +812,6 @@ ngx_http_xslt_apply_stylesheet(ngx_http_
     b->pos = buf;
     b->last = buf + len;
     b->memory = 1;
-    b->last_buf = 1;
 
     if (encoding) {
         r->headers_out.charset.len = ngx_strlen(encoding);
@@ -841,6 +822,8 @@ ngx_http_xslt_apply_stylesheet(ngx_http_
         return b;
     }
 
+    b->last_buf = 1;
+
     if (type) {
         len = ngx_strlen(type);
 
@@ -855,6 +838,8 @@ ngx_http_xslt_apply_stylesheet(ngx_http_
         r->headers_out.content_type.data = (u_char *) "text/html";
     }
 
+    r->headers_out.content_type_lowcase = NULL;
+
     return b;
 }
 
@@ -863,30 +848,25 @@ 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;
+    u_char                    *p, *last, *value, *dst, *src, **s;
+    size_t                     len;
+    ngx_uint_t                 i;
+    ngx_str_t                  string;
+    ngx_http_complex_value_t  *param;
 
     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)
-        {
+        if (ngx_http_complex_value(r, &param[i], &string) != NGX_OK) {
             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;
+        last = string.data + string.len - 1;
 
         while (p && *p) {
 
@@ -1069,8 +1049,8 @@ ngx_http_xslt_stylesheet(ngx_conf_t *cf,
     ngx_pool_cleanup_t                *cln;
     ngx_http_xslt_file_t              *file;
     ngx_http_xslt_sheet_t             *sheet;
-    ngx_http_xslt_param_t             *param;
-    ngx_http_script_compile_t          sc;
+    ngx_http_complex_value_t          *param;
+    ngx_http_compile_complex_value_t   ccv;
     ngx_http_xslt_filter_main_conf_t  *xmcf;
 
     value = cf->args->elts;
@@ -1138,7 +1118,7 @@ found:
     }
 
     if (ngx_array_init(&sheet->params, cf->pool, n - 2,
-                       sizeof(ngx_http_xslt_param_t))
+                       sizeof(ngx_http_complex_value_t))
         != NGX_OK)
     {
         return NGX_CONF_ERROR;
@@ -1151,22 +1131,17 @@ found:
             return NGX_CONF_ERROR;
         }
 
-        param->lengths = NULL;
-        param->values = NULL;
-
-        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_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;
+        ccv.cf = cf;
+        ccv.value = &value[i];
+        ccv.complex_value = param;
+        ccv.zero = 1;
 
-        if (ngx_http_script_compile(&sc) != NGX_OK) {
+        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
             return NGX_CONF_ERROR;
         }
+
     }
 
     return NGX_CONF_OK;
@@ -1194,7 +1169,7 @@ ngx_http_xslt_filter_create_main_conf(ng
 
     conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     if (ngx_array_init(&conf->dtd_files, cf->pool, 1,
@@ -1222,7 +1197,7 @@ ngx_http_xslt_filter_create_conf(ngx_con
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -1269,6 +1244,10 @@ ngx_http_xslt_filter_init(ngx_conf_t *cf
 {
     xmlInitParser();
 
+#if (NGX_HAVE_EXSLT)
+    exsltRegisterAll();
+#endif
+
     ngx_http_next_header_filter = ngx_http_top_header_filter;
     ngx_http_top_header_filter = ngx_http_xslt_header_filter;
 
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.7.19';
+our $VERSION = '0.8.5';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -607,7 +607,7 @@ sendfile(r, filename, offset = -1, bytes
 
     ngx_http_request_t        *r;
     char                      *filename;
-    int                        offset;
+    off_t                      offset;
     size_t                     bytes;
     ngx_str_t                  path;
     ngx_buf_t                 *b;
@@ -662,7 +662,7 @@ sendfile(r, filename, offset = -1, bytes
         }
 
         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
-                      ngx_open_file_n " \"%s\" failed", filename);
+                      "%s \"%s\" failed", of.failed, filename);
         XSRETURN_EMPTY;
     }
 
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -154,10 +154,14 @@ static ngx_http_ssi_command_t  ngx_http_
 #endif
 
 
-static ngx_str_t  ngx_null_name = ngx_null_string;
+static ngx_str_t    ngx_null_name = ngx_null_string;
+
+static HV          *nginx_stash;
 
+#if (NGX_HAVE_PERL_MULTIPLICITY)
+static ngx_uint_t   ngx_perl_term;
+#endif
 
-static HV  *nginx_stash;
 
 static void
 ngx_http_perl_xs_init(pTHX)
@@ -171,7 +175,6 @@ ngx_http_perl_xs_init(pTHX)
 static ngx_int_t
 ngx_http_perl_handler(ngx_http_request_t *r)
 {
-    /* TODO: Win32 */
     if (r->zero_in_uri) {
         return NGX_HTTP_NOT_FOUND;
     }
@@ -285,7 +288,7 @@ ngx_http_perl_sleep_handler(ngx_http_req
         return;
     }
 
-    if (ngx_handle_write_event(wev, 0) == NGX_ERROR) {
+    if (ngx_handle_write_event(wev, 0) != NGX_OK) {
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
     }
 }
@@ -781,7 +784,7 @@ ngx_http_perl_create_main_conf(ngx_conf_
 
     pmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_main_conf_t));
     if (pmcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     if (ngx_array_init(&pmcf->requires, cf->pool, 1, sizeof(u_char *))
@@ -821,6 +824,12 @@ ngx_http_perl_cleanup_perl(void *data)
     (void) perl_destruct(perl);
 
     perl_free(perl);
+
+    if (ngx_perl_term) {
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "perl term");
+
+        PERL_SYS_TERM();
+    }
 }
 
 #endif
@@ -860,7 +869,7 @@ ngx_http_perl_create_loc_conf(ngx_conf_t
 
     plcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_loc_conf_t));
     if (plcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -1041,15 +1050,13 @@ ngx_http_perl_init_worker(ngx_cycle_t *c
 
     pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_perl_module);
 
-    {
-
-    dTHXa(pmcf->perl);
-    PERL_SET_CONTEXT(pmcf->perl);
+    if (pmcf) {
+        dTHXa(pmcf->perl);
+        PERL_SET_CONTEXT(pmcf->perl);
 
-    /* set worker's $$ */
+        /* set worker's $$ */
 
-    sv_setiv(GvSV(gv_fetchpv("$", TRUE, SVt_PV)), (I32) ngx_pid);
-
+        sv_setiv(GvSV(gv_fetchpv("$", TRUE, SVt_PV)), (I32) ngx_pid);
     }
 
     return NGX_OK;
@@ -1059,16 +1066,24 @@ ngx_http_perl_init_worker(ngx_cycle_t *c
 static void
 ngx_http_perl_exit(ngx_cycle_t *cycle)
 {
+#if (NGX_HAVE_PERL_MULTIPLICITY)
+
+    ngx_perl_term = 1;
+
+#else
     ngx_http_perl_main_conf_t  *pmcf;
 
     pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_perl_module);
 
-    {
+    if (pmcf && nginx_stash) {
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "perl term");
 
-    dTHXa(pmcf->perl);
-    PERL_SET_CONTEXT(pmcf->perl);
+        (void) perl_destruct(pmcf->perl);
 
-    PERL_SYS_TERM();
+        perl_free(pmcf->perl);
 
+        PERL_SYS_TERM();
     }
+
+#endif
 }
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -18,12 +18,18 @@ static ngx_int_t ngx_http_init_phase_han
     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);
+    ngx_array_t *servers, ngx_array_t *ports);
+static ngx_int_t ngx_http_add_ports(ngx_conf_t *cf,
+    ngx_http_core_srv_conf_t *cscf, ngx_array_t *ports,
+    ngx_http_listen_t *listen);
+static ngx_int_t ngx_http_add_addresses(ngx_conf_t *cf,
+    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port,
+    ngx_http_listen_t *listen);
 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_core_srv_conf_t *cscf, ngx_http_conf_port_t *port,
     ngx_http_listen_t *listen);
 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_http_core_srv_conf_t *cscf, ngx_http_conf_addr_t *addr);
 
 static char *ngx_http_merge_locations(ngx_conf_t *cf,
     ngx_queue_t *locations, void **loc_conf, ngx_http_module_t *module,
@@ -43,13 +49,23 @@ static ngx_http_location_tree_node_t *
     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);
+    ngx_http_core_main_conf_t *cmcf, ngx_array_t *ports);
+static ngx_int_t ngx_http_server_names(ngx_conf_t *cf,
+    ngx_http_core_main_conf_t *cmcf, ngx_http_conf_addr_t *addr);
+static ngx_int_t ngx_http_cmp_conf_addrs(const void *one, const void *two);
 static int ngx_libc_cdecl ngx_http_cmp_dns_wildcards(const void *one,
     const void *two);
 
 static ngx_int_t ngx_http_init_listening(ngx_conf_t *cf,
-    ngx_http_conf_in_port_t *in_port);
+    ngx_http_conf_port_t *port);
+static ngx_listening_t *ngx_http_add_listening(ngx_conf_t *cf,
+    ngx_http_conf_addr_t *addr);
+static ngx_int_t ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
+    ngx_http_conf_addr_t *addr);
+#if (NGX_HAVE_INET6)
+static ngx_int_t ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
+    ngx_http_conf_addr_t *addr);
+#endif
 
 ngx_uint_t   ngx_http_max_module;
 
@@ -106,7 +122,7 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
     char                        *rv;
     ngx_uint_t                   mi, m, s;
     ngx_conf_t                   pcf;
-    ngx_array_t                  in_ports;
+    ngx_array_t                  ports;
     ngx_http_module_t           *module;
     ngx_http_conf_ctx_t         *ctx;
     ngx_http_core_loc_conf_t    *clcf;
@@ -351,18 +367,14 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
      * 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) {
+    if (ngx_http_init_server_lists(cf, &cmcf->servers, &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) {
+    if (ngx_http_optimize_servers(cf, cmcf, &ports) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
@@ -489,7 +501,7 @@ ngx_http_init_phase_handlers(ngx_conf_t 
     use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
     use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;
 
-    n = use_rewrite + use_access + 1; /* find config phase */
+    n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;
 
     for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
         n += cmcf->phases[i].handlers.nelts;
@@ -558,6 +570,15 @@ ngx_http_init_phase_handlers(ngx_conf_t 
 
             continue;
 
+        case NGX_HTTP_TRY_FILES_PHASE:
+            if (cmcf->try_files) {
+                ph->checker = ngx_http_core_try_files_phase;
+                n++;
+                ph++;
+            }
+
+            continue;
+
         case NGX_HTTP_CONTENT_PHASE:
             checker = ngx_http_core_content_phase;
             break;
@@ -1090,16 +1111,13 @@ inclusive:
 
 static ngx_int_t
 ngx_http_init_server_lists(ngx_conf_t *cf, ngx_array_t *servers,
-    ngx_array_t *in_ports)
+    ngx_array_t *ports)
 {
-    ngx_uint_t                  s, l, p, a;
+    ngx_uint_t                  s, i;
     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))
+    if (ngx_array_init(ports, cf->temp_pool, 2, sizeof(ngx_http_conf_port_t))
         != NGX_OK)
     {
         return NGX_ERROR;
@@ -1113,92 +1131,11 @@ ngx_http_init_server_lists(ngx_conf_t *c
         /* "listen" directives */
 
         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++) {
-
-                if (listen[l].port != in_port[p].port) {
-                    continue;
-                }
-
-                /* the port is already in the port list */
-
-                in_addr = in_port[p].addrs.elts;
-                for (a = 0; a < in_port[p].addrs.nelts; a++) {
-
-                    if (listen[l].addr != in_addr[a].addr) {
-                        continue;
-                    }
-
-                    /* the address is already in the address list */
-
-                    if (ngx_http_add_names(cf, cscfp[s], &in_addr[a]) != NGX_OK)
-                    {
-                        return NGX_ERROR;
-                    }
-
-                    /*
-                     * check the duplicate "default" server
-                     * for this address:port
-                     */
-
-                    if (listen[l].conf.default_server) {
+        for (i = 0; i < cscfp[s]->listen.nelts; i++) {
 
-                        if (in_addr[a].default_server) {
-                            ngx_log_error(NGX_LOG_ERR, cf->log, 0,
-                                      "the duplicate default server in %s:%ui",
-                                       listen[l].file_name, listen[l].line);
-
-                            return NGX_ERROR;
-                        }
-
-                        in_addr[a].core_srv_conf = cscfp[s];
-                        in_addr[a].default_server = 1;
-#if (NGX_HTTP_SSL)
-                        in_addr[a].ssl = listen[l].conf.ssl;
-#endif
-                        in_addr[a].listen_conf = &listen[l].conf;
-                    }
-
-                    goto found;
-                }
-
-                /*
-                 * add the address to the addresses list that
-                 * bound to this port
-                 */
-
-                if (ngx_http_add_address(cf, cscfp[s], &in_port[p], &listen[l])
-                    != NGX_OK)
-                {
-                    return NGX_ERROR;
-                }
-
-                goto found;
-            }
-
-            /* add the port to the in_port list */
-
-            in_port = ngx_array_push(in_ports);
-            if (in_port == NULL) {
+            if (ngx_http_add_ports(cf, cscfp[s], ports, &listen[i]) != NGX_OK) {
                 return NGX_ERROR;
             }
-
-            in_port->port = listen[l].port;
-            in_port->addrs.elts = NULL;
-
-            if (ngx_http_add_address(cf, cscfp[s], in_port, &listen[l])
-                != NGX_OK)
-            {
-                return NGX_ERROR;
-            }
-
-        found:
-
-            continue;
         }
     }
 
@@ -1206,67 +1143,203 @@ ngx_http_init_server_lists(ngx_conf_t *c
 }
 
 
+static ngx_int_t
+ngx_http_add_ports(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+    ngx_array_t *ports, ngx_http_listen_t *listen)
+{
+    in_port_t                 p;
+    ngx_uint_t                i;
+    struct sockaddr          *sa;
+    struct sockaddr_in       *sin;
+    ngx_http_conf_port_t     *port;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6      *sin6;
+#endif
+
+    sa = (struct sockaddr *) &listen->sockaddr;
+
+    switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        sin6 = (struct sockaddr_in6 *) sa;
+        p = sin6->sin6_port;
+        break;
+#endif
+
+    default: /* AF_INET */
+        sin = (struct sockaddr_in *) sa;
+        p = sin->sin_port;
+        break;
+    }
+
+    port = ports->elts;
+    for (i = 0; i < ports->nelts; i++) {
+
+        if (p != port[i].port || sa->sa_family != port[i].family) {
+            continue;
+        }
+
+        /* a port is already in the port list */
+
+        return ngx_http_add_addresses(cf, cscf, &port[i], listen);
+    }
+
+    /* add a port to the port list */
+
+    port = ngx_array_push(ports);
+    if (port == NULL) {
+        return NGX_ERROR;
+    }
+
+    port->family = sa->sa_family;
+    port->port = p;
+    port->addrs.elts = NULL;
+
+    return ngx_http_add_address(cf, cscf, port, listen);
+}
+
+
+static ngx_int_t
+ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+    ngx_http_conf_port_t *port, ngx_http_listen_t *listen)
+{
+    u_char                *p;
+    size_t                 len, off;
+    ngx_uint_t             i;
+    struct sockaddr       *sa;
+    ngx_http_conf_addr_t  *addr;
+
+    /*
+     * we can not compare whole sockaddr struct's as kernel
+     * may fill some fields in inherited sockaddr struct's
+     */
+
+    sa = (struct sockaddr *) &listen->sockaddr;
+
+    switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        off = offsetof(struct sockaddr_in6, sin6_addr);
+        len = 16;
+        break;
+#endif
+
+    default: /* AF_INET */
+        off = offsetof(struct sockaddr_in, sin_addr);
+        len = 4;
+        break;
+    }
+
+    p = listen->sockaddr + off;
+
+    addr = port->addrs.elts;
+
+    for (i = 0; i < port->addrs.nelts; i++) {
+
+        if (ngx_memcmp(p, (u_char *) addr[i].sockaddr + off, len) != 0) {
+            continue;
+        }
+
+        /* the address is already in the address list */
+
+        if (ngx_http_add_names(cf, cscf, &addr[i]) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        /* check the duplicate "default" server for this address:port */
+
+        if (listen->conf.default_server) {
+
+            if (addr[i].default_server) {
+                ngx_log_error(NGX_LOG_ERR, cf->log, 0,
+                              "the duplicate default server in %s:%ui",
+                               listen->file_name, listen->line);
+
+                return NGX_ERROR;
+            }
+
+            addr[i].core_srv_conf = cscf;
+            addr[i].default_server = 1;
+#if (NGX_HTTP_SSL)
+            addr[i].ssl = listen->conf.ssl;
+#endif
+            addr[i].listen_conf = &listen->conf;
+        }
+
+        return NGX_OK;
+    }
+
+    /* add the address to the addresses list that bound to this port */
+
+    return ngx_http_add_address(cf, cscf, port, listen);
+}
+
+
 /*
  * add the server address, the server names and the server core module
- * configurations to the port (in_port)
+ * configurations to the port list
  */
 
 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_port_t *port, ngx_http_listen_t *listen)
 {
-    ngx_http_conf_in_addr_t  *in_addr;
+    ngx_http_conf_addr_t  *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))
+    if (port->addrs.elts == NULL) {
+        if (ngx_array_init(&port->addrs, cf->temp_pool, 4,
+                           sizeof(ngx_http_conf_addr_t))
             != NGX_OK)
         {
             return NGX_ERROR;
         }
     }
 
-    in_addr = ngx_array_push(&in_port->addrs);
-    if (in_addr == NULL) {
+    addr = ngx_array_push(&port->addrs);
+    if (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;
+    addr->sockaddr = (struct sockaddr *) &listen->sockaddr;
+    addr->socklen = listen->socklen;
+    addr->hash.buckets = NULL;
+    addr->hash.size = 0;
+    addr->wc_head = NULL;
+    addr->wc_tail = NULL;
+    addr->names.elts = NULL;
 #if (NGX_PCRE)
-    in_addr->nregex = 0;
-    in_addr->regex = NULL;
+    addr->nregex = 0;
+    addr->regex = NULL;
 #endif
-    in_addr->core_srv_conf = cscf;
-    in_addr->default_server = listen->conf.default_server;
-    in_addr->bind = listen->conf.bind;
+    addr->core_srv_conf = cscf;
+    addr->default_server = listen->conf.default_server;
+    addr->bind = listen->conf.bind;
+    addr->wildcard = listen->conf.wildcard;
 #if (NGX_HTTP_SSL)
-    in_addr->ssl = listen->conf.ssl;
+    addr->ssl = listen->conf.ssl;
 #endif
-    in_addr->listen_conf = &listen->conf;
+    addr->listen_conf = &listen->conf;
 
-    return ngx_http_add_names(cf, cscf, in_addr);
+    return ngx_http_add_names(cf, cscf, addr);
 }
 
 
 /*
  * add the server names and the server core module
- * configurations to the address:port (in_addr)
+ * configurations to the address:port
  */
 
 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_http_conf_addr_t *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,
+    if (addr->names.elts == NULL) {
+        if (ngx_array_init(&addr->names, cf->temp_pool, 4,
                            sizeof(ngx_http_server_name_t))
             != NGX_OK)
         {
@@ -1284,7 +1357,7 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0,
                        "name: %V", &server_names[i].name);
 
-        name = ngx_array_push(&in_addr->names);
+        name = ngx_array_push(&addr->names);
         if (name == NULL) {
             return NGX_ERROR;
         }
@@ -1298,187 +1371,189 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h
 
 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_array_t *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
+    ngx_uint_t               s, p, a;
+    ngx_http_conf_port_t    *port;
+    ngx_http_conf_addr_t    *addr;
+    ngx_http_server_name_t  *name;
 
-    in_port = in_ports->elts;
-    for (p = 0; p < in_ports->nelts; p++) {
+    port = ports->elts;
+    for (p = 0; p < 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);
+        ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
+                 sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs);
 
         /*
-         * check whether all name-based servers have
-         * the same configuraiton as the default server
+         * check whether all name-based servers have the same
+         * configuraiton as a default server for given address:port
          */
 
-        in_addr = in_port[p].addrs.elts;
-        for (a = 0; a < in_port[p].addrs.nelts; a++) {
-
-            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) {
-                    goto virtual_names;
-                }
-            }
+        addr = port[p].addrs.elts;
+        for (a = 0; a < port[p].addrs.nelts; a++) {
 
-            /*
-             * if all name-based servers have the same configuration
-             * as the default server, then we do not need to check
-             * them at run-time at all
-             */
-
-            in_addr[a].names.nelts = 0;
-
-            continue;
-
-        virtual_names:
+            name = addr[a].names.elts;
+            for (s = 0; s < addr[a].names.nelts; s++) {
 
-            ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));
-
-            ha.temp_pool = ngx_create_pool(16384, cf->log);
-            if (ha.temp_pool == NULL) {
-                return NGX_ERROR;
-            }
-
-            ha.pool = cf->pool;
-
-            if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {
-                goto failed;
-            }
-
+                if (addr[a].core_srv_conf == name[s].core_srv_conf
 #if (NGX_PCRE)
-            regex = 0;
+                    && name[s].captures == 0
 #endif
-
-            name = in_addr[a].names.elts;
-
-            for (s = 0; s < in_addr[a].names.nelts; s++) {
-
-#if (NGX_PCRE)
-                if (name[s].regex) {
-                    regex++;
+                    )
+                {
                     continue;
                 }
-#endif
 
-                rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf,
-                                      NGX_HASH_WILDCARD_KEY);
-
-                if (rc == NGX_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);
+                if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) {
                     return NGX_ERROR;
                 }
 
-                if (rc == NGX_BUSY) {
-                    ngx_log_error(NGX_LOG_WARN, cf->log, 0,
-                                "conflicting server name \"%V\" on %s, ignored",
-                                &name[s].name, in_addr[a].listen_conf->addr);
-                }
+                break;
             }
+        }
 
-            hash.key = ngx_hash_key_lc;
-            hash.max_size = cmcf->server_names_hash_max_size;
-            hash.bucket_size = cmcf->server_names_hash_bucket_size;
-            hash.name = "server_names_hash";
-            hash.pool = cf->pool;
+        if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
+    ngx_http_conf_addr_t *addr)
+{
+    ngx_int_t                rc;
+    ngx_uint_t               s;
+    ngx_hash_init_t          hash;
+    ngx_hash_keys_arrays_t   ha;
+    ngx_http_server_name_t  *name;
+#if (NGX_PCRE)
+    ngx_uint_t               regex, i;
+
+    regex = 0;
+#endif
 
-            if (ha.keys.nelts) {
-                hash.hash = &in_addr[a].hash;
-                hash.temp_pool = NULL;
+    ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));
+
+    ha.temp_pool = ngx_create_pool(16384, cf->log);
+    if (ha.temp_pool == NULL) {
+        return NGX_ERROR;
+    }
+
+    ha.pool = cf->pool;
+
+    if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {
+        goto failed;
+    }
+
+    name = addr->names.elts;
 
-                if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK)
-                {
-                    goto failed;
-                }
-            }
+    for (s = 0; s < addr->names.nelts; s++) {
 
-            if (ha.dns_wc_head.nelts) {
+#if (NGX_PCRE)
+        if (name[s].regex) {
+            regex++;
+            continue;
+        }
+#endif
 
-                ngx_qsort(ha.dns_wc_head.elts,
-                          (size_t) ha.dns_wc_head.nelts,
-                          sizeof(ngx_hash_key_t),
-                          ngx_http_cmp_dns_wildcards);
+        rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf,
+                              NGX_HASH_WILDCARD_KEY);
+
+        if (rc == NGX_ERROR) {
+            return NGX_ERROR;
+        }
 
-                hash.hash = NULL;
-                hash.temp_pool = ha.temp_pool;
+        if (rc == NGX_DECLINED) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "invalid server name or wildcard \"%V\" on %s",
+                          &name[s].name, addr->listen_conf->addr);
+            return NGX_ERROR;
+        }
+
+        if (rc == NGX_BUSY) {
+            ngx_log_error(NGX_LOG_WARN, cf->log, 0,
+                          "conflicting server name \"%V\" on %s, ignored",
+                          &name[s].name, addr->listen_conf->addr);
+        }
+    }
 
-                if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,
-                                           ha.dns_wc_head.nelts)
-                    != NGX_OK)
-                {
-                    goto failed;
-                }
+    hash.key = ngx_hash_key_lc;
+    hash.max_size = cmcf->server_names_hash_max_size;
+    hash.bucket_size = cmcf->server_names_hash_bucket_size;
+    hash.name = "server_names_hash";
+    hash.pool = cf->pool;
 
-                in_addr[a].wc_head = (ngx_hash_wildcard_t *) hash.hash;
-            }
+    if (ha.keys.nelts) {
+        hash.hash = &addr->hash;
+        hash.temp_pool = NULL;
 
-            if (ha.dns_wc_tail.nelts) {
+        if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) {
+            goto failed;
+        }
+    }
+
+    if (ha.dns_wc_head.nelts) {
 
-                ngx_qsort(ha.dns_wc_tail.elts,
-                          (size_t) ha.dns_wc_tail.nelts,
-                          sizeof(ngx_hash_key_t),
-                          ngx_http_cmp_dns_wildcards);
+        ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts,
+                  sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);
+
+        hash.hash = NULL;
+        hash.temp_pool = ha.temp_pool;
 
-                hash.hash = NULL;
-                hash.temp_pool = ha.temp_pool;
+        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,
+                                   ha.dns_wc_head.nelts)
+            != NGX_OK)
+        {
+            goto failed;
+        }
+
+        addr->wc_head = (ngx_hash_wildcard_t *) hash.hash;
+    }
+
+    if (ha.dns_wc_tail.nelts) {
 
-                if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,
-                                           ha.dns_wc_tail.nelts)
-                    != NGX_OK)
-                {
-                    goto failed;
-                }
+        ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts,
+                  sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);
+
+        hash.hash = NULL;
+        hash.temp_pool = ha.temp_pool;
 
-                in_addr[a].wc_tail = (ngx_hash_wildcard_t *) hash.hash;
-            }
+        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,
+                                   ha.dns_wc_tail.nelts)
+            != NGX_OK)
+        {
+            goto failed;
+        }
 
-            ngx_destroy_pool(ha.temp_pool);
+        addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash;
+    }
+
+    ngx_destroy_pool(ha.temp_pool);
 
 #if (NGX_PCRE)
 
-            if (regex == 0) {
-                continue;
-            }
-
-            in_addr[a].nregex = regex;
-            in_addr[a].regex = ngx_palloc(cf->pool,
-                                       regex * sizeof(ngx_http_server_name_t));
+    if (regex == 0) {
+        return NGX_OK;
+    }
 
-            if (in_addr[a].regex == NULL) {
-                return NGX_ERROR;
-            }
+    addr->nregex = regex;
+    addr->regex = ngx_palloc(cf->pool, regex * sizeof(ngx_http_server_name_t));
+    if (addr->regex == NULL) {
+        return NGX_ERROR;
+    }
 
-            for (i = 0, s = 0; s < in_addr[a].names.nelts; s++) {
-                if (name[s].regex) {
-                    in_addr[a].regex[i++] = name[s];
-                }
-            }
-#endif
-        }
-
-        if (ngx_http_init_listening(cf, &in_port[p]) != NGX_OK) {
-            return NGX_ERROR;
+    for (i = 0, s = 0; s < addr->names.nelts; s++) {
+        if (name[s].regex) {
+            addr->regex[i++] = name[s];
         }
     }
 
+#endif
+
     return NGX_OK;
 
 failed:
@@ -1490,15 +1565,15 @@ failed:
 
 
 static ngx_int_t
-ngx_http_cmp_conf_in_addrs(const void *one, const void *two)
+ngx_http_cmp_conf_addrs(const void *one, const void *two)
 {
-    ngx_http_conf_in_addr_t  *first, *second;
+    ngx_http_conf_addr_t  *first, *second;
 
-    first = (ngx_http_conf_in_addr_t *) one;
-    second = (ngx_http_conf_in_addr_t *) two;
+    first = (ngx_http_conf_addr_t *) one;
+    second = (ngx_http_conf_addr_t *) two;
 
-    if (first->addr == INADDR_ANY) {
-        /* the INADDR_ANY must be the last resort, shift it to the end */
+    if (first->wildcard) {
+        /* a wildcard address must be the last resort, shift it to the end */
         return 1;
     }
 
@@ -1531,171 +1606,259 @@ ngx_http_cmp_dns_wildcards(const void *o
 
 
 static ngx_int_t
-ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_in_port_t *in_port)
+ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)
 {
-    ngx_uint_t                 i, a, last, bind_all, done;
+    ngx_uint_t                 i, last, bind_wildcard;
     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;
+    ngx_http_port_t           *hport;
+    ngx_http_conf_addr_t      *addr;
 
-    in_addr = in_port->addrs.elts;
-    last = in_port->addrs.nelts;
+    addr = port->addrs.elts;
+    last = 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 there is a binding to an "*:port" then we need to bind() to
+     * the "*:port" only and ignore other implicit bindings.  The bindings
+     * have been already sorted: explicit bindings are on the start, then
+     * implicit bindings go, and wildcard binding is in the end.
      */
 
-    if (in_addr[last - 1].addr == INADDR_ANY) {
-        in_addr[last - 1].bind = 1;
-        bind_all = 0;
+    if (addr[last - 1].wildcard) {
+        addr[last - 1].bind = 1;
+        bind_wildcard = 1;
 
     } else {
-        bind_all = 1;
+        bind_wildcard = 0;
     }
 
-    a = 0;
+    i = 0;
 
-    while (a < last) {
+    while (i < last) {
 
-        if (!bind_all && !in_addr[a].bind) {
-            a++;
+        if (bind_wildcard && !addr[i].bind) {
+            i++;
             continue;
         }
 
-        ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr,
-                                              in_port->port);
+        ls = ngx_http_add_listening(cf, &addr[i]);
         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) {
+        hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t));
+        if (hport == 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;
+        ls->servers = hport;
 
-        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;
+        if (i == last - 1) {
+            hport->naddrs = last;
 
         } 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;
+            hport->naddrs = 1;
+            i = 0;
         }
 
-        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 (NGX_HTTP_SSL)
-            hip->addrs[i].ssl = in_addr[i].ssl;
-#endif
+        switch (ls->sockaddr->sa_family) {
 
-            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) {
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            if (ngx_http_add_addrs6(cf, hport, addr) != NGX_OK) {
                 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;
+            break;
 #endif
+        default: /* AF_INET */
+            if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) {
+                return NGX_ERROR;
+            }
+            break;
         }
 
-        if (done) {
-            return NGX_OK;
-        }
-
-        in_addr++;
-        in_port->addrs.elts = in_addr;
+        addr++;
         last--;
-
-        a = 0;
     }
 
     return NGX_OK;
 }
 
 
+static ngx_listening_t *
+ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
+{
+    ngx_listening_t           *ls;
+    ngx_http_core_loc_conf_t  *clcf;
+    ngx_http_core_srv_conf_t  *cscf;
+
+    ls = ngx_create_listening(cf, addr->sockaddr, addr->socklen);
+    if (ls == NULL) {
+        return NULL;
+    }
+
+    ls->addr_ntop = 1;
+
+    ls->handler = ngx_http_init_connection;
+
+    cscf = addr->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->logp = clcf->error_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 = addr->listen_conf->backlog;
+    ls->rcvbuf = addr->listen_conf->rcvbuf;
+    ls->sndbuf = addr->listen_conf->sndbuf;
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+    ls->accept_filter = addr->listen_conf->accept_filter;
+#endif
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+    ls->deferred_accept = addr->listen_conf->deferred_accept;
+#endif
+
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+    ls->ipv6only = addr->listen_conf->ipv6only;
+#endif
+
+    return ls;
+}
+
+
+static ngx_int_t
+ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
+    ngx_http_conf_addr_t *addr)
+{
+    ngx_uint_t                 i;
+    ngx_http_in_addr_t        *addrs;
+    struct sockaddr_in        *sin;
+    ngx_http_virtual_names_t  *vn;
+
+    hport->addrs = ngx_pcalloc(cf->pool,
+                               hport->naddrs * sizeof(ngx_http_in_addr_t));
+    if (hport->addrs == NULL) {
+        return NGX_ERROR;
+    }
+
+    addrs = hport->addrs;
+
+    for (i = 0; i < hport->naddrs; i++) {
+
+        sin = (struct sockaddr_in *) addr[i].sockaddr;
+        addrs[i].addr = sin->sin_addr.s_addr;
+        addrs[i].conf.core_srv_conf = addr[i].core_srv_conf;
+#if (NGX_HTTP_SSL)
+        addrs[i].conf.ssl = addr[i].ssl;
+#endif
+
+        if (addr[i].hash.buckets == NULL
+            && (addr[i].wc_head == NULL
+                || addr[i].wc_head->hash.buckets == NULL)
+            && (addr[i].wc_tail == NULL
+                || addr[i].wc_tail->hash.buckets == NULL)
+#if (NGX_PCRE)
+            && addr[i].nregex == 0
+#endif
+            )
+        {
+            continue;
+        }
+
+        vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));
+        if (vn == NULL) {
+            return NGX_ERROR;
+        }
+
+        addrs[i].conf.virtual_names = vn;
+
+        vn->names.hash = addr[i].hash;
+        vn->names.wc_head = addr[i].wc_head;
+        vn->names.wc_tail = addr[i].wc_tail;
+#if (NGX_PCRE)
+        vn->nregex = addr[i].nregex;
+        vn->regex = addr[i].regex;
+#endif
+    }
+
+    return NGX_OK;
+}
+
+
+#if (NGX_HAVE_INET6)
+
+static ngx_int_t
+ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
+    ngx_http_conf_addr_t *addr)
+{
+    ngx_uint_t                 i;
+    ngx_http_in6_addr_t       *addrs6;
+    struct sockaddr_in6       *sin6;
+    ngx_http_virtual_names_t  *vn;
+
+    hport->addrs = ngx_pcalloc(cf->pool,
+                               hport->naddrs * sizeof(ngx_http_in6_addr_t));
+    if (hport->addrs == NULL) {
+        return NGX_ERROR;
+    }
+
+    addrs6 = hport->addrs;
+
+    for (i = 0; i < hport->naddrs; i++) {
+
+        sin6 = (struct sockaddr_in6 *) addr[i].sockaddr;
+        addrs6[i].addr6 = sin6->sin6_addr;
+        addrs6[i].conf.core_srv_conf = addr[i].core_srv_conf;
+#if (NGX_HTTP_SSL)
+        addrs6[i].conf.ssl = addr[i].ssl;
+#endif
+
+        if (addr[i].hash.buckets == NULL
+            && (addr[i].wc_head == NULL
+                || addr[i].wc_head->hash.buckets == NULL)
+            && (addr[i].wc_head == NULL
+                || 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;
+        }
+
+        addrs6[i].conf.virtual_names = vn;
+
+        vn->names.hash = addr[i].hash;
+        vn->names.wc_head = addr[i].wc_head;
+        vn->names.wc_tail = addr[i].wc_tail;
+#if (NGX_PCRE)
+        vn->nregex = addr[i].nregex;
+        vn->regex = addr[i].regex;
+#endif
+    }
+
+    return NGX_OK;
+}
+
+#endif
+
+
 char *
 ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -10,12 +10,13 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_garbage_collector.h>
 
 
-typedef struct ngx_http_request_s   ngx_http_request_t;
-typedef struct ngx_http_upstream_s  ngx_http_upstream_t;
-typedef struct ngx_http_log_ctx_s   ngx_http_log_ctx_t;
+typedef struct ngx_http_request_s     ngx_http_request_t;
+typedef struct ngx_http_upstream_s    ngx_http_upstream_t;
+typedef struct ngx_http_cache_s       ngx_http_cache_t;
+typedef struct ngx_http_file_cache_s  ngx_http_file_cache_t;
+typedef struct ngx_http_log_ctx_s     ngx_http_log_ctx_t;
 
 typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
@@ -23,21 +24,18 @@ typedef u_char *(*ngx_http_log_handler_p
     ngx_http_request_t *sr, u_char *buf, size_t len);
 
 
-#if (NGX_HTTP_CACHE)
-#include <ngx_http_cache.h>
-#endif
-/* STUB */
-#include <ngx_http_cache.h>
-
 #include <ngx_http_variables.h>
 #include <ngx_http_request.h>
 #include <ngx_http_upstream.h>
 #include <ngx_http_upstream_round_robin.h>
 #include <ngx_http_config.h>
 #include <ngx_http_busy_lock.h>
+#include <ngx_http_script.h>
 #include <ngx_http_core_module.h>
-#include <ngx_http_script.h>
 
+#if (NGX_HTTP_CACHE)
+#include <ngx_http_cache.h>
+#endif
 #if (NGX_HTTP_SSI)
 #include <ngx_http_ssi_filter_module.h>
 #endif
@@ -76,15 +74,23 @@ ngx_int_t ngx_http_parse_header_line(ngx
     ngx_uint_t allow_underscores);
 ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers,
     ngx_str_t *name, ngx_str_t *value);
+ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len,
+    ngx_str_t *value);
+void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri,
+    ngx_str_t *args);
+
 
 ngx_int_t ngx_http_find_server_conf(ngx_http_request_t *r);
 void ngx_http_update_location_config(ngx_http_request_t *r);
 void ngx_http_handler(ngx_http_request_t *r);
+void ngx_http_run_posted_requests(ngx_connection_t *c);
+ngx_int_t ngx_http_post_request(ngx_http_request_t *r);
 void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
 
 void ngx_http_empty_handler(ngx_event_t *wev);
 void ngx_http_request_empty_handler(ngx_http_request_t *r);
 
+
 #define NGX_HTTP_LAST   1
 #define NGX_HTTP_FLUSH  2
 
@@ -97,6 +103,9 @@ ngx_int_t ngx_http_read_client_request_b
 ngx_int_t ngx_http_send_header(ngx_http_request_t *r);
 ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,
     ngx_int_t error);
+ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,
+    ngx_module_t *m, ngx_int_t error);
+void ngx_http_clean_header(ngx_http_request_t *r);
 
 
 time_t ngx_http_parse_time(u_char *value, size_t len);
@@ -106,6 +115,7 @@ size_t ngx_http_get_time(char *buf, time
 
 ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r);
 void ngx_http_block_reading(ngx_http_request_t *r);
+void ngx_http_test_reading(ngx_http_request_t *r);
 
 
 char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
deleted file mode 100644
--- a/src/http/ngx_http_cache.c
+++ /dev/null
@@ -1,576 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- */
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_http.h>
-
-
-#if 0
-
-static ngx_http_module_t  ngx_http_cache_module_ctx = {
-    NULL,                                  /* pre conf */
-
-    NULL,                                  /* create main configuration */
-    NULL,                                  /* init main configuration */
-
-    NULL,                                  /* create server configuration */
-    NULL,                                  /* merge server configuration */
-
-    NULL,                                  /* create location configuration */
-    NULL                                   /* merge location configuration */
-};
-
-
-ngx_module_t  ngx_http_cache_module = {
-    NGX_MODULE,
-    &ngx_http_cache_module_ctx,            /* module context */
-    NULL,                                  /* module directives */
-    NGX_HTTP_MODULE,                       /* module type */
-    NULL,                                  /* init module */
-    NULL                                   /* init process */
-};
-
-#endif
-
-
-static ngx_int_t ngx_http_cache_create(ngx_http_request_t *r)
-{
-    ngx_str_t  *key;
-
-    if (!(r->cache = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t)))) {
-        return NGX_ERROR;
-    }
-
-    if (ngx_array_init(&r->cache->key, r->pool, 5, sizeof(ngx_str_t))
-                                                                  == NGX_ERROR)
-    {
-        return NGX_ERROR;
-    }
-
-    /* preallocate the primary key */
-
-    if (!(key = ngx_array_push(&r->cache->key))) {
-        return NGX_ERROR;
-    }
-
-    key->len = 0;
-    key->data = NULL;
-
-    /*
-     * we use offsetof() because sizeof() pads the struct size to the int size
-     */
-
-    r->cache->header_size = offsetof(ngx_http_cache_header_t, key);
-
-    r->cache->log = r->connection->log;
-    r->cache->file.log = r->connection->log;
-
-    return NGX_OK;
-}
-
-
-ngx_int_t ngx_http_cache_get(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx)
-{
-    ngx_str_t         *key;
-    ngx_http_cache_t  *c;
-
-    if (r->cache == NULL) {
-        if (ngx_http_cache_create(r) == NGX_ERROR) {
-            return NGX_ABORT;
-        }
-    }
-
-    c = r->cache;
-    key = c->key.elts;
-
-    if (ctx->primary) {
-        key[0] = ctx->key;
-        c->header_size += ctx->key.len;
-        c->key_len += ctx->key.len;
-        c->buf = ctx->buf;
-
-    } else {
-        if (key[0].len == 0) {
-            key[0] = r->uri;
-            c->header_size += r->uri.len;
-            c->key_len += ctx->key.len;
-        }
-
-        if (!(key = ngx_array_push(&r->cache->key))) {
-            return NGX_ABORT;
-        }
-
-        c->header_size += ctx->key.len;
-        c->key_len += ctx->key.len;
-    }
-
-#if 0
-
-    if (ctx->memory) {
-        ngx_http_memory_cache_get(r, ctx);
-    }
-
-#endif
-
-    if (ctx->file) {
-        return ngx_http_file_cache_get(r, ctx);
-    }
-
-    return NGX_DECLINED;
-}
-
-
-#if 0
-
-
-ngx_http_cache_t *ngx_http_cache_get(ngx_http_cache_hash_t *hash,
-                                     ngx_http_cleanup_t *cleanup,
-                                     ngx_str_t *key, uint32_t *crc)
-{
-    ngx_uint_t         i;
-    ngx_http_cache_t  *c;
-
-    *crc = ngx_crc(key->data, key->len);
-
-    c = hash->elts + *crc % hash->hash * hash->nelts;
-
-    if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) {
-        return (void *) NGX_ERROR;
-    }
-
-    for (i = 0; i < hash->nelts; i++) {
-        if (c[i].crc == *crc
-            && c[i].key.len == key->len
-            && ngx_rstrncmp(c[i].key.data, key->data, key->len) == 0)
-        {
-#if 0
-            if (c[i].expired) {
-                ngx_mutex_unlock(&hash->mutex);
-                return (void *) NGX_AGAIN;
-            }
-#endif
-
-            c[i].refs++;
-
-            if ((!(c[i].notify && (ngx_event_flags & NGX_USE_KQUEUE_EVENT)))
-                && (ngx_cached_time - c[i].updated >= hash->update))
-            {
-                c[i].expired = 1;
-            }
-
-            ngx_mutex_unlock(&hash->mutex);
-
-            if (cleanup) {
-                cleanup->data.cache.hash = hash;
-                cleanup->data.cache.cache = &c[i];
-                cleanup->valid = 1;
-                cleanup->cache = 1;
-            }
-
-            return &c[i];
-        }
-    }
-
-    ngx_mutex_unlock(&hash->mutex);
-
-    return NULL;
-}
-
-
-ngx_http_cache_t *ngx_http_cache_alloc(ngx_http_cache_hash_t *hash,
-                                       ngx_http_cache_t *cache,
-                                       ngx_http_cleanup_t *cleanup,
-                                       ngx_str_t *key, uint32_t crc,
-                                       ngx_str_t *value, ngx_log_t *log)
-{
-    time_t             old;
-    ngx_uint_t         i;
-    ngx_http_cache_t  *c;
-
-    old = ngx_cached_time + 1;
-
-    c = hash->elts + crc % hash->hash * hash->nelts;
-
-    if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) {
-        return (void *) NGX_ERROR;
-    }
-
-    if (cache == NULL) {
-
-        /* allocate a new entry */
-
-        for (i = 0; i < hash->nelts; i++) {
-            if (c[i].refs > 0) {
-                /* a busy entry */
-                continue;
-            }
-
-            if (c[i].key.len == 0) {
-                /* a free entry is found */
-                cache = &c[i];
-                break;
-            }
-
-            /* looking for the oldest cache entry */
-
-            if (old > c[i].accessed) {
-
-                old = c[i].accessed;
-                cache = &c[i];
-            }
-        }
-
-        if (cache == NULL) {
-            ngx_mutex_unlock(&hash->mutex);
-            return NULL;
-        }
-
-        ngx_http_cache_free(cache, key, value, log);
-
-        if (cache->key.data == NULL) {
-            cache->key.data = ngx_alloc(key->len, log);
-            if (cache->key.data == NULL) {
-                ngx_http_cache_free(cache, NULL, NULL, log);
-                ngx_mutex_unlock(&hash->mutex);
-                return NULL;
-            }
-        }
-
-        cache->key.len = key->len;
-        ngx_memcpy(cache->key.data, key->data, key->len);
-
-    } else if (value) {
-        ngx_http_cache_free(cache, key, value, log);
-    }
-
-    if (value) {
-        if (cache->data.value.data == NULL) {
-            cache->data.value.data = ngx_alloc(value->len, log);
-            if (cache->data.value.data == NULL) {
-                ngx_http_cache_free(cache, NULL, NULL, log);
-                ngx_mutex_unlock(&hash->mutex);
-                return NULL;
-            }
-        }
-
-        cache->data.value.len = value->len;
-        ngx_memcpy(cache->data.value.data, value->data, value->len);
-    }
-
-    cache->crc = crc;
-    cache->key.len = key->len;
-
-    cache->refs = 1;
-    cache->count = 0;
-
-    cache->deleted = 0;
-    cache->expired = 0;
-    cache->memory = 0;
-    cache->mmap = 0;
-    cache->notify = 0;
-
-    if (cleanup) {
-        cleanup->data.cache.hash = hash;
-        cleanup->data.cache.cache = cache;
-        cleanup->valid = 1;
-        cleanup->cache = 1;
-    }
-
-    ngx_mutex_unlock(&hash->mutex);
-
-    return cache;
-}
-
-
-void ngx_http_cache_free(ngx_http_cache_t *cache,
-                         ngx_str_t *key, ngx_str_t *value, ngx_log_t *log)
-{
-    if (cache->memory) {
-        if (cache->data.value.data
-            && (value == NULL || value->len > cache->data.value.len))
-        {
-            ngx_free(cache->data.value.data);
-            cache->data.value.data = NULL;
-        }
-    }
-
-    /* TODO: mmap */
-
-    cache->data.value.len = 0;
-
-    if (cache->fd != NGX_INVALID_FILE) {
-
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
-                       "http cache close fd: %d", cache->fd);
-
-        if (ngx_close_file(cache->fd) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                          ngx_close_file_n " \"%s\" failed",
-                          cache->key.data);
-        }
-
-        cache->fd = NGX_INVALID_FILE;
-    }
-
-    if (cache->key.data && (key == NULL || key->len > cache->key.len)) {
-        ngx_free(cache->key.data);
-        cache->key.data = NULL;
-    }
-
-    cache->key.len = 0;
-
-    cache->refs = 0;
-}
-
-
-void ngx_http_cache_lock(ngx_http_cache_hash_t *hash, ngx_http_cache_t *cache)
-{
-    if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) {
-        return;
-    }
-}
-
-
-void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash,
-                           ngx_http_cache_t *cache, ngx_log_t *log)
-{
-    if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) {
-        return;
-    }
-
-    cache->refs--;
-
-    if (cache->refs == 0 && cache->deleted) {
-        ngx_http_cache_free(cache, NULL, NULL, log);
-    }
-
-    ngx_mutex_unlock(&hash->mutex);
-}
-
-
-#if 0
-
-ngx_http_cache_add_file_event(ngx_http_cache_hash_t *hash,
-                              ngx_http_cache_t *cache)
-{
-    ngx_event_t                 *ev;
-    ngx_http_cache_event_ctx_t  *ctx;
-
-    ev = &ngx_cycle->read_events[fd];
-    ngx_memzero(ev, sizeof(ngx_event_t);
-
-    ev->data = data;
-    ev->event_handler = ngx_http_cache_invalidate;
-
-    return ngx_add_event(ev, NGX_VNODE_EVENT, 0);
-}
-
-
-void ngx_http_cache_invalidate(ngx_event_t *ev)
-{
-    ngx_http_cache_event_ctx_t  *ctx;
-
-    ctx = ev->data;
-
-    ngx_http_cache_lock(&ctx->hash->mutex);
-
-    if (ctx->cache->refs == 0)
-        ngx_http_cache_free(ctx->cache, NULL, NULL, ctx->log);
-
-    } else {
-        ctx->cache->deleted = 1;
-    }
-
-    ngx_http_cache_unlock(&ctx->hash->mutex);
-}
-
-#endif
-
-
-/* TODO: currently fd only */
-
-ngx_int_t ngx_http_send_cached(ngx_http_request_t *r)
-{
-    ngx_int_t            rc;
-    ngx_hunk_t          *h;
-    ngx_chain_t          out;
-    ngx_http_log_ctx_t  *ctx;
-
-    ctx = r->connection->log->data;
-    ctx->action = "sending response to client";
-
-    r->headers_out.status = NGX_HTTP_OK;
-    r->headers_out.content_length_n = r->cache->data.size;
-    r->headers_out.last_modified_time = r->cache->last_modified;
-
-    if (ngx_http_set_content_type(r) != NGX_OK) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-    /* we need to allocate all before the header would be sent */
-
-    if (!(h = ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)))) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-    if (!(h->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)))) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-    rc = ngx_http_send_header(r);
-
-    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
-        return rc;
-    }
-
-    h->type = r->main ? NGX_HUNK_FILE : NGX_HUNK_FILE|NGX_HUNK_LAST;
-
-    h->file_pos = 0;
-    h->file_last = r->cache->data.size;
-
-    h->file->fd = r->cache->fd;
-    h->file->log = r->connection->log;
-
-    out.hunk = h;
-    out.next = NULL;
-
-    return ngx_http_output_filter(r, &out);
-}
-
-
-char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
-{
-    char  *p = conf;
-
-    ngx_int_t              i, j, dup, invalid;
-    ngx_str_t              *value, line;
-    ngx_http_cache_t       *c;
-    ngx_http_cache_hash_t  *ch, **chp;
-
-    chp = (ngx_http_cache_hash_t **) (p + cmd->offset);
-    if (*chp) {
-        return "is duplicate";
-    }
-
-    if (!(ch = ngx_pcalloc(cf->pool, sizeof(ngx_http_cache_hash_t)))) {
-        return NGX_CONF_ERROR;
-    }
-    *chp = ch;
-
-    dup = 0;
-    invalid = 0;
-
-    value = cf->args->elts;
-
-    for (i = 1; i < cf->args->nelts; i++) {
-
-        if (value[i].data[1] != '=') {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "invalid value \"%s\"", value[i].data);
-            return NGX_CONF_ERROR;
-        }
-
-        switch (value[i].data[0]) {
-
-        case 'h':
-            if (ch->hash) {
-                dup = 1;
-                break;
-            }
-
-            ch->hash = ngx_atoi(value[i].data + 2, value[i].len - 2);
-            if (ch->hash == (size_t)  NGX_ERROR || ch->hash == 0) {
-                invalid = 1;
-                break;
-            }
-
-            continue;
-
-        case 'n':
-            if (ch->nelts) {
-                dup = 1;
-                break;
-            }
-
-            ch->nelts = ngx_atoi(value[i].data + 2, value[i].len - 2);
-            if (ch->nelts == (size_t) NGX_ERROR || ch->nelts == 0) {
-                invalid = 1;
-                break;
-            }
-
-            continue;
-
-        case 'l':
-            if (ch->life) {
-                dup = 1;
-                break;
-            }
-
-            line.len = value[i].len - 2;
-            line.data = value[i].data + 2;
-
-            ch->life = ngx_parse_time(&line, 1);
-            if (ch->life == NGX_ERROR || ch->life == 0) {
-                invalid = 1;
-                break;
-            }
-
-            continue;
-
-        case 'u':
-            if (ch->update) {
-                dup = 1;
-                break;
-            }
-
-            line.len = value[i].len - 2;
-            line.data = value[i].data + 2;
-
-            ch->update = ngx_parse_time(&line, 1);
-            if (ch->update == NGX_ERROR || ch->update == 0) {
-                invalid = 1;
-                break;
-            }
-
-            continue;
-
-        default:
-            invalid = 1;
-        }
-
-        if (dup) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "duplicate value \"%s\"", value[i].data);
-            return NGX_CONF_ERROR;
-        }
-
-        if (invalid) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "invalid value \"%s\"", value[i].data);
-            return NGX_CONF_ERROR;
-        }
-    }
-
-    ch->elts = ngx_pcalloc(cf->pool,
-                           ch->hash * ch->nelts * sizeof(ngx_http_cache_t));
-    if (ch->elts == NULL) {
-        return NGX_CONF_ERROR;
-    }
-
-    for (i = 0; i < (ngx_int_t) ch->hash; i++) {
-        c = ch->elts + i * ch->nelts;
-
-        for (j = 0; j < (ngx_int_t) ch->nelts; j++) {
-            c[j].fd = NGX_INVALID_FILE;
-        }
-    }
-
-    return NGX_CONF_OK;
-}
-
-
-#endif
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -13,145 +13,126 @@
 #include <ngx_http.h>
 
 
-/*
- * The 3 bits allows the 7 uses before the cache entry allocation.
- * We can use maximum 7 bits, i.e up to the 127 uses.
- */
-#define NGX_HTTP_CACHE_LAZY_ALLOCATION_BITS  3
+#define NGX_HTTP_CACHE_MISS          1
+#define NGX_HTTP_CACHE_EXPIRED       2
+#define NGX_HTTP_CACHE_STALE         3
+#define NGX_HTTP_CACHE_UPDATING      4
+#define NGX_HTTP_CACHE_HIT           5
+
+#define NGX_HTTP_CACHE_KEY_LEN       16
 
 
 typedef struct {
-    uint32_t         crc;
-    ngx_str_t        key;
-    time_t           accessed;
-
-    unsigned         refs:20;    /* 1048576 references */
-
-    unsigned         count:NGX_HTTP_CACHE_LAZY_ALLOCATION_BITS;
-
-    unsigned         deleted:1;
-    unsigned         expired:1;
-    unsigned         memory:1;
-    unsigned         mmap:1;
-    unsigned         notify:1;
-
-    ngx_fd_t         fd;
-#if (NGX_USE_HTTP_FILE_CACHE_UNIQ)
-    ngx_file_uniq_t  uniq;       /* no needed with kqueue */
-#endif
-    time_t           last_modified;
-    time_t           updated;
-
-    union {
-        off_t        size;
-        ngx_str_t    value;
-    } data;
-} ngx_http_cache_entry_t;
-
-
-typedef struct {
-    time_t       expires;
-    time_t       last_modified;
-    time_t       date;
-    off_t        length;
-    size_t       key_len;
-    char         key[1];
-} ngx_http_cache_header_t;
-
-
-#define NGX_HTTP_CACHE_HASH   7
-#define NGX_HTTP_CACHE_NELTS  4
-
-typedef struct {
-    ngx_http_cache_entry_t   *elts;
-    size_t                    hash;
-    size_t                    nelts;
-    time_t                    life;
-    time_t                    update;
-#if (NGX_THREADS)
-    ngx_mutex_t               mutex;
-#endif
-    ngx_pool_t               *pool;
-} ngx_http_cache_hash_t;
+    ngx_uint_t                       status;
+    time_t                           valid;
+} ngx_http_cache_valid_t;
 
 
 typedef struct {
-    ngx_http_cache_hash_t    *hash;
-    ngx_http_cache_entry_t   *cache;
-    ngx_file_t                file;
-    ngx_array_t               key;
-    uint32_t                  crc;
-    u_char                    md5[16];
-    ngx_path_t               *path;
-    ngx_buf_t                *buf;
-    time_t                    expires;
-    time_t                    last_modified;
-    time_t                    date;
-    off_t                     length;
-    size_t                    key_len;
-    size_t                    file_start;
-    ngx_file_uniq_t           uniq;
-    ngx_log_t                *log;
+    ngx_rbtree_node_t                node;
+    ngx_queue_t                      queue;
+
+    u_char                           key[NGX_HTTP_CACHE_KEY_LEN
+                                         - sizeof(ngx_rbtree_key_t)];
+
+    unsigned                         count:20;
+    unsigned                         uses:10;
+    unsigned                         valid_msec:10;
+    unsigned                         error:10;
+    unsigned                         exists:1;
+    unsigned                         updating:1;
+                                     /* 12 unused bits */
+
+    ngx_file_uniq_t                  uniq;
+    time_t                           expire;
+    time_t                           valid_sec;
+    size_t                           body_start;
+    off_t                            length;
+} ngx_http_file_cache_node_t;
+
 
-    /* STUB */
-    ssize_t                   header_size;
-    ngx_str_t                 key0;
-} ngx_http_cache_t;
+struct ngx_http_cache_s {
+    ngx_file_t                       file;
+    ngx_array_t                      keys;
+    uint32_t                         crc32;
+    u_char                           key[NGX_HTTP_CACHE_KEY_LEN];
+
+    ngx_file_uniq_t                  uniq;
+    time_t                           valid_sec;
+    time_t                           last_modified;
+    time_t                           date;
+
+    size_t                           header_start;
+    size_t                           body_start;
+    off_t                            length;
+
+    ngx_uint_t                       min_uses;
+    ngx_uint_t                       error;
+    ngx_uint_t                       valid_msec;
+
+    ngx_buf_t                       *buf;
+
+    ngx_http_file_cache_t           *file_cache;
+    ngx_http_file_cache_node_t      *node;
+
+    unsigned                         updated:1;
+    unsigned                         exists:1;
+    unsigned                         temp_file:1;
+};
 
 
 typedef struct {
-    ngx_path_t               *path;
-    ngx_str_t                 key;
-    ngx_buf_t                *buf;
-
-    unsigned                  file:1;
-    unsigned                  memory:1;
-    unsigned                  primary:1;
-} ngx_http_cache_ctx_t;
+    time_t                           valid_sec;
+    time_t                           last_modified;
+    time_t                           date;
+    uint32_t                         crc32;
+    u_short                          valid_msec;
+    u_short                          header_start;
+    u_short                          body_start;
+} ngx_http_file_cache_header_t;
 
 
-#define NGX_HTTP_CACHE_STALE     1
-#define NGX_HTTP_CACHE_AGED      2
-#define NGX_HTTP_CACHE_THE_SAME  3
-
-
-ngx_int_t ngx_http_cache_get(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx);
-
-ngx_int_t ngx_http_file_cache_get(ngx_http_request_t *r,
-                                  ngx_http_cache_ctx_t *ctx);
-
-ngx_int_t ngx_http_file_cache_open(ngx_http_cache_t *c);
-
-ngx_int_t ngx_http_cache_cleaner_handler(ngx_gc_t *gc, ngx_str_t *name,
-                                         ngx_dir_t *dir);
+typedef struct {
+    ngx_rbtree_t                     rbtree;
+    ngx_rbtree_node_t                sentinel;
+    ngx_queue_t                      queue;
+    ngx_atomic_t                     cold;
+    off_t                            size;
+} ngx_http_file_cache_sh_t;
 
 
-#if 0
+struct ngx_http_file_cache_s {
+    ngx_http_file_cache_sh_t        *sh;
+    ngx_slab_pool_t                 *shpool;
 
-ngx_http_cache_t *ngx_http_cache_get(ngx_http_cache_hash_t *cache,
-                                     ngx_http_cleanup_t *cleanup,
-                                     ngx_str_t *key, uint32_t *crc);
+    ngx_path_t                      *path;
 
-ngx_http_cache_t *ngx_http_cache_alloc(ngx_http_cache_hash_t *hash,
-                                       ngx_http_cache_t *cache,
-                                       ngx_http_cleanup_t *cleanup,
-                                       ngx_str_t *key, uint32_t crc,
-                                       ngx_str_t *value, ngx_log_t *log);
-void ngx_http_cache_free(ngx_http_cache_t *cache,
-                         ngx_str_t *key, ngx_str_t *value, ngx_log_t *log);
-void ngx_http_cache_lock(ngx_http_cache_hash_t *hash, ngx_http_cache_t *cache);
-void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash,
-                           ngx_http_cache_t *cache, ngx_log_t *log);
+    off_t                            max_size;
+    size_t                           bsize;
+
+    time_t                           inactive;
 
-int ngx_http_cache_update_file(ngx_http_request_t *r,ngx_http_cache_ctx_t *ctx,
-                               ngx_str_t *temp_file);
+    ngx_msec_t                       last;
+    ngx_uint_t                       files;
 
-int ngx_http_send_cached(ngx_http_request_t *r);
+    ngx_shm_zone_t                  *shm_zone;
+};
 
 
-char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+void ngx_http_file_cache_create_key(ngx_http_request_t *r);
+ngx_int_t ngx_http_file_cache_open(ngx_http_request_t *r);
+void ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf);
+void ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf);
+ngx_int_t ngx_http_cache_send(ngx_http_request_t *);
+void ngx_http_file_cache_free(ngx_http_request_t *r, ngx_temp_file_t *tf);
+time_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status);
 
-#endif
+char *ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+char *ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
+extern ngx_str_t  ngx_http_cache_status[];
 
 
 #endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */
--- a/src/http/ngx_http_config.h
+++ b/src/http/ngx_http_config.h
@@ -69,8 +69,10 @@ typedef struct {
     ((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index]
 
 #define ngx_http_cycle_get_module_main_conf(cycle, module)                    \
-    ((ngx_http_conf_ctx_t *)                                                  \
-         cycle->conf_ctx[ngx_http_module.index])->main_conf[module.ctx_index]
+    (cycle->conf_ctx[ngx_http_module.index] ?                                 \
+        ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index])      \
+            ->main_conf[module.ctx_index]:                                    \
+        NULL)
 
 
 #endif /* _NGX_HTTP_CONFIG_H_INCLUDED_ */
--- a/src/http/ngx_http_copy_filter_module.c
+++ b/src/http/ngx_http_copy_filter_module.c
@@ -117,10 +117,6 @@ ngx_http_copy_filter(ngx_http_request_t 
             r->buffered |= NGX_HTTP_COPY_BUFFERED;
         }
 
-        if (r != r->main) {
-            r->out = ctx->in;
-        }
-
         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args);
     }
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -23,7 +23,6 @@ typedef struct {
 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);
@@ -39,6 +38,8 @@ static char *ngx_http_core_server(ngx_co
     void *dummy);
 static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd,
     void *dummy);
+static ngx_int_t ngx_http_core_regex_location(ngx_conf_t *cf,
+    ngx_http_core_loc_conf_t *clcf, ngx_str_t *regex, ngx_uint_t caseless);
 
 static char *ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
@@ -56,6 +57,8 @@ static char *ngx_http_core_directio(ngx_
     void *conf);
 static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -108,6 +111,19 @@ static ngx_conf_enum_t  ngx_http_core_sa
 };
 
 
+static ngx_conf_enum_t  ngx_http_core_if_modified_since[] = {
+    { ngx_string("off"), NGX_HTTP_IMS_OFF },
+    { ngx_string("exact"), NGX_HTTP_IMS_EXACT },
+    { ngx_string("before"), NGX_HTTP_IMS_BEFORE },
+    { ngx_null_string, 0 }
+};
+
+
+static ngx_path_init_t  ngx_http_client_temp_path = {
+    ngx_string(NGX_HTTP_CLIENT_TEMP_PATH), { 0, 0, 0 }
+};
+
+
 #if (NGX_HTTP_GZIP)
 
 static ngx_conf_enum_t  ngx_http_gzip_http_version[] = {
@@ -336,15 +352,22 @@ static ngx_command_t  ngx_http_core_comm
       ngx_conf_set_path_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_core_loc_conf_t, client_body_temp_path),
-      (void *) ngx_garbage_collector_temp_handler },
+      NULL },
 
     { ngx_string("client_body_in_file_only"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      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, client_body_in_file_only),
       &ngx_http_core_request_body_in_file },
 
+    { ngx_string("client_body_in_single_buffer"),
+      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, client_body_in_single_buffer),
+      NULL },
+
     { ngx_string("sendfile"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                         |NGX_CONF_TAKE1,
@@ -410,6 +433,14 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, limit_rate),
       NULL },
 
+    { ngx_string("limit_rate_after"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+                        |NGX_CONF_TAKE1,
+      ngx_conf_set_size_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, limit_rate_after),
+      NULL },
+
     { ngx_string("keepalive_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
       ngx_http_core_keepalive,
@@ -417,6 +448,13 @@ static ngx_command_t  ngx_http_core_comm
       0,
       NULL },
 
+    { ngx_string("keepalive_requests"),
+      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, keepalive_requests),
+      NULL },
+
     { ngx_string("satisfy"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_enum_slot,
@@ -515,6 +553,13 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, server_tokens),
       NULL },
 
+    { ngx_string("if_modified_since"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, if_modified_since),
+      &ngx_http_core_if_modified_since },
+
     { ngx_string("error_page"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                         |NGX_CONF_2MORE,
@@ -523,6 +568,13 @@ static ngx_command_t  ngx_http_core_comm
       0,
       NULL },
 
+    { ngx_string("try_files"),
+      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE,
+      ngx_http_core_try_files,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("post_action"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                         |NGX_CONF_TAKE1,
@@ -661,7 +713,7 @@ ngx_module_t  ngx_http_core_module = {
 };
 
 
-static ngx_str_t  ngx_http_core_get_method = { 3, (u_char *) "GET " };
+ngx_str_t  ngx_http_core_get_method = { 3, (u_char *) "GET " };
 
 
 void
@@ -793,7 +845,7 @@ ngx_http_core_find_config_phase(ngx_http
 {
     u_char                    *p;
     size_t                     len;
-    ngx_int_t                  rc, expect;
+    ngx_int_t                  rc;
     ngx_http_core_loc_conf_t  *clcf;
 
     r->content_handler = NULL;
@@ -837,15 +889,6 @@ ngx_http_core_find_config_phase(ngx_http
         return NGX_OK;
     }
 
-    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) {
@@ -1016,6 +1059,192 @@ ngx_http_core_post_access_phase(ngx_http
 
 
 ngx_int_t
+ngx_http_core_try_files_phase(ngx_http_request_t *r,
+    ngx_http_phase_handler_t *ph)
+{
+    size_t                        len, root, alias, reserve, allocated;
+    u_char                       *p, *name;
+    ngx_str_t                     path, args;
+    ngx_uint_t                    test_dir;
+    ngx_http_try_file_t          *tf;
+    ngx_open_file_info_t          of;
+    ngx_http_script_code_pt       code;
+    ngx_http_script_engine_t      e;
+    ngx_http_core_loc_conf_t     *clcf;
+    ngx_http_script_len_code_pt   lcode;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "try files phase: %ui", r->phase_handler);
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    if (clcf->try_files == NULL) {
+        r->phase_handler++;
+        return NGX_AGAIN;
+    }
+
+    allocated = 0;
+    root = 0;
+    name = NULL;
+    /* suppress MSVC warning */
+    path.data = NULL;
+
+    tf = clcf->try_files;
+
+    alias = clcf->alias ? clcf->name.len : 0;
+
+    for ( ;; ) {
+
+        if (tf->lengths) {
+            ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
+
+            e.ip = tf->lengths->elts;
+            e.request = r;
+
+            /* 1 is for terminating '\0' as in static names */
+            len = 1;
+
+            while (*(uintptr_t *) e.ip) {
+                lcode = *(ngx_http_script_len_code_pt *) e.ip;
+                len += lcode(&e);
+            }
+
+        } else {
+            len = tf->name.len;
+        }
+
+        /* 16 bytes are preallocation */
+        reserve = ngx_abs((ssize_t) (len - r->uri.len)) + alias + 16;
+
+        if (reserve > allocated) {
+
+            /* we just need to allocate path and to copy a root */
+
+            if (ngx_http_map_uri_to_path(r, &path, &root, reserve) == NULL) {
+                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return NGX_OK;
+            }
+
+            name = path.data + root;
+            allocated = path.len - root - (r->uri.len - alias);
+         }
+
+        if (tf->values == NULL) {
+
+            /* tf->name.len includes the terminating '\0' */
+
+            ngx_memcpy(name, tf->name.data, tf->name.len);
+
+            path.len = (name + tf->name.len - 1) - path.data;
+
+        } else {
+            e.ip = tf->values->elts;
+            e.pos = name;
+            e.flushed = 1;
+
+            while (*(uintptr_t *) e.ip) {
+                code = *(ngx_http_script_code_pt *) e.ip;
+                code((ngx_http_script_engine_t *) &e);
+            }
+
+            path.len = e.pos - path.data;
+
+            *e.pos = '\0';
+
+            if (alias && ngx_strncmp(name, clcf->name.data, alias) == 0) {
+                ngx_memcpy(name, name + alias, len - alias);
+                path.len -= alias;
+            }
+        }
+
+        test_dir = tf->test_dir;
+
+        tf++;
+
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "try to use file: \"%s\" \"%s\"", name, path.data);
+
+        if (tf->lengths == NULL && tf->name.len == 0) {
+
+            if (tf->code) {
+                ngx_http_finalize_request(r, tf->code);
+                return NGX_OK;
+            }
+
+            path.len -= root;
+            path.data += root;
+
+            if (path.data[0] == '@') {
+                (void) ngx_http_named_location(r, &path);
+
+            } else {
+                ngx_http_split_args(r, &path, &args);
+
+                (void) ngx_http_internal_redirect(r, &path, &args);
+            }
+
+            return NGX_OK;
+        }
+
+        ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+        of.directio = clcf->directio;
+        of.valid = clcf->open_file_cache_valid;
+        of.min_uses = clcf->open_file_cache_min_uses;
+        of.test_only = 1;
+        of.errors = clcf->open_file_cache_errors;
+        of.events = clcf->open_file_cache_events;
+
+        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+            != NGX_OK)
+        {
+            if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR) {
+                ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
+                              "%s \"%s\" failed", of.failed, path.data);
+            }
+
+            continue;
+        }
+
+        if (of.is_dir && !test_dir) {
+            continue;
+        }
+
+        path.len -= root;
+        path.data += root;
+
+        if (!alias) {
+            r->uri = path;
+
+        } else {
+            r->uri.len = alias + path.len;
+            r->uri.data = ngx_pnalloc(r->pool, r->uri.len);
+            if (r->uri.data == NULL) {
+                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return NGX_OK;
+            }
+
+            p = ngx_copy(r->uri.data, clcf->name.data, alias);
+            ngx_memcpy(p, name, path.len);
+        }
+
+        if (ngx_http_set_exten(r) != NGX_OK) {
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return NGX_OK;
+        }
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "try file uri: \"%V\"", &r->uri);
+
+        r->phase_handler++;
+        return NGX_AGAIN;
+    }
+
+    /* not reached */
+}
+
+
+ngx_int_t
 ngx_http_core_content_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph)
 {
@@ -1085,10 +1314,10 @@ ngx_http_update_location_config(ngx_http
     }
 
     if (r == r->main) {
-        r->connection->log->file = clcf->err_log->file;
+        r->connection->log->file = clcf->error_log->file;
 
         if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
-            r->connection->log->log_level = clcf->err_log->log_level;
+            r->connection->log->log_level = clcf->error_log->log_level;
         }
     }
 
@@ -1110,8 +1339,15 @@ ngx_http_update_location_config(ngx_http
         r->request_body_file_log_level = NGX_LOG_WARN;
     }
 
-    if (r->keepalive && clcf->keepalive_timeout == 0) {
-        r->keepalive = 0;
+    r->request_body_in_single_buf = clcf->client_body_in_single_buffer;
+
+    if (r->keepalive) {
+        if (clcf->keepalive_timeout == 0) {
+            r->keepalive = 0;
+
+        } else if (r->connection->requests >= clcf->keepalive_requests) {
+            r->keepalive = 0;
+        }
     }
 
     if (!clcf->tcp_nopush) {
@@ -1143,7 +1379,7 @@ ngx_http_core_find_location(ngx_http_req
     ngx_int_t                  rc;
     ngx_http_core_loc_conf_t  *pclcf;
 #if (NGX_PCRE)
-    ngx_int_t                  n;
+    ngx_int_t                  n, len;
     ngx_uint_t                 noregex;
     ngx_http_core_loc_conf_t  *clcf, **clcfp;
 
@@ -1177,12 +1413,26 @@ ngx_http_core_find_location(ngx_http_req
 
     if (noregex == 0 && pclcf->regex_locations) {
 
+        len = 0;
+
         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 ((*clcfp)->captures) {
+
+                len = (NGX_HTTP_MAX_CAPTURES + 1) * 3;
+
+                if (r->captures == NULL) {
+                    r->captures = ngx_palloc(r->pool, len * sizeof(int));
+                    if (r->captures == NULL) {
+                        return NGX_ERROR;
+                    }
+                }
+            }
+
+            n = ngx_regex_exec((*clcfp)->regex, &r->uri, r->captures, len);
 
             if (n == NGX_REGEX_NO_MATCHED) {
                 continue;
@@ -1200,6 +1450,9 @@ ngx_http_core_find_location(ngx_http_req
 
             r->loc_conf = (*clcfp)->loc_conf;
 
+            r->ncaptures = len;
+            r->captures_data = r->uri.data;
+
             /* look up nested locations */
 
             rc = ngx_http_core_find_location(r);
@@ -1294,80 +1547,41 @@ ngx_http_core_find_static_location(ngx_h
 }
 
 
-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;
-    }
-
-    /* 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;
+    u_char      c, *lowcase;
+    size_t      len;
+    ngx_uint_t  i, hash;
 
     if (r->headers_out.content_type.len == 0) {
         return NULL;
     }
 
+    len = r->headers_out.content_type_len;
+
     if (r->headers_out.content_type_lowcase == NULL) {
 
-        p = ngx_pnalloc(r->pool, r->headers_out.content_type_len);
-
-        if (p == NULL) {
+        lowcase = ngx_pnalloc(r->pool, len);
+        if (lowcase == NULL) {
             return NULL;
         }
 
-        r->headers_out.content_type_lowcase = p;
+        r->headers_out.content_type_lowcase = lowcase;
 
         hash = 0;
 
-        for (i = 0; i < r->headers_out.content_type_len; i++) {
+        for (i = 0; i < len; i++) {
             c = ngx_tolower(r->headers_out.content_type.data[i]);
             hash = ngx_hash(hash, c);
-            *p++ = c;
+            lowcase[i] = 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);
+    return ngx_hash_find(types_hash, r->headers_out.content_type_hash,
+                         r->headers_out.content_type_lowcase, len);
 }
 
 
@@ -1396,7 +1610,7 @@ ngx_http_set_content_type(ngx_http_reque
 
                 exten = ngx_pnalloc(r->pool, r->exten.len);
                 if (exten == NULL) {
-                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                    return NGX_ERROR;
                 }
 
                 hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len);
@@ -1467,16 +1681,24 @@ ngx_http_send_header(ngx_http_request_t 
 ngx_int_t
 ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    ngx_int_t  rc;
-
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+    ngx_int_t          rc;
+    ngx_connection_t  *c;
+
+    c = r->connection;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http output filter \"%V?%V\"", &r->uri, &r->args);
 
     rc = ngx_http_top_body_filter(r, in);
 
     if (rc == NGX_ERROR) {
+
+        if (c->destroyed) {
+            return NGX_DONE;
+        }
+
         /* NGX_ERROR may be returned by any filter */
-        r->connection->error = 1;
+        c->error = 1;
     }
 
     return rc;
@@ -1502,13 +1724,11 @@ ngx_http_map_uri_to_path(ngx_http_reques
         return NULL;
     }
 
-    reserved += r->uri.len - alias + 1;
-
     if (clcf->root_lengths == NULL) {
 
         *root_length = clcf->root.len;
 
-        path->len = clcf->root.len + reserved;
+        path->len = clcf->root.len + reserved + r->uri.len - alias + 1;
 
         path->data = ngx_pnalloc(r->pool, path->len);
         if (path->data == NULL) {
@@ -1518,6 +1738,16 @@ ngx_http_map_uri_to_path(ngx_http_reques
         last = ngx_copy(path->data, clcf->root.data, clcf->root.len);
 
     } else {
+
+#if (NGX_PCRE)
+        ngx_uint_t  captures;
+
+        captures = alias && clcf->captures;
+        reserved += captures ? 1 : r->uri.len - alias + 1;
+#else
+        reserved += r->uri.len - alias + 1;
+#endif
+
         if (ngx_http_script_run(r, path, clcf->root_lengths->elts, reserved,
                                 clcf->root_values->elts)
             == NULL)
@@ -1525,13 +1755,19 @@ ngx_http_map_uri_to_path(ngx_http_reques
             return NULL;
         }
 
-        if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, path, 0)== NGX_ERROR)
-        {
+        if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, path, 0) != NGX_OK) {
             return NULL;
         }
 
         *root_length = path->len - reserved;
         last = path->data + *root_length;
+
+#if (NGX_PCRE)
+        if (captures) {
+            *last = '\0';
+            return last;
+        }
+#endif
     }
 
     last = ngx_cpystrn(last, r->uri.data + alias, r->uri.len - alias + 1);
@@ -1612,38 +1848,6 @@ 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_sock_ntop((struct sockaddr *) &sin, s->data,
-                           NGX_INET_ADDRSTRLEN);
-
-    return NGX_OK;
-}
-
-
 #if (NGX_HTTP_GZIP)
 
 ngx_int_t
@@ -1813,7 +2017,6 @@ ngx_http_subrequest(ngx_http_request_t *
 {
     ngx_connection_t              *c;
     ngx_http_request_t            *sr;
-    ngx_http_log_ctx_t            *ctx;
     ngx_http_core_srv_conf_t      *cscf;
     ngx_http_postponed_request_t  *pr, *p;
 
@@ -1843,7 +2046,7 @@ ngx_http_subrequest(ngx_http_request_t *
 
     if (ngx_list_init(&sr->headers_out.headers, r->pool, 20,
                       sizeof(ngx_table_elt_t))
-        == NGX_ERROR)
+        != NGX_OK)
     {
         return NGX_ERROR;
     }
@@ -1878,6 +2081,7 @@ ngx_http_subrequest(ngx_http_request_t *
 
     sr->zero_in_uri = (flags & NGX_HTTP_ZERO_IN_URI) != 0;
     sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;
+    sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0;
 
     sr->unparsed_uri = r->unparsed_uri;
     sr->method_name = ngx_http_core_get_method;
@@ -1891,16 +2095,12 @@ ngx_http_subrequest(ngx_http_request_t *
     sr->parent = r;
     sr->post_subrequest = ps;
     sr->read_event_handler = ngx_http_request_empty_handler;
-    sr->write_event_handler = ngx_http_request_empty_handler;
-
-    if (c->data == r) {
+    sr->write_event_handler = ngx_http_handler;
+
+    if (c->data == r && r->postponed == NULL) {
         c->data = sr;
     }
 
-    sr->in_addr = r->in_addr;
-    sr->port = r->port;
-    sr->port_text = r->port_text;
-
     sr->variables = r->variables;
 
     sr->log_handler = r->log_handler;
@@ -1922,39 +2122,19 @@ ngx_http_subrequest(ngx_http_request_t *
         r->postponed = pr;
     }
 
-    ctx = c->log->data;
-    ctx->current_request = sr;
-
     sr->internal = 1;
-    sr->fast_subrequest = 1;
 
     sr->discard_body = r->discard_body;
+    sr->expect_tested = 1;
     sr->main_filter_need_in_memory = r->main_filter_need_in_memory;
 
     sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
 
-    ngx_http_handler(sr);
-
-    if (!c->destroyed) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http subrequest done \"%V?%V\"", uri, &sr->args);
-
-        r->main->subrequests++;
-
-        *psr = sr;
-
-        if (sr->fast_subrequest) {
-            sr->fast_subrequest = 0;
-
-            if (sr->done) {
-                return NGX_OK;
-            }
-        }
-
-        return NGX_AGAIN;
-    }
-
-    return NGX_DONE;
+    r->main->subrequests++;
+
+    *psr = sr;
+
+    return ngx_http_post_request(sr);
 }
 
 
@@ -2001,6 +2181,10 @@ ngx_http_internal_redirect(ngx_http_requ
 
     ngx_http_update_location_config(r);
 
+#if (NGX_HTTP_CACHE)
+    r->cache = NULL;
+#endif
+
     r->internal = 1;
 
     ngx_http_handler(r);
@@ -2018,33 +2202,37 @@ ngx_http_named_location(ngx_http_request
 
     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
 
-    for (clcfp = cscf->named_locations; *clcfp; clcfp++) {
-
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "test location: \"%V\"", &(*clcfp)->name);
-
-        if (name->len != (*clcfp)->name.len
-            || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0)
-        {
-            continue;
+    if (cscf->named_locations) {
+
+        for (clcfp = cscf->named_locations; *clcfp; clcfp++) {
+
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "test location: \"%V\"", &(*clcfp)->name);
+
+            if (name->len != (*clcfp)->name.len
+                || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0)
+            {
+                continue;
+            }
+
+            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "using location: %V \"%V?%V\"",
+                           name, &r->uri, &r->args);
+
+            r->internal = 1;
+            r->content_handler = NULL;
+            r->loc_conf = (*clcfp)->loc_conf;
+
+            ngx_http_update_location_config(r);
+
+            cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+            r->phase_handler = cmcf->phase_engine.location_rewrite_index;
+
+            ngx_http_core_run_phases(r);
+
+            return NGX_DONE;
         }
-
-        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "using location: %V \"%V?%V\"", name, &r->uri, &r->args);
-
-        r->internal = 1;
-        r->content_handler = NULL;
-        r->loc_conf = (*clcfp)->loc_conf;
-
-        ngx_http_update_location_config(r);
-
-        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
-
-        r->phase_handler = cmcf->phase_engine.location_rewrite_index;
-
-        ngx_http_core_run_phases(r);
-
-        return NGX_DONE;
     }
 
     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -2185,8 +2373,10 @@ static char *
 ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
 {
     char                      *rv;
+    u_char                    *mod;
+    size_t                     len;
+    ngx_str_t                 *value, *name;
     ngx_uint_t                 i;
-    ngx_str_t                 *value;
     ngx_conf_t                 save;
     ngx_http_module_t         *module;
     ngx_http_conf_ctx_t       *ctx, *pctx;
@@ -2228,45 +2418,32 @@ ngx_http_core_location(ngx_conf_t *cf, n
     value = cf->args->elts;
 
     if (cf->args->nelts == 3) {
-        if (value[1].len == 1 && value[1].data[0] == '=') {
-            clcf->name = value[2];
+
+        len = value[1].len;
+        mod = value[1].data;
+        name = &value[2];
+
+        if (len == 1 && mod[0] == '=') {
+
+            clcf->name = *name;
             clcf->exact_match = 1;
 
-        } else if (value[1].len == 2
-                   && value[1].data[0] == '^'
-                   && value[1].data[1] == '~')
-        {
-            clcf->name = value[2];
+        } else if (len == 2 && mod[0] == '^' && mod[1] == '~') {
+
+            clcf->name = *name;
             clcf->noregex = 1;
 
-        } else if ((value[1].len == 1 && value[1].data[0] == '~')
-                   || (value[1].len == 2
-                       && value[1].data[0] == '~'
-                       && value[1].data[1] == '*'))
-        {
-#if (NGX_PCRE)
-            ngx_str_t  err;
-            u_char     errstr[NGX_MAX_CONF_ERRSTR];
-
-            err.len = NGX_MAX_CONF_ERRSTR;
-            err.data = errstr;
-
-            clcf->regex = ngx_regex_compile(&value[2],
-                                     value[1].len == 2 ? NGX_REGEX_CASELESS: 0,
-                                     cf->pool, &err);
-
-            if (clcf->regex == NULL) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+        } else if (len == 1 && mod[0] == '~') {
+
+            if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
                 return NGX_CONF_ERROR;
             }
 
-            clcf->name = value[2];
-#else
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "the using of the regex \"%V\" "
-                               "requires PCRE library", &value[2]);
-            return NGX_CONF_ERROR;
-#endif
+        } else if (len == 2 && mod[0] == '~' && mod[1] == '*') {
+
+            if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
+                return NGX_CONF_ERROR;
+            }
 
         } else {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -2276,10 +2453,47 @@ ngx_http_core_location(ngx_conf_t *cf, n
 
     } else {
 
-        clcf->name = value[1];
-
-        if (value[1].data[0] == '@') {
-            clcf->named = 1;
+        name = &value[1];
+
+        if (name->data[0] == '=') {
+
+            clcf->name.len = name->len - 1;
+            clcf->name.data = name->data + 1;
+            clcf->exact_match = 1;
+
+        } else if (name->data[0] == '^' && name->data[1] == '~') {
+
+            clcf->name.len = name->len - 2;
+            clcf->name.data = name->data + 2;
+            clcf->noregex = 1;
+
+        } else if (name->data[0] == '~') {
+
+            name->len--;
+            name->data++;
+
+            if (name->data[0] == '*') {
+
+                name->len--;
+                name->data++;
+
+                if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
+                    return NGX_CONF_ERROR;
+                }
+
+            } else {
+                if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
+                    return NGX_CONF_ERROR;
+                }
+            }
+
+        } else {
+
+            clcf->name = *name;
+
+            if (name->data[0] == '@') {
+                clcf->named = 1;
+            }
         }
     }
 
@@ -2317,13 +2531,13 @@ ngx_http_core_location(ngx_conf_t *cf, n
             return NGX_CONF_ERROR;
         }
 
+        len = pclcf->name.len;
+
 #if (NGX_PCRE)
         if (clcf->regex == NULL
-            && ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len)
-               != 0)
+            && ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0)
 #else
-        if (ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len)
-            != 0)
+        if (ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0)
 #endif
         {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -2349,6 +2563,41 @@ ngx_http_core_location(ngx_conf_t *cf, n
 }
 
 
+static ngx_int_t
+ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf,
+    ngx_str_t *regex, ngx_uint_t caseless)
+{
+#if (NGX_PCRE)
+    ngx_str_t  err;
+    u_char     errstr[NGX_MAX_CONF_ERRSTR];
+
+    err.len = NGX_MAX_CONF_ERRSTR;
+    err.data = errstr;
+
+    clcf->regex = ngx_regex_compile(regex, caseless ? NGX_REGEX_CASELESS: 0,
+                                    cf->pool, &err);
+
+    if (clcf->regex == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+        return NGX_ERROR;
+    }
+
+    clcf->name = *regex;
+    clcf->captures = (ngx_regex_capture_count(clcf->regex) > 0);
+
+    return NGX_OK;
+
+#else
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "the using of the regex \"%V\" requires PCRE library",
+                       regex);
+    return NGX_ERROR;
+
+#endif
+}
+
+
 static char *
 ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
@@ -2390,7 +2639,7 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c
     if (ngx_strcmp(value[0].data, "include") == 0) {
         file = value[1];
 
-        if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){
+        if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
             return NGX_CONF_ERROR;
         }
 
@@ -2454,14 +2703,14 @@ ngx_http_core_create_main_conf(ngx_conf_
 
     cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_main_conf_t));
     if (cmcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     if (ngx_array_init(&cmcf->servers, cf->pool, 4,
                        sizeof(ngx_http_core_srv_conf_t *))
         != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     cmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT;
@@ -2513,7 +2762,7 @@ ngx_http_core_create_srv_conf(ngx_conf_t
 
     cscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_srv_conf_t));
     if (cscf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -2522,17 +2771,18 @@ ngx_http_core_create_srv_conf(ngx_conf_t
      *     conf->client_large_buffers.num = 0;
      */
 
-    if (ngx_array_init(&cscf->listen, cf->pool, 4, sizeof(ngx_http_listen_t))
-        == NGX_ERROR)
+    if (ngx_array_init(&cscf->listen, cf->temp_pool, 4,
+                       sizeof(ngx_http_listen_t))
+        != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     if (ngx_array_init(&cscf->server_names, cf->temp_pool, 4,
                        sizeof(ngx_http_server_name_t))
-        == NGX_ERROR)
+        != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     cscf->connection_pool_size = NGX_CONF_UNSET_SIZE;
@@ -2554,6 +2804,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
     ngx_http_core_srv_conf_t *conf = child;
 
     ngx_http_listen_t       *ls;
+    struct sockaddr_in      *sin;
     ngx_http_server_name_t  *sn;
 
     /* TODO: it does not merge, it inits only */
@@ -2566,18 +2817,25 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
 
         ngx_memzero(ls, sizeof(ngx_http_listen_t));
 
-        ls->addr = INADDR_ANY;
+        sin = (struct sockaddr_in *) &ls->sockaddr;
+
+        sin->sin_family = AF_INET;
 #if (NGX_WIN32)
-        ls->port = 80;
+        sin->sin_port = htons(80);
 #else
-        /* STUB: getuid() should be cached */
-        ls->port = (getuid() == 0) ? 80 : 8000;
+        sin->sin_port = htons((getuid() == 0) ? 80 : 8000);
 #endif
-        ls->family = AF_INET;
+        sin->sin_addr.s_addr = INADDR_ANY;
+
+        ls->socklen = sizeof(struct sockaddr_in);
 
         ls->conf.backlog = NGX_LISTEN_BACKLOG;
         ls->conf.rcvbuf = -1;
         ls->conf.sndbuf = -1;
+        ls->conf.wildcard = 1;
+
+        (void) ngx_sock_ntop((struct sockaddr *) &ls->sockaddr, ls->conf.addr,
+                             NGX_SOCKADDR_STRLEN, 1);
     }
 
     if (conf->server_name.data == NULL) {
@@ -2590,6 +2848,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
 
 #if (NGX_PCRE)
         sn->regex = NULL;
+        sn->captures = 0;
 #endif
         sn->core_srv_conf = conf;
         sn->name.len = conf->server_name.len;
@@ -2634,7 +2893,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t
 
     lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_loc_conf_t));
     if (lcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -2645,8 +2904,9 @@ ngx_http_core_create_loc_conf(ngx_conf_t
      *     lcf->post_action = { 0, NULL };
      *     lcf->types = NULL;
      *     lcf->default_type = { 0, NULL };
-     *     lcf->err_log = NULL;
+     *     lcf->error_log = NULL;
      *     lcf->error_pages = NULL;
+     *     lcf->try_files = NULL;
      *     lcf->client_body_path = NULL;
      *     lcf->regex = NULL;
      *     lcf->exact_match = 0;
@@ -2659,8 +2919,10 @@ ngx_http_core_create_loc_conf(ngx_conf_t
     lcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE;
     lcf->client_body_timeout = NGX_CONF_UNSET_MSEC;
     lcf->satisfy = NGX_CONF_UNSET_UINT;
+    lcf->if_modified_since = NGX_CONF_UNSET_UINT;
+    lcf->client_body_in_file_only = NGX_CONF_UNSET_UINT;
+    lcf->client_body_in_single_buffer = NGX_CONF_UNSET;
     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;
@@ -2670,8 +2932,10 @@ ngx_http_core_create_loc_conf(ngx_conf_t
     lcf->send_lowat = NGX_CONF_UNSET_SIZE;
     lcf->postpone_output = NGX_CONF_UNSET_SIZE;
     lcf->limit_rate = NGX_CONF_UNSET_SIZE;
+    lcf->limit_rate_after = NGX_CONF_UNSET_SIZE;
     lcf->keepalive_timeout = NGX_CONF_UNSET_MSEC;
     lcf->keepalive_header = NGX_CONF_UNSET;
+    lcf->keepalive_requests = NGX_CONF_UNSET_UINT;
     lcf->lingering_time = NGX_CONF_UNSET_MSEC;
     lcf->lingering_timeout = NGX_CONF_UNSET_MSEC;
     lcf->resolver_timeout = NGX_CONF_UNSET_MSEC;
@@ -2739,7 +3003,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
             conf->root.len = sizeof("html") - 1;
             conf->root.data = (u_char *) "html";
 
-            if (ngx_conf_full_name(cf->cycle, &conf->root, 0) == NGX_ERROR) {
+            if (ngx_conf_full_name(cf->cycle, &conf->root, 0) != NGX_OK) {
                 return NGX_CONF_ERROR;
             }
         }
@@ -2823,11 +3087,11 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
         }
     }
 
-    if (conf->err_log == NULL) {
-        if (prev->err_log) {
-            conf->err_log = prev->err_log;
+    if (conf->error_log == NULL) {
+        if (prev->error_log) {
+            conf->error_log = prev->error_log;
         } else {
-            conf->err_log = cf->cycle->new_log;
+            conf->error_log = &cf->cycle->new_log;
         }
     }
 
@@ -2848,9 +3112,13 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
 
     ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy,
                               NGX_HTTP_SATISFY_ALL);
+    ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since,
+                              NGX_HTTP_IMS_EXACT);
+    ngx_conf_merge_uint_value(conf->client_body_in_file_only,
+                              prev->client_body_in_file_only, 0);
+    ngx_conf_merge_value(conf->client_body_in_single_buffer,
+                              prev->client_body_in_single_buffer, 0);
     ngx_conf_merge_value(conf->internal, prev->internal, 0);
-    ngx_conf_merge_value(conf->client_body_in_file_only,
-                              prev->client_body_in_file_only, 0);
     ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
     ngx_conf_merge_size_value(conf->sendfile_max_chunk,
                               prev->sendfile_max_chunk, 0);
@@ -2864,10 +3132,14 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
     ngx_conf_merge_size_value(conf->postpone_output, prev->postpone_output,
                               1460);
     ngx_conf_merge_size_value(conf->limit_rate, prev->limit_rate, 0);
+    ngx_conf_merge_size_value(conf->limit_rate_after, prev->limit_rate_after,
+                              0);
     ngx_conf_merge_msec_value(conf->keepalive_timeout,
                               prev->keepalive_timeout, 75000);
     ngx_conf_merge_sec_value(conf->keepalive_header,
                               prev->keepalive_header, 0);
+    ngx_conf_merge_uint_value(conf->keepalive_requests,
+                              prev->keepalive_requests, 100);
     ngx_conf_merge_msec_value(conf->lingering_time,
                               prev->lingering_time, 30000);
     ngx_conf_merge_msec_value(conf->lingering_timeout,
@@ -2893,10 +3165,13 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
         conf->resolver = prev->resolver;
     }
 
-    ngx_conf_merge_path_value(conf->client_body_temp_path,
+    if (ngx_conf_merge_path_value(cf, &conf->client_body_temp_path,
                               prev->client_body_temp_path,
-                              NGX_HTTP_CLIENT_TEMP_PATH, 0, 0, 0,
-                              ngx_garbage_collector_temp_handler, cf);
+                              &ngx_http_client_temp_path)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
 
     ngx_conf_merge_value(conf->reset_timedout_connection,
                               prev->reset_timedout_connection, 0);
@@ -2948,8 +3223,6 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
 }
 
 
-/* AF_INET only */
-
 static char *
 ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
@@ -2990,17 +3263,18 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx
 
     ngx_memzero(ls, sizeof(ngx_http_listen_t));
 
-    ls->family = u.family;
-    ls->addr = u.addr.in_addr;
-    ls->port = u.port;
+    ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen);
+
+    ls->socklen = u.socklen;
     ls->file_name = cf->conf_file->file.name.data;
     ls->line = cf->conf_file->line;
     ls->conf.backlog = NGX_LISTEN_BACKLOG;
     ls->conf.rcvbuf = -1;
     ls->conf.sndbuf = -1;
-
-    n = ngx_inet_ntop(AF_INET, &ls->addr, ls->conf.addr, NGX_INET_ADDRSTRLEN);
-    ngx_sprintf(&ls->conf.addr[n], ":%ui", ls->port);
+    ls->conf.wildcard = u.wildcard;
+
+    (void) ngx_sock_ntop((struct sockaddr *) &ls->sockaddr, ls->conf.addr,
+                         NGX_SOCKADDR_STRLEN, 1);
 
     if (cf->args->nelts == 2) {
         return NGX_CONF_OK;
@@ -3099,6 +3373,45 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx
             continue;
         }
 
+        if (ngx_strncmp(value[n].data, "ipv6only=o", 10) == 0) {
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+            struct sockaddr  *sa;
+
+            sa = (struct sockaddr *) ls->sockaddr;
+
+            if (sa->sa_family == AF_INET6) {
+
+                if (ngx_strcmp(&value[n].data[10], "n") == 0) {
+                    ls->conf.ipv6only = 1;
+
+                } else if (ngx_strcmp(&value[n].data[10], "ff") == 0) {
+                    ls->conf.ipv6only = 2;
+
+                } else {
+                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                       "invalid ipv6only flags \"%s\"",
+                                       &value[n].data[9]);
+                    return NGX_CONF_ERROR;
+                }
+
+                ls->conf.bind = 1;
+
+            } else {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "ipv6only is not supported "
+                                   "on addr \"%s\", ignored",
+                                   ls->conf.addr);
+            }
+
+            continue;
+#else
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "bind ipv6only is not supported "
+                               "on this platform");
+            return NGX_CONF_ERROR;
+#endif
+        }
+
         if (ngx_strcmp(value[n].data, "ssl") == 0) {
 #if (NGX_HTTP_SSL)
             ls->conf.ssl = 1;
@@ -3188,6 +3501,7 @@ ngx_http_core_server_name(ngx_conf_t *cf
 
 #if (NGX_PCRE)
         sn->regex = NULL;
+        sn->captures = 0;
 #endif
         sn->core_srv_conf = cscf;
         sn->name = value[i];
@@ -3214,6 +3528,7 @@ ngx_http_core_server_name(ngx_conf_t *cf
             return NGX_CONF_ERROR;
         }
 
+        sn->captures = (ngx_regex_capture_count(sn->regex) > 0);
         sn->name = value[i];
         }
 #else
@@ -3266,18 +3581,6 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c
         return NGX_CONF_ERROR;
     }
 
-#if (NGX_PCRE)
-
-    if (lcf->regex && alias) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "the \"alias\" directive may not be used "
-                           "inside location given by regular expression");
-
-        return NGX_CONF_ERROR;
-    }
-
-#endif
-
     value = cf->args->elts;
 
     if (ngx_strstr(value[1].data, "$document_root")
@@ -3310,31 +3613,43 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c
     }
 
     if (lcf->root.data[0] != '$') {
-        if (ngx_conf_full_name(cf->cycle, &lcf->root, 0) == NGX_ERROR) {
+        if (ngx_conf_full_name(cf->cycle, &lcf->root, 0) != NGX_OK) {
             return NGX_CONF_ERROR;
         }
     }
 
     n = ngx_http_script_variables_count(&lcf->root);
 
-    if (n == 0) {
-        return NGX_CONF_OK;
+    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+    if (n) {
+        sc.cf = cf;
+        sc.source = &lcf->root;
+        sc.lengths = &lcf->root_lengths;
+        sc.values = &lcf->root_values;
+        sc.variables = n;
+        sc.complete_lengths = 1;
+        sc.complete_values = 1;
+
+        if (ngx_http_script_compile(&sc) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
     }
 
-    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
-
-    sc.cf = cf;
-    sc.source = &lcf->root;
-    sc.lengths = &lcf->root_lengths;
-    sc.values = &lcf->root_values;
-    sc.variables = n;
-    sc.complete_lengths = 1;
-    sc.complete_values = 1;
-
-    if (ngx_http_script_compile(&sc) != NGX_OK) {
+#if (NGX_PCRE)
+
+    if (alias && lcf->regex
+        && (ngx_regex_capture_count(lcf->regex) <= 0 || sc.ncaptures == 0))
+    {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "the \"alias\" directive must use captures "
+                           "inside location given by regular expression");
+
         return NGX_CONF_ERROR;
     }
 
+#endif
+
     return NGX_CONF_OK;
 }
 
@@ -3470,7 +3785,7 @@ ngx_http_core_directio(ngx_conf_t *cf, n
     value = cf->args->elts;
 
     if (ngx_strcmp(value[1].data, "off") == 0) {
-        clcf->directio = NGX_MAX_OFF_T_VALUE;
+        clcf->directio = NGX_OPEN_FILE_DIRECTIO_OFF;
         return NGX_CONF_OK;
     }
 
@@ -3488,13 +3803,13 @@ ngx_http_core_error_page(ngx_conf_t *cf,
 {
     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;
-    ngx_array_t                *uri_lengths, *uri_values;
-    ngx_http_err_page_t        *err;
-    ngx_http_script_compile_t   sc;
+    u_char                            *p;
+    ngx_int_t                          overwrite;
+    ngx_str_t                         *value, uri, args;
+    ngx_uint_t                         i, n;
+    ngx_http_err_page_t               *err;
+    ngx_http_complex_value_t           cv;
+    ngx_http_compile_complex_value_t   ccv;
 
     if (lcf->error_pages == NULL) {
         lcf->error_pages = ngx_array_create(cf->pool, 4,
@@ -3536,29 +3851,32 @@ ngx_http_core_error_page(ngx_conf_t *cf,
     }
 
     uri = value[cf->args->nelts - 1];
-    uri_lengths = NULL;
-    uri_values = NULL;
-
-    nvar = ngx_http_script_variables_count(&uri);
-
-    if (nvar) {
-        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
-
-        sc.cf = cf;
-        sc.source = &uri;
-        sc.lengths = &uri_lengths;
-        sc.values = &uri_values;
-        sc.variables = nvar;
-        sc.complete_lengths = 1;
-        sc.complete_values = 1;
-
-        if (ngx_http_script_compile(&sc) != NGX_OK) {
-            return NGX_CONF_ERROR;
+
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &uri;
+    ccv.complex_value = &cv;
+
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    args.len = 0;
+    args.data = NULL;
+
+    if (cv.lengths == NULL && uri.data[0] == '/') {
+        p = (u_char *) ngx_strchr(uri.data, '?');
+
+        if (p) {
+            cv.value.len = p - uri.data;
+            cv.value.data = uri.data;
+            p++;
+            args.len = (uri.data + uri.len) - p;
+            args.data = p;
         }
     }
 
-    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) {
@@ -3597,21 +3915,88 @@ ngx_http_core_error_page(ngx_conf_t *cf,
             }
         }
 
-        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;
+        err->value = cv;
+        err->args = args;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_core_loc_conf_t *clcf = conf;
+
+    ngx_str_t                  *value;
+    ngx_int_t                   code;
+    ngx_uint_t                  i, n;
+    ngx_http_try_file_t        *tf;
+    ngx_http_script_compile_t   sc;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    if (clcf->try_files) {
+        return "is duplicate";
+    }
+
+    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+    cmcf->try_files = 1;
+
+    tf = ngx_pcalloc(cf->pool, cf->args->nelts * sizeof(ngx_http_try_file_t));
+    if (tf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    clcf->try_files = tf;
+
+    value = cf->args->elts;
+
+    for (i = 0; i < cf->args->nelts - 1; i++) {
+
+        tf[i].name = value[i + 1];
+
+        if (tf[i].name.data[tf[i].name.len - 1] == '/') {
+            tf[i].test_dir = 1;
+            tf[i].name.len--;
+            tf[i].name.data[tf[i].name.len] = '\0';
+        }
+
+        n = ngx_http_script_variables_count(&tf[i].name);
+
+        if (n) {
+            ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+            sc.cf = cf;
+            sc.source = &tf[i].name;
+            sc.lengths = &tf[i].lengths;
+            sc.values = &tf[i].values;
+            sc.variables = n;
+            sc.complete_lengths = 1;
+            sc.complete_values = 1;
+
+            if (ngx_http_script_compile(&sc) != NGX_OK) {
+                return NGX_CONF_ERROR;
+            }
 
         } else {
-            err->uri = uri;
-            err->args.len = 0;
-            err->args.data = NULL;
+            /* add trailing '\0' to length */
+            tf[i].name.len++;
         }
-
-        err->uri_lengths = uri_lengths;
-        err->uri_values = uri_values;
+    }
+
+    if (tf[i - 1].name.data[0] == '=') {
+
+        code = ngx_atoi(tf[i - 1].name.data + 1, tf[i - 1].name.len - 2);
+
+        if (code == NGX_ERROR) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid code \"%*s\"",
+                               tf[i - 1].name.len - 1, tf[i - 1].name.data);
+            return NGX_CONF_ERROR;
+        }
+
+        tf[i].code = code;
     }
 
     return NGX_CONF_OK;
@@ -3701,12 +4086,25 @@ ngx_http_core_error_log(ngx_conf_t *cf, 
 {
     ngx_http_core_loc_conf_t *lcf = conf;
 
-    lcf->err_log = ngx_log_create_errlog(cf->cycle, cf->args);
-    if (lcf->err_log == NULL) {
+    ngx_str_t  *value;
+
+    if (lcf->error_log) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    lcf->error_log = ngx_log_create(cf->cycle, &value[1]);
+    if (lcf->error_log == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    return ngx_set_error_log_levels(cf, lcf->err_log);
+    if (cf->args->nelts == 2) {
+        lcf->error_log->log_level = NGX_LOG_ERR;
+        return NGX_CONF_OK;
+    }
+
+    return ngx_log_set_levels(cf, lcf->error_log);
 }
 
 
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -28,6 +28,11 @@
 #define NGX_HTTP_SATISFY_ANY            1
 
 
+#define NGX_HTTP_IMS_OFF                0
+#define NGX_HTTP_IMS_EXACT              1
+#define NGX_HTTP_IMS_BEFORE             2
+
+
 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;
 
@@ -35,9 +40,13 @@ typedef struct ngx_http_core_loc_conf_s 
 typedef struct {
     unsigned                   default_server:1;
     unsigned                   bind:1;
+    unsigned                   wildcard:1;
 #if (NGX_HTTP_SSL)
     unsigned                   ssl:1;
 #endif
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+    unsigned                   ipv6only:2;
+#endif
 
     int                        backlog;
     int                        rcvbuf;
@@ -50,15 +59,13 @@ typedef struct {
     ngx_uint_t                 deferred_accept;
 #endif
 
-    u_char                     addr[NGX_INET_ADDRSTRLEN + sizeof(":65535")];
-
+    u_char                     addr[NGX_SOCKADDR_STRLEN + 1];
 } ngx_http_listen_conf_t;
 
 
 typedef struct {
-    in_addr_t                  addr;
-    in_port_t                  port;
-    int                        family;
+    u_char                     sockaddr[NGX_SOCKADDRLEN];
+    socklen_t                  socklen;
 
     u_char                    *file_name;
     ngx_uint_t                 line;
@@ -81,6 +88,7 @@ typedef enum {
     NGX_HTTP_ACCESS_PHASE,
     NGX_HTTP_POST_ACCESS_PHASE,
 
+    NGX_HTTP_TRY_FILES_PHASE,
     NGX_HTTP_CONTENT_PHASE,
 
     NGX_HTTP_LOG_PHASE
@@ -129,6 +137,8 @@ typedef struct {
 
     ngx_hash_keys_arrays_t    *variables_keys;
 
+    ngx_uint_t                 try_files;       /* unsigned  try_files:1 */
+
     ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];
 } ngx_http_core_main_conf_t;
 
@@ -165,8 +175,6 @@ typedef struct {
 
 
 typedef struct {
-    in_addr_t                  addr;
-
     /* the default server configuration for this address:port */
     ngx_http_core_srv_conf_t  *core_srv_conf;
 
@@ -175,25 +183,42 @@ typedef struct {
 #if (NGX_HTTP_SSL)
     ngx_uint_t                 ssl;   /* unsigned  ssl:1; */
 #endif
-} ngx_http_in_addr_t;
-
-
-typedef struct {
-    in_port_t                  port;
-    ngx_str_t                  port_text;
-    ngx_http_in_addr_t        *addrs;
-    ngx_uint_t                 naddrs;
-} ngx_http_in_port_t;
-
-
-typedef struct {
-    in_port_t                  port;
-    ngx_array_t                addrs;     /* array of ngx_http_conf_in_addr_t */
-} ngx_http_conf_in_port_t;
+} ngx_http_addr_conf_t;
 
 
 typedef struct {
     in_addr_t                  addr;
+    ngx_http_addr_conf_t       conf;
+} ngx_http_in_addr_t;
+
+
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+    struct in6_addr            addr6;
+    ngx_http_addr_conf_t       conf;
+} ngx_http_in6_addr_t;
+
+#endif
+
+
+typedef struct {
+    /* ngx_http_in_addr_t or ngx_http_in6_addr_t */
+    void                      *addrs;
+    ngx_uint_t                 naddrs;
+} ngx_http_port_t;
+
+
+typedef struct {
+    ngx_int_t                  family;
+    in_port_t                  port;
+    ngx_array_t                addrs;     /* array of ngx_http_conf_addr_t */
+} ngx_http_conf_port_t;
+
+
+typedef struct {
+    struct sockaddr           *sockaddr;
+    socklen_t                  socklen;
 
     ngx_hash_t                 hash;
     ngx_hash_wildcard_t       *wc_head;
@@ -211,17 +236,19 @@ typedef struct {
 
     unsigned                   default_server:1;
     unsigned                   bind:1;
+    unsigned                   wildcard:1;
 #if (NGX_HTTP_SSL)
     unsigned                   ssl:1;
 #endif
 
     ngx_http_listen_conf_t    *listen_conf;
-} ngx_http_conf_in_addr_t;
+} ngx_http_conf_addr_t;
 
 
 struct ngx_http_server_name_s {
 #if (NGX_PCRE)
     ngx_regex_t               *regex;
+    ngx_uint_t                 captures;      /* unsigned  captures:1; */
 #endif
     ngx_http_core_srv_conf_t  *core_srv_conf; /* virtual name server conf */
     ngx_str_t                  name;
@@ -231,18 +258,28 @@ struct ngx_http_server_name_s {
 typedef struct {
     ngx_int_t                  status;
     ngx_int_t                  overwrite;
-    ngx_str_t                  uri;
+    ngx_http_complex_value_t   value;
     ngx_str_t                  args;
-    ngx_array_t               *uri_lengths;
-    ngx_array_t               *uri_values;
 } ngx_http_err_page_t;
 
 
+typedef struct {
+    ngx_array_t               *lengths;
+    ngx_array_t               *values;
+    ngx_str_t                  name;
+
+    unsigned                   code:10;
+    unsigned                   test_dir:1;
+} ngx_http_try_file_t;
+
+
 struct ngx_http_core_loc_conf_s {
     ngx_str_t     name;          /* location name */
 
 #if (NGX_PCRE)
     ngx_regex_t  *regex;
+
+    unsigned      captures:1;
 #endif
 
     unsigned      noname:1;   /* "if () {}" block or limit_except */
@@ -258,7 +295,9 @@ struct ngx_http_core_loc_conf_s {
 #endif
 
     ngx_http_location_tree_node_t   *static_locations;
+#if (NGX_PCRE)
     ngx_http_core_loc_conf_t       **regex_locations;
+#endif
 
     /* pointer to the modules' loc_conf */
     void        **loc_conf;
@@ -285,6 +324,7 @@ struct ngx_http_core_loc_conf_s {
     size_t        send_lowat;              /* send_lowat */
     size_t        postpone_output;         /* postpone_output */
     size_t        limit_rate;              /* limit_rate */
+    size_t        limit_rate_after;        /* limit_rate_after */
     size_t        sendfile_max_chunk;      /* sendfile_max_chunk */
 
     ngx_msec_t    client_body_timeout;     /* client_body_timeout */
@@ -298,10 +338,14 @@ struct ngx_http_core_loc_conf_s {
 
     time_t        keepalive_header;        /* keepalive_timeout */
 
+    ngx_uint_t    keepalive_requests;      /* keepalive_requests */
     ngx_uint_t    satisfy;                 /* satisfy */
+    ngx_uint_t    if_modified_since;       /* if_modified_since */
+    ngx_uint_t    client_body_in_file_only; /* client_body_in_file_only */
 
+    ngx_flag_t    client_body_in_single_buffer;
+                                           /* client_body_in_singe_buffer */
     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 */
@@ -327,6 +371,7 @@ struct ngx_http_core_loc_conf_s {
 #endif
 
     ngx_array_t  *error_pages;             /* error_page */
+    ngx_http_try_file_t    *try_files;     /* try_files */
 
     ngx_path_t   *client_body_temp_path;   /* client_body_temp_path */
 
@@ -336,7 +381,7 @@ struct ngx_http_core_loc_conf_s {
     ngx_flag_t    open_file_cache_errors;
     ngx_flag_t    open_file_cache_events;
 
-    ngx_log_t    *err_log;
+    ngx_log_t    *error_log;
 
     ngx_uint_t    types_hash_max_size;
     ngx_uint_t    types_hash_bucket_size;
@@ -385,6 +430,8 @@ ngx_int_t ngx_http_core_access_phase(ngx
     ngx_http_phase_handler_t *ph);
 ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph);
+ngx_int_t ngx_http_core_try_files_phase(ngx_http_request_t *r,
+    ngx_http_phase_handler_t *ph);
 ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph);
 
@@ -395,7 +442,6 @@ ngx_int_t ngx_http_set_exten(ngx_http_re
 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
@@ -425,6 +471,8 @@ extern ngx_module_t  ngx_http_core_modul
 
 extern ngx_uint_t ngx_http_max_module;
 
+extern ngx_str_t  ngx_http_core_get_method;
+
 
 #define ngx_http_clear_content_length(r)                                      \
                                                                               \
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -7,253 +7,1566 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_http.h>
+#include <ngx_md5.h>
 
 
-#if (NGX_HAVE_OPENSSL_MD5_H)
-#include <openssl/md5.h>
-#else
-#include <md5.h>
-#endif
-
-#if (NGX_OPENSSL_MD5)
-#define  MD5Init    MD5_Init
-#define  MD5Update  MD5_Update
-#define  MD5Final   MD5_Final
-#endif
+static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,
+    ngx_http_cache_t *c);
+static ngx_http_file_cache_node_t *
+    ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);
+static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+static void ngx_http_file_cache_cleanup(void *data);
+static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);
+static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);
+static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,
+    ngx_queue_t *q, u_char *name);
+static ngx_int_t
+    ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache);
+static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,
+    ngx_str_t *path);
+static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,
+    ngx_str_t *path);
+static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,
+    ngx_str_t *path);
+static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,
+    ngx_http_cache_t *c);
+static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,
+    ngx_str_t *path);
 
 
-ngx_int_t ngx_http_file_cache_get(ngx_http_request_t *r,
-                                  ngx_http_cache_ctx_t *ctx)
-{
-    ngx_uint_t         i;
-    ngx_str_t         *key;
-    ngx_http_cache_t  *c;
-    MD5_CTX            md5;
-
-    c = r->cache;
-
-    c->file.name.len = ctx->path->name.len + 1 + ctx->path->len + 32;
-    if (!(c->file.name.data = ngx_palloc(r->pool, c->file.name.len + 1))) {
-        return NGX_ABORT;
-    }
-
-    MD5Init(&md5);
+ngx_str_t  ngx_http_cache_status[] = {
+    ngx_string("MISS"),
+    ngx_string("EXPIRED"),
+    ngx_string("STALE"),
+    ngx_string("UPDATING"),
+    ngx_string("HIT")
+};
 
-    key = c->key.elts;
-    for (i = 0; i < c->key.nelts; i++) {
-        MD5Update(&md5, key[i].data, key[i].len);
-    }
 
-    MD5Update(&md5, ctx->key.data, ctx->key.len);
-
-    MD5Final(c->md5, &md5);
-
-    ngx_memcpy(c->file.name.data, ctx->path->name.data, ctx->path->name.len);
-
-    ngx_md5_text(c->file.name.data + ctx->path->name.len + 1 + ctx->path->len,
-                 c->md5);
-
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                  "file cache key: %V, md5: %s", &ctx->key,
-                  c->file.name.data + ctx->path->name.len + 1 + ctx->path->len);
-
-    ngx_create_hashed_filename(&c->file, ctx->path);
-
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "file cache name: %s", c->file.name.data);
-
-    return ngx_http_file_cache_open(r->cache);
-}
+static u_char  ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' };
 
 
-ngx_int_t ngx_http_file_cache_open(ngx_http_cache_t *c)
+static ngx_int_t
+ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)
 {
-    ssize_t                   n;
-    ngx_err_t                 err;
-    ngx_http_cache_header_t  *h;
+    ngx_http_file_cache_t  *ocache = data;
 
-    c->file.fd = ngx_open_file(c->file.name.data,
-                               NGX_FILE_RDONLY, NGX_FILE_OPEN);
-
-    if (c->file.fd == NGX_INVALID_FILE) {
-        err = ngx_errno;
+    size_t                  len;
+    ngx_http_file_cache_t  *cache;
 
-        if (err == NGX_ENOENT || err == NGX_ENOTDIR) {
-            return NGX_DECLINED;
-        }
+    cache = shm_zone->data;
 
-        ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno,
-                      ngx_open_file_n " \"%s\" failed", c->file.name.data);
-        return NGX_ERROR;
-    }
-
-    if (c->uniq) {
-        if (ngx_fd_info(c->file.fd, &c->file.info) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno,
-                          ngx_fd_info_n " \"%s\" failed", c->file.name.data);
+    if (ocache) {
+        if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) {
+            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
+                          "cache \"%V\" uses the \"%V\" cache path "
+                          "while previously it used the \"%V\" cache path",
+                          &shm_zone->shm.name, &cache->path->name,
+                          &ocache->path->name);
 
             return NGX_ERROR;
         }
 
-        if (ngx_file_uniq(&c->file.info) == c->uniq) {
-            if (ngx_close_file(c->file.fd) == NGX_FILE_ERROR) {
-                ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
-                              ngx_close_file_n " \"%s\" failed",
-                              c->file.name.data);
+        cache->sh = ocache->sh;
+
+        cache->shpool = ocache->shpool;
+        cache->bsize = ocache->bsize;
+
+        cache->max_size /= cache->bsize;
+
+        return NGX_OK;
+    }
+
+    cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+    if (shm_zone->shm.exists) {
+        cache->sh = cache->shpool->data;
+        cache->bsize = ngx_fs_bsize(cache->path->name.data);
+
+        return NGX_OK;
+    }
+
+    cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));
+    if (cache->sh == NULL) {
+        return NGX_ERROR;
+    }
+
+    cache->shpool->data = cache->sh;
+
+    ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel,
+                    ngx_http_file_cache_rbtree_insert_value);
+
+    ngx_queue_init(&cache->sh->queue);
+
+    cache->sh->cold = 1;
+    cache->sh->size = 0;
+
+    cache->bsize = ngx_fs_bsize(cache->path->name.data);
+
+    cache->max_size /= cache->bsize;
+
+    len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len;
+
+    cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);
+    if (cache->shpool->log_ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z",
+                &shm_zone->shm.name);
+
+    return NGX_OK;
+}
+
+
+void
+ngx_http_file_cache_create_key(ngx_http_request_t *r)
+{
+    size_t             len;
+    ngx_str_t         *key;
+    ngx_uint_t         i;
+    ngx_md5_t          md5;
+    ngx_http_cache_t  *c;
+
+    c = r->cache;
+
+    len = 0;
+
+    ngx_crc32_init(c->crc32);
+    ngx_md5_init(&md5);
+
+    key = c->keys.elts;
+    for (i = 0; i < c->keys.nelts; i++) {
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http cache key: \"%V\"", &key[i]);
+
+        len += key[i].len;
+
+        ngx_crc32_update(&c->crc32, key[i].data, key[i].len);
+        ngx_md5_update(&md5, key[i].data, key[i].len);
+    }
+
+    c->header_start = sizeof(ngx_http_file_cache_header_t)
+                      + sizeof(ngx_http_file_cache_key) + len + 1;
+
+    ngx_crc32_final(c->crc32);
+    ngx_md5_final(c->key, &md5);
+}
+
+
+ngx_int_t
+ngx_http_file_cache_open(ngx_http_request_t *r)
+{
+    u_char                        *p;
+    time_t                         now;
+    ssize_t                        n;
+    ngx_int_t                      rc, rv;
+    ngx_uint_t                     cold, test;
+    ngx_path_t                    *path;
+    ngx_http_cache_t              *c;
+    ngx_pool_cleanup_t            *cln;
+    ngx_open_file_info_t           of;
+    ngx_http_file_cache_t         *cache;
+    ngx_http_core_loc_conf_t      *clcf;
+    ngx_http_file_cache_header_t  *h;
+
+    c = r->cache;
+    cache = c->file_cache;
+
+    cln = ngx_pool_cleanup_add(r->pool, 0);
+    if (cln == NULL) {
+        return NGX_ERROR;
+    }
+
+    rc = ngx_http_file_cache_exists(cache, c);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http file cache exists: %i e:%d", rc, c->exists);
+
+    if (rc == NGX_ERROR) {
+        return rc;
+    }
+
+    cln->handler = ngx_http_file_cache_cleanup;
+    cln->data = c;
+
+    if (rc == NGX_AGAIN) {
+        return rc;
+    }
+
+    cold = cache->sh->cold;
+
+    if (rc == NGX_OK) {
+
+        if (c->error) {
+            return c->error;
+        }
+
+        c->temp_file = 1;
+        test = c->exists ? 1 : 0;
+        rv = NGX_DECLINED;
+
+    } else { /* rc == NGX_DECLINED */
+
+        if (c->min_uses > 1) {
+
+            if (!cold) {
+                return NGX_AGAIN;
             }
 
-            return NGX_HTTP_CACHE_THE_SAME;
+            test = 1;
+            rv = NGX_AGAIN;
+
+        } else {
+            c->temp_file = 1;
+            test = cold ? 1 : 0;
+            rv = NGX_DECLINED;
         }
     }
 
-    n = ngx_read_file(&c->file, c->buf->pos, c->buf->end - c->buf->last, 0);
+    path = cache->path;
+
+    c->file.name.len = path->name.len + 1 + path->len
+                       + 2 * NGX_HTTP_CACHE_KEY_LEN;
+
+    c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);
+    if (c->file.name.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(c->file.name.data, path->name.data, path->name.len);
+
+    p = c->file.name.data + path->name.len + 1 + path->len;
+    p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN);
+    *p = '\0';
+
+    ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                   "cache file: \"%s\"", c->file.name.data);
+
+    if (!test) {
+        return NGX_DECLINED;
+    }
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
-    if (n == NGX_ERROR || n == NGX_AGAIN) {
+    of.uniq = c->uniq;
+    of.valid = clcf->open_file_cache_valid;
+    of.min_uses = clcf->open_file_cache_min_uses;
+    of.events = clcf->open_file_cache_events;
+    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
+
+    if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool)
+        != NGX_OK)
+    {
+        switch (of.err) {
+
+        case 0:
+            return NGX_ERROR;
+
+        case NGX_ENOENT:
+        case NGX_ENOTDIR:
+            return rv;
+
+        default:
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
+                          ngx_open_file_n " \"%s\" failed", c->file.name.data);
+            return NGX_ERROR;
+        }
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http file cache fd: %d", of.fd);
+
+    c->file.fd = of.fd;
+    c->file.log = r->connection->log;
+
+    c->buf = ngx_create_temp_buf(r->pool, c->body_start);
+    if (c->buf == NULL) {
+        return NGX_ERROR;
+    }
+
+    n = ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);
+
+    if (n == NGX_ERROR) {
         return n;
     }
 
-    if (n <= c->header_size) {
-        ngx_log_error(NGX_LOG_CRIT, c->log, 0,
+    if ((size_t) n <= c->header_start) {
+        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
                       "cache file \"%s\" is too small", c->file.name.data);
         return NGX_ERROR;
     }
 
-    h = (ngx_http_cache_header_t *) c->buf->pos;
-    c->expires = h->expires;
-    c->last_modified= h->last_modified;
-    c->date = h->date;
-    c->length = h->length;
+    h = (ngx_http_file_cache_header_t *) c->buf->pos;
 
-    if (h->key_len > (size_t) (c->buf->end - c->buf->pos)) {
-        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
-                      "cache file \"%s\" is probably invalid",
-                      c->file.name.data);
+    if (h->crc32 != c->crc32 || (size_t) h->header_start != c->header_start) {
+        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+                      "cache file \"%s\" has md5 collision", c->file.name.data);
         return NGX_DECLINED;
     }
 
-#if 0
+    c->buf->last += n;
 
-    /* TODO */
+    c->valid_sec = h->valid_sec;
+    c->last_modified = h->last_modified;
+    c->date = h->date;
+    c->valid_msec = h->valid_msec;
+    c->length = of.size;
+    c->body_start = h->body_start;
 
-    if (c->key_len && h->key_len != c->key_len)  {
+    r->cached = 1;
+
+    if (cold) {
 
-        ngx_strncmp(h->key, c->key_data, h->key_len) != 0))
+        ngx_shmtx_lock(&cache->shpool->mutex);
 
-        h->key[h->key_len] = '\0';
-        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
-                          "md5 collision: \"%s\" and \"%s\"",
-                          h->key, c->key.data);
-        return NGX_DECLINED;
+        if (!c->node->exists) {
+            c->node->uses = 1;
+            c->node->body_start = c->body_start;
+            c->node->exists = 1;
+            c->node->uniq = of.uniq;
+
+            cache->sh->size += (c->length + cache->bsize - 1) / cache->bsize;
+        }
+
+        ngx_shmtx_unlock(&cache->shpool->mutex);
     }
 
-#endif
+    now = ngx_time();
+
+    if (c->valid_sec < now) {
 
-    c->buf->last += n;
+        ngx_shmtx_lock(&cache->shpool->mutex);
+
+        if (c->node->updating) {
+            rc = NGX_HTTP_CACHE_UPDATING;
 
-    if (c->expires < ngx_time()) {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http file cache expired");
-        return NGX_HTTP_CACHE_STALE;
+        } else {
+            c->node->updating = 1;
+            rc = NGX_HTTP_CACHE_STALE;
+        }
+
+        ngx_shmtx_unlock(&cache->shpool->mutex);
+
+        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http file cache expired: %i %T %T",
+                       rc, c->valid_sec, now);
+
+        return rc;
     }
 
-    /* TODO: NGX_HTTP_CACHE_AGED */
-
-    /* STUB */ return NGX_DECLINED;
-
     return NGX_OK;
 }
 
 
-#if 0
+static ngx_int_t
+ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
+{
+    ngx_int_t                    rc;
+    ngx_http_file_cache_node_t  *fcn;
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+
+    fcn = ngx_http_file_cache_lookup(cache, c->key);
+
+    if (fcn) {
+        ngx_queue_remove(&fcn->queue);
+
+        if (fcn->error) {
+
+            if (fcn->valid_sec < ngx_time()) {
+                goto renew;
+            }
+
+            rc = NGX_OK;
+
+            goto done;
+        }
+
+        fcn->uses++;
+        fcn->count++;
+
+        if (fcn->exists) {
+
+            c->exists = fcn->exists;
+            c->body_start = fcn->body_start;
+
+            rc = NGX_OK;
+
+            goto done;
+        }
+
+        if (fcn->uses >= c->min_uses) {
+
+            c->exists = fcn->exists;
+            c->body_start = fcn->body_start;
+
+            rc = NGX_OK;
+
+        } else {
+            rc = NGX_AGAIN;
+        }
+
+        goto done;
+    }
+
+    fcn = ngx_slab_alloc_locked(cache->shpool,
+                                sizeof(ngx_http_file_cache_node_t));
+    if (fcn == NULL) {
+        ngx_shmtx_unlock(&cache->shpool->mutex);
+
+        (void) ngx_http_file_cache_forced_expire(cache);
+
+        ngx_shmtx_lock(&cache->shpool->mutex);
+
+        fcn = ngx_slab_alloc_locked(cache->shpool,
+                                    sizeof(ngx_http_file_cache_node_t));
+        if (fcn == NULL) {
+            rc = NGX_ERROR;
+            goto failed;
+        }
+    }
+
+    ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
+
+    ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
+               NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
+
+    ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
+
+renew:
+
+    rc = NGX_DECLINED;
+
+    fcn->uses = 1;
+    fcn->count = 1;
+    fcn->valid_msec = 0;
+    fcn->error = 0;
+    fcn->exists = 0;
+    fcn->valid_sec = 0;
+    fcn->uniq = 0;
+    fcn->body_start = 0;
+    fcn->length = 0;
+
+done:
+
+    fcn->expire = ngx_time() + cache->inactive;
+
+    ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
+
+    c->uniq = fcn->uniq;
+    c->error = fcn->error;
+    c->node = fcn;
+
+failed:
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
+
+    return rc;
+}
 
 
-int ngx_http_cache_update_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx,
-                               ngx_str_t *temp_file)
+static ngx_http_file_cache_node_t *
+ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)
 {
-    int        retry;
-    ngx_err_t  err;
+    ngx_int_t                    rc;
+    ngx_rbtree_key_t             node_key;
+    ngx_rbtree_node_t           *node, *sentinel;
+    ngx_http_file_cache_node_t  *fcn;
+
+    ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t));
+
+    node = cache->sh->rbtree.root;
+    sentinel = cache->sh->rbtree.sentinel;
+
+    while (node != sentinel) {
+
+        if (node_key < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (node_key > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        /* node_key == node->key */
+
+        do {
+            fcn = (ngx_http_file_cache_node_t *) node;
 
-    retry = 0;
+            rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key,
+                            NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
+
+            if (rc == 0) {
+                return fcn;
+            }
+
+            node = (rc < 0) ? node->left : node->right;
+
+        } while (node != sentinel && node_key == node->key);
+
+        break;
+    }
+
+    /* not found */
+
+    return NULL;
+}
+
+
+static void
+ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_rbtree_node_t           **p;
+    ngx_http_file_cache_node_t   *cn, *cnt;
 
     for ( ;; ) {
-        if (ngx_rename_file(temp_file->data, ctx->file.name.data) == NGX_OK) {
-            return NGX_OK;
+
+        if (node->key < temp->key) {
+
+            p = &temp->left;
+
+        } else if (node->key > temp->key) {
+
+            p = &temp->right;
+
+        } else { /* node->key == temp->key */
+
+            cn = (ngx_http_file_cache_node_t *) node;
+            cnt = (ngx_http_file_cache_node_t *) temp;
+
+            p = (ngx_memcmp(cn->key, cnt->key,
+                            NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))
+                 < 0)
+                    ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
         }
 
-        err = ngx_errno;
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}
+
+
+void
+ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf)
+{
+    ngx_http_file_cache_header_t  *h = (ngx_http_file_cache_header_t *) buf;
+
+    u_char            *p;
+    ngx_str_t         *key;
+    ngx_uint_t         i;
+    ngx_http_cache_t  *c;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http file cache set header");
+
+    c = r->cache;
+
+    h->valid_sec = c->valid_sec;
+    h->last_modified = c->last_modified;
+    h->date = c->date;
+    h->crc32 = c->crc32;
+    h->valid_msec = (u_short) c->valid_msec;
+    h->header_start = (u_short) c->header_start;
+    h->body_start = (u_short) c->body_start;
+
+    p = buf + sizeof(ngx_http_file_cache_header_t);
+
+    p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key));
+
+    key = c->keys.elts;
+    for (i = 0; i < c->keys.nelts; i++) {
+        p = ngx_copy(p, key[i].data, key[i].len);
+    }
+
+    *p = LF;
+}
+
+
+void
+ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
+{
+    off_t                   size;
+    ngx_int_t               rc;
+    ngx_file_uniq_t         uniq;
+    ngx_file_info_t         fi;
+    ngx_http_cache_t        *c;
+    ngx_ext_rename_file_t   ext;
+    ngx_http_file_cache_t  *cache;
+
+    c = r->cache;
+
+    if (c->updated) {
+        return;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http file cache update");
+
+    c->updated = 1;
+
+    cache = c->file_cache;
+
+    uniq = 0;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http file cache rename: \"%s\" to \"%s\"",
+                   tf->file.name.data, c->file.name.data);
+
+    ext.access = NGX_FILE_OWNER_ACCESS;
+    ext.path_access = NGX_FILE_OWNER_ACCESS;
+    ext.time = -1;
+    ext.create_path = 1;
+    ext.delete_file = 1;
+    ext.log_rename_error = 1;
+    ext.log = r->connection->log;
+
+    rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext);
+
+    if (rc == NGX_OK) {
+
+        if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+                          ngx_fd_info_n " \"%s\" failed", tf->file.name.data);
+
+            rc = NGX_ERROR;
+
+        } else {
+            uniq = ngx_file_uniq(&fi);
+        }
+    }
+
+    size = (c->length + cache->bsize - 1) / cache->bsize;
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+
+    c->node->count--;
+    c->node->uniq = uniq;
+    c->node->body_start = c->body_start;
 
-#if (NGX_WIN32)
-        if (err == NGX_EEXIST) {
-            if (ngx_win32_rename_file(temp_file, &ctx->file.name, r->pool)
-                                                                  == NGX_ERROR)
-            {
-                return NGX_ERROR;
+    size = size - (c->node->length + cache->bsize - 1) / cache->bsize;
+
+    c->node->length = c->length;
+
+    cache->sh->size += size;
+
+    if (rc == NGX_OK) {
+        c->node->exists = 1;
+    }
+
+    c->node->updating = 0;
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
+}
+
+
+ngx_int_t
+ngx_http_cache_send(ngx_http_request_t *r)
+{
+    ngx_int_t          rc;
+    ngx_buf_t         *b;
+    ngx_chain_t        out;
+    ngx_http_cache_t  *c;
+
+    c = r->cache;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                  "http file cache send: %s", c->file.name.data);
+
+    /* 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 = c->body_start;
+    b->file_last = c->length;
+
+    b->in_file = (c->length - c->body_start) ? 1: 0;
+    b->last_buf = (r == r->main) ? 1: 0;
+    b->last_in_chain = 1;
+
+    b->file->fd = c->file.fd;
+    b->file->name = c->file.name;
+    b->file->log = r->connection->log;
+
+    out.buf = b;
+    out.next = NULL;
+
+    return ngx_http_output_filter(r, &out);
+}
+
+
+void
+ngx_http_file_cache_free(ngx_http_request_t *r, ngx_temp_file_t *tf)
+{
+    ngx_http_cache_t       *c;
+    ngx_http_file_cache_t  *cache;
+
+    c = r->cache;
+
+    if (c->updated) {
+        return;
+    }
+
+    c->updated = 1;
+
+    cache = c->file_cache;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http file cache free");
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+
+    c->node->count--;
+
+    if (c->error) {
+        c->node->valid_sec = c->valid_sec;
+        c->node->valid_msec = c->valid_msec;
+        c->node->error = c->error;
+    }
+
+    c->node->updating = 0;
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
+
+    if (c->temp_file) {
+        if (tf && tf->file.fd != NGX_INVALID_FILE) {
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http file cache incomplete: \"%s\"",
+                           tf->file.name.data);
+
+            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",
+                              tf->file.name.data);
             }
         }
-#endif
-
-        if (retry || (err != NGX_ENOENT && err != NGX_ENOTDIR)) {
-            ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
-                          ngx_rename_file_n "(\"%s\", \"%s\") failed",
-                          temp_file->data, ctx->file.name.data);
-
-            return NGX_ERROR;
-        }
-
-        if (ngx_create_path(&ctx->file, ctx->path) == NGX_ERROR) {
-            return NGX_ERROR;
-        }
-
-        retry = 1;
     }
 }
 
 
-#endif
+static void
+ngx_http_file_cache_cleanup(void *data)
+{
+    ngx_http_cache_t  *c = data;
+
+    ngx_http_file_cache_t  *cache;
+
+    if (c->updated) {
+        return;
+    }
+
+    c->updated = 1;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
+                   "http file cache cleanup");
+
+    if (c->error) {
+        return;
+    }
+
+    cache = c->file_cache;
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+
+    c->node->count--;
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
+}
+
+
+static time_t
+ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
+{
+    u_char                      *name;
+    size_t                       len;
+    time_t                       wait;
+    ngx_uint_t                   tries;
+    ngx_path_t                  *path;
+    ngx_queue_t                 *q;
+    ngx_http_file_cache_node_t  *fcn;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                   "http file cache forced expire");
+
+    path = cache->path;
+    len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
+
+    name = ngx_alloc(len + 1, ngx_cycle->log);
+    if (name == NULL) {
+        return 10;
+    }
+
+    ngx_memcpy(name, path->name.data, path->name.len);
+
+    wait = 10;
+    tries = 0;
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+
+    for (q = ngx_queue_last(&cache->sh->queue);
+         q != ngx_queue_sentinel(&cache->sh->queue);
+         q = ngx_queue_prev(q))
+    {
+        fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
+
+        ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                  "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd",
+                  fcn->count, fcn->exists,
+                  fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
+
+        if (fcn->count) {
+
+            if (tries++ < 20) {
+                continue;
+            }
+
+            wait = 1;
+
+            break;
+        }
+
+        if (!fcn->exists) {
+
+            ngx_queue_remove(q);
+            ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
+            ngx_slab_free_locked(cache->shpool, fcn);
+
+            break;
+        }
+
+        ngx_http_file_cache_delete(cache, q, name);
+
+        break;
+    }
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
+
+    ngx_free(name);
+
+    return wait;
+}
 
 
-ngx_int_t ngx_http_cache_cleaner_handler(ngx_gc_t *gc, ngx_str_t *name,
-                                         ngx_dir_t *dir)
+static time_t
+ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
+{
+    u_char                      *name, *p;
+    size_t                       len;
+    time_t                       now, wait;
+    ngx_path_t                  *path;
+    ngx_queue_t                 *q;
+    ngx_http_file_cache_node_t  *fcn;
+    u_char                       key[2 * NGX_HTTP_CACHE_KEY_LEN];
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                   "http file cache expire");
+
+    path = cache->path;
+    len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
+
+    name = ngx_alloc(len + 1, ngx_cycle->log);
+    if (name == NULL) {
+        return 10;
+    }
+
+    ngx_memcpy(name, path->name.data, path->name.len);
+
+    now = ngx_time();
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+
+    for ( ;; ) {
+
+        if (ngx_queue_empty(&cache->sh->queue)) {
+            wait = 10;
+            break;
+        }
+
+        q = ngx_queue_last(&cache->sh->queue);
+
+        fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
+
+        wait = fcn->expire - now;
+
+        if (wait > 0) {
+            wait = wait > 10 ? 10 : wait;
+            break;
+        }
+
+        ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                       "http file cache expire: #%d %d %02xd%02xd%02xd%02xd",
+                       fcn->count, fcn->exists,
+                       fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
+
+        if (fcn->count) {
+
+            p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
+                             sizeof(ngx_rbtree_key_t));
+
+            len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
+            (void) ngx_hex_dump(p, fcn->key, len);
+
+            /*
+             * abnormally exited workers may leave locked cache entries,
+             * and although it may be safe to remove them completely,
+             * we prefer to remove them from inactive queue and rbtree
+             * only, and to allow other leaks
+             */
+
+            ngx_queue_remove(q);
+            ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
+
+            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+                       "ignore long locked inactive cache entry %*s, count:%d",
+                       2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);
+
+            continue;
+        }
+
+        if (!fcn->exists) {
+
+            ngx_queue_remove(q);
+            ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
+            ngx_slab_free_locked(cache->shpool, fcn);
+
+            continue;
+        }
+
+        ngx_http_file_cache_delete(cache, q, name);
+    }
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
+
+    ngx_free(name);
+
+    return wait;
+}
+
+
+static void
+ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,
+    u_char *name)
 {
-    int               rc;
-    ngx_buf_t         buf;
-    ngx_http_cache_t  c;
-    u_char            data[sizeof(ngx_http_cache_header_t)];
+    u_char                      *p;
+    size_t                       len;
+    ngx_path_t                  *path;
+    ngx_http_file_cache_node_t  *fcn;
+
+    fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
+
+    cache->sh->size -= (fcn->length + cache->bsize - 1) / cache->bsize;
+
+    path = cache->path;
+
+    p = name + path->name.len + 1 + path->len;
+
+    p = ngx_hex_dump(p, (u_char *) &fcn->node.key, sizeof(ngx_rbtree_key_t));
+
+    len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
+    p = ngx_hex_dump(p, fcn->key, len);
+    *p = '\0';
+
+    ngx_queue_remove(q);
+
+    ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
+
+    ngx_slab_free_locked(cache->shpool, fcn);
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
+
+    len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
+
+    ngx_create_hashed_filename(path, name, len);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                   "http file cache expire: \"%s\"", name);
+
+    if (ngx_delete_file(name) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
+                      ngx_delete_file_n " \"%s\" failed", name);
+    }
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+}
+
+
+static time_t
+ngx_http_file_cache_manager(void *data)
+{
+    ngx_http_file_cache_t  *cache = data;
+
+    off_t           size;
+    time_t          next;
+    ngx_tree_ctx_t  tree;
+
+    if (cache->sh->cold) {
+
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                       "http file cache manager update");
+
+        tree.init_handler = NULL;
+        tree.file_handler = ngx_http_file_cache_manage_file;
+        tree.pre_tree_handler = ngx_http_file_cache_noop;
+        tree.post_tree_handler = ngx_http_file_cache_noop;
+        tree.spec_handler = ngx_http_file_cache_delete_file;
+        tree.data = cache;
+        tree.alloc = 0;
+        tree.log = ngx_cycle->log;
+
+        cache->last = ngx_current_msec;
+        cache->files = 0;
+
+        if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
+            return 10;
+        }
+
+        cache->sh->cold = 0;
+
+        ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+                      "http file cache: %V %.3fM, bsize: %uz",
+                      &cache->path->name,
+                      ((double) cache->sh->size * cache->bsize) / (1024 * 1024),
+                      cache->bsize);
+    }
+
+    next = ngx_http_file_cache_expire(cache);
+
+    cache->last = ngx_current_msec;
+    cache->files = 0;
+
+    for ( ;; ) {
+        ngx_shmtx_lock(&cache->shpool->mutex);
+
+        size = cache->sh->size;
+
+        ngx_shmtx_unlock(&cache->shpool->mutex);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                       "http file cache size: %O", size);
+
+        if (size < cache->max_size) {
+            return next;
+        }
+
+        next = ngx_http_file_cache_forced_expire(cache);
+
+        if (ngx_http_file_cache_manager_sleep(cache) != NGX_OK) {
+            return next;
+        }
+    }
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache)
+{
+    ngx_msec_t  elapsed;
+
+    if (cache->files++ > 100) {
+
+        ngx_time_update(0, 0);
+
+        elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                       "http file cache manager time: %M", elapsed);
+
+        if (elapsed > 200) {
+
+            /*
+             * if processing 100 files takes more than 200ms,
+             * it seems that many operations require disk i/o,
+             * therefore sleep 200ms
+             */
+
+            ngx_msleep(200);
+
+            ngx_time_update(0, 0);
+        }
+
+        cache->last = ngx_current_msec;
+        cache->files = 0;
+    }
+
+    return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+    ngx_http_file_cache_t  *cache;
+
+    cache = ctx->data;
+
+    if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {
+        (void) ngx_http_file_cache_delete_file(ctx, path);
+    }
+
+    return ngx_http_file_cache_manager_sleep(cache);
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
+{
+    u_char                        *p;
+    ngx_fd_t                       fd;
+    ngx_int_t                      n;
+    ngx_uint_t                     i;
+    ngx_file_info_t                fi;
+    ngx_http_cache_t               c;
+    ngx_http_file_cache_t         *cache;
+    ngx_http_file_cache_header_t   h;
+
+    if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
+        return NGX_ERROR;
+    }
 
     ngx_memzero(&c, sizeof(ngx_http_cache_t));
 
-    c.file.fd = NGX_INVALID_FILE;
-    c.file.name = *name;
-    c.file.log = gc->log;
+    fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
 
-    c.header_size = sizeof(ngx_http_cache_header_t);
-    c.buf = &buf;
-    c.log = gc->log;
-    c.key_len = 0;
+    if (fd == NGX_INVALID_FILE) {
+        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+                      ngx_open_file_n " \"%s\" failed", name->data);
+        return NGX_ERROR;
+    }
 
-    buf.memory = 1;
-    buf.temporary = 1;
-    buf.pos = data;
-    buf.last = data;
-    buf.start = data;
-    buf.end = data + sizeof(ngx_http_cache_header_t);
+    c.file.fd = fd;
+    c.file.name = *name;
+    c.file.log = ctx->log;
 
-    rc = ngx_http_file_cache_open(&c);
-
-    /* TODO: NGX_AGAIN */
-
-    if (rc != NGX_ERROR&& rc != NGX_DECLINED && rc != NGX_HTTP_CACHE_STALE) {
-        return NGX_OK;
+    n = ngx_read_file(&c.file, (u_char *) &h,
+                      sizeof(ngx_http_file_cache_header_t), 0);
+    if (n == NGX_ERROR) {
+        return NGX_ERROR;
     }
 
-    if (ngx_delete_file(name->data) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_CRIT, c.log, ngx_errno,
-                      ngx_delete_file_n " \"%s\" failed", name->data);
+    if ((size_t) n < sizeof(ngx_http_file_cache_header_t)) {
+        ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
+                      "cache file \"%s\" is too small", name->data);
+        return NGX_ERROR;
+    }
+
+    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+                      ngx_fd_info_n " \"%s\" failed", name->data);
+
+    } else {
+        c.uniq = ngx_file_uniq(&fi);
+        c.valid_sec = h.valid_sec;
+        c.valid_msec = h.valid_msec;
+        c.body_start = h.body_start;
+        c.length = ngx_file_size(&fi);
+    }
+
+    if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
+                      ngx_close_file_n " \"%s\" failed", name->data);
+    }
+
+    if (c.body_start == 0) {
         return NGX_ERROR;
     }
 
-    gc->deleted++;
-    gc->freed += ngx_de_size(dir);
+    p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];
+
+    for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) {
+        n = ngx_hextoi(p, 2);
+
+        if (n == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        p += 2;
+
+        c.key[i] = (u_char) n;
+    }
+
+    cache = ctx->data;
+
+    return ngx_http_file_cache_add(cache, &c);
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
+{
+    ngx_http_file_cache_node_t  *fcn;
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+
+    fcn = ngx_http_file_cache_lookup(cache, c->key);
+
+    if (fcn == NULL) {
+
+        fcn = ngx_slab_alloc_locked(cache->shpool,
+                                    sizeof(ngx_http_file_cache_node_t));
+        if (fcn == NULL) {
+            ngx_shmtx_unlock(&cache->shpool->mutex);
+            return NGX_ERROR;
+        }
+
+        ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
+
+        ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
+                   NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
+
+        ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
+
+        fcn->uses = 1;
+        fcn->count = 0;
+        fcn->valid_msec = c->valid_msec;
+        fcn->error = 0;
+        fcn->exists = 1;
+        fcn->uniq = c->uniq;
+        fcn->valid_sec = c->valid_sec;
+        fcn->body_start = c->body_start;
+        fcn->length = c->length;
+
+        cache->sh->size += (c->length + cache->bsize - 1) / cache->bsize;
+
+    } else {
+        ngx_queue_remove(&fcn->queue);
+    }
+
+    fcn->expire = ngx_time() + cache->inactive;
+
+    ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+                   "http file cache delete: \"%s\"", path->data);
+
+    if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+                      ngx_delete_file_n " \"%s\" failed", path->data);
+    }
 
     return NGX_OK;
 }
+
+
+time_t
+ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)
+{
+    ngx_uint_t               i;
+    ngx_http_cache_valid_t  *valid;
+
+    if (cache_valid == NULL) {
+        return 0;
+    }
+
+    valid = cache_valid->elts;
+    for (i = 0; i < cache_valid->nelts; i++) {
+
+        if (valid[i].status == 0) {
+            return valid[i].valid;
+        }
+
+        if (valid[i].status == status) {
+            return valid[i].valid;
+        }
+    }
+
+    return 0;
+}
+
+
+char *
+ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    off_t                   max_size;
+    u_char                 *last, *p;
+    time_t                  inactive;
+    ssize_t                 size;
+    ngx_str_t               s, name, *value;
+    ngx_uint_t              i, n;
+    ngx_http_file_cache_t  *cache;
+
+    cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));
+    if (cache == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
+    if (cache->path == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    inactive = 600;
+
+    name.len = 0;
+    size = 0;
+    max_size = NGX_MAX_OFF_T_VALUE;
+
+    value = cf->args->elts;
+
+    cache->path->name = value[1];
+
+    if (cache->path->name.data[cache->path->name.len - 1] == '/') {
+        cache->path->name.len--;
+    }
+
+    if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    for (i = 2; i < cf->args->nelts; i++) {
+
+        if (ngx_strncmp(value[i].data, "levels=", 7) == 0) {
+
+            p = value[i].data + 7;
+            last = value[i].data + value[i].len;
+
+            for (n = 0; n < 3 && p < last; n++) {
+
+                if (*p > '0' && *p < '3') {
+
+                    cache->path->level[n] = *p++ - '0';
+                    cache->path->len += cache->path->level[n] + 1;
+
+                    if (p == last) {
+                        break;
+                    }
+
+                    if (*p++ == ':' && n < 2 && p != last) {
+                        continue;
+                    }
+
+                    goto invalid_levels;
+                }
+
+                goto invalid_levels;
+            }
+
+            if (cache->path->len < 10 + 3) {
+                continue;
+            }
+
+        invalid_levels:
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid \"levels\" \"%V\"", &value[i]);
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_strncmp(value[i].data, "keys_zone=", 10) == 0) {
+
+            name.data = value[i].data + 10;
+
+            p = (u_char *) ngx_strchr(name.data, ':');
+
+            if (p) {
+                *p = '\0';
+
+                name.len = p - name.data;
+
+                p++;
+
+                s.len = value[i].data + value[i].len - p;
+                s.data = p;
+
+                size = ngx_parse_size(&s);
+                if (size > 8191) {
+                    continue;
+                }
+            }
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid keys zone size \"%V\"", &value[i]);
+            return NGX_CONF_ERROR;
+        }
+
+        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) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid inactive value \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) {
+
+            s.len = value[i].len - 9;
+            s.data = value[i].data + 9;
+
+            max_size = ngx_parse_offset(&s);
+            if (max_size < 0) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid max_size value \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[i]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (name.len == 0 || size == 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"%V\" must have \"keys_zone\" parameter",
+                           &cmd->name);
+        return NGX_CONF_ERROR;
+    }
+
+    cache->path->manager = ngx_http_file_cache_manager;
+    cache->path->data = cache;
+
+    if (ngx_add_path(cf, &cache->path) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);
+    if (cache->shm_zone == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (cache->shm_zone->data) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "duplicate zone \"%V\"", &name);
+        return NGX_CONF_ERROR;
+    }
+
+
+    cache->shm_zone->init = ngx_http_file_cache_init;
+    cache->shm_zone->data = cache;
+
+    cache->inactive = inactive;
+    cache->max_size = max_size;
+
+    return NGX_CONF_OK;
+}
+
+
+char *
+ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf)
+{
+    char  *p = conf;
+
+    time_t                    valid;
+    ngx_str_t                *value;
+    ngx_uint_t                i, n, status;
+    ngx_array_t             **a;
+    ngx_http_cache_valid_t   *v;
+    static ngx_uint_t         statuses[] = { 200, 301, 302 };
+
+    a = (ngx_array_t **) (p + cmd->offset);
+
+    if (*a == NGX_CONF_UNSET_PTR) {
+        *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t));
+        if (*a == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    value = cf->args->elts;
+    n = cf->args->nelts - 1;
+
+    valid = ngx_parse_time(&value[n], 1);
+    if (valid < 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid time value \"%V\"", &value[n]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (n == 1) {
+
+        for (i = 0; i < 3; i++) {
+            v = ngx_array_push(*a);
+            if (v == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            v->status = statuses[i];
+            v->valid = valid;
+        }
+
+        return NGX_CONF_OK;
+    }
+
+    for (i = 1; i < n; i++) {
+
+        if (ngx_strcmp(value[i].data, "any") == 0) {
+
+            status = 0;
+
+        } else {
+
+            status = ngx_atoi(value[i].data, value[i].len);
+            if (status < 100) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid status \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+        }
+
+        v = ngx_array_push(*a);
+        if (v == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        v->status = status;
+        v->valid = valid;
+    }
+
+    return NGX_CONF_OK;
+}
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -61,7 +61,8 @@ static ngx_str_t ngx_http_status_lines[]
 
     /* ngx_null_string, */  /* "207 Multi-Status" */
 
-#define NGX_HTTP_LEVEL_200  7
+#define NGX_HTTP_LAST_LEVEL_200  207
+#define NGX_HTTP_LEVEL_200       (NGX_HTTP_LAST_LEVEL_200 - 200)
 
     /* ngx_null_string, */  /* "300 Multiple Choices" */
 
@@ -74,7 +75,8 @@ static ngx_str_t ngx_http_status_lines[]
     /* ngx_null_string, */  /* "306 unused" */
     /* ngx_null_string, */  /* "307 Temporary Redirect" */
 
-#define NGX_HTTP_LEVEL_300  4
+#define NGX_HTTP_LAST_LEVEL_300  305
+#define NGX_HTTP_LEVEL_300       (NGX_HTTP_LAST_LEVEL_300 - 301)
 
     ngx_string("400 Bad Request"),
     ngx_string("401 Unauthorized"),
@@ -106,7 +108,8 @@ static ngx_str_t ngx_http_status_lines[]
     /* ngx_null_string, */  /* "423 Locked" */
     /* ngx_null_string, */  /* "424 Failed Dependency" */
 
-#define NGX_HTTP_LEVEL_400  17
+#define NGX_HTTP_LAST_LEVEL_400  417
+#define NGX_HTTP_LEVEL_400       (NGX_HTTP_LAST_LEVEL_400 - 400)
 
     ngx_string("500 Internal Server Error"),
     ngx_string("501 Method Not Implemented"),
@@ -120,6 +123,9 @@ static ngx_str_t ngx_http_status_lines[]
     /* ngx_null_string, */  /* "508 unused" */
     /* ngx_null_string, */  /* "509 unused" */
     /* ngx_null_string, */  /* "510 Not Extended" */
+
+#define NGX_HTTP_LAST_LEVEL_500  508
+
 };
 
 
@@ -153,16 +159,20 @@ ngx_http_header_filter(ngx_http_request_
 {
     u_char                    *p;
     size_t                     len;
-    ngx_str_t                  host;
+    ngx_str_t                  host, *status_line;
     ngx_buf_t                 *b;
-    ngx_uint_t                 status, i;
+    ngx_uint_t                 status, i, port;
     ngx_chain_t                out;
     ngx_list_part_t           *part;
     ngx_table_elt_t           *header;
+    ngx_connection_t          *c;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
-    /* AF_INET only */
-    u_char                     addr[NGX_INET_ADDRSTRLEN];
+    struct sockaddr_in        *sin;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6       *sin6;
+#endif
+    u_char                     addr[NGX_SOCKADDR_STRLEN];
 
     r->header_sent = 1;
 
@@ -196,17 +206,21 @@ ngx_http_header_filter(ngx_http_request_
 
     if (r->headers_out.status_line.len) {
         len += r->headers_out.status_line.len;
+        status_line = &r->headers_out.status_line;
 #if (NGX_SUPPRESS_WARN)
-        status = NGX_INVALID_ARRAY_INDEX;
+        status = 0;
 #endif
 
     } else {
 
-        if (r->headers_out.status < NGX_HTTP_MOVED_PERMANENTLY) {
+        status = r->headers_out.status;
+
+        if (status >= NGX_HTTP_OK
+            && status < NGX_HTTP_LAST_LEVEL_200)
+        {
             /* 2XX */
-            status = r->headers_out.status - NGX_HTTP_OK;
 
-            if (r->headers_out.status == NGX_HTTP_NO_CONTENT) {
+            if (status == NGX_HTTP_NO_CONTENT) {
                 r->header_only = 1;
                 r->headers_out.content_type.len = 0;
                 r->headers_out.content_type.data = NULL;
@@ -216,30 +230,50 @@ ngx_http_header_filter(ngx_http_request_
                 r->headers_out.content_length_n = -1;
             }
 
-        } else if (r->headers_out.status < NGX_HTTP_BAD_REQUEST) {
+            status -= NGX_HTTP_OK;
+            status_line = &ngx_http_status_lines[status];
+            len += ngx_http_status_lines[status].len;
+
+        } else if (status >= NGX_HTTP_MOVED_PERMANENTLY
+                   && status < NGX_HTTP_LAST_LEVEL_300)
+        {
             /* 3XX */
-            status = r->headers_out.status - NGX_HTTP_MOVED_PERMANENTLY
-                                           + NGX_HTTP_LEVEL_200;
 
-            if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
+            if (status == NGX_HTTP_NOT_MODIFIED) {
                 r->header_only = 1;
             }
 
-        } else if (r->headers_out.status < NGX_HTTP_INTERNAL_SERVER_ERROR) {
+            status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200;
+            status_line = &ngx_http_status_lines[status];
+            len += ngx_http_status_lines[status].len;
+
+        } else if (status >= NGX_HTTP_BAD_REQUEST
+                   && status < NGX_HTTP_LAST_LEVEL_400)
+        {
             /* 4XX */
-            status = r->headers_out.status - NGX_HTTP_BAD_REQUEST
-                                           + NGX_HTTP_LEVEL_200
-                                           + NGX_HTTP_LEVEL_300;
+            status = status - NGX_HTTP_BAD_REQUEST
+                            + NGX_HTTP_LEVEL_200
+                            + NGX_HTTP_LEVEL_300;
+
+            status_line = &ngx_http_status_lines[status];
+            len += ngx_http_status_lines[status].len;
+
+        } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR
+                   && status < NGX_HTTP_LAST_LEVEL_500)
+        {
+            /* 5XX */
+            status = status - NGX_HTTP_INTERNAL_SERVER_ERROR
+                            + NGX_HTTP_LEVEL_200
+                            + NGX_HTTP_LEVEL_300
+                            + NGX_HTTP_LEVEL_400;
+
+            status_line = &ngx_http_status_lines[status];
+            len += ngx_http_status_lines[status].len;
 
         } else {
-            /* 5XX */
-            status = r->headers_out.status - NGX_HTTP_INTERNAL_SERVER_ERROR
-                                           + NGX_HTTP_LEVEL_200
-                                           + NGX_HTTP_LEVEL_300
-                                           + NGX_HTTP_LEVEL_400;
+            len += NGX_INT_T_LEN;
+            status_line = NULL;
         }
-
-        len += ngx_http_status_lines[status].len;
     }
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
@@ -276,6 +310,8 @@ ngx_http_header_filter(ngx_http_request_
         len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
     }
 
+    c = r->connection;
+
     if (r->headers_out.location
         && r->headers_out.location->value.len
         && r->headers_out.location->value.data[0] == '/')
@@ -290,38 +326,53 @@ ngx_http_header_filter(ngx_http_request_
             host = r->headers_in.server;
 
         } else {
+            host.len = NGX_SOCKADDR_STRLEN;
             host.data = addr;
 
-            if (ngx_http_server_addr(r, &host) != NGX_OK) {
+            if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {
                 return NGX_ERROR;
             }
         }
 
-#if (NGX_HTTP_SSL)
-        if (r->connection->ssl) {
-            len += sizeof("Location: https://") - 1
-                   + host.len
-                   + r->headers_out.location->value.len + 2;
+        switch (c->local_sockaddr->sa_family) {
 
-            if (clcf->port_in_redirect && r->port != 443) {
-                len += r->port_text->len;
-            }
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+            port = ntohs(sin6->sin6_port);
+            break;
+#endif
+        default: /* AF_INET */
+            sin = (struct sockaddr_in *) c->local_sockaddr;
+            port = ntohs(sin->sin_port);
+            break;
+        }
 
-        } else
+        len += sizeof("Location: https://") - 1
+               + host.len
+               + r->headers_out.location->value.len + 2;
+
+        if (clcf->port_in_redirect) {
+
+#if (NGX_HTTP_SSL)
+            if (c->ssl)
+                port = (port == 443) ? 0 : port;
+            else
 #endif
-        {
-            len += sizeof("Location: http://") - 1
-                   + host.len
-                   + r->headers_out.location->value.len + 2;
+                port = (port == 80) ? 0 : port;
 
-            if (clcf->port_in_redirect && r->port != 80) {
-                len += r->port_text->len;
-            }
+        } else {
+            port = 0;
+        }
+
+        if (port) {
+            len += sizeof(":65535") - 1;
         }
 
     } else {
         host.len = 0;
         host.data = NULL;
+        port = 0;
     }
 
     if (r->chunked) {
@@ -385,13 +436,11 @@ ngx_http_header_filter(ngx_http_request_
     b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1);
 
     /* status line */
-    if (r->headers_out.status_line.len) {
-        b->last = ngx_copy(b->last, r->headers_out.status_line.data,
-                           r->headers_out.status_line.len);
+    if (status_line) {
+        b->last = ngx_copy(b->last, status_line->data, status_line->len);
 
     } else {
-        b->last = ngx_copy(b->last, ngx_http_status_lines[status].data,
-                           ngx_http_status_lines[status].len);
+        b->last = ngx_sprintf(b->last, "%ui", status);
     }
     *b->last++ = CR; *b->last++ = LF;
 
@@ -465,7 +514,7 @@ ngx_http_header_filter(ngx_http_request_
                              sizeof("Location: http") - 1);
 
 #if (NGX_HTTP_SSL)
-        if (r->connection->ssl) {
+        if (c->ssl) {
             *b->last++ ='s';
         }
 #endif
@@ -473,21 +522,8 @@ ngx_http_header_filter(ngx_http_request_
         *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/';
         b->last = ngx_copy(b->last, host.data, host.len);
 
-        if (clcf->port_in_redirect) {
-#if (NGX_HTTP_SSL)
-            if (r->connection->ssl) {
-                if (r->port != 443) {
-                    b->last = ngx_copy(b->last, r->port_text->data,
-                                       r->port_text->len);
-                }
-            } else
-#endif
-            {
-                if (r->port != 80) {
-                    b->last = ngx_copy(b->last, r->port_text->data,
-                                       r->port_text->len);
-                }
-            }
+        if (port) {
+            b->last = ngx_sprintf(b->last, ":%ui", port);
         }
 
         b->last = ngx_copy(b->last, r->headers_out.location->value.data,
@@ -555,8 +591,8 @@ ngx_http_header_filter(ngx_http_request_
         *b->last++ = CR; *b->last++ = LF;
     }
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "%*s\n", (size_t) (b->last - b->pos), b->pos);
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "%*s", (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
@@ -143,7 +143,7 @@ ngx_http_parse_request_line(ngx_http_req
                 break;
             }
 
-            if (ch < 'A' || ch > 'Z') {
+            if ((ch < 'A' || ch > 'Z') && ch != '_') {
                 return NGX_HTTP_PARSE_INVALID_METHOD;
             }
 
@@ -257,7 +257,7 @@ ngx_http_parse_request_line(ngx_http_req
                 break;
             }
 
-            if (ch < 'A' || ch > 'Z') {
+            if ((ch < 'A' || ch > 'Z') && ch != '_') {
                 return NGX_HTTP_PARSE_INVALID_METHOD;
             }
 
@@ -1337,12 +1337,7 @@ ngx_http_parse_unsafe_uri(ngx_http_reque
         goto unsafe;
     }
 
-    if (p[0] == '.' && len == 3 && p[1] == '.' && (p[2] == '/'
-#if (NGX_WIN32)
-                                                   || p[2] == '\\'
-#endif
-        ))
-    {
+    if (p[0] == '.' && len == 3 && p[1] == '.' && (ngx_path_separator(p[2]))) {
         goto unsafe;
     }
 
@@ -1367,30 +1362,22 @@ ngx_http_parse_unsafe_uri(ngx_http_reque
             continue;
         }
 
-        if ((ch == '/'
-#if (NGX_WIN32)
-             || ch == '\\'
-#endif
-            ) && len > 2)
-        {
+        if (ngx_path_separator(ch) && len > 2) {
+
             /* detect "/../" */
 
-            if (p[0] == '.' && p[1] == '.' && p[2] == '/') {
+            if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) {
                 goto unsafe;
             }
 
 #if (NGX_WIN32)
 
-            if (p[2] == '\\') {
-                goto unsafe;
-            }
-
             if (len > 3) {
 
                 /* detect "/.../" */
 
                 if (p[0] == '.' && p[1] == '.' && p[2] == '.'
-                    && (p[3] == '/' || p[3] == '\\'))
+                    && ngx_path_separator(p[3]))
                 {
                     goto unsafe;
                 }
@@ -1481,3 +1468,81 @@ ngx_http_parse_multi_header_lines(ngx_ar
 
     return NGX_DECLINED;
 }
+
+
+ngx_int_t
+ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
+{
+    u_char  *p, *last;
+
+    if (r->args.len == 0) {
+        return NGX_DECLINED;
+    }
+
+    p = r->args.data;
+    last = p + r->args.len;
+
+    for ( /* void */ ; p < last; p++) {
+
+        /* we need '=' after name, so drop one char from last */
+
+        p = ngx_strlcasestrn(p, last - 1, name, len - 1);
+
+        if (p == NULL) {
+            return NGX_DECLINED;
+        }
+
+        if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
+
+            value->data = p + len + 1;
+
+            p = ngx_strlchr(p, last, '&');
+
+            if (p == NULL) {
+                p = r->args.data + r->args.len;
+            }
+
+            value->len = p - value->data;
+
+            return NGX_OK;
+        }
+    }
+
+    return NGX_DECLINED;
+}
+
+
+void
+ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
+{
+    u_char  ch, *p, *last;
+
+    p = uri->data;
+
+    last = p + uri->len;
+
+    args->len = 0;
+
+    while (p < last) {
+
+        ch = *p++;
+
+        if (ch == '?') {
+            args->len = last - p;
+            args->data = p;
+
+            uri->len = p - 1 - uri->data;
+
+            if (ngx_strlchr(p, last, '\0') != NULL) {
+                r->zero_in_uri = 1;
+            }
+
+            return;
+        }
+
+        if (ch == '\0') {
+            r->zero_in_uri = 1;
+            continue;
+        }
+    }
+}
--- a/src/http/ngx_http_postpone_filter_module.c
+++ b/src/http/ngx_http_postpone_filter_module.c
@@ -9,8 +9,8 @@
 #include <ngx_http.h>
 
 
-static ngx_int_t
-    ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r,
+    ngx_chain_t *in);
 static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf);
 
 
@@ -51,174 +51,119 @@ static ngx_http_output_body_filter_pt   
 static ngx_int_t
 ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    ngx_int_t                      rc;
-    ngx_chain_t                   *out;
-    ngx_http_postponed_request_t  *pr, **ppr;
+    ngx_connection_t              *c;
+    ngx_http_postponed_request_t  *pr;
 
-    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+    c = r->connection;
+
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in);
 
-    if (r != r->connection->data || (r->postponed && in)) {
-
-        if (r->postponed) {
-            for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }
-
-            ppr = pr->request ? &pr->next : NULL;
-
-        } else {
-            ppr = &r->postponed;
-#if (NGX_SUPPRESS_WARN)
-            pr = NULL;
-#endif
-        }
+    if (r != c->data) {
 
-        if (ppr) {
-            pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
-            if (pr == NULL) {
-                return NGX_ERROR;
-            }
-
-            *ppr = pr;
-
-            pr->request = NULL;
-            pr->out = NULL;
-            pr->next = NULL;
-        }
-
-        if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_ERROR) {
-            return NGX_ERROR;
+        if (in) {
+            ngx_http_postpone_filter_add(r, in);
+            return NGX_OK;
         }
 
-#if 1
-        {
-        ngx_chain_t  *cl;
-        ngx_buf_t    *b = NULL;
-        for (cl = pr->out; cl; cl = cl->next) {
-            if (cl->buf == b) {
-                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                              "the same buf was used in postponed %p %p",
-                               b, b->pos);
-                ngx_debug_point();
-                return NGX_ERROR;
-            }
-            b = cl->buf;
-        }
-        }
+#if 0
+        /* TODO: SSI may pass NULL */
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                      "http postpone filter NULL inactive request",
+                      &r->uri, &r->args);
 #endif
 
-        if (r != r->connection->data || r->postponed->request) {
-            return NGX_AGAIN;
-        }
-    }
-
-    if (r->postponed) {
-        out = r->postponed->out;
-        if (out) {
-            r->postponed = r->postponed->next;
-        }
-
-    } else {
-        out = in;
-    }
-
-    rc = NGX_OK;
-
-    if (out
-        || (r->connection->buffered
-            & (NGX_HTTP_LOWLEVEL_BUFFERED|NGX_LOWLEVEL_BUFFERED)))
-    {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "http postpone filter out \"%V?%V\"", &r->uri, &r->args);
-
-        if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) {
-
-            rc = ngx_http_next_filter(r->main, out);
-
-            if (rc == NGX_ERROR) {
-                /* NGX_ERROR may be returned by any filter */
-                r->connection->error = 1;
-            }
-        }
+        return NGX_OK;
     }
 
     if (r->postponed == NULL) {
-        return rc;
+
+        if (in || c->buffered) {
+            return ngx_http_next_filter(r->main, in);
+        }
+
+        return NGX_OK;
+    }
+
+    if (in) {
+        ngx_http_postpone_filter_add(r, in);
     }
 
-    rc = ngx_http_postpone_filter_output_postponed_request(r);
+    do {
+        pr = r->postponed;
+
+        if (pr->request) {
+
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http postpone filter wake \"%V?%V\"",
+                           &pr->request->uri, &pr->request->args);
+
+            r->postponed = pr->next;
+
+            c->data = pr->request;
+
+            return ngx_http_post_request(pr->request);
+        }
 
-    if (rc == NGX_ERROR) {
-        /* NGX_ERROR may be returned by any filter */
-        r->connection->error = 1;
-    }
+        if (pr->out == NULL) {
+            ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                          "http postpone filter NULL output",
+                          &r->uri, &r->args);
+
+        } else {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http postpone filter output \"%V?%V\"",
+                           &r->uri, &r->args);
 
-    return rc;
+            if (ngx_http_next_filter(r->main, pr->out) == NGX_ERROR) {
+                return NGX_ERROR;
+            }
+        }
+
+        r->postponed = pr->next;
+
+    } while (r->postponed);
+
+    return NGX_OK;
 }
 
 
 static ngx_int_t
-ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r)
+ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    ngx_int_t                      rc;
-    ngx_chain_t                   *out;
-    ngx_http_log_ctx_t            *ctx;
-    ngx_http_postponed_request_t  *pr;
+    ngx_http_postponed_request_t  *pr, **ppr;
 
-    for ( ;; ) {
-        pr = r->postponed;
+    if (r->postponed) {
+        for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }
 
-        if (pr == NULL) {
-            break;
+        if (pr->request == NULL) {
+            goto found;
         }
 
-        if (pr->request) {
-
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http postpone filter handle \"%V?%V\"",
-                           &pr->request->uri, &pr->request->args);
-
-            ctx = r->connection->log->data;
-            ctx->current_request = pr->request;
-
-            if (!pr->request->done) {
-                r->connection->data = pr->request;
-                return NGX_AGAIN;
-            }
-
-            rc = ngx_http_postpone_filter_output_postponed_request(pr->request);
+        ppr = &pr->next;
 
-            if (rc == NGX_AGAIN || rc == NGX_ERROR) {
-                return rc;
-            }
-        }
-
-        out = pr->out;
+    } else {
+        ppr = &r->postponed;
+    }
 
-        if (out) {
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http postpone filter out postponed \"%V?%V\"",
-                           &r->uri, &r->args);
-
-            if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) {
-                if (ngx_http_next_filter(r->main, out) == NGX_ERROR) {
-                    return NGX_ERROR;
-                }
-            }
-        }
-
-        r->postponed = r->postponed->next;
+    pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
+    if (pr == NULL) {
+        return NGX_ERROR;
     }
 
-    if (r->out) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "http postpone filter out again \"%V?%V\"",
-                       &r->uri, &r->args);
+    *ppr = pr;
 
-        r->connection->data = r;
-        return NGX_AGAIN;
+    pr->request = NULL;
+    pr->out = NULL;
+    pr->next = NULL;
+
+found:
+
+    if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) {
+        return NGX_OK;
     }
 
-    return NGX_OK;
+    return NGX_ERROR;
 }
 
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -38,8 +38,8 @@ static ngx_int_t ngx_http_find_virtual_s
 static void ngx_http_request_handler(ngx_event_t *ev);
 static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);
 static void ngx_http_writer(ngx_http_request_t *r);
-
-static void ngx_http_test_reading(ngx_http_request_t *r);
+static void ngx_http_request_finalizer(ngx_http_request_t *r);
+
 static void ngx_http_set_keepalive(ngx_http_request_t *r);
 static void ngx_http_keepalive_handler(ngx_event_t *ev);
 static void ngx_http_set_lingering_close(ngx_http_request_t *r);
@@ -198,7 +198,7 @@ ngx_http_init_connection(ngx_connection_
     c->write->handler = ngx_http_empty_handler;
 
 #if (NGX_STAT_STUB)
-    ngx_atomic_fetch_add(ngx_stat_reading, 1);
+    (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
 #endif
 
     if (rev->ready) {
@@ -215,9 +215,9 @@ ngx_http_init_connection(ngx_connection_
 
     ngx_add_timer(rev, c->listening->post_accept_timeout);
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
 #if (NGX_STAT_STUB)
-        ngx_atomic_fetch_add(ngx_stat_reading, -1);
+        (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
 #endif
         ngx_http_close_connection(c);
         return;
@@ -232,16 +232,22 @@ ngx_http_init_request(ngx_event_t *rev)
     ngx_uint_t                  i;
     ngx_connection_t           *c;
     ngx_http_request_t         *r;
-    ngx_http_in_port_t         *hip;
-    ngx_http_in_addr_t         *hia;
+    struct sockaddr_in         *sin;
+    ngx_http_port_t            *port;
+    ngx_http_in_addr_t         *addr;
     ngx_http_log_ctx_t         *ctx;
+    ngx_http_addr_conf_t       *addr_conf;
     ngx_http_connection_t      *hc;
     ngx_http_core_srv_conf_t   *cscf;
     ngx_http_core_loc_conf_t   *clcf;
     ngx_http_core_main_conf_t  *cmcf;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6        *sin6;
+    ngx_http_in6_addr_t        *addr6;
+#endif
 
 #if (NGX_STAT_STUB)
-    ngx_atomic_fetch_add(ngx_stat_reading, -1);
+    (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
 #endif
 
     c = rev->data;
@@ -253,6 +259,8 @@ ngx_http_init_request(ngx_event_t *rev)
         return;
     }
 
+    c->requests++;
+
     hc = c->data;
 
     if (hc == NULL) {
@@ -292,58 +300,84 @@ ngx_http_init_request(ngx_event_t *rev)
 
     /* find the server configuration for the address:port */
 
-    /* AF_INET only */
-
-    hip = c->listening->servers;
-    hia = hip->addrs;
-
-    r->port = hip->port;
-    r->port_text = &hip->port_text;
-
-    i = 0;
+    port = c->listening->servers;
 
     r->connection = c;
 
-    if (hip->naddrs > 1) {
+    if (port->naddrs > 1) {
 
         /*
-         * There are several addresses on this port and one of them
-         * is the "*:port" wildcard so getsockname() is needed to determine
-         * the server address.
-         *
-         * AcceptEx() already has given this address.
+         * there are several addresses on this port and one of them
+         * is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
+         * is required to determine a server address
          */
 
-#if (NGX_WIN32)
-        if (c->local_sockaddr) {
-            r->in_addr =
-                   ((struct sockaddr_in *) c->local_sockaddr)->sin_addr.s_addr;
-
-        } else
+        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+            ngx_http_close_connection(c);
+            return;
+        }
+
+        switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+
+            addr6 = port->addrs;
+
+            /* the last address is "*" */
+
+            for (i = 0; i < port->naddrs - 1; i++) {
+                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
+                    break;
+                }
+            }
+
+            addr_conf = &addr6[i].conf;
+
+            break;
 #endif
-        {
-            if (ngx_http_server_addr(r, NULL) != NGX_OK) {
-                ngx_http_close_connection(c);
-                return;
+
+        default: /* AF_INET */
+            sin = (struct sockaddr_in *) c->local_sockaddr;
+
+            addr = port->addrs;
+
+            /* the last address is "*" */
+
+            for (i = 0; i < port->naddrs - 1; i++) {
+                if (addr[i].addr == sin->sin_addr.s_addr) {
+                    break;
+                }
             }
-        }
-
-        /* the last address is "*" */
-
-        for ( /* void */ ; i < hip->naddrs - 1; i++) {
-            if (hia[i].addr == r->in_addr) {
-                break;
-            }
+
+            addr_conf = &addr[i].conf;
+
+            break;
         }
 
     } else {
-        r->in_addr = hia[0].addr;
+
+        switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            addr6 = port->addrs;
+            addr_conf = &addr6[0].conf;
+            break;
+#endif
+
+        default: /* AF_INET */
+            addr = port->addrs;
+            addr_conf = &addr[0].conf;
+            break;
+        }
     }
 
-    r->virtual_names = hia[i].virtual_names;
+    r->virtual_names = addr_conf->virtual_names;
 
     /* the default server configuration for the address:port */
-    cscf = hia[i].core_srv_conf;
+    cscf = addr_conf->core_srv_conf;
 
     r->main_conf = cscf->ctx->main_conf;
     r->srv_conf = cscf->ctx->srv_conf;
@@ -357,13 +391,13 @@ ngx_http_init_request(ngx_event_t *rev)
     ngx_http_ssl_srv_conf_t  *sscf;
 
     sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
-    if (sscf->enable || hia[i].ssl) {
+    if (sscf->enable || addr_conf->ssl) {
 
         if (c->ssl == NULL) {
 
             c->log->action = "SSL handshaking";
 
-            if (hia[i].ssl && sscf->ssl.ctx == NULL) {
+            if (addr_conf->ssl && sscf->ssl.ctx == NULL) {
                 ngx_log_error(NGX_LOG_ERR, c->log, 0,
                               "no \"ssl_certificate\" is defined "
                               "in server listening on SSL port");
@@ -372,7 +406,7 @@ ngx_http_init_request(ngx_event_t *rev)
             }
 
             if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)
-                == NGX_ERROR)
+                != NGX_OK)
             {
                 ngx_http_close_connection(c);
                 return;
@@ -388,9 +422,9 @@ ngx_http_init_request(ngx_event_t *rev)
 #endif
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-    c->log->file = clcf->err_log->file;
+    c->log->file = clcf->error_log->file;
     if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
-        c->log->log_level = clcf->err_log->log_level;
+        c->log->log_level = clcf->error_log->log_level;
     }
 
     if (c->buffer == NULL) {
@@ -415,15 +449,17 @@ ngx_http_init_request(ngx_event_t *rev)
 
     if (ngx_list_init(&r->headers_out.headers, r->pool, 20,
                       sizeof(ngx_table_elt_t))
-        == NGX_ERROR)
+        != NGX_OK)
     {
-        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        ngx_destroy_pool(r->pool);
+        ngx_http_close_connection(c);
         return;
     }
 
     r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
     if (r->ctx == NULL) {
-        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        ngx_destroy_pool(r->pool);
+        ngx_http_close_connection(c);
         return;
     }
 
@@ -432,7 +468,8 @@ ngx_http_init_request(ngx_event_t *rev)
     r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
                                         * sizeof(ngx_http_variable_value_t));
     if (r->variables == NULL) {
-        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        ngx_destroy_pool(r->pool);
+        ngx_http_close_connection(c);
         return;
     }
 
@@ -463,9 +500,9 @@ ngx_http_init_request(ngx_event_t *rev)
     r->log_handler = ngx_http_log_error_handler;
 
 #if (NGX_STAT_STUB)
-    ngx_atomic_fetch_add(ngx_stat_reading, 1);
+    (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
     r->stat_reading = 1;
-    ngx_atomic_fetch_add(ngx_stat_requests, 1);
+    (void) ngx_atomic_fetch_add(ngx_stat_requests, 1);
 #endif
 
     rev->handler(rev);
@@ -504,7 +541,7 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
             ngx_add_timer(rev, c->listening->post_accept_timeout);
         }
 
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
             ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         }
 
@@ -667,7 +704,6 @@ ngx_http_process_request_line(ngx_event_
 
             r->request_line.len = r->request_end - r->request_start;
             r->request_line.data = r->request_start;
-            *r->request_end = '\0';
 
 
             if (r->args_start) {
@@ -775,7 +811,7 @@ ngx_http_process_request_line(ngx_event_
 
             if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
                               sizeof(ngx_table_elt_t))
-                == NGX_ERROR)
+                != NGX_OK)
             {
                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                 return;
@@ -784,7 +820,7 @@ ngx_http_process_request_line(ngx_event_
 
             if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,
                                sizeof(ngx_table_elt_t *))
-                == NGX_ERROR)
+                != NGX_OK)
             {
                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                 return;
@@ -1038,7 +1074,7 @@ ngx_http_read_request_header(ngx_http_re
             ngx_add_timer(rev, cscf->client_header_timeout);
         }
 
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
             ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
             return NGX_ERROR;
         }
@@ -1412,9 +1448,7 @@ ngx_http_process_request_header(ngx_http
         }
     }
 
-    if (r->method & (NGX_HTTP_POST|NGX_HTTP_PUT)
-        && r->headers_in.content_length_n == -1)
-    {
+    if (r->method & NGX_HTTP_PUT && r->headers_in.content_length_n == -1) {
         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                   "client sent %V method without \"Content-Length\" header",
                   &r->method_name);
@@ -1513,9 +1547,9 @@ ngx_http_process_request(ngx_http_reques
     }
 
 #if (NGX_STAT_STUB)
-    ngx_atomic_fetch_add(ngx_stat_reading, -1);
+    (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
     r->stat_reading = 0;
-    ngx_atomic_fetch_add(ngx_stat_writing, 1);
+    (void) ngx_atomic_fetch_add(ngx_stat_writing, 1);
     r->stat_writing = 1;
 #endif
 
@@ -1525,7 +1559,7 @@ ngx_http_process_request(ngx_http_reques
 
     ngx_http_handler(r);
 
-    return;
+    ngx_http_run_posted_requests(c);
 }
 
 
@@ -1558,15 +1592,9 @@ ngx_http_validate_host(u_char *host, siz
             continue;
         }
 
-        if (ch == '/' || ch == '\0') {
+        if (ngx_path_separator(ch) || ch == '\0') {
             return -1;
         }
-
-#if (NGX_WIN32)
-        if (ch == '\\') {
-            return -1;
-        }
-#endif
     }
 
     if (dot) {
@@ -1611,6 +1639,7 @@ ngx_http_find_virtual_server(ngx_http_re
 #if (NGX_PCRE)
 
     if (r->virtual_names->nregex) {
+        size_t                   ncaptures;
         ngx_int_t                n;
         ngx_uint_t               i;
         ngx_str_t                name;
@@ -1619,11 +1648,33 @@ ngx_http_find_virtual_server(ngx_http_re
         name.len = len;
         name.data = server;
 
+        ncaptures = 0;
+
         sn = r->virtual_names->regex;
 
         for (i = 0; i < r->virtual_names->nregex; i++) {
 
-            n = ngx_regex_exec(sn[i].regex, &name, NULL, 0);
+            if (sn[i].captures && r->captures == NULL) {
+
+                ncaptures = (NGX_HTTP_MAX_CAPTURES + 1) * 3;
+
+                r->captures = ngx_palloc(r->pool, ncaptures * sizeof(int));
+                if (r->captures == NULL) {
+                    return NGX_ERROR;
+                }
+
+                if (server == buf) {
+                    server = ngx_pnalloc(r->pool, len);
+                    if (server == NULL) {
+                        return NGX_ERROR;
+                    }
+
+                    ngx_memcpy(server, buf, len);
+                    name.data = server;
+                }
+            }
+
+            n = ngx_regex_exec(sn[i].regex, &name, r->captures, ncaptures);
 
             if (n == NGX_REGEX_NO_MATCHED) {
                 continue;
@@ -1641,6 +1692,9 @@ ngx_http_find_virtual_server(ngx_http_re
 
             cscf = sn[i].core_srv_conf;
 
+            r->ncaptures = ncaptures;
+            r->captures_data = server;
+
             goto found;
         }
     }
@@ -1655,10 +1709,10 @@ found:
     r->loc_conf = cscf->ctx->loc_conf;
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-    r->connection->log->file = clcf->err_log->file;
+    r->connection->log->file = clcf->error_log->file;
 
     if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
-        r->connection->log->log_level = clcf->err_log->log_level;
+        r->connection->log->log_level = clcf->error_log->log_level;
     }
 
     return NGX_OK;
@@ -1678,12 +1732,73 @@ ngx_http_request_handler(ngx_event_t *ev
     ctx = c->log->data;
     ctx->current_request = r;
 
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http run request: \"%V?%V\"", &r->uri, &r->args);
+
     if (ev->write) {
         r->write_event_handler(r);
 
     } else {
         r->read_event_handler(r);
     }
+
+    ngx_http_run_posted_requests(c);
+}
+
+
+void
+ngx_http_run_posted_requests(ngx_connection_t *c)
+{
+    ngx_http_request_t         *r;
+    ngx_http_log_ctx_t         *ctx;
+    ngx_http_posted_request_t  *pr;
+
+    for ( ;; ) {
+
+        if (c->destroyed) {
+            return;
+        }
+
+        r = c->data;
+        pr = r->main->posted_requests;
+
+        if (pr == NULL) {
+            return;
+        }
+
+        r->main->posted_requests = pr->next;
+
+        r = pr->request;
+
+        ctx = c->log->data;
+        ctx->current_request = r;
+
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "http posted request: \"%V?%V\"", &r->uri, &r->args);
+
+        r->write_event_handler(r);
+    }
+}
+
+
+ngx_int_t
+ngx_http_post_request(ngx_http_request_t *r)
+{
+    ngx_http_posted_request_t  *pr, **p;
+
+    pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
+    if (pr == NULL) {
+        return NGX_ERROR;
+    }
+
+    pr->request = r;
+    pr->next = NULL;
+
+    for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }
+
+    *p = pr;
+
+    return NGX_OK;
 }
 
 
@@ -1692,7 +1807,6 @@ ngx_http_finalize_request(ngx_http_reque
 {
     ngx_connection_t          *c;
     ngx_http_request_t        *pr;
-    ngx_http_log_ctx_t        *ctx;
     ngx_http_core_loc_conf_t  *clcf;
 
     if (rc == NGX_DONE) {
@@ -1702,9 +1816,14 @@ ngx_http_finalize_request(ngx_http_reque
 
     c = r->connection;
 
-    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                   "http finalize request: %d, \"%V?%V\"",
-                   rc, &r->uri, &r->args);
+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http finalize request: %d, \"%V?%V\" %d",
+                   rc, &r->uri, &r->args, r == c->data);
+
+    if (rc == NGX_OK && r->filter_finalize) {
+        c->error = 1;
+        return;
+    }
 
     if (rc == NGX_DECLINED) {
         r->content_handler = NULL;
@@ -1760,88 +1879,91 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
-    if (r != r->main && !r->logged) {
-
-        if (clcf->log_subrequest) {
-            ngx_http_log_request(r);
-        }
-
-        r->logged = 1;
-    }
-
-    if (r != r->main || rc == NGX_AGAIN) {
-        if (ngx_http_set_write_handler(r) != NGX_OK) {
+    if (r != r->main) {
+
+        if (r->buffered || r->postponed) {
+
+            if (ngx_http_set_write_handler(r) != NGX_OK) {
+                ngx_http_close_request(r->main, 0);
+            }
+
             return;
         }
-    }
-
-    r->done = 1;
-
-    if (r != c->data) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http finalize non-active request: \"%V?%V\"",
-                       &r->uri, &r->args);
-        return;
-    }
-
-    if (r != r->main) {
+
+#if (NGX_DEBUG)
+        if (r != c->data) {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http finalize non-active request: \"%V?%V\"",
+                           &r->uri, &r->args);
+        }
+#endif
 
         pr = r->parent;
 
+        if (r == c->data) {
+
+            if (!r->logged) {
+
+                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+                if (clcf->log_subrequest) {
+                    ngx_http_log_request(r);
+                }
+
+                r->logged = 1;
+
+            } else {
+                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                              "subrequest: \"%V?%V\" logged again",
+                              &r->uri, &r->args);
+            }
+
+            r->done = 1;
+
+            if (pr->postponed && pr->postponed->request == r) {
+                pr->postponed = pr->postponed->next;
+            }
+
+            c->data = pr;
+
+        } else {
+
+            r->write_event_handler = ngx_http_request_finalizer;
+
+            if (r->waited) {
+                r->done = 1;
+            }
+        }
+
+        if (ngx_http_post_request(pr) != NGX_OK) {
+            ngx_http_close_request(r->main, 0);
+            return;
+        }
+
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http parent request: \"%V?%V\"", &pr->uri, &pr->args);
-
-        if (rc != NGX_AGAIN) {
-            c->data = pr;
-        }
-
-        ctx = c->log->data;
-        ctx->current_request = pr;
-
-        if (pr->postponed) {
-
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                           "http request: \"%V?%V\" has postponed",
-                           &pr->uri, &pr->args);
-
-            if (rc != NGX_AGAIN && pr->postponed->request == r) {
-                pr->postponed = pr->postponed->next;
-            }
-
-            if (r->fast_subrequest) {
-
-                if (rc == NGX_AGAIN) {
-                    r->fast_subrequest = 0;
-                }
-
-                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                               "http fast subrequest: \"%V?%V\" done",
-                               &r->uri, &r->args);
-                return;
-            }
-
-            if (rc != NGX_AGAIN) {
-                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                               "http wake parent request: \"%V?%V\"",
-                               &pr->uri, &pr->args);
-
-                pr->write_event_handler(pr);
-            }
+                       "http wake parent request: \"%V?%V\"",
+                       &pr->uri, &pr->args);
+
+        return;
+    }
+
+    if (r->buffered || c->buffered || r->postponed) {
+
+        if (ngx_http_set_write_handler(r) != NGX_OK) {
+            ngx_http_close_request(r, 0);
         }
 
         return;
     }
 
-    if (rc == NGX_AGAIN) {
+    if (r != c->data) {
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                      "http finalize non-active request: \"%V?%V\"",
+                      &r->uri, &r->args);
         return;
     }
 
-    if (c->buffered) {
-        (void) ngx_http_set_write_handler(r);
-        return;
-    }
+    r->done = 1;
 
     if (!r->post_action) {
         r->request_complete = 1;
@@ -1869,6 +1991,8 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
     if (!ngx_terminate
          && !ngx_exiting
          && r->keepalive
@@ -1908,7 +2032,7 @@ ngx_http_set_write_handler(ngx_http_requ
         ngx_add_timer(wev, clcf->send_timeout);
     }
 
-    if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
+    if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
         ngx_http_close_request(r, 0);
         return NGX_ERROR;
     }
@@ -1931,6 +2055,8 @@ ngx_http_writer(ngx_http_request_t *r)
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,
                    "http writer handler: \"%V?%V\"", &r->uri, &r->args);
 
+    clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
+
     if (wev->timedout) {
         if (!wev->delayed) {
             ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
@@ -1945,10 +2071,9 @@ ngx_http_writer(ngx_http_request_t *r)
         wev->delayed = 0;
 
         if (!wev->ready) {
-            clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
             ngx_add_timer(wev, clcf->send_timeout);
 
-            if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
+            if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
                 ngx_http_close_request(r, 0);
             }
 
@@ -1960,9 +2085,7 @@ ngx_http_writer(ngx_http_request_t *r)
             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,
                            "http writer delayed");
 
-            clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
-
-            if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
+            if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
                 ngx_http_close_request(r, 0);
             }
 
@@ -1980,21 +2103,22 @@ ngx_http_writer(ngx_http_request_t *r)
                    "http writer output filter: %d, \"%V?%V\"",
                    rc, &r->uri, &r->args);
 
-    if (rc == NGX_AGAIN) {
-        clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
+    if (rc == NGX_ERROR) {
+        ngx_http_finalize_request(r, rc);
+        return;
+    }
+
+    if (r->buffered || r->postponed || (r == r->main && c->buffered)) {
+
         if (!wev->ready && !wev->delayed) {
             ngx_add_timer(wev, clcf->send_timeout);
         }
 
-        if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
+        if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
             ngx_http_close_request(r, 0);
         }
 
-        if (r == r->main || r->buffered) {
-            return;
-        }
-
-        rc = NGX_OK;
+        return;
     }
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,
@@ -2004,6 +2128,16 @@ ngx_http_writer(ngx_http_request_t *r)
 }
 
 
+static void
+ngx_http_request_finalizer(ngx_http_request_t *r)
+{
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http finalizer done: \"%V?%V\"", &r->uri, &r->args);
+
+    ngx_http_finalize_request(r, 0);
+}
+
+
 void
 ngx_http_block_reading(ngx_http_request_t *r)
 {
@@ -2015,16 +2149,14 @@ ngx_http_block_reading(ngx_http_request_
     if ((ngx_event_flags & NGX_USE_LEVEL_EVENT)
         && r->connection->read->active)
     {
-        if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0)
-            == NGX_ERROR)
-        {
+        if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) {
             ngx_http_close_request(r, 0);
         }
     }
 }
 
 
-static void
+void
 ngx_http_test_reading(ngx_http_request_t *r)
 {
     int                n;
@@ -2079,7 +2211,7 @@ ngx_http_test_reading(ngx_http_request_t
 
     if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) {
 
-        if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+        if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
             ngx_http_close_request(r, 0);
         }
     }
@@ -2119,6 +2251,7 @@ ngx_http_set_keepalive(ngx_http_request_
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler");
 
     if (r->discard_body) {
+        r->write_event_handler = ngx_http_request_empty_handler;
         r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
         ngx_add_timer(rev, clcf->lingering_timeout);
         return;
@@ -2173,7 +2306,7 @@ ngx_http_set_keepalive(ngx_http_request_
 
     ngx_add_timer(rev, clcf->keepalive_timeout);
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         ngx_http_close_connection(c);
         return;
     }
@@ -2186,7 +2319,7 @@ ngx_http_set_keepalive(ngx_http_request_
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request");
 
 #if (NGX_STAT_STUB)
-        ngx_atomic_fetch_add(ngx_stat_reading, 1);
+        (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
 #endif
 
         hc->pipeline = 1;
@@ -2260,7 +2393,7 @@ ngx_http_set_keepalive(ngx_http_request_
     rev->handler = ngx_http_keepalive_handler;
 
     if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
-        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
+        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {
             ngx_http_close_connection(c);
             return;
         }
@@ -2292,8 +2425,15 @@ ngx_http_set_keepalive(ngx_http_request_
                        (const void *) &tcp_nodelay, sizeof(int))
             == -1)
         {
+#if (NGX_SOLARIS)
+            /* Solaris returns EINVAL if a socket has been shut down */
+            c->log_error = NGX_ERROR_IGNORE_EINVAL;
+#endif
+
             ngx_connection_error(c, ngx_socket_errno,
                                  "setsockopt(TCP_NODELAY) failed");
+
+            c->log_error = NGX_ERROR_INFO;
             ngx_http_close_connection(c);
             return;
         }
@@ -2385,7 +2525,7 @@ ngx_http_keepalive_handler(ngx_event_t *
     c->log_error = NGX_ERROR_INFO;
 
     if (n == NGX_AGAIN) {
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
             ngx_http_close_connection(c);
         }
 
@@ -2409,7 +2549,7 @@ ngx_http_keepalive_handler(ngx_event_t *
     b->last += n;
 
 #if (NGX_STAT_STUB)
-    ngx_atomic_fetch_add(ngx_stat_reading, 1);
+    (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
 #endif
 
     c->log->handler = ngx_http_log_error;
@@ -2438,7 +2578,7 @@ ngx_http_set_lingering_close(ngx_http_re
     r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
     ngx_add_timer(rev, clcf->lingering_timeout);
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         ngx_http_close_request(r, 0);
         return;
     }
@@ -2447,7 +2587,7 @@ ngx_http_set_lingering_close(ngx_http_re
     wev->handler = ngx_http_empty_handler;
 
     if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
-        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
+        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {
             ngx_http_close_request(r, 0);
             return;
         }
@@ -2506,7 +2646,7 @@ ngx_http_lingering_close_handler(ngx_eve
 
     } while (rev->ready);
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         ngx_http_close_request(r, 0);
         return;
     }
@@ -2554,7 +2694,13 @@ ngx_http_send_special(ngx_http_request_t
     }
 
     if (flags & NGX_HTTP_LAST) {
-        b->last_buf = 1;
+
+        if (r == r->main && !r->post_action) {
+            b->last_buf = 1;
+
+        } else {
+            b->last_in_chain = 1;
+        }
     }
 
     if (flags & NGX_HTTP_FLUSH) {
@@ -2638,11 +2784,11 @@ ngx_http_request_done(ngx_http_request_t
 #if (NGX_STAT_STUB)
 
     if (r->stat_reading) {
-        ngx_atomic_fetch_add(ngx_stat_reading, -1);
+        (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
     }
 
     if (r->stat_writing) {
-        ngx_atomic_fetch_add(ngx_stat_writing, -1);
+        (void) ngx_atomic_fetch_add(ngx_stat_writing, -1);
     }
 
 #endif
@@ -2723,7 +2869,7 @@ ngx_http_close_connection(ngx_connection
 #endif
 
 #if (NGX_STAT_STUB)
-    ngx_atomic_fetch_add(ngx_stat_active, -1);
+    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
 #endif
 
     c->destroyed = 1;
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -10,6 +10,7 @@
 
 #define NGX_HTTP_MAX_URI_CHANGES           10
 #define NGX_HTTP_MAX_SUBREQUESTS           50
+#define NGX_HTTP_MAX_CAPTURES              9
 
 /* must be 2^n */
 #define NGX_HTTP_LC_HEADER_LEN             32
@@ -58,6 +59,7 @@
 
 #define NGX_HTTP_ZERO_IN_URI               1
 #define NGX_HTTP_SUBREQUEST_IN_MEMORY      2
+#define NGX_HTTP_SUBREQUEST_WAITED         4
 
 
 #define NGX_HTTP_OK                        200
@@ -321,6 +323,14 @@ struct ngx_http_postponed_request_s {
 };
 
 
+typedef struct ngx_http_posted_request_s  ngx_http_posted_request_t;
+
+struct ngx_http_posted_request_s {
+    ngx_http_request_t               *request;
+    ngx_http_posted_request_t        *next;
+};
+
+
 typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
 typedef void (*ngx_http_event_handler_pt)(ngx_http_request_t *r);
 
@@ -338,7 +348,9 @@ struct ngx_http_request_s {
     ngx_http_event_handler_pt         read_event_handler;
     ngx_http_event_handler_pt         write_event_handler;
 
+#if (NGX_HTTP_CACHE)
     ngx_http_cache_t                 *cache;
+#endif
 
     ngx_http_upstream_t              *upstream;
     ngx_array_t                      *upstream_states;
@@ -373,10 +385,8 @@ struct ngx_http_request_s {
     ngx_http_request_t               *parent;
     ngx_http_postponed_request_t     *postponed;
     ngx_http_post_subrequest_t       *post_subrequest;
+    ngx_http_posted_request_t        *posted_requests;
 
-    uint32_t                          in_addr;
-    ngx_uint_t                        port;
-    ngx_str_t                        *port_text;    /* ":80" */
     ngx_http_virtual_names_t         *virtual_names;
 
     ngx_int_t                         phase_handler;
@@ -385,6 +395,12 @@ struct ngx_http_request_s {
 
     ngx_http_variable_value_t        *variables;
 
+#if (NGX_PCRE)
+    ngx_uint_t                        ncaptures;
+    int                              *captures;
+    u_char                           *captures_data;
+#endif
+
     size_t                            limit_rate;
 
     /* used to learn the Apache compatible response length without a header */
@@ -428,9 +444,12 @@ struct ngx_http_request_s {
     unsigned                          request_body_file_group_access:1;
     unsigned                          request_body_file_log_level:3;
 
-    unsigned                          fast_subrequest:1;
     unsigned                          subrequest_in_memory:1;
+    unsigned                          waited:1;
 
+#if (NGX_HTTP_CACHE)
+    unsigned                          cached:1;
+#endif
     unsigned                          gzip:2;
 
     unsigned                          proxy:1;
@@ -438,10 +457,12 @@ struct ngx_http_request_s {
     unsigned                          no_cache:1;
 
     /*
-     * instead of using the request context data in ngx_http_limit_zone_module
-     * we use the single bit in the request structure
+     * instead of using the request context data in
+     * ngx_http_limit_zone_module and ngx_http_limit_req_module
+     * we use the single bits in the request structure
      */
     unsigned                          limit_zone_set:1;
+    unsigned                          limit_req_set:1;
 
 #if 0
     unsigned                          cacheable:1;
@@ -457,6 +478,8 @@ struct ngx_http_request_s {
     unsigned                          discard_body:1;
     unsigned                          internal:1;
     unsigned                          error_page:1;
+    unsigned                          ignore_content_encoding:1;
+    unsigned                          filter_finalize:1;
     unsigned                          post_action:1;
     unsigned                          request_complete:1;
     unsigned                          request_output:1;
@@ -465,7 +488,6 @@ struct ngx_http_request_s {
     unsigned                          root_tested:1;
     unsigned                          done:1;
     unsigned                          logged:1;
-    unsigned                          utf8:1;
 
     unsigned                          buffered:4;
 
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -15,6 +15,7 @@ static ngx_int_t ngx_http_write_request_
     ngx_chain_t *body);
 static void ngx_http_read_discarded_request_body_handler(ngx_http_request_t *r);
 static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
+static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);
 
 
 /*
@@ -41,6 +42,10 @@ ngx_http_read_client_request_body(ngx_ht
         return NGX_OK;
     }
 
+    if (ngx_http_test_expect(r) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
     rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
     if (rb == NULL) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -327,7 +332,7 @@ ngx_http_do_read_client_request_body(ngx
             clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
             ngx_add_timer(c->read, clcf->client_body_timeout);
 
-            if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
             }
 
@@ -433,6 +438,10 @@ ngx_http_discard_request_body(ngx_http_r
         return NGX_OK;
     }
 
+    if (ngx_http_test_expect(r) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
     rev = r->connection->read;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");
@@ -462,7 +471,7 @@ ngx_http_discard_request_body(ngx_http_r
 
     r->read_event_handler = ngx_http_read_discarded_request_body_handler;
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
@@ -519,7 +528,7 @@ ngx_http_read_discarded_request_body_han
 
     /* rc == NGX_AGAIN */
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         c->error = 1;
         ngx_http_finalize_request(r, rc);
         return;
@@ -581,3 +590,45 @@ ngx_http_read_discarded_request_body(ngx
 
     return NGX_AGAIN;
 }
+
+
+static ngx_int_t
+ngx_http_test_expect(ngx_http_request_t *r)
+{
+    ngx_int_t   n;
+    ngx_str_t  *expect;
+
+    if (r->expect_tested
+        || r->headers_in.expect == NULL
+        || r->http_version < NGX_HTTP_VERSION_11)
+    {
+        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;
+    }
+
+    /* we assume that such small packet should be send successfully */
+
+    return NGX_ERROR;
+}
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -9,11 +9,208 @@
 #include <ngx_http.h>
 
 
+static ngx_int_t ngx_http_script_init_arrays(ngx_http_script_compile_t *sc);
+static ngx_int_t ngx_http_script_done(ngx_http_script_compile_t *sc);
+static ngx_int_t ngx_http_script_add_copy_code(ngx_http_script_compile_t *sc,
+    ngx_str_t *value, ngx_uint_t last);
+static ngx_int_t ngx_http_script_add_var_code(ngx_http_script_compile_t *sc,
+    ngx_str_t *name);
+static ngx_int_t ngx_http_script_add_args_code(ngx_http_script_compile_t *sc);
+#if (NGX_PCRE)
+static ngx_int_t ngx_http_script_add_capture_code(ngx_http_script_compile_t *sc,
+     ngx_uint_t n);
+#endif
+static ngx_int_t
+     ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc);
+static size_t ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e);
+static void ngx_http_script_full_name_code(ngx_http_script_engine_t *e);
+
+
 #define ngx_http_script_exit  (u_char *) &ngx_http_script_exit_code
 
 static uintptr_t ngx_http_script_exit_code = (uintptr_t) NULL;
 
 
+void
+ngx_http_script_flush_complex_value(ngx_http_request_t *r,
+    ngx_http_complex_value_t *val)
+{
+    ngx_uint_t *index;
+
+    index = val->flushes;
+
+    if (index) {
+        while (*index != (ngx_uint_t) -1) {
+
+            if (r->variables[*index].no_cacheable) {
+                r->variables[*index].valid = 0;
+                r->variables[*index].not_found = 0;
+            }
+
+            index++;
+        }
+    }
+}
+
+
+ngx_int_t
+ngx_http_complex_value(ngx_http_request_t *r, ngx_http_complex_value_t *val,
+    ngx_str_t *value)
+{
+    size_t                        len;
+    ngx_http_script_code_pt       code;
+    ngx_http_script_len_code_pt   lcode;
+    ngx_http_script_engine_t      e;
+
+    if (val->lengths == NULL) {
+        *value = val->value;
+        return NGX_OK;
+    }
+
+    ngx_http_script_flush_complex_value(r, val);
+
+    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
+
+    e.ip = val->lengths;
+    e.request = r;
+    e.flushed = 1;
+
+    len = 0;
+
+    while (*(uintptr_t *) e.ip) {
+        lcode = *(ngx_http_script_len_code_pt *) e.ip;
+        len += lcode(&e);
+    }
+
+    value->len = len;
+    value->data = ngx_pnalloc(r->pool, len);
+    if (value->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    e.ip = val->values;
+    e.pos = value->data;
+    e.buf = *value;
+
+    while (*(uintptr_t *) e.ip) {
+        code = *(ngx_http_script_code_pt *) e.ip;
+        code((ngx_http_script_engine_t *) &e);
+    }
+
+    *value = e.buf;
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv)
+{
+    ngx_str_t                  *v;
+    ngx_uint_t                  i, n, nv, nc;
+    ngx_array_t                 flushes, lengths, values, *pf, *pl, *pv;
+    ngx_http_script_compile_t   sc;
+
+    v = ccv->value;
+
+    if (v->len == 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, ccv->cf, 0, "empty parameter");
+        return NGX_ERROR;
+    }
+
+    nv = 0;
+    nc = 0;
+
+    for (i = 0; i < v->len; i++) {
+        if (v->data[i] == '$') {
+            if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') {
+                nc++;
+
+            } else {
+                nv++;
+            }
+        }
+    }
+
+    if (v->data[0] != '$' && (ccv->conf_prefix || ccv->root_prefix)) {
+
+        if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        ccv->conf_prefix = 0;
+        ccv->root_prefix = 0;
+    }
+
+    ccv->complex_value->value = *v;
+    ccv->complex_value->flushes = NULL;
+    ccv->complex_value->lengths = NULL;
+    ccv->complex_value->values = NULL;
+
+    if (nv == 0 && nc == 0) {
+        return NGX_OK;
+    }
+
+    n = nv + 1;
+
+    if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    n = nv * (2 * sizeof(ngx_http_script_copy_code_t)
+                  + sizeof(ngx_http_script_var_code_t))
+        + sizeof(uintptr_t);
+
+    if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    n = (nv * (2 * sizeof(ngx_http_script_copy_code_t)
+                   + sizeof(ngx_http_script_var_code_t))
+                + sizeof(uintptr_t)
+                + v->len
+                + sizeof(uintptr_t) - 1)
+            & ~(sizeof(uintptr_t) - 1);
+
+    if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    pf = &flushes;
+    pl = &lengths;
+    pv = &values;
+
+    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+    sc.cf = ccv->cf;
+    sc.source = v;
+    sc.flushes = &pf;
+    sc.lengths = &pl;
+    sc.values = &pv;
+    sc.complete_lengths = 1;
+    sc.complete_values = 1;
+    sc.zero = ccv->zero;
+    sc.conf_prefix = ccv->conf_prefix;
+    sc.root_prefix = ccv->root_prefix;
+
+    if (ngx_http_script_compile(&sc) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (flushes.nelts) {
+        ccv->complex_value->flushes = flushes.elts;
+        ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1;
+    }
+
+    ccv->complex_value->lengths = lengths.elts;
+    ccv->complex_value->values = values.elts;
+
+    return NGX_OK;
+}
+
+
 ngx_uint_t
 ngx_http_script_variables_count(ngx_str_t *value)
 {
@@ -32,53 +229,14 @@ ngx_http_script_variables_count(ngx_str_
 ngx_int_t
 ngx_http_script_compile(ngx_http_script_compile_t *sc)
 {
-    u_char                                ch;
-    size_t                                size;
-    ngx_int_t                             index, *p;
-    ngx_str_t                             name;
-    uintptr_t                            *code;
-    ngx_uint_t                            i, n, bracket;
-    ngx_http_script_var_code_t           *var_code;
-    ngx_http_script_copy_code_t          *copy;
-    ngx_http_script_copy_capture_code_t  *copy_capture;
-
-    if (sc->flushes && *sc->flushes == NULL) {
-        n = sc->variables ? sc->variables : 1;
-        *sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t));
-        if (*sc->flushes == NULL) {
-            return NGX_ERROR;
-        }
-    }
-
+    u_char       ch;
+    ngx_str_t    name;
+    ngx_uint_t   i, bracket;
 
-    if (*sc->lengths == NULL) {
-        n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
-                             + sizeof(ngx_http_script_var_code_t))
-            + sizeof(uintptr_t);
-
-        *sc->lengths = ngx_array_create(sc->cf->pool, n, 1);
-        if (*sc->lengths == NULL) {
-            return NGX_ERROR;
-        }
+    if (ngx_http_script_init_arrays(sc) != NGX_OK) {
+        return NGX_ERROR;
     }
 
-
-    if (*sc->values == NULL) {
-        n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
-                              + sizeof(ngx_http_script_var_code_t))
-                + sizeof(uintptr_t)
-                + sc->source->len
-                + sizeof(uintptr_t) - 1)
-            & ~(sizeof(uintptr_t) - 1);
-
-        *sc->values = ngx_array_create(sc->cf->pool, n, 1);
-        if (*sc->values == NULL) {
-            return NGX_ERROR;
-        }
-    }
-
-    sc->variables = 0;
-
     for (i = 0; i < sc->source->len; /* void */ ) {
 
         name.len = 0;
@@ -89,6 +247,12 @@ ngx_http_script_compile(ngx_http_script_
                 goto invalid_variable;
             }
 
+#if (NGX_PCRE)
+            {
+            ngx_uint_t  n;
+
+            /* NGX_HTTP_MAX_CAPTURES is 9 */
+
             if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {
 
                 n = sc->source->data[i] - '0';
@@ -99,36 +263,16 @@ ngx_http_script_compile(ngx_http_script_
 
                 sc->captures_mask |= 1 << n;
 
-                copy_capture = ngx_http_script_add_code(*sc->lengths,
-                                   sizeof(ngx_http_script_copy_capture_code_t),
-                                   NULL);
-                if (copy_capture == NULL) {
+                if (ngx_http_script_add_capture_code(sc, n) != NGX_OK) {
                     return NGX_ERROR;
                 }
 
-                copy_capture->code = (ngx_http_script_code_pt)
-                                         ngx_http_script_copy_capture_len_code;
-                copy_capture->n = 2 * n;
-
-
-                copy_capture = ngx_http_script_add_code(*sc->values,
-                                   sizeof(ngx_http_script_copy_capture_code_t),
-                                   &sc->main);
-                if (copy_capture == NULL) {
-                    return NGX_ERROR;
-                }
-
-                copy_capture->code = ngx_http_script_copy_capture_code;
-                copy_capture->n = 2 * n;
-
-                if (sc->ncaptures < n) {
-                    sc->ncaptures = n;
-                }
-
                 i++;
 
                 continue;
             }
+            }
+#endif
 
             if (sc->source->data[i] == '{') {
                 bracket = 1;
@@ -177,43 +321,10 @@ ngx_http_script_compile(ngx_http_script_
 
             sc->variables++;
 
-            index = ngx_http_get_variable_index(sc->cf, &name);
-
-            if (index == NGX_ERROR) {
+            if (ngx_http_script_add_var_code(sc, &name) != NGX_OK) {
                 return NGX_ERROR;
             }
 
-            if (sc->flushes) {
-                p = ngx_array_push(*sc->flushes);
-                if (p == NULL) {
-                    return NGX_ERROR;
-                }
-
-                *p = index;
-            }
-
-            var_code = ngx_http_script_add_code(*sc->lengths,
-                                            sizeof(ngx_http_script_var_code_t),
-                                            NULL);
-            if (var_code == NULL) {
-                return NGX_ERROR;
-            }
-
-            var_code->code = (ngx_http_script_code_pt)
-                                            ngx_http_script_copy_var_len_code;
-            var_code->index = (uintptr_t) index;
-
-
-            var_code = ngx_http_script_add_code(*sc->values,
-                                            sizeof(ngx_http_script_var_code_t),
-                                            &sc->main);
-            if (var_code == NULL) {
-                return NGX_ERROR;
-            }
-
-            var_code->code = ngx_http_script_copy_var_code;
-            var_code->index = (uintptr_t) index;
-
             continue;
         }
 
@@ -221,22 +332,10 @@ 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) {
+            if (ngx_http_script_add_args_code(sc) != NGX_OK) {
                 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) {
-                return NGX_ERROR;
-            }
-
-            *code = (uintptr_t) ngx_http_script_start_args_code;
-
             i++;
 
             continue;
@@ -244,62 +343,35 @@ ngx_http_script_compile(ngx_http_script_
 
         name.data = &sc->source->data[i];
 
-        while (i < sc->source->len
-               && sc->source->data[i] != '$'
-               && !(sc->source->data[i] == '?' && sc->compile_args))
-        {
+        while (i < sc->source->len) {
+
+            if (sc->source->data[i] == '$') {
+                break;
+            }
+
+            if (sc->source->data[i] == '?') {
+
+                sc->args = 1;
+
+                if (sc->compile_args) {
+                    break;
+                }
+            }
+
             i++;
             name.len++;
         }
 
         sc->size += name.len;
 
-        copy = ngx_http_script_add_code(*sc->lengths,
-                                        sizeof(ngx_http_script_copy_code_t),
-                                        NULL);
-        if (copy == NULL) {
-            return NGX_ERROR;
-        }
-
-        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
-        copy->len = name.len;
-
-        size = (sizeof(ngx_http_script_copy_code_t) + name.len
-                   + sizeof(uintptr_t) - 1)
-                & ~(sizeof(uintptr_t) - 1);
-
-        copy = ngx_http_script_add_code(*sc->values, size, &sc->main);
-        if (copy == NULL) {
+        if (ngx_http_script_add_copy_code(sc, &name, (i == sc->source->len))
+            != NGX_OK)
+        {
             return NGX_ERROR;
         }
-
-        copy->code = ngx_http_script_copy_code;
-        copy->len = name.len;
-
-        ngx_memcpy((u_char *) copy + sizeof(ngx_http_script_copy_code_t),
-                   name.data, name.len);
     }
 
-    if (sc->complete_lengths) {
-        code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);
-        if (code == NULL) {
-            return NGX_ERROR;
-        }
-
-        *code = (uintptr_t) NULL;
-    }
-
-    if (sc->complete_values) {
-        code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),
-                                        &sc->main);
-        if (code == NULL) {
-            return NGX_ERROR;
-        }
-
-        *code = (uintptr_t) NULL;
-    }
-
-    return NGX_OK;
+    return ngx_http_script_done(sc);
 
 invalid_variable:
 
@@ -376,6 +448,95 @@ ngx_http_script_flush_no_cacheable_varia
 }
 
 
+static ngx_int_t
+ngx_http_script_init_arrays(ngx_http_script_compile_t *sc)
+{
+    ngx_uint_t   n;
+
+    if (sc->flushes && *sc->flushes == NULL) {
+        n = sc->variables ? sc->variables : 1;
+        *sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t));
+        if (*sc->flushes == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+    if (*sc->lengths == NULL) {
+        n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
+                             + sizeof(ngx_http_script_var_code_t))
+            + sizeof(uintptr_t);
+
+        *sc->lengths = ngx_array_create(sc->cf->pool, n, 1);
+        if (*sc->lengths == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+    if (*sc->values == NULL) {
+        n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
+                              + sizeof(ngx_http_script_var_code_t))
+                + sizeof(uintptr_t)
+                + sc->source->len
+                + sizeof(uintptr_t) - 1)
+            & ~(sizeof(uintptr_t) - 1);
+
+        *sc->values = ngx_array_create(sc->cf->pool, n, 1);
+        if (*sc->values == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+    sc->variables = 0;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_script_done(ngx_http_script_compile_t *sc)
+{
+    ngx_str_t    zero;
+    uintptr_t   *code;
+
+    if (sc->zero) {
+
+        zero.len = 1;
+        zero.data = (u_char *) "\0";
+
+        if (ngx_http_script_add_copy_code(sc, &zero, 0) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
+
+    if (sc->conf_prefix || sc->root_prefix) {
+        if (ngx_http_script_add_full_name_code(sc) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
+
+    if (sc->complete_lengths) {
+        code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);
+        if (code == NULL) {
+            return NGX_ERROR;
+        }
+
+        *code = (uintptr_t) NULL;
+    }
+
+    if (sc->complete_values) {
+        code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),
+                                        &sc->main);
+        if (code == NULL) {
+            return NGX_ERROR;
+        }
+
+        *code = (uintptr_t) NULL;
+    }
+
+    return NGX_OK;
+}
+
+
 void *
 ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes, size_t size)
 {
@@ -400,7 +561,7 @@ ngx_http_script_add_code(ngx_array_t *co
 
     new = ngx_array_push_n(codes, size);
     if (new == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     if (code) {
@@ -414,6 +575,49 @@ ngx_http_script_add_code(ngx_array_t *co
 }
 
 
+static ngx_int_t
+ngx_http_script_add_copy_code(ngx_http_script_compile_t *sc, ngx_str_t *value,
+    ngx_uint_t last)
+{
+    u_char                       *p;
+    size_t                        size, len, zero;
+    ngx_http_script_copy_code_t  *code;
+
+    zero = (sc->zero && last);
+    len = value->len + zero;
+
+    code = ngx_http_script_add_code(*sc->lengths,
+                                    sizeof(ngx_http_script_copy_code_t), NULL);
+    if (code == NULL) {
+        return NGX_ERROR;
+    }
+
+    code->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+    code->len = len;
+
+    size = (sizeof(ngx_http_script_copy_code_t) + len + sizeof(uintptr_t) - 1)
+            & ~(sizeof(uintptr_t) - 1);
+
+    code = ngx_http_script_add_code(*sc->values, size, &sc->main);
+    if (code == NULL) {
+        return NGX_ERROR;
+    }
+
+    code->code = ngx_http_script_copy_code;
+    code->len = len;
+
+    p = ngx_cpymem((u_char *) code + sizeof(ngx_http_script_copy_code_t),
+                   value->data, value->len);
+
+    if (zero) {
+        *p = '\0';
+        sc->zero = 0;
+    }
+
+    return NGX_OK;
+}
+
+
 size_t
 ngx_http_script_copy_len_code(ngx_http_script_engine_t *e)
 {
@@ -430,20 +634,67 @@ ngx_http_script_copy_len_code(ngx_http_s
 void
 ngx_http_script_copy_code(ngx_http_script_engine_t *e)
 {
+    u_char                       *p;
     ngx_http_script_copy_code_t  *code;
 
     code = (ngx_http_script_copy_code_t *) e->ip;
 
+    p = e->pos;
+
     if (!e->skip) {
-        e->pos = ngx_copy(e->pos, e->ip + sizeof(ngx_http_script_copy_code_t),
+        e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_script_copy_code_t),
                           code->len);
     }
 
     e->ip += sizeof(ngx_http_script_copy_code_t)
           + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                   "http script copy: \"%V\"", &e->buf);
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script copy: \"%*s\"", e->pos - p, p);
+}
+
+
+static ngx_int_t
+ngx_http_script_add_var_code(ngx_http_script_compile_t *sc, ngx_str_t *name)
+{
+    ngx_int_t                    index, *p;
+    ngx_http_script_var_code_t  *code;
+
+    index = ngx_http_get_variable_index(sc->cf, name);
+
+    if (index == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    if (sc->flushes) {
+        p = ngx_array_push(*sc->flushes);
+        if (p == NULL) {
+            return NGX_ERROR;
+        }
+
+        *p = index;
+    }
+
+    code = ngx_http_script_add_code(*sc->lengths,
+                                    sizeof(ngx_http_script_var_code_t), NULL);
+    if (code == NULL) {
+        return NGX_ERROR;
+    }
+
+    code->code = (ngx_http_script_code_pt) ngx_http_script_copy_var_len_code;
+    code->index = (uintptr_t) index;
+
+    code = ngx_http_script_add_code(*sc->values,
+                                    sizeof(ngx_http_script_var_code_t),
+                                    &sc->main);
+    if (code == NULL) {
+        return NGX_ERROR;
+    }
+
+    code->code = ngx_http_script_copy_var_code;
+    code->index = (uintptr_t) index;
+
+    return NGX_OK;
 }
 
 
@@ -475,6 +726,7 @@ ngx_http_script_copy_var_len_code(ngx_ht
 void
 ngx_http_script_copy_var_code(ngx_http_script_engine_t *e)
 {
+    u_char                      *p;
     ngx_http_variable_value_t   *value;
     ngx_http_script_var_code_t  *code;
 
@@ -492,69 +744,37 @@ ngx_http_script_copy_var_code(ngx_http_s
         }
 
         if (value && !value->not_found) {
-            e->pos = ngx_copy(e->pos, value->data, value->len);
+            p = e->pos;
+            e->pos = ngx_copy(p, value->data, value->len);
 
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP,
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP,
                            e->request->connection->log, 0,
-                           "http script var: \"%V\"", &e->buf);
+                           "http script var: \"%*s\"", e->pos - p, p);
         }
     }
 }
 
 
-size_t
-ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e)
+static ngx_int_t
+ngx_http_script_add_args_code(ngx_http_script_compile_t *sc)
 {
-    ngx_http_script_copy_capture_code_t  *code;
-
-    code = (ngx_http_script_copy_capture_code_t *) e->ip;
-
-    e->ip += sizeof(ngx_http_script_copy_capture_code_t);
+    uintptr_t   *code;
 
-    if (code->n < e->ncaptures) {
-        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]
-                   + 2 * ngx_escape_uri(NULL,
-                                &e->line.data[e->captures[code->n]],
-                                e->captures[code->n + 1] - e->captures[code->n],
-                                NGX_ESCAPE_ARGS);
-        } else {
-            return e->captures[code->n + 1] - e->captures[code->n];
-        }
+    code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);
+    if (code == NULL) {
+        return NGX_ERROR;
     }
 
-    return 0;
-}
-
-
-void
-ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)
-{
-    ngx_http_script_copy_capture_code_t  *code;
-
-    code = (ngx_http_script_copy_capture_code_t *) e->ip;
-
-    e->ip += sizeof(ngx_http_script_copy_capture_code_t);
+    *code = (uintptr_t) ngx_http_script_mark_args_code;
 
-    if (code->n < e->ncaptures) {
-        if ((e->is_args || e->quote)
-            && (e->request->quoted_uri || e->request->plus_in_uri))
-        {
-            e->pos = (u_char *) ngx_escape_uri(e->pos,
-                                &e->line.data[e->captures[code->n]],
-                                e->captures[code->n + 1] - e->captures[code->n],
-                                NGX_ESCAPE_ARGS);
-        } else {
-            e->pos = ngx_copy(e->pos,
-                              &e->line.data[e->captures[code->n]],
-                              e->captures[code->n + 1] - e->captures[code->n]);
-        }
+    code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), &sc->main);
+    if (code == NULL) {
+        return NGX_ERROR;
     }
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                   "http script capture: \"%V\"", &e->buf);
+    *code = (uintptr_t) ngx_http_script_start_args_code;
+
+    return NGX_OK;
 }
 
 
@@ -580,7 +800,6 @@ ngx_http_script_start_args_code(ngx_http
 }
 
 
-
 #if (NGX_PCRE)
 
 void
@@ -609,16 +828,27 @@ ngx_http_script_regex_start_code(ngx_htt
         e->line.data = e->sp->data;
     }
 
-    rc = ngx_regex_exec(code->regex, &e->line, e->captures, code->ncaptures);
+    if (code->ncaptures && r->captures == NULL) {
+
+        r->captures = ngx_palloc(r->pool,
+                                 (NGX_HTTP_MAX_CAPTURES + 1) * 3 * sizeof(int));
+        if (r->captures == NULL) {
+            e->ip = ngx_http_script_exit;
+            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+            return;
+        }
+    }
+
+    rc = ngx_regex_exec(code->regex, &e->line, r->captures, code->ncaptures);
 
     if (rc == NGX_REGEX_NO_MATCHED) {
-        if (e->log) {
+        if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
             ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
                           "\"%V\" does not match \"%V\"",
                           &code->name, &e->line);
         }
 
-        e->ncaptures = 0;
+        r->ncaptures = 0;
 
         if (code->test) {
             if (code->negative_test) {
@@ -650,12 +880,13 @@ ngx_http_script_regex_start_code(ngx_htt
         return;
     }
 
-    if (e->log) {
+    if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
         ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
                       "\"%V\" matches \"%V\"", &code->name, &e->line);
     }
 
-    e->ncaptures = code->ncaptures;
+    r->ncaptures = code->ncaptures;
+    r->captures_data = e->line.data;
 
     if (code->test) {
         if (code->negative_test) {
@@ -706,7 +937,7 @@ ngx_http_script_regex_start_code(ngx_htt
         }
 
         for (n = 1; n < (ngx_uint_t) rc; n++) {
-            e->buf.len += e->captures[2 * n + 1] - e->captures[2 * n];
+            e->buf.len += r->captures[2 * n + 1] - r->captures[2 * n];
         }
 
     } else {
@@ -715,8 +946,6 @@ ngx_http_script_regex_start_code(ngx_htt
         le.ip = code->lengths->elts;
         le.line = e->line;
         le.request = r;
-        le.captures = e->captures;
-        le.ncaptures = e->ncaptures;
         le.quote = code->redirect;
 
         len = 0;
@@ -786,7 +1015,7 @@ ngx_http_script_regex_end_code(ngx_http_
 
         e->buf.len = e->pos - e->buf.data;
 
-        if (e->log) {
+        if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
             ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
                           "rewritten redirect: \"%V\"", &e->buf);
         }
@@ -828,7 +1057,7 @@ ngx_http_script_regex_end_code(ngx_http_
         }
     }
 
-    if (e->log) {
+    if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
         ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
                       "rewritten data: \"%V\", args: \"%V\"",
                       &e->buf, &r->args);
@@ -855,9 +1084,194 @@ ngx_http_script_regex_end_code(ngx_http_
     e->ip += sizeof(ngx_http_script_regex_end_code_t);
 }
 
+
+static ngx_int_t
+ngx_http_script_add_capture_code(ngx_http_script_compile_t *sc, ngx_uint_t n)
+{
+    ngx_http_script_copy_capture_code_t  *code;
+
+    code = ngx_http_script_add_code(*sc->lengths,
+                                    sizeof(ngx_http_script_copy_capture_code_t),
+                                    NULL);
+    if (code == NULL) {
+        return NGX_ERROR;
+    }
+
+    code->code = (ngx_http_script_code_pt)
+                      ngx_http_script_copy_capture_len_code;
+    code->n = 2 * n;
+
+
+    code = ngx_http_script_add_code(*sc->values,
+                                    sizeof(ngx_http_script_copy_capture_code_t),
+                                    &sc->main);
+    if (code == NULL) {
+        return NGX_ERROR;
+    }
+
+    code->code = ngx_http_script_copy_capture_code;
+    code->n = 2 * n;
+
+    if (sc->ncaptures < n) {
+        sc->ncaptures = n;
+    }
+
+    return NGX_OK;
+}
+
+
+size_t
+ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e)
+{
+    int                                  *cap;
+    u_char                               *p;
+    ngx_uint_t                            n;
+    ngx_http_request_t                   *r;
+    ngx_http_script_copy_capture_code_t  *code;
+
+    r = e->request;
+
+    code = (ngx_http_script_copy_capture_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_script_copy_capture_code_t);
+
+    n = code->n;
+
+    if (n < r->ncaptures) {
+
+        cap = r->captures;
+
+        if ((e->is_args || e->quote)
+            && (e->request->quoted_uri || e->request->plus_in_uri))
+        {
+            p = r->captures_data;
+
+            return cap[n + 1] - cap[n]
+                   + 2 * ngx_escape_uri(NULL, &p[cap[n]], cap[n + 1] - cap[n],
+                                        NGX_ESCAPE_ARGS);
+        } else {
+            return cap[n + 1] - cap[n];
+        }
+    }
+
+    return 0;
+}
+
+
+void
+ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)
+{
+    int                                  *cap;
+    u_char                               *p, *pos;
+    ngx_uint_t                            n;
+    ngx_http_request_t                   *r;
+    ngx_http_script_copy_capture_code_t  *code;
+
+    r = e->request;
+
+    code = (ngx_http_script_copy_capture_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_script_copy_capture_code_t);
+
+    n = code->n;
+
+    pos = e->pos;
+
+    if (n < r->ncaptures) {
+
+        cap = r->captures;
+        p = r->captures_data;
+
+        if ((e->is_args || e->quote)
+            && (e->request->quoted_uri || e->request->plus_in_uri))
+        {
+            e->pos = (u_char *) ngx_escape_uri(pos, &p[cap[n]],
+                                               cap[n + 1] - cap[n],
+                                               NGX_ESCAPE_ARGS);
+        } else {
+            e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);
+        }
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script capture: \"%*s\"", e->pos - pos, pos);
+}
+
 #endif
 
 
+static ngx_int_t
+ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc)
+{
+    ngx_http_script_full_name_code_t  *code;
+
+    code = ngx_http_script_add_code(*sc->lengths,
+                                    sizeof(ngx_http_script_full_name_code_t),
+                                    NULL);
+    if (code == NULL) {
+        return NGX_ERROR;
+    }
+
+    code->code = (ngx_http_script_code_pt) ngx_http_script_full_name_len_code;
+    code->conf_prefix = sc->conf_prefix;
+
+    code = ngx_http_script_add_code(*sc->values,
+                                    sizeof(ngx_http_script_full_name_code_t),
+                                    &sc->main);
+    if (code == NULL) {
+        return NGX_ERROR;
+    }
+
+    code->code = ngx_http_script_full_name_code;
+    code->conf_prefix = sc->conf_prefix;
+
+    return NGX_OK;
+}
+
+
+static size_t
+ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e)
+{
+    ngx_http_script_full_name_code_t  *code;
+
+    code = (ngx_http_script_full_name_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_script_full_name_code_t);
+
+    return code->conf_prefix ? ngx_cycle->conf_prefix.len:
+                               ngx_cycle->prefix.len;
+}
+
+
+static void
+ngx_http_script_full_name_code(ngx_http_script_engine_t *e)
+{
+    ngx_http_script_full_name_code_t  *code;
+
+    ngx_str_t  value;
+
+    code = (ngx_http_script_full_name_code_t *) e->ip;
+
+    value.data = e->buf.data;
+    value.len = e->pos - e->buf.data;
+
+    if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &value, code->conf_prefix)
+        != NGX_OK)
+    {
+        e->ip = ngx_http_script_exit;
+        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        return;
+    }
+
+    e->buf = value;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script fullname: \"%V\"", &value);
+
+    e->ip += sizeof(ngx_http_script_full_name_code_t);
+}
+
+
 void
 ngx_http_script_return_code(ngx_http_script_engine_t *e)
 {
@@ -928,8 +1342,8 @@ ngx_http_script_equal_code(ngx_http_scri
 
     e->ip += sizeof(uintptr_t);
 
-    if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len)
-        == 0)
+    if (val->len == res->len
+        && ngx_strncmp(val->data, res->data, res->len) == 0)
     {
         *res = ngx_http_variable_true_value;
         return;
@@ -956,8 +1370,8 @@ ngx_http_script_not_equal_code(ngx_http_
 
     e->ip += sizeof(uintptr_t);
 
-    if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len)
-        == 0)
+    if (val->len == res->len
+        && ngx_strncmp(val->data, res->data, res->len) == 0)
     {
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
                        "http script not equal: no");
@@ -1000,6 +1414,7 @@ ngx_http_script_file_code(ngx_http_scrip
     of.directio = clcf->directio;
     of.valid = clcf->open_file_cache_valid;
     of.min_uses = clcf->open_file_cache_min_uses;
+    of.test_only = 1;
     of.errors = clcf->open_file_cache_errors;
     of.events = clcf->open_file_cache_events;
 
@@ -1008,7 +1423,7 @@ ngx_http_script_file_code(ngx_http_scrip
     {
         if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR) {
             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
-                          ngx_file_info_n " \"%s\" failed", value->data);
+                          "%s \"%s\" failed", of.failed, value->data);
         }
 
         switch (code->op) {
@@ -1114,8 +1529,6 @@ ngx_http_script_complex_value_code(ngx_h
     le.ip = code->lengths->elts;
     le.line = e->line;
     le.request = e->request;
-    le.captures = e->captures;
-    le.ncaptures = e->ncaptures;
     le.quote = e->quote;
 
     for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {
@@ -1163,9 +1576,6 @@ ngx_http_script_set_var_code(ngx_http_sc
     ngx_http_request_t          *r;
     ngx_http_script_var_code_t  *code;
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                   "http script set var");
-
     code = (ngx_http_script_var_code_t *) e->ip;
 
     e->ip += sizeof(ngx_http_script_var_code_t);
@@ -1179,6 +1589,20 @@ ngx_http_script_set_var_code(ngx_http_sc
     r->variables[code->index].no_cacheable = 0;
     r->variables[code->index].not_found = 0;
     r->variables[code->index].data = e->sp->data;
+
+#if (NGX_DEBUG)
+    {
+    ngx_http_variable_t        *v;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+    v = cmcf->variables.elts;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script set $%V", &v[code->index].name);
+    }
+#endif
 }
 
 
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -30,9 +30,6 @@ typedef struct {
     unsigned                    is_args:1;
     unsigned                    log:1;
 
-    int                        *captures;
-    ngx_uint_t                  ncaptures;
-
     ngx_int_t                   status;
     ngx_http_request_t         *request;
 } ngx_http_script_engine_t;
@@ -56,86 +53,114 @@ typedef struct {
     unsigned                    compile_args:1;
     unsigned                    complete_lengths:1;
     unsigned                    complete_values:1;
+    unsigned                    zero:1;
+    unsigned                    conf_prefix:1;
+    unsigned                    root_prefix:1;
+
     unsigned                    dup_capture:1;
-
     unsigned                    args:1;
 } ngx_http_script_compile_t;
 
 
+typedef struct {
+    ngx_str_t                   value;
+    ngx_uint_t                 *flushes;
+    void                       *lengths;
+    void                       *values;
+} ngx_http_complex_value_t;
+
+
+typedef struct {
+    ngx_conf_t                 *cf;
+    ngx_str_t                  *value;
+    ngx_http_complex_value_t   *complex_value;
+
+    unsigned                    zero:1;
+    unsigned                    conf_prefix:1;
+    unsigned                    root_prefix:1;
+} ngx_http_compile_complex_value_t;
+
+
 typedef void (*ngx_http_script_code_pt) (ngx_http_script_engine_t *e);
 typedef size_t (*ngx_http_script_len_code_pt) (ngx_http_script_engine_t *e);
 
 
 typedef struct {
-    ngx_http_script_code_pt         code;
-    uintptr_t                       len;
+    ngx_http_script_code_pt     code;
+    uintptr_t                   len;
 } ngx_http_script_copy_code_t;
 
 
 typedef struct {
-    ngx_http_script_code_pt         code;
-    uintptr_t                       index;
+    ngx_http_script_code_pt     code;
+    uintptr_t                   index;
 } ngx_http_script_var_code_t;
 
 
 typedef struct {
-    ngx_http_script_code_pt         code;
-    ngx_http_set_variable_pt        handler;
-    uintptr_t                       data;
+    ngx_http_script_code_pt     code;
+    ngx_http_set_variable_pt    handler;
+    uintptr_t                   data;
 } ngx_http_script_var_handler_code_t;
 
 
 typedef struct {
-    ngx_http_script_code_pt          code;
-    uintptr_t                        n;
+    ngx_http_script_code_pt     code;
+    uintptr_t                   n;
 } ngx_http_script_copy_capture_code_t;
 
 
 #if (NGX_PCRE)
 
 typedef struct {
-    ngx_http_script_code_pt          code;
-    ngx_regex_t                     *regex;
-    ngx_array_t                     *lengths;
-    uintptr_t                        size;
-    uintptr_t                        ncaptures;
-    uintptr_t                        status;
-    uintptr_t                        next;
+    ngx_http_script_code_pt     code;
+    ngx_regex_t                *regex;
+    ngx_array_t                *lengths;
+    uintptr_t                   size;
+    uintptr_t                   ncaptures;
+    uintptr_t                   status;
+    uintptr_t                   next;
 
-    uintptr_t                        test:1;
-    uintptr_t                        negative_test:1;
-    uintptr_t                        uri:1;
-    uintptr_t                        args:1;
+    uintptr_t                   test:1;
+    uintptr_t                   negative_test:1;
+    uintptr_t                   uri:1;
+    uintptr_t                   args:1;
 
     /* add the r->args to the new arguments */
-    uintptr_t                        add_args:1;
+    uintptr_t                   add_args:1;
 
-    uintptr_t                        redirect:1;
-    uintptr_t                        break_cycle:1;
+    uintptr_t                   redirect:1;
+    uintptr_t                   break_cycle:1;
 
-    ngx_str_t                        name;
+    ngx_str_t                   name;
 } ngx_http_script_regex_code_t;
 
 
 typedef struct {
-    ngx_http_script_code_pt          code;
+    ngx_http_script_code_pt     code;
 
-    uintptr_t                        uri:1;
-    uintptr_t                        args:1;
+    uintptr_t                   uri:1;
+    uintptr_t                   args:1;
 
     /* add the r->args to the new arguments */
-    uintptr_t                        add_args:1;
+    uintptr_t                   add_args:1;
 
-    uintptr_t                        redirect:1;
+    uintptr_t                   redirect:1;
 } ngx_http_script_regex_end_code_t;
 
 #endif
 
 
 typedef struct {
-    ngx_http_script_code_pt          code;
-    uintptr_t                        status;
-    uintptr_t                        null;
+    ngx_http_script_code_pt     code;
+    uintptr_t                   conf_prefix;
+} ngx_http_script_full_name_code_t;
+
+
+typedef struct {
+    ngx_http_script_code_pt     code;
+    uintptr_t                   status;
+    uintptr_t                   null;
 } ngx_http_script_return_code_t;
 
 
@@ -152,32 +177,38 @@ typedef enum {
 
 
 typedef struct {
-    ngx_http_script_code_pt          code;
-    uintptr_t                        op;
+    ngx_http_script_code_pt     code;
+    uintptr_t                   op;
 } ngx_http_script_file_code_t;
 
 
 typedef struct {
-    ngx_http_script_code_pt          code;
-    uintptr_t                        next;
-    void                           **loc_conf;
+    ngx_http_script_code_pt     code;
+    uintptr_t                   next;
+    void                      **loc_conf;
 } ngx_http_script_if_code_t;
 
 
 typedef struct {
-    ngx_http_script_code_pt          code;
-    ngx_array_t                     *lengths;
+    ngx_http_script_code_pt     code;
+    ngx_array_t                *lengths;
 } ngx_http_script_complex_value_code_t;
 
 
 typedef struct {
-    ngx_http_script_code_pt          code;
-    uintptr_t                        value;
-    uintptr_t                        text_len;
-    uintptr_t                        text_data;
+    ngx_http_script_code_pt     code;
+    uintptr_t                   value;
+    uintptr_t                   text_len;
+    uintptr_t                   text_data;
 } ngx_http_script_value_code_t;
 
 
+void ngx_http_script_flush_complex_value(ngx_http_request_t *r,
+    ngx_http_complex_value_t *val);
+ngx_int_t ngx_http_complex_value(ngx_http_request_t *r,
+    ngx_http_complex_value_t *val, ngx_str_t *value);
+ngx_int_t ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv);
+
 ngx_uint_t ngx_http_script_variables_count(ngx_str_t *value);
 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,
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -275,14 +275,16 @@ static ngx_str_t ngx_http_error_pages[] 
 
     ngx_null_string,                     /* 201, 204 */
 
-#define NGX_HTTP_LEVEL_200  1
+#define NGX_HTTP_LAST_LEVEL_200  202
+#define NGX_HTTP_LEVEL_200       (NGX_HTTP_LAST_LEVEL_200 - 201)
 
     /* 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
+#define NGX_HTTP_LAST_LEVEL_300  304
+#define NGX_HTTP_LEVEL_300       (NGX_HTTP_LAST_LEVEL_300 - 301)
 
     ngx_string(ngx_http_error_400_page),
     ngx_string(ngx_http_error_401_page),
@@ -302,7 +304,8 @@ static ngx_str_t ngx_http_error_pages[] 
     ngx_string(ngx_http_error_415_page),
     ngx_string(ngx_http_error_416_page),
 
-#define NGX_HTTP_LEVEL_400  17
+#define NGX_HTTP_LAST_LEVEL_400  417
+#define NGX_HTTP_LEVEL_400       (NGX_HTTP_LAST_LEVEL_400 - 400)
 
     ngx_string(ngx_http_error_495_page), /* 495, https certificate error */
     ngx_string(ngx_http_error_496_page), /* 496, https no certificate */
@@ -318,6 +321,9 @@ static ngx_str_t ngx_http_error_pages[] 
     ngx_null_string,                     /* 505 */
     ngx_null_string,                     /* 506 */
     ngx_string(ngx_http_error_507_page)
+
+#define NGX_HTTP_LAST_LEVEL_500  508
+
 };
 
 
@@ -379,6 +385,8 @@ ngx_http_special_response_handler(ngx_ht
         }
     }
 
+    r->expect_tested = 1;
+
     if (ngx_http_discard_request_body(r) != NGX_OK) {
         error = NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
@@ -400,16 +408,22 @@ ngx_http_special_response_handler(ngx_ht
         /* 204 */
         err = 0;
 
-    } else if (error < NGX_HTTP_BAD_REQUEST) {
+    } else if (error >= NGX_HTTP_MOVED_PERMANENTLY
+               && error < NGX_HTTP_LAST_LEVEL_300)
+    {
         /* 3XX */
         err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200;
 
-    } else if (error < NGX_HTTP_OWN_CODES) {
+    } else if (error >= NGX_HTTP_BAD_REQUEST
+               && error < NGX_HTTP_LAST_LEVEL_400)
+    {
         /* 4XX */
         err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_LEVEL_200
                                            + NGX_HTTP_LEVEL_300;
 
-    } else {
+    } else if (error >= NGX_HTTP_OWN_CODES
+               && error < NGX_HTTP_LAST_LEVEL_500)
+    {
         /* 49X, 5XX */
         err = error - NGX_HTTP_OWN_CODES + NGX_HTTP_LEVEL_200
                                          + NGX_HTTP_LEVEL_300
@@ -421,85 +435,113 @@ ngx_http_special_response_handler(ngx_ht
                 r->err_status = NGX_HTTP_BAD_REQUEST;
                 break;
         }
+
+    } else {
+        /* unknown code, zero body */
+        err = 0;
     }
 
     return ngx_http_send_special_response(r, clcf, err);
 }
 
 
+ngx_int_t
+ngx_http_filter_finalize_request(ngx_http_request_t *r, ngx_module_t *m,
+    ngx_int_t error)
+{
+    void       *ctx;
+    ngx_int_t   rc;
+
+    ngx_http_clean_header(r);
+
+    ctx = NULL;
+
+    if (m) {
+        ctx = r->ctx[m->ctx_index];
+    }
+
+    /* clear the modules contexts */
+    ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
+
+    if (m) {
+        r->ctx[m->ctx_index] = ctx;
+    }
+
+    r->filter_finalize = 1;
+
+    rc = ngx_http_special_response_handler(r, error);
+
+    /* NGX_ERROR resets any pending data */
+
+    switch (rc) {
+
+    case NGX_OK:
+    case NGX_DONE:
+        return NGX_ERROR;
+
+    default:
+        return rc;
+    }
+}
+
+
+void
+ngx_http_clean_header(ngx_http_request_t *r)
+{
+    ngx_memzero(&r->headers_out.status,
+                sizeof(ngx_http_headers_out_t)
+                    - offsetof(ngx_http_headers_out_t, status));
+
+    r->headers_out.headers.part.nelts = 0;
+    r->headers_out.headers.part.next = NULL;
+    r->headers_out.headers.last = &r->headers_out.headers.part;
+
+    r->headers_out.content_length_n = -1;
+    r->headers_out.last_modified_time = -1;
+}
+
+
 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_int_t                  overwrite;
+    ngx_str_t                  uri, args;
     ngx_table_elt_t           *location;
     ngx_http_core_loc_conf_t  *clcf;
 
-    r->err_status = err_page->overwrite;
+    overwrite = err_page->overwrite;
+
+    if (overwrite && overwrite != NGX_HTTP_OK) {
+        r->expect_tested = 1;
+    }
+
+    r->err_status = 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 (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {
+        return NGX_ERROR;
     }
 
-    if (uri->data[0] == '/') {
+    if (uri.data[0] == '/') {
+
+        if (err_page->value.lengths) {
+            ngx_http_split_args(r, &uri, &args);
+
+        } else {
+            args = err_page->args;
+        }
 
         if (r->method != NGX_HTTP_HEAD) {
             r->method = NGX_HTTP_GET;
             r->method_name = ngx_http_get_name;
         }
 
-        return ngx_http_internal_redirect(r, uri, args);
+        return ngx_http_internal_redirect(r, &uri, &args);
     }
 
-    if (uri->data[0] == '@') {
-        return ngx_http_named_location(r, uri);
+    if (uri.data[0] == '@') {
+        return ngx_http_named_location(r, &uri);
     }
 
     location = ngx_list_push(&r->headers_out.headers);
@@ -513,7 +555,7 @@ ngx_http_send_error_page(ngx_http_reques
     location->hash = 1;
     location->key.len = sizeof("Location") - 1;
     location->key.data = (u_char *) "Location";
-    location->value = *uri;
+    location->value = uri;
 
     r->headers_out.location = location;
 
@@ -568,6 +610,7 @@ ngx_http_send_special_response(ngx_http_
             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_type_lowcase = NULL;
 
         } else {
             r->headers_out.content_length_n = -1;
@@ -670,6 +713,7 @@ ngx_http_send_refresh(ngx_http_request_t
     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_type_lowcase = NULL;
 
     r->headers_out.location->hash = 0;
     r->headers_out.location = NULL;
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -9,6 +9,15 @@
 #include <ngx_http.h>
 
 
+#if (NGX_HTTP_CACHE)
+static ngx_int_t ngx_http_upstream_cache(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_cache_status(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+#endif
+
 static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx);
 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);
@@ -20,27 +29,40 @@ static ngx_int_t ngx_http_upstream_reini
     ngx_http_upstream_t *u);
 static void ngx_http_upstream_send_request(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
-static void ngx_http_upstream_send_request_handler(ngx_event_t *wev);
-static void ngx_http_upstream_process_header(ngx_event_t *rev);
+static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_header(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
 static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
 static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
 static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c);
-static void ngx_http_upstream_process_body_in_memory(ngx_event_t *rev);
+static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
 static void ngx_http_upstream_send_response(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
 static void
     ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r);
-static void ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev);
+static void
+    ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+static void
+    ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
+    ngx_uint_t do_write);
 static ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data);
 static ngx_int_t ngx_http_upstream_non_buffered_filter(void *data,
     ssize_t bytes);
 static void ngx_http_upstream_process_downstream(ngx_http_request_t *r);
-static void ngx_http_upstream_process_body(ngx_event_t *ev);
+static void ngx_http_upstream_process_upstream(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_request(ngx_http_request_t *r);
 static void ngx_http_upstream_store(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
-static void ngx_http_upstream_dummy_handler(ngx_event_t *wev);
+static void ngx_http_upstream_dummy_handler(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
 static void ngx_http_upstream_next(ngx_http_request_t *r,
     ngx_http_upstream_t *u, ngx_uint_t ft_type);
 static void ngx_http_upstream_cleanup(void *data);
@@ -50,10 +72,14 @@ static void ngx_http_upstream_finalize_r
 static ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t
-    ngx_http_upstream_process_multi_header_lines(ngx_http_request_t *r,
+    ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r,
@@ -69,10 +95,15 @@ static ngx_int_t ngx_http_upstream_copy_
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_copy_content_length(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
+
 #if (NGX_HTTP_GZIP)
 static ngx_int_t ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
@@ -85,6 +116,8 @@ static ngx_int_t ngx_http_upstream_statu
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_upstream_response_time_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_response_length_variable(
+    ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
 
 static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);
 static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -126,8 +159,7 @@ ngx_http_upstream_header_t  ngx_http_ups
     { ngx_string("Last-Modified"),
                  ngx_http_upstream_process_header_line,
                  offsetof(ngx_http_upstream_headers_in_t, last_modified),
-                 ngx_http_upstream_copy_header_line,
-                 offsetof(ngx_http_headers_out_t, last_modified), 0 },
+                 ngx_http_upstream_copy_last_modified, 0, 0 },
 
     { ngx_string("Server"),
                  ngx_http_upstream_process_header_line,
@@ -158,21 +190,19 @@ ngx_http_upstream_header_t  ngx_http_ups
                  ngx_http_upstream_copy_header_line, 0, 1 },
 
     { ngx_string("Cache-Control"),
-                 ngx_http_upstream_process_multi_header_lines,
-                 offsetof(ngx_http_upstream_headers_in_t, cache_control),
+                 ngx_http_upstream_process_cache_control, 0,
                  ngx_http_upstream_copy_multi_header_lines,
                  offsetof(ngx_http_headers_out_t, cache_control), 1 },
 
     { ngx_string("Expires"),
-                 ngx_http_upstream_process_header_line,
-                 offsetof(ngx_http_upstream_headers_in_t, expires),
+                 ngx_http_upstream_process_expires, 0,
                  ngx_http_upstream_copy_header_line,
                  offsetof(ngx_http_headers_out_t, expires), 1 },
 
     { ngx_string("Accept-Ranges"),
                  ngx_http_upstream_process_header_line,
                  offsetof(ngx_http_upstream_headers_in_t, accept_ranges),
-                 ngx_http_upstream_copy_header_line,
+                 ngx_http_upstream_copy_allow_ranges,
                  offsetof(ngx_http_headers_out_t, accept_ranges), 1 },
 
     { ngx_string("Connection"),
@@ -188,8 +218,7 @@ ngx_http_upstream_header_t  ngx_http_ups
                  ngx_http_upstream_copy_header_line, 0, 0 },
 
     { ngx_string("X-Accel-Expires"),
-                 ngx_http_upstream_process_header_line,
-                 offsetof(ngx_http_upstream_headers_in_t, x_accel_expires),
+                 ngx_http_upstream_process_accel_expires, 0,
                  ngx_http_upstream_copy_header_line, 0, 0 },
 
     { ngx_string("X-Accel-Redirect"),
@@ -274,13 +303,28 @@ ngx_module_t  ngx_http_upstream_module =
 static ngx_http_variable_t  ngx_http_upstream_vars[] = {
 
     { ngx_string("upstream_addr"), NULL,
-      ngx_http_upstream_addr_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
+      ngx_http_upstream_addr_variable, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("upstream_status"), NULL,
-      ngx_http_upstream_status_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
+      ngx_http_upstream_status_variable, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("upstream_response_time"), NULL,
-      ngx_http_upstream_response_time_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
+      ngx_http_upstream_response_time_variable, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+    { ngx_string("upstream_response_length"), NULL,
+      ngx_http_upstream_response_length_variable, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+#if (NGX_HTTP_CACHE)
+
+    { ngx_string("upstream_cache_status"), NULL,
+      ngx_http_upstream_cache_status, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+#endif
 
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
@@ -295,6 +339,15 @@ static ngx_http_upstream_next_t  ngx_htt
     { 0, 0 }
 };
 
+
+ngx_conf_bitmask_t  ngx_http_upstream_cache_method_mask[] = {
+   { ngx_string("GET"),  NGX_HTTP_GET},
+   { ngx_string("HEAD"), NGX_HTTP_HEAD },
+   { ngx_string("POST"), NGX_HTTP_POST },
+   { ngx_null_string, 0 }
+};
+
+
 void
 ngx_http_upstream_init(ngx_http_request_t *r)
 {
@@ -319,7 +372,9 @@ ngx_http_upstream_init(ngx_http_request_
 
     u = r->upstream;
 
-    if (!r->post_action && !u->conf->ignore_client_abort) {
+    u->store = (u->conf->store || u->conf->store_lengths);
+
+    if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
         r->read_event_handler = ngx_http_upstream_rd_check_broken_connection;
         r->write_event_handler = ngx_http_upstream_wr_check_broken_connection;
     }
@@ -340,6 +395,25 @@ ngx_http_upstream_init(ngx_http_request_
         u->request_bufs = r->request_body->bufs;
     }
 
+#if (NGX_HTTP_CACHE)
+
+    if (u->conf->cache) {
+        ngx_int_t  rc;
+
+        rc = ngx_http_upstream_cache(r, u);
+
+        if (rc == NGX_DONE) {
+            return;
+        }
+
+        if (rc != NGX_DECLINED) {
+            ngx_http_finalize_request(r, rc);
+            return;
+        }
+    }
+
+#endif
+
     if (u->create_request(r) != NGX_OK) {
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         return;
@@ -386,14 +460,26 @@ ngx_http_upstream_init(ngx_http_request_
     cln->data = r;
     u->cleanup = &cln->handler;
 
-    u->store = (u->conf->store || u->conf->store_lengths);
-
     if (u->resolved == NULL) {
 
         uscf = u->conf->upstream;
 
     } else {
 
+        if (u->resolved->sockaddr) {
+
+            if (ngx_http_upstream_create_round_robin_peer(r, u->resolved)
+                != NGX_OK)
+            {
+                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return;
+            }
+
+            ngx_http_upstream_connect(r, u);
+
+            return;
+        }
+
         host = &u->resolved->host;
 
         umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
@@ -405,7 +491,7 @@ ngx_http_upstream_init(ngx_http_request_
             uscf = uscfp[i];
 
             if (uscf->host.len == host->len
-                && ((uscf->port == 0 && u->resolved->default_port)
+                && ((uscf->port == 0 && u->resolved->no_port)
                      || uscf->port == u->resolved->port)
                 && ngx_memcmp(uscf->host.data, host->data, host->len) == 0)
             {
@@ -457,6 +543,178 @@ found:
 }
 
 
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+    ngx_int_t          rc;
+    ngx_http_cache_t  *c;
+
+    if (!(r->method & u->conf->cache_methods)) {
+        return NGX_DECLINED;
+    }
+
+    if (r->method & NGX_HTTP_HEAD) {
+        u->method = ngx_http_core_get_method;
+    }
+
+    c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t));
+    if (c == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (ngx_array_init(&c->keys, r->pool, 4, sizeof(ngx_str_t)) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    r->cache = c;
+    c->file.log = r->connection->log;
+
+    if (u->create_key(r) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    /* TODO: add keys */
+
+    ngx_http_file_cache_create_key(r);
+
+    u->cacheable = 1;
+
+    c->min_uses = u->conf->cache_min_uses;
+    c->body_start = u->conf->buffer_size;
+    c->file_cache = u->conf->cache->data;
+
+    u->cache_status = NGX_HTTP_CACHE_MISS;
+
+    rc = ngx_http_file_cache_open(r);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http upstream cache: %i", rc);
+
+    switch (rc) {
+
+    case NGX_HTTP_CACHE_UPDATING:
+
+        if (u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) {
+            u->cache_status = rc;
+            rc = NGX_OK;
+
+        } else {
+            rc = NGX_HTTP_CACHE_STALE;
+        }
+
+        break;
+
+    case NGX_OK:
+        u->cache_status = NGX_HTTP_CACHE_HIT;
+    }
+
+    switch (rc) {
+
+    case NGX_OK:
+
+        rc = ngx_http_upstream_cache_send(r, u);
+
+        if (rc != NGX_HTTP_UPSTREAM_INVALID_HEADER) {
+            return rc;
+        }
+
+        break;
+
+    case NGX_ERROR:
+
+        return NGX_ERROR;
+
+    case NGX_HTTP_CACHE_STALE:
+
+        c->valid_sec = 0;
+        u->buffer.start = NULL;
+        u->cache_status = NGX_HTTP_CACHE_EXPIRED;
+
+        break;
+
+    case NGX_DECLINED:
+
+        if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) {
+            u->buffer.start = NULL;
+
+        } else {
+            u->buffer.pos = u->buffer.start + c->header_start;
+            u->buffer.last = u->buffer.pos;
+        }
+
+        break;
+
+    case NGX_AGAIN:
+
+        u->cacheable = 0;
+
+        break;
+
+    default:
+
+        /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */
+
+        u->cache_status = NGX_HTTP_CACHE_HIT;
+
+        return rc;
+    }
+
+    r->cached = 0;
+
+    return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+    ngx_int_t          rc;
+    ngx_http_cache_t  *c;
+
+    r->cached = 1;
+    c = r->cache;
+
+    /* TODO: cache stack */
+
+    u->buffer = *c->buf;
+    u->buffer.pos += c->header_start;
+
+    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)
+    {
+        return NGX_ERROR;
+    }
+
+    rc = u->process_header(r);
+
+    if (rc == NGX_OK) {
+
+        if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
+            return NGX_DONE;
+        }
+
+        return ngx_http_cache_send(r);
+    }
+
+    if (rc == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    /* rc == NGX_HTTP_UPSTREAM_INVALID_HEADER */
+
+    /* TODO: delete file */
+
+    return rc;
+}
+
+#endif
+
+
 static void
 ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx)
 {
@@ -511,6 +769,37 @@ ngx_http_upstream_resolve_handler(ngx_re
 
 
 static void
+ngx_http_upstream_handler(ngx_event_t *ev)
+{
+    ngx_connection_t     *c;
+    ngx_http_request_t   *r;
+    ngx_http_log_ctx_t   *ctx;
+    ngx_http_upstream_t  *u;
+
+    c = ev->data;
+    r = c->data;
+
+    u = r->upstream;
+    c = r->connection;
+
+    ctx = c->log->data;
+    ctx->current_request = r;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream request: \"%V?%V\"", &r->uri, &r->args);
+
+    if (ev->write) {
+        u->write_event_handler(r, u);
+
+    } else {
+        u->read_event_handler(r, u);
+    }
+
+    ngx_http_run_posted_requests(c);
+}
+
+
+static void
 ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r)
 {
     ngx_http_upstream_check_broken_connection(r, r->connection->read);
@@ -531,6 +820,7 @@ ngx_http_upstream_check_broken_connectio
     int                  n;
     char                 buf[1];
     ngx_err_t            err;
+    ngx_int_t            event;
     ngx_connection_t     *c;
     ngx_http_upstream_t  *u;
 
@@ -542,8 +832,22 @@ ngx_http_upstream_check_broken_connectio
     u = r->upstream;
 
     if (c->error) {
-        ngx_http_upstream_finalize_request(r, u,
-                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);
+        if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
+
+            event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
+
+            if (ngx_del_event(ev, event, 0) != NGX_OK) {
+                ngx_http_upstream_finalize_request(r, u,
+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return;
+            }
+        }
+
+        if (!u->cacheable) {
+            ngx_http_upstream_finalize_request(r, u,
+                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);
+        }
+
         return;
     }
 
@@ -566,7 +870,7 @@ ngx_http_upstream_check_broken_connectio
             ev->error = 1;
         }
 
-        if (!u->cacheable && !u->store && u->peer.connection) {
+        if (!u->cacheable && 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");
@@ -597,17 +901,15 @@ ngx_http_upstream_check_broken_connectio
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err,
                    "http upstream recv(): %d", n);
 
-    /*
-     * we do not need to disable the write event because
-     * that event has NGX_USE_CLEAR_EVENT type
-     */
-
     if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {
         return;
     }
 
     if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
-        if (ngx_del_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+
+        event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
+
+        if (ngx_del_event(ev, event, 0) != NGX_OK) {
             ngx_http_upstream_finalize_request(r, u,
                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
             return;
@@ -632,7 +934,7 @@ ngx_http_upstream_check_broken_connectio
     ev->eof = 1;
     c->error = 1;
 
-    if (!u->cacheable && !u->store && u->peer.connection) {
+    if (!u->cacheable && u->peer.connection) {
         ngx_log_error(NGX_LOG_INFO, ev->log, err,
                       "client closed prematurely connection, "
                       "so upstream connection is closed too");
@@ -712,14 +1014,19 @@ ngx_http_upstream_connect(ngx_http_reque
 
     c->data = r;
 
-    c->write->handler = ngx_http_upstream_send_request_handler;
-    c->read->handler = ngx_http_upstream_process_header;
+    c->write->handler = ngx_http_upstream_handler;
+    c->read->handler = ngx_http_upstream_handler;
+
+    u->write_event_handler = ngx_http_upstream_send_request_handler;
+    u->read_event_handler = ngx_http_upstream_process_header;
 
     c->sendfile &= r->connection->sendfile;
     u->output.sendfile = c->sendfile;
 
     c->pool = r->pool;
-    c->read->log = c->write->log = c->log = r->connection->log;
+    c->log = r->connection->log;
+    c->read->log = c->log;
+    c->write->log = c->log;
 
     /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */
 
@@ -792,7 +1099,7 @@ ngx_http_upstream_ssl_init_connection(ng
 
     if (ngx_ssl_create_connection(u->conf->ssl, c,
                                   NGX_SSL_BUFFER|NGX_SSL_CLIENT)
-        == NGX_ERROR)
+        != NGX_OK)
     {
         ngx_http_upstream_finalize_request(r, u,
                                            NGX_HTTP_INTERNAL_SERVER_ERROR);
@@ -838,8 +1145,8 @@ ngx_http_upstream_ssl_handshake(ngx_conn
             u->peer.save_session(&u->peer, u->peer.data);
         }
 
-        c->write->handler = ngx_http_upstream_send_request_handler;
-        c->read->handler = ngx_http_upstream_process_header;
+        c->write->handler = ngx_http_upstream_handler;
+        c->read->handler = ngx_http_upstream_handler;
 
         ngx_http_upstream_send_request(r, u);
 
@@ -901,22 +1208,18 @@ ngx_http_upstream_reinit(ngx_http_reques
 
     /* reinit u->buffer */
 
-#if 0
-    if (u->cache) {
-        u->buffer.pos = u->buffer.start + u->cache->ctx.header_size;
-        u->buffer.last = u->buffer.pos;
-
-    } else {
-        u->buffer.pos = u->buffer.start;
-        u->buffer.last = u->buffer.start;
+    u->buffer.pos = u->buffer.start;
+
+#if (NGX_HTTP_CACHE)
+
+    if (r->cache) {
+        u->buffer.pos += r->cache->header_start;
     }
-#else
-
-        u->buffer.pos = u->buffer.start;
-        u->buffer.last = u->buffer.start;
 
 #endif
 
+    u->buffer.last = u->buffer.pos;
+
     return NGX_OK;
 }
 
@@ -955,8 +1258,7 @@ ngx_http_upstream_send_request(ngx_http_
     if (rc == NGX_AGAIN) {
         ngx_add_timer(c->write, u->conf->send_timeout);
 
-        if (ngx_handle_write_event(c->write, u->conf->send_lowat) == NGX_ERROR)
-        {
+        if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) {
             ngx_http_upstream_finalize_request(r, u,
                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
             return;
@@ -993,14 +1295,14 @@ ngx_http_upstream_send_request(ngx_http_
          * it's better to do here because we postpone header buffer allocation
          */
 
-        ngx_http_upstream_process_header(c->read);
+        ngx_http_upstream_process_header(r, u);
         return;
     }
 #endif
 
-    c->write->handler = ngx_http_upstream_dummy_handler;
-
-    if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+    u->write_event_handler = ngx_http_upstream_dummy_handler;
+
+    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
         ngx_http_upstream_finalize_request(r, u,
                                            NGX_HTTP_INTERNAL_SERVER_ERROR);
         return;
@@ -1009,20 +1311,17 @@ ngx_http_upstream_send_request(ngx_http_
 
 
 static void
-ngx_http_upstream_send_request_handler(ngx_event_t *wev)
+ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
+    ngx_http_upstream_t *u)
 {
-    ngx_connection_t     *c;
-    ngx_http_request_t   *r;
-    ngx_http_upstream_t  *u;
-
-    c = wev->data;
-    r = c->data;
-    u = r->upstream;
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,
+    ngx_connection_t  *c;
+
+    c = u->peer.connection;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http upstream send request handler");
 
-    if (wev->timedout) {
+    if (c->write->timedout) {
         ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
         return;
     }
@@ -1037,9 +1336,9 @@ ngx_http_upstream_send_request_handler(n
 #endif
 
     if (u->header_sent) {
-        wev->handler = ngx_http_upstream_dummy_handler;
-
-        (void) ngx_handle_write_event(wev, 0);
+        u->write_event_handler = ngx_http_upstream_dummy_handler;
+
+        (void) ngx_handle_write_event(c->write, 0);
 
         return;
     }
@@ -1049,30 +1348,20 @@ ngx_http_upstream_send_request_handler(n
 
 
 static void
-ngx_http_upstream_process_header(ngx_event_t *rev)
+ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
-    ssize_t                         n;
-    ngx_int_t                       rc;
-    ngx_str_t                      *uri, args;
-    ngx_uint_t                      i, flags;
-    ngx_list_part_t                *part;
-    ngx_table_elt_t                *h;
-    ngx_connection_t               *c;
-    ngx_http_request_t             *r;
-    ngx_http_upstream_t            *u;
-    ngx_http_upstream_header_t     *hh;
-    ngx_http_upstream_main_conf_t  *umcf;
-
-    c = rev->data;
-    r = c->data;
-    u = r->upstream;
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+    ssize_t            n;
+    ngx_int_t          rc;
+    ngx_connection_t  *c;
+
+    c = u->peer.connection;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http upstream process header");
 
     c->log->action = "reading response header from upstream";
 
-    if (rev->timedout) {
+    if (c->read->timedout) {
         ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
         return;
     }
@@ -1106,70 +1395,68 @@ ngx_http_upstream_process_header(ngx_eve
             return;
         }
 
-#if 0
-        if (u->cache) {
-            u->buffer.pos += u->cache->ctx.header_size;
+#if (NGX_HTTP_CACHE)
+
+        if (r->cache) {
+            u->buffer.pos += r->cache->header_start;
             u->buffer.last = u->buffer.pos;
         }
 #endif
     }
 
-    n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);
-
-    if (n == NGX_AGAIN) {
+    for ( ;; ) {
+
+        n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);
+
+        if (n == NGX_AGAIN) {
 #if 0
-        ngx_add_timer(rev, u->read_timeout);
+            ngx_add_timer(rev, u->read_timeout);
 #endif
 
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
-            ngx_http_upstream_finalize_request(r, u,
+            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+                ngx_http_upstream_finalize_request(r, u,
                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return;
+            }
+
+            return;
+        }
+
+        if (n == 0) {
+            ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                          "upstream prematurely closed connection");
+        }
+
+        if (n == NGX_ERROR || n == 0) {
+            ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
             return;
         }
 
-        return;
-    }
-
-    if (n == 0) {
-        ngx_log_error(NGX_LOG_ERR, rev->log, 0,
-                      "upstream prematurely closed connection");
-    }
-
-    if (n == NGX_ERROR || n == 0) {
-        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
-        return;
-    }
-
-    u->buffer.last += n;
+        u->buffer.last += n;
 
 #if 0
-    u->valid_header_in = 0;
-
-    u->peer.cached = 0;
-#endif
-
-    rc = u->process_header(r);
-
-    if (rc == NGX_AGAIN) {
-#if 0
-        ngx_add_timer(rev, u->read_timeout);
+        u->valid_header_in = 0;
+
+        u->peer.cached = 0;
 #endif
 
-        if (u->buffer.pos == u->buffer.end) {
-            ngx_log_error(NGX_LOG_ERR, rev->log, 0,
-                          "upstream sent too big header");
-
-            ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);
-            return;
+        rc = u->process_header(r);
+
+        if (rc == NGX_AGAIN) {
+
+            if (u->buffer.pos == u->buffer.end) {
+                ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                              "upstream sent too big header");
+
+                ngx_http_upstream_next(r, u,
+                                       NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);
+                return;
+            }
+
+            continue;
         }
 
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
-            ngx_http_upstream_finalize_request(r, u,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-            return;
-        }
-
-        return;
+        break;
     }
 
     if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
@@ -1200,124 +1487,10 @@ ngx_http_upstream_process_header(ngx_eve
         }
     }
 
-    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
-
-    if (u->headers_in.x_accel_redirect) {
-
-        ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);
-
-        part = &u->headers_in.headers.part;
-        h = part->elts;
-
-        for (i = 0; /* void */; i++) {
-
-            if (i >= part->nelts) {
-                if (part->next == NULL) {
-                    break;
-                }
-
-                part = part->next;
-                h = part->elts;
-                i = 0;
-            }
-
-            hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
-                               h[i].lowcase_key, h[i].key.len);
-
-            if (hh && hh->redirect) {
-                if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
-                    ngx_http_finalize_request(r,
-                                              NGX_HTTP_INTERNAL_SERVER_ERROR);
-                    return;
-                }
-            }
-        }
-
-        uri = &u->headers_in.x_accel_redirect->value;
-        args.len = 0;
-        args.data = NULL;
-        flags = 0;
-
-        if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
-            ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
-            return;
-        }
-
-        if (flags & NGX_HTTP_ZERO_IN_URI) {
-            r->zero_in_uri = 1;
-        }
-
-        if (r->method != NGX_HTTP_HEAD) {
-            r->method = NGX_HTTP_GET;
-        }
-
-        r->valid_unparsed_uri = 0;
-
-        ngx_http_internal_redirect(r, uri, &args);
+    if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
         return;
     }
 
-    part = &u->headers_in.headers.part;
-    h = part->elts;
-
-    for (i = 0; /* void */; i++) {
-
-        if (i >= part->nelts) {
-            if (part->next == NULL) {
-                break;
-            }
-
-            part = part->next;
-            h = part->elts;
-            i = 0;
-        }
-
-        if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
-                          h[i].lowcase_key, h[i].key.len))
-        {
-            continue;
-        }
-
-        hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
-                           h[i].lowcase_key, h[i].key.len);
-
-        if (hh) {
-            if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
-                ngx_http_upstream_finalize_request(r, u,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-                return;
-            }
-
-            continue;
-        }
-
-        if (ngx_http_upstream_copy_header_line(r, &h[i], 0) != NGX_OK) {
-            ngx_http_upstream_finalize_request(r, u,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-            return;
-        }
-    }
-
-    if (r->headers_out.server && r->headers_out.server->value.data == NULL) {
-        r->headers_out.server->hash = 0;
-    }
-
-    if (r->headers_out.date && r->headers_out.date->value.data == NULL) {
-        r->headers_out.date->hash = 0;
-    }
-
-    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;
-
-    } else {
-        u->length = NGX_MAX_SIZE_T_VALUE;
-    }
-
     if (!r->subrequest_in_memory) {
         ngx_http_upstream_send_response(r, u);
         return;
@@ -1337,19 +1510,27 @@ ngx_http_upstream_process_header(ngx_eve
         return;
     }
 
-    if (u->buffer.last - u->buffer.pos >= (ssize_t) u->length) {
-        if (u->input_filter(u->input_filter_ctx, 0) == NGX_ERROR) {
+    n = u->buffer.last - u->buffer.pos;
+
+    if (n) {
+        u->buffer.last -= n;
+
+        u->state->response_length += n;
+
+        if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
             ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
             return;
         }
 
-        ngx_http_upstream_finalize_request(r, u, 0);
-        return;
+        if (u->length == 0) {
+            ngx_http_upstream_finalize_request(r, u, 0);
+            return;
+        }
     }
 
-    rev->handler = ngx_http_upstream_process_body_in_memory;
-
-    ngx_http_upstream_process_body_in_memory(rev);
+    u->read_event_handler = ngx_http_upstream_process_body_in_memory;
+
+    ngx_http_upstream_process_body_in_memory(r, u);
 }
 
 
@@ -1359,10 +1540,6 @@ ngx_http_upstream_test_next(ngx_http_req
     ngx_uint_t                 status;
     ngx_http_upstream_next_t  *un;
 
-    if (!(u->conf->next_upstream & NGX_HTTP_UPSTREAM_FT_STATUS)) {
-        return NGX_DECLINED;
-    }
-
     status = u->headers_in.status_n;
 
     for (un = ngx_http_upstream_next_errors; un->status; un++) {
@@ -1376,16 +1553,21 @@ ngx_http_upstream_test_next(ngx_http_req
             return NGX_OK;
         }
 
-        if (status == NGX_HTTP_NOT_FOUND && u->conf->intercept_404) {
-            ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND);
-            return NGX_OK;
-        }
-
 #if (NGX_HTTP_CACHE)
 
-        if (u->peer.tries == 0 && u->stale && (u->conf->use_stale & un->mask)) {
-            ngx_http_upstream_finalize_request(r, u,
-                                              ngx_http_send_cached_response(r));
+        if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
+            && (u->conf->cache_use_stale & un->mask))
+        {
+            ngx_int_t  rc;
+
+            rc = u->reinit_request(r);
+
+            if (rc == NGX_OK) {
+                u->cache_status = NGX_HTTP_CACHE_STALE;
+                rc = ngx_http_upstream_cache_send(r, u);
+            }
+
+            ngx_http_upstream_finalize_request(r, u, rc);
             return NGX_OK;
         }
 
@@ -1406,6 +1588,13 @@ ngx_http_upstream_intercept_errors(ngx_h
     ngx_http_err_page_t       *err_page;
     ngx_http_core_loc_conf_t  *clcf;
 
+    status = u->headers_in.status_n;
+
+    if (status == NGX_HTTP_NOT_FOUND && u->conf->intercept_404) {
+        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND);
+        return NGX_OK;
+    }
+
     if (!u->conf->intercept_errors) {
         return NGX_DECLINED;
     }
@@ -1416,15 +1605,14 @@ ngx_http_upstream_intercept_errors(ngx_h
         return NGX_DECLINED;
     }
 
-    status = u->headers_in.status_n;
-
     err_page = clcf->error_pages->elts;
     for (i = 0; i < clcf->error_pages->nelts; i++) {
 
         if (err_page[i].status == status) {
 
-            if (status == NGX_HTTP_UNAUTHORIZED) {
-
+            if (status == NGX_HTTP_UNAUTHORIZED
+                && u->headers_in.www_authenticate)
+            {
                 h = ngx_list_push(&r->headers_out.headers);
 
                 if (h == NULL) {
@@ -1492,19 +1680,151 @@ ngx_http_upstream_test_connect(ngx_conne
 }
 
 
-static void
-ngx_http_upstream_process_body_in_memory(ngx_event_t *rev)
+static ngx_int_t
+ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
-    size_t                size;
-    ssize_t               n;
-    ngx_buf_t            *b;
-    ngx_connection_t     *c;
-    ngx_http_request_t   *r;
-    ngx_http_upstream_t  *u;
-
-    c = rev->data;
-    r = c->data;
-    u = r->upstream;
+    ngx_str_t                      *uri, args;
+    ngx_uint_t                      i, flags;
+    ngx_list_part_t                *part;
+    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);
+
+    if (u->headers_in.x_accel_redirect
+        && !(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT))
+    {
+        ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);
+
+        part = &u->headers_in.headers.part;
+        h = part->elts;
+
+        for (i = 0; /* void */; i++) {
+
+            if (i >= part->nelts) {
+                if (part->next == NULL) {
+                    break;
+                }
+
+                part = part->next;
+                h = part->elts;
+                i = 0;
+            }
+
+            hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
+                               h[i].lowcase_key, h[i].key.len);
+
+            if (hh && hh->redirect) {
+                if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
+                    ngx_http_finalize_request(r,
+                                              NGX_HTTP_INTERNAL_SERVER_ERROR);
+                    return NGX_DONE;
+                }
+            }
+        }
+
+        uri = &u->headers_in.x_accel_redirect->value;
+        args.len = 0;
+        args.data = NULL;
+        flags = 0;
+
+        if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
+            ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
+            return NGX_DONE;
+        }
+
+        if (flags & NGX_HTTP_ZERO_IN_URI) {
+            r->zero_in_uri = 1;
+        }
+
+        if (r->method != NGX_HTTP_HEAD) {
+            r->method = NGX_HTTP_GET;
+        }
+
+        r->valid_unparsed_uri = 0;
+
+        ngx_http_internal_redirect(r, uri, &args);
+        return NGX_DONE;
+    }
+
+    part = &u->headers_in.headers.part;
+    h = part->elts;
+
+    for (i = 0; /* void */; i++) {
+
+        if (i >= part->nelts) {
+            if (part->next == NULL) {
+                break;
+            }
+
+            part = part->next;
+            h = part->elts;
+            i = 0;
+        }
+
+        if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
+                          h[i].lowcase_key, h[i].key.len))
+        {
+            continue;
+        }
+
+        hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
+                           h[i].lowcase_key, h[i].key.len);
+
+        if (hh) {
+            if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
+                ngx_http_upstream_finalize_request(r, u,
+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return NGX_DONE;
+            }
+
+            continue;
+        }
+
+        if (ngx_http_upstream_copy_header_line(r, &h[i], 0) != NGX_OK) {
+            ngx_http_upstream_finalize_request(r, u,
+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return NGX_DONE;
+        }
+    }
+
+    if (r->headers_out.server && r->headers_out.server->value.data == NULL) {
+        r->headers_out.server->hash = 0;
+    }
+
+    if (r->headers_out.date && r->headers_out.date->value.data == NULL) {
+        r->headers_out.date->hash = 0;
+    }
+
+    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;
+
+    } else {
+        u->length = NGX_MAX_SIZE_T_VALUE;
+    }
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
+    ngx_http_upstream_t *u)
+{
+    size_t             size;
+    ssize_t            n;
+    ngx_buf_t         *b;
+    ngx_event_t       *rev;
+    ngx_connection_t  *c;
+
+    c = u->peer.connection;
+    rev = c->read;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http upstream process body on memory");
@@ -1539,6 +1859,8 @@ ngx_http_upstream_process_body_in_memory
             return;
         }
 
+        u->state->response_length += n;
+
         if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
             ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
             return;
@@ -1549,7 +1871,7 @@ ngx_http_upstream_process_body_in_memory
         }
     }
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
         return;
     }
@@ -1567,40 +1889,47 @@ static void
 ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
     int                        tcp_nodelay;
-    ssize_t                    size;
+    ssize_t                    n;
     ngx_int_t                  rc;
     ngx_event_pipe_t          *p;
     ngx_connection_t          *c;
-    ngx_pool_cleanup_t        *cl;
-    ngx_pool_cleanup_file_t   *clf;
     ngx_http_core_loc_conf_t  *clcf;
 
     rc = ngx_http_send_header(r);
 
-    if (rc == NGX_ERROR || rc > NGX_OK || r->post_action || r->header_only) {
+    if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) {
         ngx_http_upstream_finalize_request(r, u, rc);
         return;
     }
 
+    c = r->connection;
+
+    if (r->header_only) {
+
+        if (u->cacheable || u->store) {
+
+            if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) {
+                ngx_connection_error(c, ngx_socket_errno,
+                                     ngx_shutdown_socket_n " failed");
+            }
+
+            r->read_event_handler = ngx_http_request_empty_handler;
+            r->write_event_handler = ngx_http_request_empty_handler;
+            c->error = 1;
+
+        } else {
+            ngx_http_upstream_finalize_request(r, u, rc);
+            return;
+        }
+    }
+
     u->header_sent = 1;
 
     if (r->request_body && r->request_body->temp_file) {
-        for (cl = r->pool->cleanup; cl; cl = cl->next) {
-            if (cl->handler == ngx_pool_cleanup_file) {
-                clf = cl->data;
-
-                if (clf->fd == r->request_body->temp_file->file.fd) {
-                    cl->handler(clf);
-                    cl->handler = NULL;
-                    r->request_body->temp_file->file.fd = NGX_INVALID_FILE;
-                    break;
-                }
-            }
-        }
+        ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd);
+        r->request_body->temp_file->file.fd = NGX_INVALID_FILE;
     }
 
-    c = r->connection;
-
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
     if (!u->buffering) {
@@ -1611,8 +1940,7 @@ ngx_http_upstream_send_response(ngx_http
             u->input_filter_ctx = r;
         }
 
-        u->peer.connection->read->handler =
-                                   ngx_http_upstream_process_non_buffered_body;
+        u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream;
         r->write_event_handler =
                              ngx_http_upstream_process_non_buffered_downstream;
 
@@ -1640,17 +1968,19 @@ ngx_http_upstream_send_response(ngx_http
             c->tcp_nodelay = NGX_TCP_NODELAY_SET;
         }
 
-        size = u->buffer.last - u->buffer.pos;
-
-        if (size) {
+        n = u->buffer.last - u->buffer.pos;
+
+        if (n) {
             u->buffer.last = u->buffer.pos;
 
-            if (u->input_filter(u->input_filter_ctx, size) == NGX_ERROR) {
+            u->state->response_length += n;
+
+            if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
                 ngx_http_upstream_finalize_request(r, u, 0);
                 return;
             }
 
-            ngx_http_upstream_process_non_buffered_body(c->write);
+            ngx_http_upstream_process_non_buffered_downstream(r);
 
         } else {
             u->buffer.pos = u->buffer.start;
@@ -1662,8 +1992,7 @@ ngx_http_upstream_send_response(ngx_http
             }
 
             if (u->peer.connection->read->ready) {
-                ngx_http_upstream_process_non_buffered_body(
-                                                     u->peer.connection->read);
+                ngx_http_upstream_process_non_buffered_upstream(r, u);
             }
         }
 
@@ -1672,30 +2001,49 @@ ngx_http_upstream_send_response(ngx_http
 
     /* TODO: preallocate event_pipe bufs, look "Content-Length" */
 
-#if 0
-
-    if (u->cache && u->cache->ctx.file.fd != NGX_INVALID_FILE) {
-        if (ngx_close_file(u->cache->ctx.file.fd) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
-                          ngx_close_file_n " \"%s\" failed",
-                          u->cache->ctx.file.name.data);
-        }
+#if (NGX_HTTP_CACHE)
+
+    if (r->cache && r->cache->file.fd != NGX_INVALID_FILE) {
+        ngx_pool_run_cleanup_file(r->pool, r->cache->file.fd);
+        r->cache->file.fd = NGX_INVALID_FILE;
     }
 
     if (u->cacheable) {
-        header = (ngx_http_cache_header_t *) u->buffer->start;
-
-        header->expires = u->cache->ctx.expires;
-        header->last_modified = u->cache->ctx.last_modified;
-        header->date = u->cache->ctx.date;
-        header->length = r->headers_out.content_length_n;
-        u->cache->ctx.length = r->headers_out.content_length_n;
-
-        header->key_len = u->cache->ctx.key0.len;
-        ngx_memcpy(&header->key, u->cache->ctx.key0.data, header->key_len);
-        header->key[header->key_len] = LF;
+        time_t  now, valid;
+
+        now = ngx_time();
+
+        valid = r->cache->valid_sec;
+
+        if (valid == 0) {
+            valid = ngx_http_file_cache_valid(u->conf->cache_valid,
+                                              u->headers_in.status_n);
+            if (valid) {
+                r->cache->valid_sec = now + valid;
+            }
+        }
+
+        if (valid) {
+            r->cache->last_modified = r->headers_out.last_modified_time;
+            r->cache->date = now;
+            r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start);
+
+            if (r->headers_out.content_length_n != -1) {
+                r->cache->length = r->cache->body_start
+                                   + r->headers_out.content_length_n;
+            }
+
+            ngx_http_file_cache_set_header(r, u->buffer.start);
+
+        } else {
+            u->cacheable = 0;
+            r->headers_out.last_modified_time = -1;
+        }
     }
 
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http cacheable: %d", u->cacheable);
+
 #endif
 
     p = u->pipe;
@@ -1723,7 +2071,7 @@ ngx_http_upstream_send_response(ngx_http
     p->temp_file->path = u->conf->temp_path;
     p->temp_file->pool = r->pool;
 
-    if (u->cacheable || u->store) {
+    if (p->cacheable) {
         p->temp_file->persistent = 1;
 
     } else {
@@ -1793,69 +2141,82 @@ ngx_http_upstream_send_response(ngx_http
     p->send_timeout = clcf->send_timeout;
     p->send_lowat = clcf->send_lowat;
 
-    u->peer.connection->read->handler = ngx_http_upstream_process_body;
+    u->read_event_handler = ngx_http_upstream_process_upstream;
     r->write_event_handler = ngx_http_upstream_process_downstream;
 
-    ngx_http_upstream_process_body(u->peer.connection->read);
+    ngx_http_upstream_process_upstream(r, u);
 }
 
 
 static void
 ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r)
 {
-    ngx_http_upstream_process_non_buffered_body(r->connection->write);
+    ngx_event_t          *wev;
+    ngx_connection_t     *c;
+    ngx_http_upstream_t  *u;
+
+    c = r->connection;
+    u = r->upstream;
+    wev = c->write;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream process non buffered downstream");
+
+    c->log->action = "sending to client";
+
+    if (wev->timedout) {
+        c->timedout = 1;
+        ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+        ngx_http_upstream_finalize_request(r, u, 0);
+        return;
+    }
+
+    ngx_http_upstream_process_non_buffered_request(r, 1);
 }
 
 
 static void
-ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev)
+ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,
+    ngx_http_upstream_t *u)
+{
+    ngx_connection_t  *c;
+
+    c = u->peer.connection;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream process non buffered upstream");
+
+    c->log->action = "reading upstream";
+
+    if (c->read->timedout) {
+        ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+        ngx_http_upstream_finalize_request(r, u, 0);
+        return;
+    }
+
+    ngx_http_upstream_process_non_buffered_request(r, 0);
+}
+
+
+static void
+ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
+    ngx_uint_t do_write)
 {
     size_t                     size;
     ssize_t                    n;
     ngx_buf_t                 *b;
     ngx_int_t                  rc;
-    ngx_uint_t                 do_write;
-    ngx_connection_t          *c, *downstream, *upstream;
-    ngx_http_request_t        *r;
+    ngx_connection_t          *downstream, *upstream;
     ngx_http_upstream_t       *u;
     ngx_http_core_loc_conf_t  *clcf;
 
-    c = ev->data;
-    r = c->data;
     u = r->upstream;
-
-    if (ev->write) {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http upstream process non buffered downstream");
-        c->log->action = "sending to client";
-
-    } else {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http upstream process non buffered upstream");
-        c->log->action = "reading upstream";
-    }
-
-    if (ev->timedout) {
-        if (ev->write) {
-            c->timedout = 1;
-            ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
-
-        } else {
-            ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
-        }
-
-        ngx_http_upstream_finalize_request(r, u, 0);
-        return;
-    }
-
     downstream = r->connection;
     upstream = u->peer.connection;
 
     b = &u->buffer;
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
-    do_write = ev->write || u->length == 0;
+    do_write = do_write || u->length == 0;
 
     for ( ;; ) {
 
@@ -1907,6 +2268,8 @@ ngx_http_upstream_process_non_buffered_b
             }
 
             if (n > 0) {
+                u->state->response_length += n;
+
                 if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
                     ngx_http_upstream_finalize_request(r, u, 0);
                     return;
@@ -1921,9 +2284,11 @@ ngx_http_upstream_process_non_buffered_b
         break;
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
     if (downstream->data == r) {
         if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
-            == NGX_ERROR)
+            != NGX_OK)
         {
             ngx_http_upstream_finalize_request(r, u, 0);
             return;
@@ -1937,7 +2302,7 @@ ngx_http_upstream_process_non_buffered_b
         ngx_del_timer(downstream->write);
     }
 
-    if (ngx_handle_read_event(upstream->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
         ngx_http_upstream_finalize_request(r, u, 0);
         return;
     }
@@ -2003,96 +2368,71 @@ ngx_http_upstream_non_buffered_filter(vo
 static void
 ngx_http_upstream_process_downstream(ngx_http_request_t *r)
 {
-    ngx_http_upstream_process_body(r->connection->write);
-}
-
-
-static void
-ngx_http_upstream_process_body(ngx_event_t *ev)
-{
-    ngx_temp_file_t      *tf;
+    ngx_event_t          *wev;
+    ngx_connection_t     *c;
     ngx_event_pipe_t     *p;
-    ngx_connection_t     *c, *downstream;
-    ngx_http_log_ctx_t   *ctx;
-    ngx_http_request_t   *r;
     ngx_http_upstream_t  *u;
 
-    c = ev->data;
-    r = c->data;
+    c = r->connection;
     u = r->upstream;
-    downstream = r->connection;
-
-    if (ev->write) {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http upstream process downstream");
-        c->log->action = "sending to client";
-
-    } else {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http upstream process upstream");
-        c->log->action = "reading upstream";
-
-        ctx = c->log->data;
-        ctx->current_request = r;
-    }
-
     p = u->pipe;
-
-    if (ev->timedout) {
-        if (ev->write) {
-            if (ev->delayed) {
-
-                ev->timedout = 0;
-                ev->delayed = 0;
-
-                if (!ev->ready) {
-                    ngx_add_timer(ev, p->send_timeout);
-
-                    if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR)
-                    {
-                        ngx_http_upstream_finalize_request(r, u, 0);
-                        return;
-                    }
-
+    wev = c->write;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream process downstream");
+
+    c->log->action = "sending to client";
+
+    if (wev->timedout) {
+
+        if (wev->delayed) {
+
+            wev->timedout = 0;
+            wev->delayed = 0;
+
+            if (!wev->ready) {
+                ngx_add_timer(wev, p->send_timeout);
+
+                if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
+                    ngx_http_upstream_finalize_request(r, u, 0);
+                }
+
+                return;
+            }
+
+            if (ngx_event_pipe(p, wev->write) == NGX_ABORT) {
+
+                if (c->destroyed) {
                     return;
                 }
 
-                if (ngx_event_pipe(p, ev->write) == NGX_ABORT) {
-
-                    if (downstream->destroyed) {
-                        return;
-                    }
-
-                    ngx_http_upstream_finalize_request(r, u, 0);
-                    return;
-                }
-
-            } else {
-                p->downstream_error = 1;
-                c->timedout = 1;
-                ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+                ngx_http_upstream_finalize_request(r, u, 0);
+                return;
             }
 
         } else {
-            p->upstream_error = 1;
-            ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+            p->downstream_error = 1;
+            c->timedout = 1;
+            ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
         }
 
     } else {
-        if (ev->write && ev->delayed) {
+
+        if (wev->delayed) {
+
             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                            "http downstream delayed");
 
-            if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) {
-                return;
+            if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
+                ngx_http_upstream_finalize_request(r, u, 0);
             }
 
             return;
         }
 
-        if (ngx_event_pipe(p, ev->write) == NGX_ABORT) {
-
-            if (downstream->destroyed) {
+        if (ngx_event_pipe(p, 1) == NGX_ABORT) {
+
+            if (c->destroyed) {
                 return;
             }
 
@@ -2101,24 +2441,79 @@ ngx_http_upstream_process_body(ngx_event
         }
     }
 
+    ngx_http_upstream_process_request(r);
+}
+
+
+static void
+ngx_http_upstream_process_upstream(ngx_http_request_t *r,
+    ngx_http_upstream_t *u)
+{
+    ngx_connection_t  *c;
+
+    c = u->peer.connection;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream process upstream");
+
+    c->log->action = "reading upstream";
+
+    if (c->read->timedout) {
+        u->pipe->upstream_error = 1;
+        ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+
+    } else {
+        c = r->connection;
+
+        if (ngx_event_pipe(u->pipe, 0) == NGX_ABORT) {
+
+            if (c->destroyed) {
+                return;
+            }
+
+            ngx_http_upstream_finalize_request(r, u, 0);
+            return;
+        }
+    }
+
+    ngx_http_upstream_process_request(r);
+}
+
+
+static void
+ngx_http_upstream_process_request(ngx_http_request_t *r)
+{
+    ngx_uint_t            del;
+    ngx_temp_file_t      *tf;
+    ngx_event_pipe_t     *p;
+    ngx_http_upstream_t  *u;
+
+    u = r->upstream;
+    p = u->pipe;
+
     if (u->peer.connection) {
 
         if (u->store) {
 
+            del = p->upstream_error;
+
             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))
-                       && tf->file.fd != NGX_INVALID_FILE)
-            {
+            if (p->upstream_eof || p->upstream_done) {
+
+                if (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 {
+                    del = 1;
+                }
+            }
+
+            if (del && tf->file.fd != NGX_INVALID_FILE) {
+
                 if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {
 
                     ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
@@ -2128,30 +2523,28 @@ ngx_http_upstream_process_body(ngx_event
             }
         }
 
-#if (NGX_HTTP_FILE_CACHE)
-
-        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->cacheable) {
-
-            /* TODO: check length & update cache */
-
-            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;
+#if (NGX_HTTP_CACHE)
+
+        if (u->cacheable) {
+
+            if (p->upstream_done) {
+                ngx_http_file_cache_update(r, u->pipe->temp_file);
+
+            } else if (p->upstream_eof) {
+
+                /* TODO: check length & update cache */
+
+                ngx_http_file_cache_update(r, u->pipe->temp_file);
+
+            } else if (p->upstream_error) {
+                ngx_http_file_cache_free(r, u->pipe->temp_file);
             }
         }
 
 #endif
 
         if (p->upstream_done || p->upstream_eof || p->upstream_error) {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "http upstream exit: %p", p->out);
 #if 0
             ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock);
@@ -2162,10 +2555,10 @@ ngx_http_upstream_process_body(ngx_event
     }
 
     if (p->downstream_error) {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http upstream downstream error");
 
-        if (!u->cacheable && u->peer.connection) {
+        if (!u->cacheable && !u->store && u->peer.connection) {
             ngx_http_upstream_finalize_request(r, u, 0);
         }
     }
@@ -2209,9 +2602,11 @@ ngx_http_upstream_store(ngx_http_request
     }
 
     ext.access = u->conf->store_access;
+    ext.path_access = u->conf->store_access;
     ext.time = -1;
     ext.create_path = 1;
     ext.delete_file = 1;
+    ext.log_rename_error = 1;
     ext.log = r->connection->log;
 
     if (u->headers_in.last_modified) {
@@ -2247,9 +2642,9 @@ ngx_http_upstream_store(ngx_http_request
 
 
 static void
-ngx_http_upstream_dummy_handler(ngx_event_t *wev)
+ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http upstream dummy handler");
 }
 
@@ -2323,12 +2718,21 @@ ngx_http_upstream_next(ngx_http_request_
 
 #if (NGX_HTTP_CACHE)
 
-            if (u->stale && (u->conf->use_stale & ft_type)) {
-                ngx_http_upstream_finalize_request(r, u,
-                                             ngx_http_send_cached_response(r));
+            if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
+                && (u->conf->cache_use_stale & ft_type))
+            {
+                ngx_int_t  rc;
+
+                rc = u->reinit_request(r);
+
+                if (rc == NGX_OK) {
+                    u->cache_status = NGX_HTTP_CACHE_STALE;
+                    rc = ngx_http_upstream_cache_send(r, u);
+                }
+
+                ngx_http_upstream_finalize_request(r, u, rc);
                 return;
             }
-
 #endif
 
             ngx_http_upstream_finalize_request(r, u, status);
@@ -2393,12 +2797,18 @@ ngx_http_upstream_finalize_request(ngx_h
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "finalize http upstream request: %i", rc);
 
-    *u->cleanup = NULL;
+    if (u->cleanup) {
+        *u->cleanup = NULL;
+    }
 
     if (u->state && u->state->response_sec) {
         tp = ngx_timeofday();
         u->state->response_sec = tp->sec - u->state->response_sec;
         u->state->response_msec = tp->msec - u->state->response_msec;
+
+        if (u->pipe) {
+            u->state->response_length = u->pipe->read_length;
+        }
     }
 
     u->finalize_request(r, rc);
@@ -2436,25 +2846,42 @@ ngx_http_upstream_finalize_request(ngx_h
 
     u->peer.connection = NULL;
 
-    if (u->header_sent && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE))
-    {
-        rc = 0;
-    }
-
     if (u->pipe && u->pipe->temp_file) {
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http upstream temp fd: %d",
                        u->pipe->temp_file->file.fd);
     }
 
-#if 0
-    if (u->cache) {
+#if (NGX_HTTP_CACHE)
+
+    if (u->cacheable) {
+        time_t  valid;
+
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http upstream cache fd: %d",
-                       u->cache->ctx.file.fd);
+                       r->cache->file.fd);
+
+        if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT) {
+
+            valid = ngx_http_file_cache_valid(u->conf->cache_valid, rc);
+
+            if (valid) {
+                r->cache->valid_sec = ngx_time() + valid;
+                r->cache->error = rc;
+            }
+        }
+
+        ngx_http_file_cache_free(r, u->pipe->temp_file);
     }
+
 #endif
 
+    if (u->header_sent
+        && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE))
+    {
+        rc = 0;
+    }
+
     if (rc == NGX_DECLINED) {
         return;
     }
@@ -2462,16 +2889,7 @@ ngx_http_upstream_finalize_request(ngx_h
     r->connection->log->action = "sending to client";
 
     if (rc == 0) {
-        if (r == r->main) {
-            if (!r->post_action) {
-                rc = ngx_http_send_special(r, NGX_HTTP_LAST);
-            }
-
-        } else {
-            if (r->out) {
-                rc = NGX_AGAIN;
-            }
-        }
+        rc = ngx_http_send_special(r, NGX_HTTP_LAST);
     }
 
     ngx_http_finalize_request(r, rc);
@@ -2495,13 +2913,23 @@ ngx_http_upstream_process_header_line(ng
 
 
 static ngx_int_t
-ngx_http_upstream_process_multi_header_lines(ngx_http_request_t *r,
+ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset)
 {
-    ngx_array_t       *pa;
-    ngx_table_elt_t  **ph;
-
-    pa = (ngx_array_t *) ((char *) &r->upstream->headers_in + offset);
+    ngx_array_t          *pa;
+    ngx_table_elt_t     **ph;
+    ngx_http_upstream_t  *u;
+
+    u = r->upstream;
+    pa = &u->headers_in.cache_control;
 
     if (pa->elts == NULL) {
        if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)
@@ -2517,14 +2945,159 @@ ngx_http_upstream_process_multi_header_l
 
     *ph = h;
 
+#if (NGX_HTTP_CACHE)
+    {
+    u_char     *p, *last;
+    ngx_int_t   n;
+
+    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL) {
+        return NGX_OK;
+    }
+
+    if (r->cache == NULL) {
+        return NGX_OK;
+    }
+
+    if (r->cache->valid_sec != 0) {
+        return NGX_OK;
+    }
+
+    last = h->value.data + h->value.len;
+
+    if (ngx_strlcasestrn(h->value.data, last, (u_char *) "no-cache", 8 - 1)
+        != NULL)
+    {
+        u->cacheable = 0;
+        return NGX_OK;
+    }
+
+    p = ngx_strlcasestrn(h->value.data, last, (u_char *) "max-age=", 8 - 1);
+
+    if (p == NULL) {
+        return NGX_OK;
+    }
+
+    n = 0;
+
+    for (p += 8; p < last; p++) {
+        if (*p == ';' || *p == ' ') {
+            break;
+        }
+
+        if (*p >= '0' && *p <= '9') {
+            n = n * 10 + *p - '0';
+            continue;
+        }
+
+        u->cacheable = 0;
+        return NGX_OK;
+    }
+
+    if (n == 0) {
+        u->cacheable = 0;
+        return NGX_OK;
+    }
+
+    r->cache->valid_sec = ngx_time() + n;
+    }
+#endif
+
     return NGX_OK;
 }
 
 
 static ngx_int_t
-ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
+ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
 {
+    ngx_http_upstream_t  *u;
+
+    u = r->upstream;
+    u->headers_in.expires = h;
+
+#if (NGX_HTTP_CACHE)
+    {
+    time_t  expires;
+
+    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) {
+        return NGX_OK;
+    }
+
+    if (r->cache == NULL) {
+        return NGX_OK;
+    }
+
+    if (r->cache->valid_sec != 0) {
+        return NGX_OK;
+    }
+
+    expires = ngx_http_parse_time(h->value.data, h->value.len);
+
+    if (expires == NGX_ERROR || expires < ngx_time()) {
+        u->cacheable = 0;
+        return NGX_OK;
+    }
+
+    r->cache->valid_sec = expires;
+    }
+#endif
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset)
+{
+    ngx_http_upstream_t  *u;
+
+    u = r->upstream;
+    u->headers_in.x_accel_expires = h;
+
+#if (NGX_HTTP_CACHE)
+    {
+    u_char     *p;
+    size_t      len;
+    ngx_int_t   n;
+
+    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES) {
+        return NGX_OK;
+    }
+
+    if (r->cache == NULL) {
+        return NGX_OK;
+    }
+
+    len = h->value.len;
+    p = h->value.data;
+
+    if (p[0] != '@') {
+        n = ngx_atoi(p, len);
+
+        switch (n) {
+        case 0:
+            u->cacheable = 0;
+        case NGX_ERROR:
+            return NGX_OK;
+
+        default:
+            r->cache->valid_sec = ngx_time() + n;
+            return NGX_OK;
+        }
+    }
+
+    p++;
+    len--;
+
+    n = ngx_atoi(p, len);
+
+    if (n != NGX_ERROR) {
+        r->cache->valid_sec = n;
+    }
+    }
+#endif
+
     return NGX_OK;
 }
 
@@ -2651,6 +3224,7 @@ ngx_http_upstream_copy_content_type(ngx_
 
     r->headers_out.content_type_len = h->value.len;
     r->headers_out.content_type = h->value;
+    r->headers_out.content_type_lowcase = NULL;
 
     for (p = h->value.data; *p; p++) {
 
@@ -2715,6 +3289,33 @@ ngx_http_upstream_copy_content_length(ng
 
 
 static ngx_int_t
+ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    ngx_table_elt_t  *ho;
+
+    ho = ngx_list_push(&r->headers_out.headers);
+    if (ho == NULL) {
+        return NGX_ERROR;
+    }
+
+    *ho = *h;
+
+#if (NGX_HTTP_CACHE)
+
+    if (r->upstream->cacheable) {
+        r->headers_out.last_modified = ho;
+        r->headers_out.last_modified_time = ngx_http_parse_time(h->value.data,
+                                                                h->value.len);
+    }
+
+#endif
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_upstream_rewrite_location(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
 {
@@ -2804,6 +3405,33 @@ ngx_http_upstream_rewrite_refresh(ngx_ht
 }
 
 
+static ngx_int_t
+ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset)
+{
+    ngx_table_elt_t  *ho;
+
+#if (NGX_HTTP_CACHE)
+
+    if (r->cached) {
+        r->allow_ranges = 1;
+        return NGX_OK;
+
+    }
+
+#endif
+
+    ho = ngx_list_push(&r->headers_out.headers);
+    if (ho == NULL) {
+        return NGX_ERROR;
+    }
+
+    *ho = *h;
+
+    return NGX_OK;
+}
+
+
 #if (NGX_HTTP_GZIP)
 
 static ngx_int_t
@@ -3051,6 +3679,66 @@ ngx_http_upstream_response_time_variable
 }
 
 
+static ngx_int_t
+ngx_http_upstream_response_length_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    u_char                     *p;
+    size_t                      len;
+    ngx_uint_t                  i;
+    ngx_http_upstream_state_t  *state;
+
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+
+    if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    len = r->upstream_states->nelts * (NGX_OFF_T_LEN + 2);
+
+    p = ngx_pnalloc(r->pool, len);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    v->data = p;
+
+    i = 0;
+    state = r->upstream_states->elts;
+
+    for ( ;; ) {
+        p = ngx_sprintf(p, "%O", state[i].response_length);
+
+        if (++i == r->upstream_states->nelts) {
+            break;
+        }
+
+        if (state[i].peer) {
+            *p++ = ',';
+            *p++ = ' ';
+
+        } else {
+            *p++ = ' ';
+            *p++ = ':';
+            *p++ = ' ';
+
+            if (++i == r->upstream_states->nelts) {
+                break;
+            }
+
+            continue;
+        }
+    }
+
+    v->len = p - v->data;
+
+    return NGX_OK;
+}
+
+
 ngx_int_t
 ngx_http_upstream_header_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
@@ -3066,6 +3754,33 @@ ngx_http_upstream_header_variable(ngx_ht
 }
 
 
+#if (NGX_HTTP_CACHE)
+
+ngx_int_t
+ngx_http_upstream_cache_status(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_uint_t  n;
+
+    if (r->upstream == NULL || r->upstream->cache_status == 0) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    n = r->upstream->cache_status - 1;
+
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->len = ngx_http_cache_status[n].len;
+    v->data = ngx_http_cache_status[n].data;
+
+    return NGX_OK;
+}
+
+#endif
+
+
 static char *
 ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
 {
@@ -3438,7 +4153,12 @@ ngx_http_upstream_hide_headers_hash(ngx_
     {
         conf->hide_headers_hash = prev->hide_headers_hash;
 
-        if (conf->hide_headers_hash.buckets) {
+        if (conf->hide_headers_hash.buckets
+#if (NGX_HTTP_CACHE)
+            && ((conf->cache == NULL) == (prev->cache == NULL))
+#endif
+           )
+        {
             return NGX_OK;
         }
 
@@ -3544,7 +4264,7 @@ ngx_http_upstream_create_main_conf(ngx_c
                        sizeof(ngx_http_upstream_srv_conf_t *))
         != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     return umcf;
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -24,8 +24,9 @@
 #define NGX_HTTP_UPSTREAM_FT_HTTP_503        0x00000040
 #define NGX_HTTP_UPSTREAM_FT_HTTP_504        0x00000080
 #define NGX_HTTP_UPSTREAM_FT_HTTP_404        0x00000100
-#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK       0x00000200
-#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING     0x00000400
+#define NGX_HTTP_UPSTREAM_FT_UPDATING        0x00000200
+#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK       0x00000400
+#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING     0x00000800
 #define NGX_HTTP_UPSTREAM_FT_NOLIVE          0x40000000
 #define NGX_HTTP_UPSTREAM_FT_OFF             0x80000000
 
@@ -38,21 +39,28 @@
 #define NGX_HTTP_UPSTREAM_INVALID_HEADER     40
 
 
-typedef struct {
-    ngx_msec_t                      bl_time;
-    ngx_uint_t                      bl_state;
+#define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT    0x00000002
+#define NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES     0x00000004
+#define NGX_HTTP_UPSTREAM_IGN_EXPIRES        0x00000008
+#define NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL  0x00000010
+
 
-    ngx_uint_t                      status;
-    time_t                          response_sec;
-    ngx_uint_t                      response_msec;
+typedef struct {
+    ngx_msec_t                       bl_time;
+    ngx_uint_t                       bl_state;
 
-    ngx_str_t                      *peer;
+    ngx_uint_t                       status;
+    time_t                           response_sec;
+    ngx_uint_t                       response_msec;
+    off_t                           response_length;
+
+    ngx_str_t                       *peer;
 } ngx_http_upstream_state_t;
 
 
 typedef struct {
-    ngx_hash_t                      headers_in_hash;
-    ngx_array_t                     upstreams;
+    ngx_hash_t                       headers_in_hash;
+    ngx_array_t                      upstreams;
                                              /* ngx_http_upstream_srv_conf_t */
 } ngx_http_upstream_main_conf_t;
 
@@ -65,21 +73,21 @@ typedef ngx_int_t (*ngx_http_upstream_in
 
 
 typedef struct {
-    ngx_http_upstream_init_pt       init_upstream;
-    ngx_http_upstream_init_peer_pt  init;
-    void                           *data;
+    ngx_http_upstream_init_pt        init_upstream;
+    ngx_http_upstream_init_peer_pt   init;
+    void                            *data;
 } ngx_http_upstream_peer_t;
 
 
 typedef struct {
-    ngx_peer_addr_t                *addrs;
-    ngx_uint_t                      naddrs;
-    ngx_uint_t                      weight;
-    ngx_uint_t                      max_fails;
-    time_t                          fail_timeout;
+    ngx_peer_addr_t                 *addrs;
+    ngx_uint_t                       naddrs;
+    ngx_uint_t                       weight;
+    ngx_uint_t                       max_fails;
+    time_t                           fail_timeout;
 
-    unsigned                        down:1;
-    unsigned                        backup:1;
+    unsigned                         down:1;
+    unsigned                         backup:1;
 } ngx_http_upstream_server_t;
 
 
@@ -92,185 +100,211 @@ typedef struct {
 
 
 struct ngx_http_upstream_srv_conf_s {
-    ngx_http_upstream_peer_t        peer;
-    void                          **srv_conf;
+    ngx_http_upstream_peer_t         peer;
+    void                           **srv_conf;
 
-    ngx_array_t                    *servers;   /* ngx_http_upstream_server_t */
+    ngx_array_t                     *servers;  /* ngx_http_upstream_server_t */
 
-    ngx_uint_t                      flags;
-    ngx_str_t                       host;
-    u_char                         *file_name;
-    ngx_uint_t                      line;
-    in_port_t                       port;
-    in_port_t                       default_port;
+    ngx_uint_t                       flags;
+    ngx_str_t                        host;
+    u_char                          *file_name;
+    ngx_uint_t                       line;
+    in_port_t                        port;
+    in_port_t                        default_port;
 };
 
 
 typedef struct {
-    ngx_http_upstream_srv_conf_t   *upstream;
+    ngx_http_upstream_srv_conf_t    *upstream;
 
-    ngx_msec_t                      connect_timeout;
-    ngx_msec_t                      send_timeout;
-    ngx_msec_t                      read_timeout;
-    ngx_msec_t                      timeout;
+    ngx_msec_t                       connect_timeout;
+    ngx_msec_t                       send_timeout;
+    ngx_msec_t                       read_timeout;
+    ngx_msec_t                       timeout;
 
-    size_t                          send_lowat;
-    size_t                          buffer_size;
+    size_t                           send_lowat;
+    size_t                           buffer_size;
 
-    size_t                          busy_buffers_size;
-    size_t                          max_temp_file_size;
-    size_t                          temp_file_write_size;
+    size_t                           busy_buffers_size;
+    size_t                           max_temp_file_size;
+    size_t                           temp_file_write_size;
+
+    size_t                           busy_buffers_size_conf;
+    size_t                           max_temp_file_size_conf;
+    size_t                           temp_file_write_size_conf;
 
-    size_t                          busy_buffers_size_conf;
-    size_t                          max_temp_file_size_conf;
-    size_t                          temp_file_write_size_conf;
+    ngx_bufs_t                       bufs;
 
-    ngx_uint_t                      next_upstream;
-    ngx_uint_t                      store_access;
-
-    ngx_bufs_t                      bufs;
+    ngx_uint_t                       ignore_headers;
+    ngx_uint_t                       next_upstream;
+    ngx_uint_t                       store_access;
+    ngx_flag_t                       buffering;
+    ngx_flag_t                       pass_request_headers;
+    ngx_flag_t                       pass_request_body;
 
-    ngx_flag_t                      buffering;
-    ngx_flag_t                      pass_request_headers;
-    ngx_flag_t                      pass_request_body;
+    ngx_flag_t                       ignore_client_abort;
+    ngx_flag_t                       intercept_errors;
+    ngx_flag_t                       cyclic_temp_file;
+
+    ngx_path_t                      *temp_path;
 
-    ngx_flag_t                      ignore_client_abort;
-    ngx_flag_t                      intercept_errors;
-    ngx_flag_t                      cyclic_temp_file;
+    ngx_hash_t                       hide_headers_hash;
+    ngx_array_t                     *hide_headers;
+    ngx_array_t                     *pass_headers;
 
-    ngx_path_t                     *temp_path;
+#if (NGX_HTTP_CACHE)
+    ngx_shm_zone_t                  *cache;
 
-    ngx_hash_t                      hide_headers_hash;
-    ngx_array_t                    *hide_headers;
-    ngx_array_t                    *pass_headers;
+    ngx_uint_t                       cache_min_uses;
+    ngx_uint_t                       cache_use_stale;
+    ngx_uint_t                       cache_methods;
 
-    ngx_str_t                       schema;
+    ngx_array_t                     *cache_valid;
+#endif
 
-    ngx_array_t                    *store_lengths;
-    ngx_array_t                    *store_values;
+    ngx_array_t                     *store_lengths;
+    ngx_array_t                     *store_values;
 
-    signed                          store:2;
-    unsigned                        intercept_404:1;
-    unsigned                        change_buffering:1;
+    signed                           store:2;
+    unsigned                         intercept_404:1;
+    unsigned                         change_buffering:1;
 
 #if (NGX_HTTP_SSL)
-    ngx_ssl_t                      *ssl;
-    ngx_flag_t                      ssl_session_reuse;
+    ngx_ssl_t                       *ssl;
+    ngx_flag_t                       ssl_session_reuse;
 #endif
 
 } ngx_http_upstream_conf_t;
 
 
 typedef struct {
-    ngx_str_t                       name;
-    ngx_http_header_handler_pt      handler;
-    ngx_uint_t                      offset;
-    ngx_http_header_handler_pt      copy_handler;
-    ngx_uint_t                      conf;
-    ngx_uint_t                      redirect;  /* unsigned   redirect:1; */
+    ngx_str_t                        name;
+    ngx_http_header_handler_pt       handler;
+    ngx_uint_t                       offset;
+    ngx_http_header_handler_pt       copy_handler;
+    ngx_uint_t                       conf;
+    ngx_uint_t                       redirect;  /* unsigned   redirect:1; */
 } ngx_http_upstream_header_t;
 
 
 typedef struct {
-    ngx_list_t                      headers;
+    ngx_list_t                       headers;
 
-    ngx_uint_t                      status_n;
-    ngx_str_t                       status_line;
+    ngx_uint_t                       status_n;
+    ngx_str_t                        status_line;
 
-    ngx_table_elt_t                *status;
-    ngx_table_elt_t                *date;
-    ngx_table_elt_t                *server;
-    ngx_table_elt_t                *connection;
+    ngx_table_elt_t                 *status;
+    ngx_table_elt_t                 *date;
+    ngx_table_elt_t                 *server;
+    ngx_table_elt_t                 *connection;
 
-    ngx_table_elt_t                *expires;
-    ngx_table_elt_t                *etag;
-    ngx_table_elt_t                *x_accel_expires;
-    ngx_table_elt_t                *x_accel_redirect;
-    ngx_table_elt_t                *x_accel_limit_rate;
+    ngx_table_elt_t                 *expires;
+    ngx_table_elt_t                 *etag;
+    ngx_table_elt_t                 *x_accel_expires;
+    ngx_table_elt_t                 *x_accel_redirect;
+    ngx_table_elt_t                 *x_accel_limit_rate;
 
-    ngx_table_elt_t                *content_type;
-    ngx_table_elt_t                *content_length;
+    ngx_table_elt_t                 *content_type;
+    ngx_table_elt_t                 *content_length;
 
-    ngx_table_elt_t                *last_modified;
-    ngx_table_elt_t                *location;
-    ngx_table_elt_t                *accept_ranges;
-    ngx_table_elt_t                *www_authenticate;
+    ngx_table_elt_t                 *last_modified;
+    ngx_table_elt_t                 *location;
+    ngx_table_elt_t                 *accept_ranges;
+    ngx_table_elt_t                 *www_authenticate;
 
 #if (NGX_HTTP_GZIP)
-    ngx_table_elt_t                *content_encoding;
+    ngx_table_elt_t                 *content_encoding;
 #endif
 
-    off_t                           content_length_n;
+    off_t                            content_length_n;
 
-    ngx_array_t                     cache_control;
+    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_str_t                        host;
+    in_port_t                        port;
+    ngx_uint_t                       no_port; /* unsigned no_port:1 */
+
+    ngx_uint_t                       naddrs;
+    in_addr_t                       *addrs;
+
+    struct sockaddr                 *sockaddr;
+    socklen_t                        socklen;
+
+    ngx_resolver_ctx_t              *ctx;
 } ngx_http_upstream_resolved_t;
 
 
+typedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+
+
 struct ngx_http_upstream_s {
-    ngx_peer_connection_t           peer;
+    ngx_http_upstream_handler_pt     read_event_handler;
+    ngx_http_upstream_handler_pt     write_event_handler;
 
-    ngx_event_pipe_t               *pipe;
+    ngx_peer_connection_t            peer;
 
-    ngx_chain_t                    *request_bufs;
+    ngx_event_pipe_t                *pipe;
 
-    ngx_output_chain_ctx_t          output;
-    ngx_chain_writer_ctx_t          writer;
+    ngx_chain_t                     *request_bufs;
 
-    ngx_http_upstream_conf_t       *conf;
+    ngx_output_chain_ctx_t           output;
+    ngx_chain_writer_ctx_t           writer;
 
-    ngx_http_upstream_headers_in_t  headers_in;
+    ngx_http_upstream_conf_t        *conf;
 
-    ngx_http_upstream_resolved_t   *resolved;
+    ngx_http_upstream_headers_in_t   headers_in;
 
-    ngx_buf_t                       buffer;
-    size_t                          length;
+    ngx_http_upstream_resolved_t    *resolved;
+
+    ngx_buf_t                        buffer;
+    size_t                           length;
 
-    ngx_chain_t                    *out_bufs;
-    ngx_chain_t                    *busy_bufs;
-    ngx_chain_t                    *free_bufs;
+    ngx_chain_t                     *out_bufs;
+    ngx_chain_t                     *busy_bufs;
+    ngx_chain_t                     *free_bufs;
 
-    ngx_int_t                     (*input_filter_init)(void *data);
-    ngx_int_t                     (*input_filter)(void *data, ssize_t bytes);
-    void                           *input_filter_ctx;
+    ngx_int_t                      (*input_filter_init)(void *data);
+    ngx_int_t                      (*input_filter)(void *data, ssize_t bytes);
+    void                            *input_filter_ctx;
 
-    ngx_int_t                     (*create_request)(ngx_http_request_t *r);
-    ngx_int_t                     (*reinit_request)(ngx_http_request_t *r);
-    ngx_int_t                     (*process_header)(ngx_http_request_t *r);
-    void                          (*abort_request)(ngx_http_request_t *r);
-    void                          (*finalize_request)(ngx_http_request_t *r,
-                                        ngx_int_t rc);
-    ngx_int_t                     (*rewrite_redirect)(ngx_http_request_t *r,
-                                        ngx_table_elt_t *h, size_t prefix);
+#if (NGX_HTTP_CACHE)
+    ngx_int_t                      (*create_key)(ngx_http_request_t *r);
+#endif
+    ngx_int_t                      (*create_request)(ngx_http_request_t *r);
+    ngx_int_t                      (*reinit_request)(ngx_http_request_t *r);
+    ngx_int_t                      (*process_header)(ngx_http_request_t *r);
+    void                           (*abort_request)(ngx_http_request_t *r);
+    void                           (*finalize_request)(ngx_http_request_t *r,
+                                         ngx_int_t rc);
+    ngx_int_t                      (*rewrite_redirect)(ngx_http_request_t *r,
+                                         ngx_table_elt_t *h, size_t prefix);
 
-    ngx_msec_t                      timeout;
+    ngx_msec_t                       timeout;
 
-    ngx_http_upstream_state_t      *state;
+    ngx_http_upstream_state_t       *state;
 
-    ngx_str_t                       method;
-    ngx_str_t                       schema;
-    ngx_str_t                       uri;
+    ngx_str_t                        method;
+    ngx_str_t                        schema;
+    ngx_str_t                        uri;
 
-    ngx_http_cleanup_pt            *cleanup;
+    ngx_http_cleanup_pt             *cleanup;
 
-    unsigned                        store:1;
-    unsigned                        cacheable:1;
-    unsigned                        accel:1;
-    unsigned                        ssl:1;
+    unsigned                         store:1;
+    unsigned                         cacheable:1;
+    unsigned                         accel:1;
+    unsigned                         ssl:1;
+#if (NGX_HTTP_CACHE)
+    unsigned                         cache_status:3;
+#endif
 
-    unsigned                        buffering:1;
+    unsigned                         buffering:1;
 
-    unsigned                        request_sent:1;
-    unsigned                        header_sent:1;
+    unsigned                         request_sent:1;
+    unsigned                         header_sent:1;
 };
 
 
@@ -295,7 +329,9 @@ ngx_int_t ngx_http_upstream_hide_headers
     uscf->srv_conf[module.ctx_index]
 
 
-extern ngx_module_t  ngx_http_upstream_module;
+extern ngx_module_t        ngx_http_upstream_module;
+extern ngx_conf_bitmask_t  ngx_http_upstream_cache_method_mask[];
+
 
 
 #endif /* _NGX_HTTP_UPSTREAM_H_INCLUDED_ */
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -279,35 +279,47 @@ ngx_http_upstream_create_round_robin_pee
     peers->number = ur->naddrs;
     peers->name = &ur->host;
 
-    for (i = 0; i < ur->naddrs; i++) {
-
-        len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1;
+    if (ur->sockaddr) {
+        peers->peer[0].sockaddr = ur->sockaddr;
+        peers->peer[0].socklen = ur->socklen;
+        peers->peer[0].name = ur->host;
+        peers->peer[0].weight = 1;
+        peers->peer[0].current_weight = 1;
+        peers->peer[0].max_fails = 1;
+        peers->peer[0].fail_timeout = 10;
 
-        p = ngx_pnalloc(r->pool, len);
-        if (p == NULL) {
-            return NGX_ERROR;
-        }
+    } else {
+
+        for (i = 0; i < ur->naddrs; i++) {
 
-        len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN);
-        len = ngx_sprintf(&p[len], ":%d", ur->port) - p;
+            len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1;
+
+            p = ngx_pnalloc(r->pool, len);
+            if (p == NULL) {
+                return NGX_ERROR;
+            }
 
-        sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in));
-        if (sin == NULL) {
-            return NGX_ERROR;
-        }
+            len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN);
+            len = ngx_sprintf(&p[len], ":%d", ur->port) - p;
 
-        sin->sin_family = AF_INET;
-        sin->sin_port = htons(ur->port);
-        sin->sin_addr.s_addr = ur->addrs[i];
+            sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in));
+            if (sin == NULL) {
+                return NGX_ERROR;
+            }
 
-        peers->peer[i].sockaddr = (struct sockaddr *) sin;
-        peers->peer[i].socklen = sizeof(struct sockaddr_in);
-        peers->peer[i].name.len = len;
-        peers->peer[i].name.data = p;
-        peers->peer[i].weight = 1;
-        peers->peer[i].current_weight = 1;
-        peers->peer[i].max_fails = 1;
-        peers->peer[i].fail_timeout = 10;
+            sin->sin_family = AF_INET;
+            sin->sin_port = htons(ur->port);
+            sin->sin_addr.s_addr = ur->addrs[i];
+
+            peers->peer[i].sockaddr = (struct sockaddr *) sin;
+            peers->peer[i].socklen = sizeof(struct sockaddr_in);
+            peers->peer[i].name.len = len;
+            peers->peer[i].name.data = p;
+            peers->peer[i].weight = 1;
+            peers->peer[i].current_weight = 1;
+            peers->peer[i].max_fails = 1;
+            peers->peer[i].fail_timeout = 10;
+        }
     }
 
     rrp->peers = peers;
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -25,6 +25,8 @@ static ngx_int_t ngx_http_variable_unkno
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 
@@ -60,6 +62,8 @@ static ngx_int_t ngx_http_variable_body_
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_body(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_request_body_file(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 
@@ -67,6 +71,8 @@ static ngx_int_t ngx_http_variable_sent_
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_sent_content_length(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_location(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_sent_last_modified(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_sent_connection(ngx_http_request_t *r,
@@ -198,6 +204,10 @@ static ngx_http_variable_t  ngx_http_cor
       ngx_http_variable_request_completion,
       0, 0, 0 },
 
+    { ngx_string("request_body"), NULL,
+      ngx_http_variable_request_body,
+      0, 0, 0 },
+
     { ngx_string("request_body_file"), NULL,
       ngx_http_variable_request_body_file,
       0, 0, 0 },
@@ -208,6 +218,9 @@ static ngx_http_variable_t  ngx_http_cor
     { ngx_string("sent_http_content_length"), NULL,
       ngx_http_variable_sent_content_length, 0, 0, 0 },
 
+    { ngx_string("sent_http_location"), NULL,
+      ngx_http_variable_sent_location, 0, 0, 0 },
+
     { ngx_string("sent_http_last_modified"), NULL,
       ngx_http_variable_sent_last_modified, 0, 0, 0 },
 
@@ -325,7 +338,8 @@ ngx_http_get_variable_index(ngx_conf_t *
 
     if (v == NULL) {
         if (ngx_array_init(&cmcf->variables, cf->pool, 4,
-                           sizeof(ngx_http_variable_t)) == NGX_ERROR)
+                           sizeof(ngx_http_variable_t))
+            != NGX_OK)
         {
             return NGX_ERROR;
         }
@@ -488,6 +502,15 @@ ngx_http_get_variable(ngx_http_request_t
         return NULL;
     }
 
+    if (ngx_strncmp(name->data, "cookie_", 7) == 0) {
+
+        if (ngx_http_variable_cookie(r, vv, (uintptr_t) name) == NGX_OK) {
+            return vv;
+        }
+
+        return NULL;
+    }
+
     if (ngx_strncmp(name->data, "arg_", 4) == 0) {
 
         if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) {
@@ -728,56 +751,56 @@ ngx_http_variable_unknown_header(ngx_htt
 
 
 static ngx_int_t
+ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    ngx_str_t *name = (ngx_str_t *) data;
+
+    ngx_str_t  cookie, s;
+
+    s.len = name->len - (sizeof("cookie_") - 1);
+    s.data = name->data + sizeof("cookie_") - 1;
+
+    if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &s, &cookie)
+        == NGX_DECLINED)
+    {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->len = cookie.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = cookie.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v,
     uintptr_t data)
 {
     ngx_str_t *name = (ngx_str_t *) data;
 
-    u_char  *p, *arg;
-    size_t   len;
+    u_char     *arg;
+    size_t      len;
+    ngx_str_t   value;
 
-    if (r->args.len == 0) {
+    len = name->len - (sizeof("arg_") - 1);
+    arg = name->data + sizeof("arg_") - 1;
+
+    if (ngx_http_arg(r, arg, len, &value) != NGX_OK) {
         v->not_found = 1;
         return NGX_OK;
     }
 
-    len = name->len - 1 - (sizeof("arg_") - 1);
-    arg = name->data + sizeof("arg_") - 1;
-
-    for (p = r->args.data; *p && *p != ' '; p++) {
-
-        /*
-         * although r->args.data is not null-terminated by itself,
-         * however, there is null in the end of request line
-         */
-
-        p = ngx_strcasestrn(p, (char *) arg, len);
-
-        if (p == NULL) {
-            v->not_found = 1;
-            return NGX_OK;
-        }
-
-        if ((p == r->args.data || *(p - 1) == '&') && *(p + len + 1) == '=') {
-
-            v->data = p + len + 2;
-
-            p = (u_char *) ngx_strchr(p, '&');
-
-            if (p == NULL) {
-                p = r->args.data + r->args.len;
-            }
-
-            v->len = p - v->data;
-            v->valid = 1;
-            v->no_cacheable = 0;
-            v->not_found = 0;
-
-            return NGX_OK;
-        }
-    }
-
-    v->not_found = 1;
+    v->data = value.data;
+    v->len = value.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
 
     return NGX_OK;
 }
@@ -812,17 +835,37 @@ static ngx_int_t
 ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
-    struct sockaddr_in  *sin;
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6  *sin6;
+#endif
 
-    /* AF_INET only */
+    switch (r->connection->sockaddr->sa_family) {
 
-    sin = (struct sockaddr_in *) r->connection->sockaddr;
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
 
-    v->len = sizeof(in_addr_t);
-    v->valid = 1;
-    v->no_cacheable = 0;
-    v->not_found = 0;
-    v->data = (u_char *) &sin->sin_addr.s_addr;
+        v->len = sizeof(struct in6_addr);
+        v->valid = 1;
+        v->no_cacheable = 0;
+        v->not_found = 0;
+        v->data = (u_char *) &sin6->sin6_addr;
+
+        break;
+#endif
+
+    default: /* AF_INET */
+        sin = (struct sockaddr_in *) r->connection->sockaddr;
+
+        v->len = sizeof(in_addr_t);
+        v->valid = 1;
+        v->no_cacheable = 0;
+        v->not_found = 0;
+        v->data = (u_char *) &sin->sin_addr;
+
+        break;
+    }
 
     return NGX_OK;
 }
@@ -846,8 +889,11 @@ static ngx_int_t
 ngx_http_variable_remote_port(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
-    ngx_uint_t           port;
-    struct sockaddr_in  *sin;
+    ngx_uint_t            port;
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6  *sin6;
+#endif
 
     v->len = 0;
     v->valid = 1;
@@ -859,16 +905,23 @@ ngx_http_variable_remote_port(ngx_http_r
         return NGX_ERROR;
     }
 
-    /* AF_INET only */
+    switch (r->connection->sockaddr->sa_family) {
 
-    if (r->connection->sockaddr->sa_family == AF_INET) {
-        sin = (struct sockaddr_in *) r->connection->sockaddr;
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+        port = ntohs(sin6->sin6_port);
+        break;
+#endif
 
+    default: /* AF_INET */
+        sin = (struct sockaddr_in *) r->connection->sockaddr;
         port = ntohs(sin->sin_port);
+        break;
+    }
 
-        if (port > 0 && port < 65536) {
-            v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
-        }
+    if (port > 0 && port < 65536) {
+        v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
     }
 
     return NGX_OK;
@@ -880,15 +933,21 @@ ngx_http_variable_server_addr(ngx_http_r
     ngx_http_variable_value_t *v, uintptr_t data)
 {
     ngx_str_t  s;
+    u_char     addr[NGX_SOCKADDR_STRLEN];
 
-    s.data = ngx_pnalloc(r->pool, NGX_INET_ADDRSTRLEN);
+    s.len = NGX_SOCKADDR_STRLEN;
+    s.data = addr;
+
+    if (ngx_connection_local_sockaddr(r->connection, &s, 0) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    s.data = ngx_pnalloc(r->pool, s.len);
     if (s.data == NULL) {
         return NGX_ERROR;
     }
 
-    if (ngx_http_server_addr(r, &s) != NGX_OK) {
-        return NGX_ERROR;
-    }
+    ngx_memcpy(s.data, addr, s.len);
 
     v->len = s.len;
     v->valid = 1;
@@ -904,11 +963,44 @@ static ngx_int_t
 ngx_http_variable_server_port(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
-    v->len = r->port_text->len - 1;
+    ngx_uint_t            port;
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6  *sin6;
+#endif
+
+    v->len = 0;
     v->valid = 1;
     v->no_cacheable = 0;
     v->not_found = 0;
-    v->data = r->port_text->data + 1;
+
+    if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
+    if (v->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    switch (r->connection->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr;
+        port = ntohs(sin6->sin6_port);
+        break;
+#endif
+
+    default: /* AF_INET */
+        sin = (struct sockaddr_in *) r->connection->local_sockaddr;
+        port = ntohs(sin->sin_port);
+        break;
+    }
+
+    if (port > 0 && port < 65536) {
+        v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
+    }
 
     return NGX_OK;
 }
@@ -987,9 +1079,7 @@ ngx_http_variable_document_root(ngx_http
             return NGX_ERROR;
         }
 
-        if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0)
-            == NGX_ERROR)
-        {
+        if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -1028,9 +1118,7 @@ ngx_http_variable_realpath_root(ngx_http
 
         path.data[path.len - 1] = '\0';
 
-        if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0)
-            == NGX_ERROR)
-        {
+        if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0) != NGX_OK) {
             return NGX_ERROR;
         }
     }
@@ -1231,6 +1319,31 @@ ngx_http_variable_sent_content_length(ng
 
 
 static ngx_int_t
+ngx_http_variable_sent_location(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_str_t  name;
+
+    if (r->headers_out.location) {
+        v->len = r->headers_out.location->value.len;
+        v->valid = 1;
+        v->no_cacheable = 0;
+        v->not_found = 0;
+        v->data = r->headers_out.location->value.data;
+
+        return NGX_OK;
+    }
+
+    name.len = sizeof("sent_http_location") - 1;
+    name.data = (u_char *) "sent_http_location";
+
+    return ngx_http_variable_unknown_header(v, &name,
+                                            &r->headers_out.headers.part,
+                                            sizeof("sent_http_") - 1);
+}
+
+
+static ngx_int_t
 ngx_http_variable_sent_last_modified(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
@@ -1371,6 +1484,59 @@ ngx_http_variable_request_completion(ngx
 
 
 static ngx_int_t
+ngx_http_variable_request_body(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    u_char       *p;
+    size_t        len;
+    ngx_buf_t    *buf, *next;
+    ngx_chain_t  *cl;
+
+    if (r->request_body == NULL
+        || r->request_body->bufs == NULL
+        || r->request_body->temp_file)
+    {
+        v->not_found = 1;
+
+        return NGX_OK;
+    }
+
+    cl = r->request_body->bufs;
+    buf = cl->buf;
+
+    if (cl->next == NULL) {
+        v->len = buf->last - buf->pos;
+        v->valid = 1;
+        v->no_cacheable = 0;
+        v->not_found = 0;
+        v->data = buf->pos;
+
+        return NGX_OK;
+    }
+
+    next = cl->next->buf;
+    len = (buf->last - buf->pos) + (next->last - next->pos);
+
+    p = ngx_pnalloc(r->pool, len);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    v->data = p;
+
+    p = ngx_cpymem(p, buf->pos, buf->last - buf->pos);
+    ngx_memcpy(p, next->pos, next->last - next->pos);
+
+    v->len = len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_request_body_file(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
@@ -1544,6 +1710,13 @@ ngx_http_variables_init_vars(ngx_conf_t 
             continue;
         }
 
+        if (ngx_strncmp(v[i].name.data, "cookie_", 7) == 0) {
+            v[i].get_handler = ngx_http_variable_cookie;
+            v[i].data = (uintptr_t) &v[i].name;
+
+            continue;
+        }
+
         if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) {
             v[i].get_handler = ngx_http_variable_argument;
             v[i].data = (uintptr_t) &v[i].name;
@@ -1589,3 +1762,87 @@ ngx_http_variables_init_vars(ngx_conf_t 
 
     return NGX_OK;
 }
+
+
+void
+ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_rbtree_node_t               **p;
+    ngx_http_variable_value_node_t   *vvn, *vvt;
+
+    for ( ;; ) {
+
+        vvn = (ngx_http_variable_value_node_t *) node;
+        vvt = (ngx_http_variable_value_node_t *) temp;
+
+        if (node->key != temp->key) {
+
+            p = (node->key < temp->key) ? &temp->left : &temp->right;
+
+        } else if (vvn->len != vvt->len) {
+
+            p = (vvn->len < vvt->len) ? &temp->left : &temp->right;
+
+        } else {
+            p = (ngx_memcmp(vvn->value->data, vvt->value->data, vvn->len) < 0)
+                 ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}
+
+
+ngx_http_variable_value_t *
+ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val,
+    uint32_t hash)
+{
+    ngx_int_t                        rc;
+    ngx_rbtree_node_t               *node, *sentinel;
+    ngx_http_variable_value_node_t  *vvn;
+
+    node = rbtree->root;
+    sentinel = rbtree->sentinel;
+
+    while (node != sentinel) {
+
+        vvn = (ngx_http_variable_value_node_t *) node;
+
+        if (hash != node->key) {
+            node = (hash < node->key) ? node->left : node->right;
+            continue;
+        }
+
+        if (val->len != vvn->len) {
+            node = (val->len < vvn->len) ? node->left : node->right;
+            continue;
+        }
+
+        rc = ngx_memcmp(val->data, vvn->value->data, val->len);
+
+        if (rc < 0) {
+            node = node->left;
+            continue;
+        }
+
+        if (rc > 0) {
+            node = node->right;
+            continue;
+        }
+
+        return vvn->value;
+    }
+
+    return NULL;
+}
--- a/src/http/ngx_http_variables.h
+++ b/src/http/ngx_http_variables.h
@@ -63,6 +63,19 @@ ngx_int_t ngx_http_variables_add_core_va
 ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf);
 
 
+typedef struct {
+    ngx_rbtree_node_t             node;
+    size_t                        len;
+    ngx_http_variable_value_t    *value;
+} ngx_http_variable_value_node_t;
+
+
+void ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+ngx_http_variable_value_t *ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree,
+    ngx_str_t *name, uint32_t hash);
+
+
 extern ngx_http_variable_value_t  ngx_http_variable_null_value;
 extern ngx_http_variable_value_t  ngx_http_variable_true_value;
 
--- a/src/http/ngx_http_write_filter_module.c
+++ b/src/http/ngx_http_write_filter_module.c
@@ -46,7 +46,7 @@ ngx_module_t  ngx_http_write_filter_modu
 ngx_int_t
 ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    off_t                      size, sent, limit;
+    off_t                      size, sent, nsent, limit;
     ngx_uint_t                 last, flush;
     ngx_msec_t                 delay;
     ngx_chain_t               *cl, *ln, **ll, *chain;
@@ -210,7 +210,8 @@ ngx_http_write_filter(ngx_http_request_t
     }
 
     if (r->limit_rate) {
-        limit = r->limit_rate * (ngx_time() - r->start_sec + 1) - c->sent;
+        limit = r->limit_rate * (ngx_time() - r->start_sec + 1)
+                - (c->sent - clcf->limit_rate_after);
 
         if (limit <= 0) {
             c->write->delayed = 1;
@@ -245,7 +246,23 @@ ngx_http_write_filter(ngx_http_request_t
     }
 
     if (r->limit_rate) {
-        delay = (ngx_msec_t) ((c->sent - sent) * 1000 / r->limit_rate + 1);
+
+        nsent = c->sent;
+
+        if (clcf->limit_rate_after) {
+
+            sent -= clcf->limit_rate_after;
+            if (sent < 0) {
+                sent = 0;
+            }
+
+            nsent -= clcf->limit_rate_after;
+            if (nsent < 0) {
+                nsent = 0;
+            }
+        }
+
+        delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate + 1);
 
         if (delay > 0) {
             c->write->delayed = 1;
--- a/src/mail/ngx_mail.c
+++ b/src/mail/ngx_mail.c
@@ -11,7 +11,16 @@
 
 
 static char *ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
-static ngx_int_t ngx_mail_cmp_conf_in_addrs(const void *one, const void *two);
+static ngx_int_t ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
+    ngx_mail_listen_t *listen);
+static char *ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);
+static ngx_int_t ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport,
+    ngx_mail_conf_addr_t *addr);
+#if (NGX_HAVE_INET6)
+static ngx_int_t ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,
+    ngx_mail_conf_addr_t *addr);
+#endif
+static ngx_int_t ngx_mail_cmp_conf_addrs(const void *one, const void *two);
 
 
 ngx_uint_t  ngx_mail_max_module;
@@ -64,18 +73,12 @@ static char *
 ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     char                        *rv;
-    u_char                      *text;
-    size_t                       len;
-    ngx_uint_t                   i, a, l, m, mi, s, p, last, bind_all, done;
+    ngx_uint_t                   i, m, mi, s;
     ngx_conf_t                   pcf;
-    ngx_array_t                  in_ports;
-    ngx_listening_t             *ls;
-    ngx_mail_listen_t           *imls;
+    ngx_array_t                  ports;
+    ngx_mail_listen_t           *listen;
     ngx_mail_module_t           *module;
-    ngx_mail_in_port_t          *imip;
     ngx_mail_conf_ctx_t         *ctx;
-    ngx_mail_conf_in_port_t     *in_port;
-    ngx_mail_conf_in_addr_t     *in_addr;
     ngx_mail_core_srv_conf_t   **cscfp;
     ngx_mail_core_main_conf_t   *cmcf;
 
@@ -216,178 +219,196 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma
     *cf = pcf;
 
 
-    if (ngx_array_init(&in_ports, cf->temp_pool, 4,
-                       sizeof(ngx_mail_conf_in_port_t))
+    if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_mail_conf_port_t))
         != NGX_OK)
     {
         return NGX_CONF_ERROR;
     }
 
-    imls = cmcf->listen.elts;
-
-    for (l = 0; l < cmcf->listen.nelts; l++) {
-
-        /* AF_INET only */
+    listen = cmcf->listen.elts;
 
-        in_port = in_ports.elts;
-        for (p = 0; p < in_ports.nelts; p++) {
-            if (in_port[p].port == imls[l].port) {
-                in_port = &in_port[p];
-                goto found;
-            }
-        }
-
-        in_port = ngx_array_push(&in_ports);
-        if (in_port == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        in_port->port = imls[l].port;
-
-        if (ngx_array_init(&in_port->addrs, cf->temp_pool, 2,
-                           sizeof(ngx_mail_conf_in_addr_t))
-            != NGX_OK)
-        {
+    for (i = 0; i < cmcf->listen.nelts; i++) {
+        if (ngx_mail_add_ports(cf, &ports, &listen[i]) != NGX_OK) {
             return NGX_CONF_ERROR;
         }
+    }
 
-    found:
+    return ngx_mail_optimize_servers(cf, &ports);
+}
+
+
+static ngx_int_t
+ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
+    ngx_mail_listen_t *listen)
+{
+    in_port_t              p;
+    ngx_uint_t             i;
+    struct sockaddr       *sa;
+    struct sockaddr_in    *sin;
+    ngx_mail_conf_port_t  *port;
+    ngx_mail_conf_addr_t  *addr;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6   *sin6;
+#endif
+
+    sa = (struct sockaddr *) &listen->sockaddr;
 
-        in_addr = ngx_array_push(&in_port->addrs);
-        if (in_addr == NULL) {
-            return NGX_CONF_ERROR;
+    switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        sin6 = (struct sockaddr_in6 *) sa;
+        p = sin6->sin6_port;
+        break;
+#endif
+
+    default: /* AF_INET */
+        sin = (struct sockaddr_in *) sa;
+        p = sin->sin_port;
+        break;
+    }
+
+    port = ports->elts;
+    for (i = 0; i < ports->nelts; i++) {
+        if (p == port[i].port && sa->sa_family == port[i].family) {
+
+            /* a port is already in the port list */
+
+            port = &port[i];
+            goto found;
         }
-
-        in_addr->addr = imls[l].addr;
-        in_addr->ctx = imls[l].ctx;
-        in_addr->bind = imls[l].bind;
-#if (NGX_MAIL_SSL)
-        in_addr->ssl = imls[l].ssl;
-#endif
     }
 
-    /* optimize the lists of ports and addresses */
+    /* add a port to the port list */
+
+    port = ngx_array_push(ports);
+    if (port == NULL) {
+        return NGX_ERROR;
+    }
+
+    port->family = sa->sa_family;
+    port->port = p;
 
-    /* AF_INET only */
+    if (ngx_array_init(&port->addrs, cf->temp_pool, 2,
+                       sizeof(ngx_mail_conf_addr_t))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+found:
+
+    addr = ngx_array_push(&port->addrs);
+    if (addr == NULL) {
+        return NGX_ERROR;
+    }
 
-    in_port = in_ports.elts;
-    for (p = 0; p < in_ports.nelts; p++) {
+    addr->sockaddr = (struct sockaddr *) &listen->sockaddr;
+    addr->socklen = listen->socklen;
+    addr->ctx = listen->ctx;
+    addr->bind = listen->bind;
+    addr->wildcard = listen->wildcard;
+#if (NGX_MAIL_SSL)
+    addr->ssl = listen->ssl;
+#endif
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+    addr->ipv6only = listen->ipv6only;
+#endif
+
+    return NGX_OK;
+}
+
 
-        ngx_sort(in_port[p].addrs.elts, (size_t) in_port[p].addrs.nelts,
-                 sizeof(ngx_mail_conf_in_addr_t), ngx_mail_cmp_conf_in_addrs);
+static char *
+ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
+{
+    ngx_uint_t             i, p, last, bind_wildcard;
+    ngx_listening_t       *ls;
+    ngx_mail_port_t       *mport;
+    ngx_mail_conf_port_t  *port;
+    ngx_mail_conf_addr_t  *addr;
 
-        in_addr = in_port[p].addrs.elts;
-        last = in_port[p].addrs.nelts;
+    port = ports->elts;
+    for (p = 0; p < ports->nelts; p++) {
+
+        ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
+                 sizeof(ngx_mail_conf_addr_t), ngx_mail_cmp_conf_addrs);
+
+        addr = port[p].addrs.elts;
+        last = 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;
+        if (addr[last - 1].wildcard) {
+            addr[last - 1].bind = 1;
+            bind_wildcard = 1;
 
         } else {
-            bind_all = 1;
+            bind_wildcard = 0;
         }
 
-        for (a = 0; a < last; /* void */ ) {
+        i = 0;
 
-            if (!bind_all && !in_addr[a].bind) {
-                a++;
+        while (i < last) {
+
+            if (bind_wildcard && !addr[i].bind) {
+                i++;
                 continue;
             }
 
-            ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr,
-                                                  in_port[p].port);
+            ls = ngx_create_listening(cf, addr[i].sockaddr, addr[i].socklen);
             if (ls == NULL) {
                 return NGX_CONF_ERROR;
             }
 
-            ls->backlog = NGX_LISTEN_BACKLOG;
-            ls->rcvbuf = -1;
-            ls->sndbuf = -1;
-
             ls->addr_ntop = 1;
             ls->handler = ngx_mail_init_connection;
             ls->pool_size = 256;
 
-            /* STUB */
-            ls->log = *cf->cycle->new_log;
+            /* TODO: error_log directive */
+            ls->logp = &cf->cycle->new_log;
             ls->log.data = &ls->addr_text;
             ls->log.handler = ngx_accept_log_error;
-            /**/
 
-            imip = ngx_palloc(cf->pool, sizeof(ngx_mail_in_port_t));
-            if (imip == NULL) {
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+            ls->ipv6only = addr[i].ipv6only;
+#endif
+
+            mport = ngx_palloc(cf->pool, sizeof(ngx_mail_port_t));
+            if (mport == NULL) {
                 return NGX_CONF_ERROR;
             }
 
-            ls->servers = imip;
-
-            in_addr = in_port[p].addrs.elts;
+            ls->servers = mport;
 
-            if (in_addr[a].bind && in_addr[a].addr != INADDR_ANY) {
-                imip->naddrs = 1;
-                done = 0;
-
-            } else if (in_port[p].addrs.nelts > 1
-                       && in_addr[last - 1].addr == INADDR_ANY)
-            {
-                imip->naddrs = last;
-                done = 1;
+            if (i == last - 1) {
+                mport->naddrs = last;
 
             } else {
-                imip->naddrs = 1;
-                done = 0;
+                mport->naddrs = 1;
+                i = 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,
-                          imip->naddrs, last);
-#endif
-
-            imip->addrs = ngx_pcalloc(cf->pool,
-                                    imip->naddrs * sizeof(ngx_mail_in_addr_t));
-            if (imip->addrs == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            for (i = 0; i < imip->naddrs; i++) {
-                imip->addrs[i].addr = in_addr[i].addr;
-                imip->addrs[i].ctx = in_addr[i].ctx;
-
-                text = ngx_pnalloc(cf->pool,
-                                   NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1);
-                if (text == NULL) {
+            switch (ls->sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+            case AF_INET6:
+                if (ngx_mail_add_addrs6(cf, mport, addr) != NGX_OK) {
                     return NGX_CONF_ERROR;
                 }
-
-                len = ngx_inet_ntop(AF_INET, &in_addr[i].addr, text,
-                                    NGX_INET_ADDRSTRLEN);
-
-                len = ngx_sprintf(text + len, ":%d", in_port[p].port) - text;
-
-                imip->addrs[i].addr_text.len = len;
-                imip->addrs[i].addr_text.data = text;
-
-#if (NGX_MAIL_SSL)
-                imip->addrs[i].ssl = in_addr[i].ssl;
+                break;
 #endif
-            }
-
-            if (done) {
+            default: /* AF_INET */
+                if (ngx_mail_add_addrs(cf, mport, addr) != NGX_OK) {
+                    return NGX_CONF_ERROR;
+                }
                 break;
             }
 
-            in_addr++;
-            in_port[p].addrs.elts = in_addr;
+            addr++;
             last--;
-
-            a = 0;
         }
     }
 
@@ -396,15 +417,111 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma
 
 
 static ngx_int_t
-ngx_mail_cmp_conf_in_addrs(const void *one, const void *two)
+ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport,
+    ngx_mail_conf_addr_t *addr)
 {
-    ngx_mail_conf_in_addr_t  *first, *second;
+    u_char              *p;
+    size_t               len;
+    ngx_uint_t           i;
+    ngx_mail_in_addr_t  *addrs;
+    struct sockaddr_in  *sin;
+    u_char               buf[NGX_SOCKADDR_STRLEN];
+
+    mport->addrs = ngx_pcalloc(cf->pool,
+                               mport->naddrs * sizeof(ngx_mail_in_addr_t));
+    if (mport->addrs == NULL) {
+        return NGX_ERROR;
+    }
+
+    addrs = mport->addrs;
+
+    for (i = 0; i < mport->naddrs; i++) {
+
+        sin = (struct sockaddr_in *) addr[i].sockaddr;
+        addrs[i].addr = sin->sin_addr.s_addr;
+
+        addrs[i].conf.ctx = addr[i].ctx;
+#if (NGX_MAIL_SSL)
+        addrs[i].conf.ssl = addr[i].ssl;
+#endif
+
+        len = ngx_sock_ntop(addr[i].sockaddr, buf, NGX_SOCKADDR_STRLEN, 1);
+
+        p = ngx_pnalloc(cf->pool, len);
+        if (p == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_memcpy(p, buf, len);
+
+        addrs[i].conf.addr_text.len = len;
+        addrs[i].conf.addr_text.data = p;
+    }
+
+    return NGX_OK;
+}
+
+
+#if (NGX_HAVE_INET6)
 
-    first = (ngx_mail_conf_in_addr_t *) one;
-    second = (ngx_mail_conf_in_addr_t *) two;
+static ngx_int_t
+ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,
+    ngx_mail_conf_addr_t *addr)
+{
+    u_char               *p;
+    size_t                len;
+    ngx_uint_t            i;
+    ngx_mail_in6_addr_t  *addrs6;
+    struct sockaddr_in6  *sin6;
+    u_char                buf[NGX_SOCKADDR_STRLEN];
+
+    mport->addrs = ngx_pcalloc(cf->pool,
+                               mport->naddrs * sizeof(ngx_mail_in6_addr_t));
+    if (mport->addrs == NULL) {
+        return NGX_ERROR;
+    }
+
+    addrs6 = mport->addrs;
+
+    for (i = 0; i < mport->naddrs; i++) {
+
+        sin6 = (struct sockaddr_in6 *) addr[i].sockaddr;
+        addrs6[i].addr6 = sin6->sin6_addr;
+
+        addrs6[i].conf.ctx = addr[i].ctx;
+#if (NGX_MAIL_SSL)
+        addrs6[i].conf.ssl = addr[i].ssl;
+#endif
 
-    if (first->addr == INADDR_ANY) {
-        /* the INADDR_ANY must be the last resort, shift it to the end */
+        len = ngx_sock_ntop(addr[i].sockaddr, buf, NGX_SOCKADDR_STRLEN, 1);
+
+        p = ngx_pnalloc(cf->pool, len);
+        if (p == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_memcpy(p, buf, len);
+
+        addrs6[i].conf.addr_text.len = len;
+        addrs6[i].conf.addr_text.data = p;
+    }
+
+    return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_mail_cmp_conf_addrs(const void *one, const void *two)
+{
+    ngx_mail_conf_addr_t  *first, *second;
+
+    first = (ngx_mail_conf_addr_t *) one;
+    second = (ngx_mail_conf_addr_t *) two;
+
+    if (first->wildcard) {
+        /* a wildcard must be the last resort, shift it to the end */
         return 1;
     }
 
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -26,50 +26,76 @@ typedef struct {
 
 
 typedef struct {
-    in_addr_t               addr;
-    in_port_t               port;
-    int                     family;
+    u_char                  sockaddr[NGX_SOCKADDRLEN];
+    socklen_t               socklen;
 
     /* server ctx */
     ngx_mail_conf_ctx_t    *ctx;
 
     unsigned                bind:1;
+    unsigned                wildcard:1;
 #if (NGX_MAIL_SSL)
     unsigned                ssl:1;
 #endif
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+    unsigned                ipv6only:2;
+#endif
 } ngx_mail_listen_t;
 
 
 typedef struct {
-    in_addr_t               addr;
     ngx_mail_conf_ctx_t    *ctx;
     ngx_str_t               addr_text;
 #if (NGX_MAIL_SSL)
     ngx_uint_t              ssl;    /* unsigned   ssl:1; */
 #endif
+} ngx_mail_addr_conf_t;
+
+typedef struct {
+    in_addr_t               addr;
+    ngx_mail_addr_conf_t    conf;
 } ngx_mail_in_addr_t;
 
 
+#if (NGX_HAVE_INET6)
+
 typedef struct {
-    ngx_mail_in_addr_t     *addrs;       /* array of ngx_mail_in_addr_t */
-    ngx_uint_t              naddrs;
-} ngx_mail_in_port_t;
+    struct in6_addr         addr6;
+    ngx_mail_addr_conf_t    conf;
+} ngx_mail_in6_addr_t;
+
+#endif
 
 
 typedef struct {
+    /* ngx_mail_in_addr_t or ngx_mail_in6_addr_t */
+    void                   *addrs;
+    ngx_uint_t              naddrs;
+} ngx_mail_port_t;
+
+
+typedef struct {
+    int                     family;
     in_port_t               port;
-    ngx_array_t             addrs;       /* array of ngx_mail_conf_in_addr_t */
-} ngx_mail_conf_in_port_t;
+    ngx_array_t             addrs;       /* array of ngx_mail_conf_addr_t */
+} ngx_mail_conf_port_t;
 
 
 typedef struct {
-    in_addr_t               addr;
+    struct sockaddr        *sockaddr;
+    socklen_t               socklen;
+
     ngx_mail_conf_ctx_t    *ctx;
+
     unsigned                bind:1;
+    unsigned                wildcard:1;
 #if (NGX_MAIL_SSL)
     unsigned                ssl:1;
 #endif
-} ngx_mail_conf_in_addr_t;
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+    unsigned                ipv6only:2;
+#endif
+} ngx_mail_conf_addr_t;
 
 
 typedef struct {
@@ -136,8 +162,13 @@ typedef enum {
     ngx_smtp_auth_plain,
     ngx_smtp_auth_cram_md5,
     ngx_smtp_helo,
-    ngx_smtp_noxclient,
-    ngx_smtp_xclient
+    ngx_smtp_helo_xclient,
+    ngx_smtp_helo_from,
+    ngx_smtp_xclient,
+    ngx_smtp_xclient_from,
+    ngx_smtp_xclient_helo,
+    ngx_smtp_from,
+    ngx_smtp_to
 } ngx_smtp_state_e;
 
 
@@ -173,7 +204,7 @@ typedef struct {
     unsigned                no_sync_literal:1;
     unsigned                starttls:1;
     unsigned                esmtp:1;
-    unsigned                auth_method:2;
+    unsigned                auth_method:3;
     unsigned                auth_wait:1;
 
     ngx_str_t               login;
@@ -187,6 +218,8 @@ typedef struct {
     ngx_str_t              *addr_text;
     ngx_str_t               host;
     ngx_str_t               smtp_helo;
+    ngx_str_t               smtp_from;
+    ngx_str_t               smtp_to;
 
     ngx_uint_t              command;
     ngx_array_t             args;
@@ -252,16 +285,19 @@ typedef struct {
 #define NGX_SMTP_STARTTLS      13
 
 
-#define NGX_MAIL_AUTH_PLAIN     0
-#define NGX_MAIL_AUTH_LOGIN     1
-#define NGX_MAIL_AUTH_APOP      2
-#define NGX_MAIL_AUTH_CRAM_MD5  3
+#define NGX_MAIL_AUTH_PLAIN             0
+#define NGX_MAIL_AUTH_LOGIN             1
+#define NGX_MAIL_AUTH_LOGIN_USERNAME    2
+#define NGX_MAIL_AUTH_APOP              3
+#define NGX_MAIL_AUTH_CRAM_MD5          4
+#define NGX_MAIL_AUTH_NONE              5
 
 
 #define NGX_MAIL_AUTH_PLAIN_ENABLED     0x0002
 #define NGX_MAIL_AUTH_LOGIN_ENABLED     0x0004
 #define NGX_MAIL_AUTH_APOP_ENABLED      0x0008
 #define NGX_MAIL_AUTH_CRAM_MD5_ENABLED  0x0010
+#define NGX_MAIL_AUTH_NONE_ENABLED      0x0020
 
 
 #define NGX_MAIL_PARSE_INVALID_COMMAND  20
@@ -338,7 +374,7 @@ ngx_int_t ngx_mail_salt(ngx_mail_session
 ngx_int_t ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c,
     ngx_uint_t n);
 ngx_int_t ngx_mail_auth_login_username(ngx_mail_session_t *s,
-    ngx_connection_t *c);
+    ngx_connection_t *c, ngx_uint_t n);
 ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s,
     ngx_connection_t *c);
 ngx_int_t ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s,
--- a/src/mail/ngx_mail_auth_http_module.c
+++ b/src/mail/ngx_mail_auth_http_module.c
@@ -40,7 +40,6 @@ struct ngx_mail_auth_http_ctx_s {
     ngx_mail_auth_http_handler_pt   handler;
 
     ngx_uint_t                      state;
-    ngx_uint_t                      hash;   /* no needed ? */
 
     u_char                         *header_name_start;
     u_char                         *header_name_end;
@@ -140,8 +139,10 @@ ngx_module_t  ngx_mail_auth_http_module 
 static ngx_str_t   ngx_mail_auth_http_method[] = {
     ngx_string("plain"),
     ngx_string("plain"),
+    ngx_string("plain"),
     ngx_string("apop"),
-    ngx_string("cram-md5")
+    ngx_string("cram-md5"),
+    ngx_string("none")
 };
 
 static ngx_str_t   ngx_mail_smtp_errcode = ngx_string("535 5.7.0");
@@ -268,7 +269,7 @@ ngx_mail_auth_http_write_handler(ngx_eve
                 ngx_del_timer(wev);
             }
 
-            if (ngx_handle_write_event(wev, 0) == NGX_ERROR) {
+            if (ngx_handle_write_event(wev, 0) != NGX_OK) {
                 ngx_close_connection(c);
                 ngx_destroy_pool(ctx->pool);
                 ngx_mail_session_internal_server_error(s);
@@ -770,6 +771,8 @@ ngx_mail_auth_http_process_headers(ngx_m
                 return;
             }
 
+            /* AF_INET only */
+
             sin = ngx_pcalloc(s->connection->pool, sizeof(struct sockaddr_in));
             if (sin == NULL) {
                 ngx_destroy_pool(ctx->pool);
@@ -894,7 +897,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t 
             return;
         }
 
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
             ngx_mail_close_connection(c);
         }
 
@@ -902,7 +905,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t 
     }
 
     if (rev->active) {
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
             ngx_mail_close_connection(c);
         }
     }
@@ -914,7 +917,6 @@ ngx_mail_auth_http_parse_header_line(ngx
     ngx_mail_auth_http_ctx_t *ctx)
 {
     u_char      c, ch, *p;
-    ngx_uint_t  hash;
     enum {
         sw_start = 0,
         sw_name,
@@ -926,7 +928,6 @@ ngx_mail_auth_http_parse_header_line(ngx
     } state;
 
     state = ctx->state;
-    hash = ctx->hash;
 
     for (p = ctx->response->pos; p < ctx->response->last; p++) {
         ch = *p;
@@ -950,12 +951,10 @@ ngx_mail_auth_http_parse_header_line(ngx
 
                 c = (u_char) (ch | 0x20);
                 if (c >= 'a' && c <= 'z') {
-                    hash = c;
                     break;
                 }
 
                 if (ch >= '0' && ch <= '9') {
-                    hash = ch;
                     break;
                 }
 
@@ -967,7 +966,6 @@ ngx_mail_auth_http_parse_header_line(ngx
         case sw_name:
             c = (u_char) (ch | 0x20);
             if (c >= 'a' && c <= 'z') {
-                hash += c;
                 break;
             }
 
@@ -978,12 +976,10 @@ ngx_mail_auth_http_parse_header_line(ngx
             }
 
             if (ch == '-') {
-                hash += ch;
                 break;
             }
 
             if (ch >= '0' && ch <= '9') {
-                hash += ch;
                 break;
             }
 
@@ -1080,7 +1076,6 @@ ngx_mail_auth_http_parse_header_line(ngx
 
     ctx->response->pos = p;
     ctx->state = state;
-    ctx->hash = hash;
 
     return NGX_AGAIN;
 
@@ -1088,7 +1083,6 @@ done:
 
     ctx->response->pos = p + 1;
     ctx->state = sw_start;
-    ctx->hash = hash;
 
     return NGX_OK;
 
@@ -1111,7 +1105,7 @@ ngx_mail_auth_http_block_read(ngx_event_
     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
                    "mail auth http block read");
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         c = rev->data;
         s = c->data;
 
@@ -1165,6 +1159,10 @@ ngx_mail_auth_http_create_request(ngx_ma
                 + sizeof(CRLF) - 1
           + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
                 + sizeof(CRLF) - 1
+          + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1
+          + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len
+          + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len
+          + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len
           + ahcf->header.len
           + sizeof(CRLF) - 1;
 
@@ -1216,9 +1214,37 @@ ngx_mail_auth_http_create_request(ngx_ma
 
     b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
     b->last = ngx_copy(b->last, s->connection->addr_text.data,
-                         s->connection->addr_text.len);
+                       s->connection->addr_text.len);
     *b->last++ = CR; *b->last++ = LF;
 
+    if (s->host.len) {
+        b->last = ngx_cpymem(b->last, "Client-Host: ",
+                             sizeof("Client-Host: ") - 1);
+        b->last = ngx_copy(b->last, s->host.data, s->host.len);
+        *b->last++ = CR; *b->last++ = LF;
+    }
+
+    if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+
+        /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */
+
+        b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ",
+                             sizeof("Auth-SMTP-Helo: ") - 1);
+        b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len);
+        *b->last++ = CR; *b->last++ = LF;
+
+        b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ",
+                             sizeof("Auth-SMTP-From: ") - 1);
+        b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len);
+        *b->last++ = CR; *b->last++ = LF;
+
+        b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ",
+                             sizeof("Auth-SMTP-To: ") - 1);
+        b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len);
+        *b->last++ = CR; *b->last++ = LF;
+
+    }
+
     if (ahcf->header.len) {
         b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);
     }
@@ -1276,7 +1302,7 @@ ngx_mail_auth_http_create_conf(ngx_conf_
 
     ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t));
     if (ahcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     ahcf->timeout = NGX_CONF_UNSET_MSEC;
--- a/src/mail/ngx_mail_core_module.c
+++ b/src/mail/ngx_mail_core_module.c
@@ -120,20 +120,20 @@ ngx_mail_core_create_main_conf(ngx_conf_
 
     cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_main_conf_t));
     if (cmcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     if (ngx_array_init(&cmcf->servers, cf->pool, 4,
                        sizeof(ngx_mail_core_srv_conf_t *))
         != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_mail_listen_t))
         != NGX_OK)
     {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     return cmcf;
@@ -274,19 +274,24 @@ ngx_mail_core_server(ngx_conf_t *cf, ngx
 }
 
 
-/* AF_INET only */
-
 static char *
 ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_mail_core_srv_conf_t  *cscf = conf;
 
+    size_t                      len, off;
+    in_port_t                   port;
     ngx_str_t                  *value;
     ngx_url_t                   u;
     ngx_uint_t                  i, m;
-    ngx_mail_listen_t          *imls;
+    struct sockaddr            *sa;
+    ngx_mail_listen_t          *ls;
     ngx_mail_module_t          *module;
+    struct sockaddr_in         *sin;
     ngx_mail_core_main_conf_t  *cmcf;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6        *sin6;
+#endif
 
     value = cf->args->elts;
 
@@ -307,11 +312,40 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx
 
     cmcf = ngx_mail_conf_get_module_main_conf(cf, ngx_mail_core_module);
 
-    imls = cmcf->listen.elts;
+    ls = cmcf->listen.elts;
 
     for (i = 0; i < cmcf->listen.nelts; i++) {
 
-        if (imls[i].addr != u.addr.in_addr || imls[i].port != u.port) {
+        sa = (struct sockaddr *) ls[i].sockaddr;
+
+        if (sa->sa_family != u.family) {
+            continue;
+        }
+
+        switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            off = offsetof(struct sockaddr_in6, sin6_addr);
+            len = 16;
+            sin6 = (struct sockaddr_in6 *) sa;
+            port = sin6->sin6_port;
+            break;
+#endif
+
+        default: /* AF_INET */
+            off = offsetof(struct sockaddr_in, sin_addr);
+            len = 4;
+            sin = (struct sockaddr_in *) sa;
+            port = sin->sin_port;
+            break;
+        }
+
+        if (ngx_memcmp(ls[i].sockaddr + off, u.sockaddr + off, len) != 0) {
+            continue;
+        }
+
+        if (port != u.port) {
             continue;
         }
 
@@ -320,17 +354,18 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx
         return NGX_CONF_ERROR;
     }
 
-    imls = ngx_array_push(&cmcf->listen);
-    if (imls == NULL) {
+    ls = ngx_array_push(&cmcf->listen);
+    if (ls == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    ngx_memzero(imls, sizeof(ngx_mail_listen_t));
+    ngx_memzero(ls, sizeof(ngx_mail_listen_t));
+
+    ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen);
 
-    imls->addr = u.addr.in_addr;
-    imls->port = u.port;
-    imls->family = u.family;
-    imls->ctx = cf->ctx;
+    ls->socklen = u.socklen;
+    ls->wildcard = u.wildcard;
+    ls->ctx = cf->ctx;
 
     for (m = 0; ngx_modules[m]; m++) {
         if (ngx_modules[m]->type != NGX_MAIL_MODULE) {
@@ -354,13 +389,54 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx
     for (i = 2; i < cf->args->nelts; i++) {
 
         if (ngx_strcmp(value[i].data, "bind") == 0) {
-            imls->bind = 1;
+            ls->bind = 1;
             continue;
         }
 
+        if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+            struct sockaddr  *sa;
+            u_char            buf[NGX_SOCKADDR_STRLEN];
+
+            sa = (struct sockaddr *) ls->sockaddr;
+
+            if (sa->sa_family == AF_INET6) {
+
+                if (ngx_strcmp(&value[i].data[10], "n") == 0) {
+                    ls->ipv6only = 1;
+
+                } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
+                    ls->ipv6only = 2;
+
+                } else {
+                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                       "invalid ipv6only flags \"%s\"",
+                                       &value[i].data[9]);
+                    return NGX_CONF_ERROR;
+                }
+
+                ls->bind = 1;
+
+            } else {
+                len = ngx_sock_ntop(sa, buf, NGX_SOCKADDR_STRLEN, 1);
+
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "ipv6only is not supported "
+                                   "on addr \"%*s\", ignored", len, buf);
+            }
+
+            continue;
+#else
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "bind ipv6only is not supported "
+                               "on this platform");
+            return NGX_CONF_ERROR;
+#endif
+        }
+
         if (ngx_strcmp(value[i].data, "ssl") == 0) {
 #if (NGX_MAIL_SSL)
-            imls->ssl = 1;
+            ls->ssl = 1;
             continue;
 #else
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -21,25 +21,27 @@ static void ngx_mail_ssl_handshake_handl
 void
 ngx_mail_init_connection(ngx_connection_t *c)
 {
-    in_addr_t             in_addr;
-    socklen_t             len;
-    ngx_uint_t            i;
-    struct sockaddr_in    sin;
-    ngx_mail_log_ctx_t   *ctx;
-    ngx_mail_in_port_t   *imip;
-    ngx_mail_in_addr_t   *imia;
-    ngx_mail_session_t   *s;
+    ngx_uint_t             i;
+    ngx_mail_port_t       *port;
+    struct sockaddr       *sa;
+    struct sockaddr_in    *sin;
+    ngx_mail_log_ctx_t    *ctx;
+    ngx_mail_in_addr_t    *addr;
+    ngx_mail_session_t    *s;
+    ngx_mail_addr_conf_t  *addr_conf;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6   *sin6;
+    ngx_mail_in6_addr_t   *addr6;
+#endif
+
 
     /* find the server configuration for the address:port */
 
     /* AF_INET only */
 
-    imip = c->listening->servers;
-    imia = imip->addrs;
+    port = c->listening->servers;
 
-    i = 0;
-
-    if (imip->naddrs > 1) {
+    if (port->naddrs > 1) {
 
         /*
          * There are several addresses on this port and one of them
@@ -49,45 +51,79 @@ ngx_mail_init_connection(ngx_connection_
          * AcceptEx() already gave this address.
          */
 
-#if (NGX_WIN32)
-        if (c->local_sockaddr) {
-            in_addr =
-                   ((struct sockaddr_in *) c->local_sockaddr)->sin_addr.s_addr;
+        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+            ngx_mail_close_connection(c);
+            return;
+        }
+
+        sa = c->local_sockaddr;
+
+        switch (sa->sa_family) {
 
-        } 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");
-                ngx_mail_close_connection(c);
-                return;
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            sin6 = (struct sockaddr_in6 *) sa;
+
+            addr6 = port->addrs;
+
+            /* the last address is "*" */
+
+            for (i = 0; i < port->naddrs - 1; i++) {
+                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
+                    break;
+                }
             }
 
-            in_addr = sin.sin_addr.s_addr;
+            addr_conf = &addr6[i].conf;
+
+            break;
+#endif
+
+        default: /* AF_INET */
+            sin = (struct sockaddr_in *) sa;
+
+            addr = port->addrs;
+
+            /* the last address is "*" */
+
+            for (i = 0; i < port->naddrs - 1; i++) {
+                if (addr[i].addr == sin->sin_addr.s_addr) {
+                    break;
+                }
+            }
+
+            addr_conf = &addr[i].conf;
+
+            break;
         }
 
-        /* the last address is "*" */
+    } else {
+        switch (c->local_sockaddr->sa_family) {
 
-        for ( /* void */ ; i < imip->naddrs - 1; i++) {
-            if (in_addr == imia[i].addr) {
-                break;
-            }
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            addr6 = port->addrs;
+            addr_conf = &addr6[0].conf;
+            break;
+#endif
+
+        default: /* AF_INET */
+            addr = port->addrs;
+            addr_conf = &addr[0].conf;
+            break;
         }
     }
 
-
     s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t));
     if (s == NULL) {
         ngx_mail_close_connection(c);
         return;
     }
 
-    s->main_conf = imia[i].ctx->main_conf;
-    s->srv_conf = imia[i].ctx->srv_conf;
+    s->main_conf = addr_conf->ctx->main_conf;
+    s->srv_conf = addr_conf->ctx->srv_conf;
 
-    s->addr_text = &imia[i].addr_text;
+    s->addr_text = &addr_conf->addr_text;
 
     c->data = s;
     s->connection = c;
@@ -124,7 +160,7 @@ ngx_mail_init_connection(ngx_connection_
         return;
     }
 
-    if (imia[i].ssl) {
+    if (addr_conf->ssl) {
 
         c->log->action = "SSL handshaking";
 
@@ -356,21 +392,22 @@ ngx_mail_auth_plain(ngx_mail_session_t *
 
 
 ngx_int_t
-ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c)
+ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c,
+    ngx_uint_t n)
 {
     ngx_str_t  *arg;
 
     arg = s->args.elts;
 
     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
-                   "mail auth login username: \"%V\"", &arg[0]);
+                   "mail auth login username: \"%V\"", &arg[n]);
 
-    s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
+    s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
     if (s->login.data == NULL){
         return NGX_ERROR;
     }
 
-    if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
+    if (ngx_decode_base64(&s->login, &arg[n]) != NGX_OK) {
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
             "client sent invalid base64 encoding in AUTH LOGIN command");
         return NGX_MAIL_PARSE_INVALID_COMMAND;
@@ -513,7 +550,7 @@ ngx_mail_send(ngx_event_t *wev)
     }
 
     if (s->out.len == 0) {
-        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
             ngx_mail_close_connection(c);
         }
 
@@ -552,7 +589,7 @@ ngx_mail_send(ngx_event_t *wev)
 
     ngx_add_timer(c->write, cscf->timeout);
 
-    if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
         ngx_mail_close_connection(c);
         return;
     }
@@ -579,7 +616,7 @@ ngx_mail_read_command(ngx_mail_session_t
     }
 
     if (n == NGX_AGAIN) {
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             ngx_mail_session_internal_server_error(s);
             return NGX_ERROR;
         }
@@ -673,7 +710,7 @@ ngx_mail_close_connection(ngx_connection
 #endif
 
 #if (NGX_STAT_STUB)
-    ngx_atomic_fetch_add(ngx_stat_active, -1);
+    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
 #endif
 
     c->destroyed = 1;
--- a/src/mail/ngx_mail_imap_handler.c
+++ b/src/mail/ngx_mail_imap_handler.c
@@ -46,7 +46,7 @@ ngx_mail_imap_init_session(ngx_mail_sess
 
     ngx_add_timer(c->read, cscf->timeout);
 
-    if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
         ngx_mail_close_connection(c);
     }
 
@@ -205,7 +205,7 @@ ngx_mail_imap_auth_state(ngx_event_t *re
             break;
 
         case ngx_imap_auth_login_username:
-            rc = ngx_mail_auth_login_username(s, c);
+            rc = ngx_mail_auth_login_username(s, c, 0);
 
             tag = 0;
             s->out.len = sizeof(imap_password) - 1;
@@ -370,6 +370,14 @@ ngx_mail_imap_authenticate(ngx_mail_sess
 
         return NGX_OK;
 
+    case NGX_MAIL_AUTH_LOGIN_USERNAME:
+
+        s->out.len = sizeof(imap_password) - 1;
+        s->out.data = imap_password;
+        s->mail_state = ngx_imap_auth_login_password;
+
+        return ngx_mail_auth_login_username(s, c, 1);
+
     case NGX_MAIL_AUTH_PLAIN:
 
         s->out.len = sizeof(imap_plain_next) - 1;
--- a/src/mail/ngx_mail_imap_module.c
+++ b/src/mail/ngx_mail_imap_module.c
@@ -36,7 +36,8 @@ static ngx_str_t  ngx_mail_imap_auth_met
     ngx_string("AUTH=PLAIN"),
     ngx_string("AUTH=LOGIN"),
     ngx_null_string,  /* APOP */
-    ngx_string("AUTH=CRAM-MD5")
+    ngx_string("AUTH=CRAM-MD5"),
+    ngx_null_string   /* NONE */
 };
 
 
--- a/src/mail/ngx_mail_parse.c
+++ b/src/mail/ngx_mail_parse.c
@@ -746,7 +746,7 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
                 s->arg_end = p;
                 goto done;
             default:
-                if (s->args.nelts <= 2) {
+                if (s->args.nelts <= 10) {
                     state = sw_argument;
                     s->arg_start = p;
                     break;
@@ -848,6 +848,10 @@ ngx_mail_auth_parse(ngx_mail_session_t *
                 return NGX_MAIL_AUTH_LOGIN;
             }
 
+            if (s->args.nelts == 2) {
+                return NGX_MAIL_AUTH_LOGIN_USERNAME;
+            }
+
             return NGX_MAIL_PARSE_INVALID_COMMAND;
         }
 
--- a/src/mail/ngx_mail_pop3_handler.c
+++ b/src/mail/ngx_mail_pop3_handler.c
@@ -67,7 +67,7 @@ ngx_mail_pop3_init_session(ngx_mail_sess
 
     ngx_add_timer(c->read, cscf->timeout);
 
-    if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
         ngx_mail_close_connection(c);
     }
 
@@ -226,7 +226,7 @@ ngx_mail_pop3_auth_state(ngx_event_t *re
             break;
 
         case ngx_pop3_auth_login_username:
-            rc = ngx_mail_auth_login_username(s, c);
+            rc = ngx_mail_auth_login_username(s, c, 0);
 
             s->out.len = sizeof(pop3_password) - 1;
             s->out.data = pop3_password;
@@ -474,6 +474,14 @@ ngx_mail_pop3_auth(ngx_mail_session_t *s
 
         return NGX_OK;
 
+    case NGX_MAIL_AUTH_LOGIN_USERNAME:
+
+        s->out.len = sizeof(pop3_password) - 1;
+        s->out.data = pop3_password;
+        s->mail_state = ngx_pop3_auth_login_password;
+
+        return ngx_mail_auth_login_username(s, c, 1);
+
     case NGX_MAIL_AUTH_PLAIN:
 
         s->out.len = sizeof(pop3_next) - 1;
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -104,7 +104,7 @@ ngx_module_t  ngx_mail_proxy_module = {
 };
 
 
-static u_char  smtp_ok[] = "235 2.0.0 OK" CRLF;
+static u_char  smtp_auth_ok[] = "235 2.0.0 OK" CRLF;
 
 
 void
@@ -201,7 +201,7 @@ ngx_mail_proxy_block_read(ngx_event_t *r
 
     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read");
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         c = rev->data;
         s = c->data;
 
@@ -465,6 +465,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
     u_char                    *p;
     ngx_int_t                  rc;
     ngx_str_t                  line;
+    ngx_buf_t                 *b;
     ngx_connection_t          *c;
     ngx_mail_session_t        *s;
     ngx_mail_proxy_conf_t     *pcf;
@@ -520,19 +521,26 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
         p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
         *p++ = CR; *p = LF;
 
-        s->mail_state = pcf->xclient ? ngx_smtp_helo: ngx_smtp_noxclient;
+        if (pcf->xclient) {
+            s->mail_state = ngx_smtp_helo_xclient;
+
+        } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+            s->mail_state = ngx_smtp_helo_from;
+
+        } else {
+            s->mail_state = ngx_smtp_helo;
+        }
 
         break;
 
-    case ngx_smtp_helo:
+    case ngx_smtp_helo_xclient:
         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
                        "mail proxy send xclient");
 
         s->connection->log->action = "sending XCLIENT to upstream";
 
-        line.len = sizeof("XCLIENT PROTO=SMTP HELO= ADDR= LOGIN= NAME="
+        line.len = sizeof("XCLIENT ADDR= LOGIN= NAME="
                           CRLF) - 1
-                   + s->esmtp + s->smtp_helo.len
                    + s->connection->addr_text.len + s->login.len + s->host.len;
 
         line.data = ngx_pnalloc(c->pool, line.len);
@@ -541,30 +549,102 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
             return;
         }
 
+        line.len = ngx_sprintf(line.data,
+                       "XCLIENT ADDR=%V%s%V NAME=%V" CRLF,
+                       &s->connection->addr_text,
+                       (s->login.len ? " LOGIN=" : ""), &s->login, &s->host)
+                   - line.data;
+
         if (s->smtp_helo.len) {
-            line.len = ngx_sprintf(line.data,
-                           "XCLIENT PROTO=%sSMTP HELO=%V ADDR=%V LOGIN=%V "
-                           "NAME=%V" CRLF,
-                           (s->esmtp ? "E" : ""), &s->smtp_helo,
-                           &s->connection->addr_text, &s->login, &s->host)
-                       - line.data;
+            s->mail_state = ngx_smtp_xclient_helo;
+
+        } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+            s->mail_state = ngx_smtp_xclient_from;
+
         } else {
-            line.len = ngx_sprintf(line.data,
-                           "XCLIENT PROTO=SMTP ADDR=%V LOGIN=%V NAME=%V" CRLF,
-                           &s->connection->addr_text, &s->login, &s->host)
-                       - line.data;
+            s->mail_state = ngx_smtp_xclient;
+        }
+
+        break;
+
+    case ngx_smtp_xclient_helo:
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+                       "mail proxy send client ehlo");
+
+        s->connection->log->action = "sending client HELO/EHLO to upstream";
+
+        line.len = sizeof("HELO " CRLF) - 1 + s->smtp_helo.len;
+
+        line.data = ngx_pnalloc(c->pool, line.len);
+        if (line.data == NULL) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
         }
 
-        s->mail_state = ngx_smtp_xclient;
+        line.len = ngx_sprintf(line.data,
+                       ((s->esmtp) ? "EHLO %V" CRLF : "HELO %V" CRLF),
+                       &s->smtp_helo)
+                   - line.data;
+
+        s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ?
+                            ngx_smtp_helo_from : ngx_smtp_helo;
+
+        break;
+
+    case ngx_smtp_helo_from:
+    case ngx_smtp_xclient_from:
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+                       "mail proxy send mail from");
+
+        s->connection->log->action = "sending MAIL FROM to upstream";
+
+        line.len = s->smtp_from.len + sizeof(CRLF) - 1;
+        line.data = ngx_pnalloc(c->pool, line.len);
+        if (line.data == NULL) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len);
+        *p++ = CR; *p = LF;
+
+        s->mail_state = ngx_smtp_from;
+
         break;
 
-    case ngx_smtp_noxclient:
-    case ngx_smtp_xclient:
+    case ngx_smtp_from:
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+                       "mail proxy send rcpt to");
+
+        s->connection->log->action = "sending RCPT TO to upstream";
+
+        line.len = s->smtp_to.len + sizeof(CRLF) - 1;
+        line.data = ngx_pnalloc(c->pool, line.len);
+        if (line.data == NULL) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len);
+        *p++ = CR; *p = LF;
 
-        ngx_memcpy(s->proxy->buffer->start, smtp_ok, sizeof(smtp_ok) - 1);
+        s->mail_state = ngx_smtp_to;
+
+        break;
+
+    case ngx_smtp_helo:
+    case ngx_smtp_xclient:
+    case ngx_smtp_to:
 
-        s->proxy->buffer->pos = s->proxy->buffer->start;
-        s->proxy->buffer->last = s->proxy->buffer->start + sizeof(smtp_ok) - 1;
+        b = s->proxy->buffer;
+
+        if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+            b->pos = b->start;
+
+        } else {
+            ngx_memcpy(b->start, smtp_auth_ok, sizeof(smtp_auth_ok) - 1);
+            b->last = b->start + sizeof(smtp_auth_ok) - 1;
+        }
 
         s->connection->read->handler = ngx_mail_proxy_handler;
         s->connection->write->handler = ngx_mail_proxy_handler;
@@ -612,7 +692,7 @@ ngx_mail_proxy_dummy_handler(ngx_event_t
 
     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler");
 
-    if (ngx_handle_write_event(wev, 0) == NGX_ERROR) {
+    if (ngx_handle_write_event(wev, 0) != NGX_OK) {
         c = wev->data;
         s = c->data;
 
@@ -703,19 +783,31 @@ ngx_mail_proxy_read_response(ngx_mail_se
     default: /* NGX_MAIL_SMTP_PROTOCOL */
         switch (state) {
 
+        case ngx_smtp_start:
+            if (p[0] == '2' && p[1] == '2' && p[2] == '0') {
+                return NGX_OK;
+            }
+            break;
+
         case ngx_smtp_helo:
-        case ngx_smtp_noxclient:
+        case ngx_smtp_helo_xclient:
+        case ngx_smtp_helo_from:
+        case ngx_smtp_from:
             if (p[0] == '2' && p[1] == '5' && p[2] == '0') {
                 return NGX_OK;
             }
             break;
 
-        case ngx_smtp_start:
         case ngx_smtp_xclient:
-            if (p[0] == '2' && p[1] == '2' && p[2] == '0') {
+        case ngx_smtp_xclient_from:
+        case ngx_smtp_xclient_helo:
+            if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') {
                 return NGX_OK;
             }
             break;
+
+        case ngx_smtp_to:
+            return NGX_OK;
         }
 
         break;
@@ -884,22 +976,22 @@ ngx_mail_proxy_handler(ngx_event_t *ev)
         return;
     }
 
-    if (ngx_handle_write_event(dst->write, 0) == NGX_ERROR) {
+    if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {
         ngx_mail_proxy_close_session(s);
         return;
     }
 
-    if (ngx_handle_read_event(dst->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(dst->read, 0) != NGX_OK) {
         ngx_mail_proxy_close_session(s);
         return;
     }
 
-    if (ngx_handle_write_event(src->write, 0) == NGX_ERROR) {
+    if (ngx_handle_write_event(src->write, 0) != NGX_OK) {
         ngx_mail_proxy_close_session(s);
         return;
     }
 
-    if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(src->read, 0) != NGX_OK) {
         ngx_mail_proxy_close_session(s);
         return;
     }
@@ -969,7 +1061,7 @@ ngx_mail_proxy_create_conf(ngx_conf_t *c
 
     pcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_proxy_conf_t));
     if (pcf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     pcf->enable = NGX_CONF_UNSET;
--- a/src/mail/ngx_mail_smtp_handler.c
+++ b/src/mail/ngx_mail_smtp_handler.c
@@ -12,6 +12,7 @@
 
 
 static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_mail_smtp_resolve_name(ngx_event_t *rev);
 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);
@@ -23,6 +24,8 @@ static ngx_int_t ngx_mail_smtp_auth(ngx_
 static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c);
 static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s,
     ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c);
 
 static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s,
     ngx_connection_t *c, char *err);
@@ -41,6 +44,7 @@ static u_char  smtp_invalid_pipelining[]
    "503 5.5.0 Improper use of SMTP command pipelining" CRLF;
 static u_char  smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF;
 static u_char  smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF;
+static u_char  smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF;
 
 
 static ngx_str_t  smtp_unavailable = ngx_string("[UNAVAILABLE]");
@@ -62,6 +66,12 @@ ngx_mail_smtp_init_session(ngx_mail_sess
         return;
     }
 
+    if (c->sockaddr->sa_family != AF_INET) {
+        s->host = smtp_tempunavail;
+        ngx_mail_smtp_greeting(s, c);
+        return;
+    }
+
     c->log->action = "in resolving client address";
 
     ctx = ngx_resolve_start(cscf->resolver, NULL);
@@ -88,9 +98,8 @@ ngx_mail_smtp_init_session(ngx_mail_sess
 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;
+    ngx_connection_t    *c;
+    ngx_mail_session_t  *s;
 
     s = ctx->data;
     c = s->connection;
@@ -131,6 +140,23 @@ ngx_mail_smtp_resolve_addr_handler(ngx_r
     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
                    "address resolved: %V", &s->host);
 
+    c->read->handler = ngx_mail_smtp_resolve_name;
+
+    ngx_post_event(c->read, &ngx_posted_events);
+}
+
+
+static void
+ngx_mail_smtp_resolve_name(ngx_event_t *rev)
+{
+    ngx_connection_t          *c;
+    ngx_mail_session_t        *s;
+    ngx_resolver_ctx_t        *ctx;
+    ngx_mail_core_srv_conf_t  *cscf;
+
+    c = rev->data;
+    s = c->data;
+
     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
 
     ctx = ngx_resolve_start(cscf->resolver, NULL);
@@ -225,7 +251,7 @@ ngx_mail_smtp_greeting(ngx_mail_session_
     timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout;
     ngx_add_timer(c->read, timeout);
 
-    if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
         ngx_mail_close_connection(c);
     }
 
@@ -267,7 +293,7 @@ ngx_mail_smtp_invalid_pipelining(ngx_eve
 
         ngx_add_timer(c->read, cscf->timeout);
 
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             ngx_mail_close_connection(c);
             return;
         }
@@ -417,8 +443,15 @@ ngx_mail_smtp_auth_state(ngx_event_t *re
                 rc = ngx_mail_smtp_mail(s, c);
                 break;
 
+            case NGX_SMTP_RCPT:
+                rc = ngx_mail_smtp_rcpt(s, c);
+                break;
+
+            case NGX_SMTP_RSET:
+                rc = ngx_mail_smtp_rset(s, c);
+                break;
+
             case NGX_SMTP_NOOP:
-            case NGX_SMTP_RSET:
                 break;
 
             case NGX_SMTP_STARTTLS:
@@ -435,7 +468,7 @@ ngx_mail_smtp_auth_state(ngx_event_t *re
             break;
 
         case ngx_smtp_auth_login_username:
-            rc = ngx_mail_auth_login_username(s, c);
+            rc = ngx_mail_auth_login_username(s, c, 0);
 
             s->out.len = sizeof(smtp_password) - 1;
             s->out.data = smtp_password;
@@ -513,6 +546,11 @@ ngx_mail_smtp_helo(ngx_mail_session_t *s
 
     ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len);
 
+    s->smtp_from.len = 0;
+    s->smtp_from.data = NULL;
+    s->smtp_to.len = 0;
+    s->smtp_to.data = NULL;
+
     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
 
     if (s->command == NGX_SMTP_HELO) {
@@ -579,6 +617,14 @@ ngx_mail_smtp_auth(ngx_mail_session_t *s
 
         return NGX_OK;
 
+    case NGX_MAIL_AUTH_LOGIN_USERNAME:
+
+        s->out.len = sizeof(smtp_password) - 1;
+        s->out.data = smtp_password;
+        s->mail_state = ngx_smtp_auth_login_password;
+
+        return ngx_mail_auth_login_username(s, c, 1);
+
     case NGX_MAIL_AUTH_PLAIN:
 
         s->out.len = sizeof(smtp_next) - 1;
@@ -618,10 +664,136 @@ ngx_mail_smtp_auth(ngx_mail_session_t *s
 static ngx_int_t
 ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c)
 {
-    ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\"");
+    u_char                     ch;
+    ngx_str_t                  l;
+    ngx_uint_t                 i;
+    ngx_mail_smtp_srv_conf_t  *sscf;
+
+    sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+    if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) {
+        ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\"");
+
+        s->out.len = sizeof(smtp_auth_required) - 1;
+        s->out.data = smtp_auth_required;
+
+        return NGX_OK;
+    }
+
+    /* auth none */
+
+    if (s->smtp_from.len) {
+        s->out.len = sizeof(smtp_bad_sequence) - 1;
+        s->out.data = smtp_bad_sequence;
+        return NGX_OK;
+    }
+
+    l.len = s->buffer->last - s->buffer->start;
+    l.data = s->buffer->start;
+
+    for (i = 0; i < l.len; i++) {
+        ch = l.data[i];
+
+        if (ch != CR && ch != LF) {
+            continue;
+        }
+
+        l.data[i] = ' ';
+    }
+
+    while (i) {
+        if (l.data[i - 1] != ' ') {
+            break;
+        }
+
+        i--;
+    }
+
+    l.len = i;
+
+    s->smtp_from.len = l.len;
+
+    s->smtp_from.data = ngx_pnalloc(c->pool, l.len);
+    if (s->smtp_from.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(s->smtp_from.data, l.data, l.len);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "smtp mail from:\"%V\"", &s->smtp_from);
+
+    s->out.len = sizeof(smtp_ok) - 1;
+    s->out.data = smtp_ok;
+
+    return NGX_OK;
+}
 
-    s->out.len = sizeof(smtp_auth_required) - 1;
-    s->out.data = smtp_auth_required;
+
+static ngx_int_t
+ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+    u_char      ch;
+    ngx_str_t   l;
+    ngx_uint_t  i;
+
+    if (s->smtp_from.len == 0) {
+        s->out.len = sizeof(smtp_bad_sequence) - 1;
+        s->out.data = smtp_bad_sequence;
+        return NGX_OK;
+    }
+
+    l.len = s->buffer->last - s->buffer->start;
+    l.data = s->buffer->start;
+
+    for (i = 0; i < l.len; i++) {
+        ch = l.data[i];
+
+        if (ch != CR && ch != LF) {
+            continue;
+        }
+
+        l.data[i] = ' ';
+    }
+
+    while (i) {
+        if (l.data[i - 1] != ' ') {
+            break;
+        }
+
+        i--;
+    }
+
+    l.len = i;
+
+    s->smtp_to.len = l.len;
+
+    s->smtp_to.data = ngx_pnalloc(c->pool, l.len);
+    if (s->smtp_to.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(s->smtp_to.data, l.data, l.len);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "smtp rcpt to:\"%V\"", &s->smtp_to);
+
+    s->auth_method = NGX_MAIL_AUTH_NONE;
+
+    return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+    s->smtp_from.len = 0;
+    s->smtp_from.data = NULL;
+    s->smtp_to.len = 0;
+    s->smtp_to.data = NULL;
+
+    s->out.len = sizeof(smtp_ok) - 1;
+    s->out.data = smtp_ok;
 
     return NGX_OK;
 }
@@ -644,6 +816,10 @@ ngx_mail_smtp_starttls(ngx_mail_session_
 
             s->smtp_helo.len = 0;
             s->smtp_helo.data = NULL;
+            s->smtp_from.len = 0;
+            s->smtp_from.data = NULL;
+            s->smtp_to.len = 0;
+            s->smtp_to.data = NULL;
 
             c->read->handler = ngx_mail_starttls_handler;
             return NGX_OK;
@@ -674,7 +850,7 @@ ngx_mail_smtp_discard_command(ngx_mail_s
     }
 
     if (n == NGX_AGAIN) {
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             ngx_mail_session_internal_server_error(s);
             return NGX_ERROR;
         }
--- a/src/mail/ngx_mail_smtp_module.c
+++ b/src/mail/ngx_mail_smtp_module.c
@@ -20,6 +20,7 @@ static ngx_conf_bitmask_t  ngx_mail_smtp
     { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
     { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
     { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+    { ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED },
     { ngx_null_string, 0 }
 };
 
@@ -28,7 +29,8 @@ static ngx_str_t  ngx_mail_smtp_auth_met
     ngx_string("PLAIN"),
     ngx_string("LOGIN"),
     ngx_null_string,  /* APOP */
-    ngx_string("CRAM-MD5")
+    ngx_string("CRAM-MD5"),
+    ngx_null_string   /* NONE */
 };
 
 
@@ -136,10 +138,10 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     ngx_mail_smtp_srv_conf_t *prev = parent;
     ngx_mail_smtp_srv_conf_t *conf = child;
 
-    u_char                    *p, *auth;
+    u_char                    *p, *auth, *last;
     size_t                     size;
     ngx_str_t                 *c;
-    ngx_uint_t                 i, m;
+    ngx_uint_t                 i, m, auth_enabled;
     ngx_mail_core_srv_conf_t  *cscf;
 
     ngx_conf_merge_size_value(conf->client_buffer_size,
@@ -192,23 +194,29 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
         conf->capabilities = prev->capabilities;
     }
 
-    size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1
-           + sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1;
+    size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1;
 
     c = conf->capabilities.elts;
     for (i = 0; i < conf->capabilities.nelts; i++) {
         size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1;
     }
 
+    auth_enabled = 0;
+
     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
          m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
          m <<= 1, i++)
     {
         if (m & conf->auth_methods) {
             size += 1 + ngx_mail_smtp_auth_methods_names[i].len;
+            auth_enabled = 1;
         }
     }
 
+    if (auth_enabled) {
+        size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1;
+    }
+
     p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
@@ -217,11 +225,14 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     conf->capability.len = size;
     conf->capability.data = p;
 
+    last = p;
+
     *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';
     p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
     *p++ = CR; *p++ = LF;
 
     for (i = 0; i < conf->capabilities.nelts; i++) {
+        last = p;
         *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';
         p = ngx_cpymem(p, c[i].data, c[i].len);
         *p++ = CR; *p++ = LF;
@@ -229,21 +240,28 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
 
     auth = p;
 
-    *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
-    *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';
+    if (auth_enabled) {
+        last = p;
+
+        *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
+        *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';
 
-    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
-         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
-         m <<= 1, i++)
-    {
-        if (m & conf->auth_methods) {
-            *p++ = ' ';
-            p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data,
-                           ngx_mail_smtp_auth_methods_names[i].len);
+        for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+             m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+             m <<= 1, i++)
+        {
+            if (m & conf->auth_methods) {
+                *p++ = ' ';
+                p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data,
+                               ngx_mail_smtp_auth_methods_names[i].len);
+            }
         }
-    }
+
+        *p++ = CR; *p = LF;
 
-    *p++ = CR; *p = LF;
+    } else {
+        last[3] = ' ';
+    }
 
     size += sizeof("250 STARTTLS" CRLF) - 1;
 
@@ -255,14 +273,13 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     conf->starttls_capability.len = size;
     conf->starttls_capability.data = p;
 
-    p = ngx_cpymem(p, conf->capability.data,
-                   conf->capability.len);
+    p = ngx_cpymem(p, conf->capability.data, conf->capability.len);
 
     p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
     *p++ = CR; *p = LF;
 
     p = conf->starttls_capability.data
-        + (auth - conf->capability.data) + 3;
+        + (last - conf->capability.data) + 3;
     *p = '-';
 
     size = (auth - conf->capability.data)
@@ -276,10 +293,15 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     conf->starttls_only_capability.len = size;
     conf->starttls_only_capability.data = p;
 
-    p = ngx_cpymem(p, conf->capability.data,
-                   auth - conf->capability.data);
+    p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data);
 
     ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
 
+    if (last < auth) {
+        p = conf->starttls_only_capability.data
+            + (last - conf->capability.data) + 3;
+        *p = '-';
+    }
+
     return NGX_CONF_OK;
 }
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -166,7 +166,7 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf)
 
     scf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_ssl_conf_t));
     if (scf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     /*
@@ -182,7 +182,7 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf)
      */
 
     scf->enable = NGX_CONF_UNSET;
-    scf->starttls = NGX_CONF_UNSET;
+    scf->starttls = NGX_CONF_UNSET_UINT;
     scf->prefer_server_ciphers = NGX_CONF_UNSET;
     scf->builtin_session_cache = NGX_CONF_UNSET;
     scf->session_timeout = NGX_CONF_UNSET;
--- a/src/os/unix/ngx_darwin_config.h
+++ b/src/os/unix/ngx_darwin_config.h
@@ -24,6 +24,7 @@
 #include <grp.h>
 #include <dirent.h>
 #include <glob.h>
+#include <sys/mount.h>          /* statfs() */
 
 #include <sys/filio.h>          /* FIONBIO */
 #include <sys/ioctl.h>
--- a/src/os/unix/ngx_darwin_init.c
+++ b/src/os/unix/ngx_darwin_init.c
@@ -64,32 +64,44 @@ ngx_os_specific_init(ngx_log_t *log)
     ngx_uint_t  i;
 
     size = sizeof(ngx_darwin_kern_ostype);
-    if (sysctlbyname("kern.ostype",
-                     ngx_darwin_kern_ostype, &size, NULL, 0) == -1) {
-        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                      "sysctlbyname(kern.ostype) failed");
+    if (sysctlbyname("kern.ostype", ngx_darwin_kern_ostype, &size, NULL, 0)
+        == -1)
+    {
+        err = ngx_errno;
+
+        if (err != NGX_ENOENT) {
 
-        if (ngx_errno != NGX_ENOMEM) {
-            return NGX_ERROR;
+            ngx_log_error(NGX_LOG_ALERT, log, err,
+                          "sysctlbyname(kern.ostype) failed");
+
+            if (err != NGX_ENOMEM) {
+                return NGX_ERROR;
+            }
+
+            ngx_darwin_kern_ostype[size - 1] = '\0';
         }
-
-        ngx_darwin_kern_ostype[size - 1] = '\0';
     }
 
     size = sizeof(ngx_darwin_kern_osrelease);
-    if (sysctlbyname("kern.osrelease",
-                     ngx_darwin_kern_osrelease, &size, NULL, 0) == -1) {
-        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                      "sysctlbyname(kern.osrelease) failed");
+    if (sysctlbyname("kern.osrelease", ngx_darwin_kern_osrelease, &size,
+                     NULL, 0)
+        == -1)
+    {
+        err = ngx_errno;
+
+        if (err != NGX_ENOENT) {
 
-        if (ngx_errno != NGX_ENOMEM) {
-            return NGX_ERROR;
-        }
+            ngx_log_error(NGX_LOG_ALERT, log, err,
+                          "sysctlbyname(kern.osrelease) failed");
 
-        ngx_darwin_kern_osrelease[size - 1] = '\0';
+            if (err != NGX_ENOMEM) {
+                return NGX_ERROR;
+            }
+
+            ngx_darwin_kern_osrelease[size - 1] = '\0';
+        }
     }
 
-
     for (i = 0; sysctls[i].name; i++) {
         size = sysctls[i].size;
 
@@ -136,8 +148,10 @@ ngx_os_specific_status(ngx_log_t *log)
     u_long      value;
     ngx_uint_t  i;
 
-    ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
-                  ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease);
+    if (ngx_darwin_kern_ostype[0]) {
+        ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
+                      ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease);
+    }
 
     for (i = 0; sysctls[i].name; i++) {
         if (sysctls[i].exists) {
--- a/src/os/unix/ngx_errno.c
+++ b/src/os/unix/ngx_errno.c
@@ -10,10 +10,11 @@
 
 #if (NGX_HAVE_STRERROR_R)
 
-u_char *ngx_strerror_r(int err, u_char *errstr, size_t size)
+u_char *
+ngx_strerror_r(int err, u_char *errstr, size_t size)
 {
     if (size == 0) {
-        return 0;
+        return errstr;
     }
 
     errstr[0] = '\0';
@@ -32,12 +33,13 @@ u_char *ngx_strerror_r(int err, u_char *
 
 /* Linux strerror_r() */
 
-u_char *ngx_strerror_r(int err, u_char *errstr, size_t size)
+u_char *
+ngx_strerror_r(int err, u_char *errstr, size_t size)
 {
     char  *str;
 
     if (size == 0) {
-        return 0;
+        return errstr;
     }
 
     errstr[0] = '\0';
--- a/src/os/unix/ngx_errno.h
+++ b/src/os/unix/ngx_errno.h
@@ -23,6 +23,7 @@ typedef int               ngx_err_t;
 #define NGX_EACCES        EACCES
 #define NGX_EBUSY         EBUSY
 #define NGX_EEXIST        EEXIST
+#define NGX_EXDEV         EXDEV
 #define NGX_ENOTDIR       ENOTDIR
 #define NGX_EISDIR        EISDIR
 #define NGX_EINVAL        EINVAL
@@ -43,6 +44,7 @@ typedef int               ngx_err_t;
 #define NGX_EHOSTUNREACH  EHOSTUNREACH
 #define NGX_ENOSYS        ENOSYS
 #define NGX_ECANCELED     ECANCELED
+#define NGX_EILSEQ        EILSEQ
 #define NGX_ENOMOREFILES  0
 
 
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -249,11 +249,6 @@ ngx_open_dir(ngx_str_t *name, ngx_dir_t 
     }
 
     dir->valid_info = 0;
-#if (NGX_HAVE_D_TYPE)
-    dir->valid_type = 1;
-#else
-    dir->valid_type = 0;
-#endif
 
     return NGX_OK;
 }
@@ -267,6 +262,9 @@ ngx_read_dir(ngx_dir_t *dir)
     if (dir->de) {
 #if (NGX_HAVE_D_TYPE)
         dir->type = dir->de->d_type;
+        dir->valid_type = dir->type ? 1 : 0;
+#else
+        dir->valid_type = 0;
 #endif
         return NGX_OK;
     }
@@ -418,3 +416,50 @@ ngx_directio_off(ngx_fd_t fd)
 }
 
 #endif
+
+
+#if (NGX_HAVE_STATFS)
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+    struct statfs  fs;
+
+    if (statfs((char *) name, &fs) == -1) {
+        return 512;
+    }
+
+    if ((fs.f_bsize % 512) != 0) {
+        return 512;
+    }
+
+    return (size_t) fs.f_bsize;
+}
+
+#elif (NGX_HAVE_STATVFS)
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+    struct statvfs  fs;
+
+    if (statvfs((char *) name, &fs) == -1) {
+        return 512;
+    }
+
+    if ((fs.f_frsize % 512) != 0) {
+        return 512;
+    }
+
+    return (size_t) fs.f_frsize;
+}
+
+#else
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+    return 512;
+}
+
+#endif
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -18,13 +18,13 @@ typedef ino_t                    ngx_fil
 
 
 typedef struct {
-    DIR                        *dir;
-    struct dirent              *de;
-    struct stat                 info;
+    DIR                         *dir;
+    struct dirent               *de;
+    struct stat                  info;
 
-    unsigned                    type:8;
-    unsigned                    valid_info:1;
-    unsigned                    valid_type:1;
+    unsigned                     type:8;
+    unsigned                     valid_info:1;
+    unsigned                     valid_type:1;
 } ngx_dir_t;
 
 
@@ -63,10 +63,11 @@ typedef struct {
 #define NGX_FILE_RDWR            O_RDWR
 #define NGX_FILE_CREATE_OR_OPEN  O_CREAT
 #define NGX_FILE_OPEN            0
-#define NGX_FILE_TRUNCATE        O_TRUNC
-#define NGX_FILE_APPEND          O_APPEND
+#define NGX_FILE_TRUNCATE        O_CREAT|O_TRUNC
+#define NGX_FILE_APPEND          O_WRONLY|O_APPEND
 
 #define NGX_FILE_DEFAULT_ACCESS  0644
+#define NGX_FILE_OWNER_ACCESS    0600
 
 
 #define ngx_close_file           close
@@ -99,9 +100,23 @@ ssize_t ngx_write_chain_to_file(ngx_file
 #define ngx_read_fd              read
 #define ngx_read_fd_n            "read()"
 
-#define ngx_write_fd             write
+/*
+ * we use inlined function instead of simple #define
+ * because glibc 2.3 sets warn_unused_result attribute for write()
+ * and in this case gcc 4.3 ignores (void) cast
+ */
+static ngx_inline ssize_t
+ngx_write_fd(ngx_fd_t fd, void *buf, size_t n)
+{
+    return write(fd, buf, n);
+}
+
 #define ngx_write_fd_n           "write()"
 
+
+#define ngx_write_console        ngx_write_fd
+
+
 #define ngx_linefeed(p)          *p++ = LF;
 #define NGX_LINEFEED_SIZE        1
 
@@ -147,8 +162,10 @@ ngx_int_t ngx_set_file_time(u_char *name
 
 #define ngx_realpath(p, r)       realpath((char *) p, (char *) r)
 #define ngx_realpath_n           "realpath()"
-#define ngx_getcwd(buf, size)    (getcwd(buf, size) != NULL)
+#define ngx_getcwd(buf, size)    (getcwd((char *) buf, size) != NULL)
 #define ngx_getcwd_n             "getcwd()"
+#define ngx_path_separator(c)    ((c) == '/')
+
 #define NGX_MAX_PATH             PATH_MAX
 
 #define NGX_DIR_MASK_LEN         0
@@ -190,10 +207,25 @@ ngx_int_t ngx_read_dir(ngx_dir_t *dir);
 
 #if (NGX_HAVE_D_TYPE)
 
+#if (NGX_LINUX)
+
+/* XFS on Linux does not set dirent.d_type */
+
+#define ngx_de_is_dir(dir)                                                   \
+    (((dir)->type) ? ((dir)->type == DT_DIR) : (S_ISDIR((dir)->info.st_mode)))
+#define ngx_de_is_file(dir)                                                  \
+    (((dir)->type) ? ((dir)->type == DT_REG) : (S_ISREG((dir)->info.st_mode)))
+#define ngx_de_is_link(dir)                                                  \
+    (((dir)->type) ? ((dir)->type == DT_LINK) : (S_ISLNK((dir)->info.st_mode)))
+
+#else
+
 #define ngx_de_is_dir(dir)       ((dir)->type == DT_DIR)
 #define ngx_de_is_file(dir)      ((dir)->type == DT_REG)
 #define ngx_de_is_link(dir)      ((dir)->type == DT_LINK)
 
+#endif /* NGX_LINUX */
+
 #else
 
 #define ngx_de_is_dir(dir)       (S_ISDIR((dir)->info.st_mode))
@@ -247,5 +279,12 @@ ngx_int_t ngx_directio_off(ngx_fd_t fd);
 
 #endif
 
+size_t ngx_fs_bsize(u_char *name);
+
+
+#define ngx_stderr               STDERR_FILENO
+#define ngx_set_stderr(fd)       dup2(fd, STDERR_FILENO)
+#define ngx_set_stderr_n         "dup2(STDERR_FILENO)"
+
 
 #endif /* _NGX_FILES_H_INCLUDED_ */
--- a/src/os/unix/ngx_freebsd.h
+++ b/src/os/unix/ngx_freebsd.h
@@ -14,7 +14,6 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(
 extern int         ngx_freebsd_kern_osreldate;
 extern int         ngx_freebsd_hw_ncpu;
 extern u_long      ngx_freebsd_net_inet_tcp_sendspace;
-extern int         ngx_freebsd_kern_ipc_zero_copy_send;
 
 extern ngx_uint_t  ngx_freebsd_sendfile_nbytes_bug;
 extern ngx_uint_t  ngx_freebsd_use_tcp_nopush;
--- a/src/os/unix/ngx_freebsd_config.h
+++ b/src/os/unix/ngx_freebsd_config.h
@@ -22,6 +22,8 @@
 #include <grp.h>
 #include <dirent.h>
 #include <glob.h>
+#include <sys/param.h>          /* ALIGN() */
+#include <sys/mount.h>          /* statfs() */
 
 #include <sys/filio.h>          /* FIONBIO */
 #include <sys/uio.h>
@@ -43,7 +45,6 @@
 #include <libutil.h>            /* setproctitle() before 4.1 */
 #include <osreldate.h>
 #include <sys/sysctl.h>
-#include <sys/param.h>          /* ALIGN() */
 
 
 #if __FreeBSD_version < 400017
--- a/src/os/unix/ngx_freebsd_init.c
+++ b/src/os/unix/ngx_freebsd_init.c
@@ -19,9 +19,6 @@ u_long  ngx_freebsd_net_inet_tcp_sendspa
 /* FreeBSD 4.9 */
 int     ngx_freebsd_machdep_hlt_logical_cpus;
 
-/* FreeBSD 5.0 */
-int     ngx_freebsd_kern_ipc_zero_copy_send;
-
 
 ngx_uint_t  ngx_freebsd_sendfile_nbytes_bug;
 ngx_uint_t  ngx_freebsd_use_tcp_nopush;
@@ -68,10 +65,6 @@ sysctl_t sysctls[] = {
       &ngx_freebsd_kern_ipc_somaxconn,
       sizeof(ngx_freebsd_kern_ipc_somaxconn), 0 },
 
-    { "kern.ipc.zero_copy.send",
-      &ngx_freebsd_kern_ipc_zero_copy_send,
-      sizeof(ngx_freebsd_kern_ipc_zero_copy_send), 0 },
-
     { NULL, NULL, 0, 0 }
 };
 
--- a/src/os/unix/ngx_linux_config.h
+++ b/src/os/unix/ngx_linux_config.h
@@ -28,6 +28,7 @@
 #include <grp.h>
 #include <dirent.h>
 #include <glob.h>
+#include <sys/vfs.h>            /* statfs() */
 
 #include <sys/uio.h>
 #include <sys/stat.h>
@@ -94,11 +95,6 @@ extern ssize_t sendfile(int s, int fd, i
 #endif
 
 
-#ifndef NGX_HAVE_GNU_CRYPT_R
-#define NGX_HAVE_GNU_CRYPT_R         1
-#endif
-
-
 #ifndef NGX_HAVE_INHERITED_NONBLOCK
 #define NGX_HAVE_INHERITED_NONBLOCK  0
 #endif
--- a/src/os/unix/ngx_os.h
+++ b/src/os/unix/ngx_os.h
@@ -13,7 +13,6 @@
 
 
 #define NGX_IO_SENDFILE    1
-#define NGX_IO_ZEROCOPY    2
 
 
 typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size);
@@ -38,6 +37,7 @@ void ngx_os_status(ngx_log_t *log);
 ngx_int_t ngx_os_specific_init(ngx_log_t *log);
 void ngx_os_specific_status(ngx_log_t *log);
 ngx_int_t ngx_daemon(ngx_log_t *log);
+ngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *sig, ngx_int_t pid);
 
 
 ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size);
@@ -54,7 +54,6 @@ extern ngx_int_t    ngx_max_sockets;
 extern ngx_uint_t   ngx_inherited_nonblocking;
 extern ngx_uint_t   ngx_tcp_nodelay_and_tcp_nopush;
 
-#define ngx_stderr_fileno  STDERR_FILENO
 
 #if (NGX_FREEBSD)
 #include <ngx_freebsd.h>
--- a/src/os/unix/ngx_posix_config.h
+++ b/src/os/unix/ngx_posix_config.h
@@ -44,6 +44,15 @@
 #include <grp.h>
 #include <dirent.h>
 #include <glob.h>
+#if (NGX_HAVE_SYS_PARAM_H)
+#include <sys/param.h>          /* statfs() */
+#endif
+#if (NGX_HAVE_SYS_MOUNT_H)
+#include <sys/mount.h>          /* statfs() */
+#endif
+#if (NGX_HAVE_SYS_STATVFS_H)
+#include <sys/statvfs.h>        /* statvfs() */
+#endif
 
 #if (NGX_HAVE_SYS_FILIO_H)
 #include <sys/filio.h>          /* FIONBIO */
--- a/src/os/unix/ngx_posix_init.c
+++ b/src/os/unix/ngx_posix_init.c
@@ -22,7 +22,7 @@ ngx_os_io_t ngx_os_io = {
     ngx_unix_recv,
     ngx_readv_chain,
     ngx_udp_unix_recv,
-    NULL,
+    ngx_unix_send,
     ngx_writev_chain,
     0
 };
--- a/src/os/unix/ngx_process.c
+++ b/src/os/unix/ngx_process.c
@@ -13,6 +13,7 @@
 typedef struct {
      int     signo;
      char   *signame;
+     char   *name;
      void  (*handler)(int signo);
 } ngx_signal_t;
 
@@ -36,39 +37,45 @@ ngx_process_t    ngx_processes[NGX_MAX_P
 ngx_signal_t  signals[] = {
     { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
       "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
+      "reload",
       ngx_signal_handler },
 
     { ngx_signal_value(NGX_REOPEN_SIGNAL),
       "SIG" ngx_value(NGX_REOPEN_SIGNAL),
+      "reopen",
       ngx_signal_handler },
 
     { ngx_signal_value(NGX_NOACCEPT_SIGNAL),
       "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
+      "",
       ngx_signal_handler },
 
     { ngx_signal_value(NGX_TERMINATE_SIGNAL),
       "SIG" ngx_value(NGX_TERMINATE_SIGNAL),
+      "stop",
       ngx_signal_handler },
 
     { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
       "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
+      "quit",
       ngx_signal_handler },
 
     { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
       "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
+      "",
       ngx_signal_handler },
 
-    { SIGALRM, "SIGALRM", ngx_signal_handler },
+    { SIGALRM, "SIGALRM", "", ngx_signal_handler },
 
-    { SIGINT, "SIGINT", ngx_signal_handler },
+    { SIGINT, "SIGINT", "", ngx_signal_handler },
 
-    { SIGIO, "SIGIO", ngx_signal_handler },
+    { SIGIO, "SIGIO", "", ngx_signal_handler },
 
-    { SIGCHLD, "SIGCHLD", ngx_signal_handler },
+    { SIGCHLD, "SIGCHLD", "", ngx_signal_handler },
 
-    { SIGPIPE, "SIGPIPE, SIG_IGN", SIG_IGN },
+    { SIGPIPE, "SIGPIPE, SIG_IGN", "", SIG_IGN },
 
-    { 0, NULL, NULL }
+    { 0, NULL, "", NULL }
 };
 
 
@@ -494,10 +501,16 @@ ngx_process_get_status(void)
         }
 
         if (WTERMSIG(status)) {
+#ifdef WCOREDUMP
             ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
                           "%s %P exited on signal %d%s",
                           process, pid, WTERMSIG(status),
                           WCOREDUMP(status) ? " (core dumped)" : "");
+#else
+            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+                          "%s %P exited on signal %d",
+                          process, pid, WTERMSIG(status));
+#endif
 
         } else {
             ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
@@ -534,3 +547,23 @@ ngx_debug_point(void)
         ngx_abort();
     }
 }
+
+
+ngx_int_t
+ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_int_t pid)
+{
+    ngx_signal_t  *sig;
+
+    for (sig = signals; sig->signo != 0; sig++) {
+        if (ngx_strcmp(name, sig->name) == 0) {
+            if (kill(pid, sig->signo) != -1) {
+                return 0;
+            }
+
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "kill(%P, %d) failed", pid, sig->signo);
+        }
+    }
+
+    return 1;
+}
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -12,7 +12,7 @@
 
 static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n,
     ngx_int_t type);
-static void ngx_start_garbage_collector(ngx_cycle_t *cycle, ngx_int_t type);
+static void ngx_start_cache_manager_process(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_children(ngx_cycle_t *cycle);
 static void ngx_master_process_exit(ngx_cycle_t *cycle);
@@ -24,9 +24,8 @@ static void ngx_channel_handler(ngx_even
 static void ngx_wakeup_worker_threads(ngx_cycle_t *cycle);
 static ngx_thread_value_t ngx_worker_thread_cycle(void *data);
 #endif
-#if 0
-static void ngx_garbage_collector_cycle(ngx_cycle_t *cycle, void *data);
-#endif
+static void ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data);
+static void ngx_cache_manager_process_handler(ngx_event_t *ev);
 
 
 ngx_uint_t    ngx_process;
@@ -123,7 +122,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
 
     ngx_start_worker_processes(cycle, ccf->worker_processes,
                                NGX_PROCESS_RESPAWN);
-    ngx_start_garbage_collector(cycle, NGX_PROCESS_RESPAWN);
+    ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN);
 
     ngx_new_binary = 0;
     delay = 0;
@@ -204,7 +203,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
             if (ngx_new_binary) {
                 ngx_start_worker_processes(cycle, ccf->worker_processes,
                                            NGX_PROCESS_RESPAWN);
-                ngx_start_garbage_collector(cycle, NGX_PROCESS_RESPAWN);
+                ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN);
                 ngx_noaccepting = 0;
 
                 continue;
@@ -223,7 +222,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
                                                    ngx_core_module);
             ngx_start_worker_processes(cycle, ccf->worker_processes,
                                        NGX_PROCESS_JUST_RESPAWN);
-            ngx_start_garbage_collector(cycle, NGX_PROCESS_JUST_RESPAWN);
+            ngx_start_cache_manager_process(cycle, NGX_PROCESS_JUST_RESPAWN);
             live = 1;
             ngx_signal_worker_processes(cycle,
                                         ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
@@ -233,7 +232,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
             ngx_restart = 0;
             ngx_start_worker_processes(cycle, ccf->worker_processes,
                                        NGX_PROCESS_RESPAWN);
-            ngx_start_garbage_collector(cycle, NGX_PROCESS_RESPAWN);
+            ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN);
             live = 1;
         }
 
@@ -361,18 +360,28 @@ ngx_start_worker_processes(ngx_cycle_t *
 
 
 static void
-ngx_start_garbage_collector(ngx_cycle_t *cycle, ngx_int_t type)
+ngx_start_cache_manager_process(ngx_cycle_t *cycle, ngx_int_t type)
 {
-#if 0
-    ngx_int_t      i;
-    ngx_channel_t  ch;
+    ngx_int_t        i;
+    ngx_uint_t       n;
+    ngx_path_t     **path;
+    ngx_channel_t    ch;
 
-    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start garbage collector");
+    path = ngx_cycle->pathes.elts;
+    for (n = 0; n < ngx_cycle->pathes.nelts; n++) {
+        if (path[n]->manager) {
+            goto start;
+        }
+    }
+
+    return;
+
+start:
 
     ch.command = NGX_CMD_OPEN_CHANNEL;
 
-    ngx_spawn_process(cycle, ngx_garbage_collector_cycle, NULL,
-                      "garbage collector", type);
+    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle, NULL,
+                      "cache manager process", type);
 
     ch.pid = ngx_processes[ngx_process_slot].pid;
     ch.slot = ngx_process_slot;
@@ -398,7 +407,6 @@ ngx_start_garbage_collector(ngx_cycle_t 
         ngx_write_channel(ngx_processes[i].channel[0],
                           &ch, sizeof(ngx_channel_t), cycle->log);
     }
-#endif
 }
 
 
@@ -1004,7 +1012,7 @@ ngx_worker_process_exit(ngx_cycle_t *cyc
                 && !c[i].read->resolver)
             {
                 ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
-                              "open socket #%d left in %ui connection %s",
+                              "open socket #%d left in connection %ui%s",
                               c[i].fd, i, ngx_debug_quit ? ", aborting" : "");
                 ngx_debug_point();
             }
@@ -1254,27 +1262,29 @@ ngx_worker_thread_cycle(void *data)
 #endif
 
 
-#if 0
-
 static void
-ngx_garbage_collector_cycle(ngx_cycle_t *cycle, void *data)
+ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data)
 {
-    ngx_uint_t         i;
-    ngx_gc_t           ctx;
-    ngx_path_t       **path;
-    ngx_event_t       *ev;
+    void         *ident[4];
+    ngx_event_t   ev;
+
+    cycle->connection_n = 512;
 
     ngx_worker_process_init(cycle, 0);
 
-    ev = &cycle->read_events0[ngx_channel];
-
-    ngx_accept_mutex = NULL;
+    ngx_close_listening_sockets(cycle);
 
-    ngx_setproctitle("garbage collector");
+    ngx_memzero(&ev, sizeof(ngx_event_t));
+    ev.handler = ngx_cache_manager_process_handler;
+    ev.data = ident;
+    ev.log = cycle->log;
+    ident[3] = (void *) -1;
 
-#if 0
-    ngx_add_timer(ev, 60 * 1000);
-#endif
+    ngx_use_accept_mutex = 0;
+
+    ngx_setproctitle("cache manager process");
+
+    ngx_add_timer(&ev, 0);
 
     for ( ;; ) {
 
@@ -1289,19 +1299,35 @@ ngx_garbage_collector_cycle(ngx_cycle_t 
             ngx_reopen_files(cycle, -1);
         }
 
-        path = cycle->pathes.elts;
-        for (i = 0; i < cycle->pathes.nelts; i++) {
-            ctx.path = path[i];
-            ctx.log = cycle->log;
-            ctx.handler = path[i]->cleaner;
-
-            ngx_collect_garbage(&ctx, &path[i]->name, 0);
-        }
-
-        ngx_add_timer(ev, 60 * 60 * 1000);
-
         ngx_process_events_and_timers(cycle);
     }
 }
 
-#endif
+
+static void
+ngx_cache_manager_process_handler(ngx_event_t *ev)
+{
+    time_t        next, n;
+    ngx_uint_t    i;
+    ngx_path_t  **path;
+
+    next = 60 * 60;
+
+    path = ngx_cycle->pathes.elts;
+    for (i = 0; i < ngx_cycle->pathes.nelts; i++) {
+
+        if (path[i]->manager) {
+            n = path[i]->manager(path[i]->data);
+
+            next = (n <= next) ? n : next;
+
+            ngx_time_update(0, 0);
+        }
+    }
+
+    if (next == 0) {
+        next = 1;
+    }
+
+    ngx_add_timer(ev, next * 1000);
+}
--- a/src/os/unix/ngx_process_cycle.h
+++ b/src/os/unix/ngx_process_cycle.h
@@ -19,9 +19,10 @@
 #define NGX_CMD_REOPEN         5
 
 
-#define NGX_PROCESS_SINGLE   0
-#define NGX_PROCESS_MASTER   1
-#define NGX_PROCESS_WORKER   2
+#define NGX_PROCESS_SINGLE     0
+#define NGX_PROCESS_MASTER     1
+#define NGX_PROCESS_WORKER     2
+#define NGX_PROCESS_SIGNALLER  3
 
 
 void ngx_master_process_cycle(ngx_cycle_t *cycle);
--- a/src/os/unix/ngx_send.c
+++ b/src/os/unix/ngx_send.c
@@ -40,6 +40,8 @@ ngx_unix_send(ngx_connection_t *c, u_cha
                 wev->ready = 0;
             }
 
+            c->sent += n;
+
             return n;
         }
 
--- a/src/os/unix/ngx_shmem.h
+++ b/src/os/unix/ngx_shmem.h
@@ -15,7 +15,9 @@
 typedef struct {
     u_char      *addr;
     size_t       size;
+    ngx_str_t    name;
     ngx_log_t   *log;
+    ngx_uint_t   exists;   /* unsigned  exists:1;  */
 } ngx_shm_t;
 
 
--- a/src/os/unix/ngx_solaris_config.h
+++ b/src/os/unix/ngx_solaris_config.h
@@ -28,6 +28,7 @@
 #include <grp.h>
 #include <dirent.h>
 #include <glob.h>
+#include <sys/statvfs.h>        /* statvfs() */
 
 #include <sys/filio.h>          /* FIONBIO */
 #include <sys/uio.h>
--- a/src/os/unix/ngx_sunpro_x86.il
+++ b/src/os/unix/ngx_sunpro_x86.il
@@ -5,7 +5,7 @@
 / ngx_atomic_uint_t ngx_atomic_cmp_set(ngx_atomic_t *lock,
 /     ngx_atomic_uint_t old, ngx_atomic_uint_t set);
 /
-/ the arguments are passed on the stack (%esp), 4(%esp), 8(%esp)
+/ the arguments are passed on stack (%esp), 4(%esp), 8(%esp)
 
         .inline ngx_atomic_cmp_set,0
         movl      (%esp), %ecx
@@ -21,7 +21,7 @@
 / ngx_atomic_int_t ngx_atomic_fetch_add(ngx_atomic_t *value,
 /     ngx_atomic_int_t add);
 /
-/ the arguments are passed on the stack (%esp), 4(%esp)
+/ the arguments are passed on stack (%esp), 4(%esp)
 
         .inline ngx_atomic_fetch_add,0
         movl      (%esp), %ecx
@@ -32,7 +32,12 @@
 
 
 / ngx_cpu_pause()
+/
+/ the "rep; nop" is used instead of "pause" to avoid the "[ PAUSE ]" hardware
+/ capability added by linker because Solaris/i386 does not know about it:
+/
+/ ld.so.1: nginx: fatal: hardware capability unsupported: 0x2000  [ PAUSE ]
 
        .inline ngx_cpu_pause,0
-       pause
+       rep; nop
        .end
deleted file mode 100644
--- a/src/os/unix/ngx_sunpro_x86.map
+++ /dev/null
@@ -1,2 +0,0 @@
-# disable { PAUSE ] hwcap for Sun Studio 11
-hwcap_1 = OVERRIDE;