changeset 511:1e91f9968443

Merge with 0.7.47.
author Maxim Dounin <mdounin@mdounin.ru>
date Sun, 05 Apr 2009 03:35:26 +0400
parents c78a94ba4ae1 (current diff) 0cc33540f2e0 (diff)
children 9773720b845e
files .hgtags auto/modules src/core/ngx_garbage_collector.c src/core/ngx_garbage_collector.h src/http/ngx_http_cache.c src/mail/ngx_mail_core_module.c
diffstat 90 files changed, 6404 insertions(+), 3250 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags
+++ b/.hgtags
@@ -228,3 +228,16 @@ ce4f9ff90bfa58834c5b0db35395fd980c8c4aa0
 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
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,190 @@
 
+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 an 
+       ".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.
@@ -382,7 +568,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.
 
 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,189 @@
 
+Изменения в 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.
--- 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
 
@@ -76,13 +78,11 @@ 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"
 
--- 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/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
@@ -305,11 +312,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
@@ -160,7 +163,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" ;;
@@ -227,6 +234,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"          ;;
 
@@ -285,6 +293,8 @@ 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
@@ -330,6 +340,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
@@ -350,7 +361,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
 
--- 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,28 @@ 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
--- 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,9 @@ ngx_feature_libs=
 ngx_feature_test="long mask = 0;
                   sched_setaffinity(0, 32, (cpu_set_t *) &mask)"
 . auto/feature
+
+
+ngx_include="sys/vfs.h";     . auto/include
+
+
+CC_AUX_FLAGS=$cc_aux_flags
--- 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
@@ -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"
 
@@ -313,7 +309,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
 
 
--- 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
@@ -175,8 +190,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 +205,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 +229,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/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -388,7 +388,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;
     }
@@ -838,7 +838,7 @@ ngx_core_module_init_conf(ngx_cycle_t *c
         ccf->pid.data = (u_char *) NGX_PID_PATH;
     }
 
-    if (ngx_conf_full_name(cycle, &ccf->pid, 0) == NGX_ERROR) {
+    if (ngx_conf_full_name(cycle, &ccf->pid, 0) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
@@ -858,7 +858,7 @@ ngx_core_module_init_conf(ngx_cycle_t *c
         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.34"
+#define nginx_version       007047
+#define NGINX_VERSION      "0.7.47"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -741,7 +741,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;
     }
 
@@ -854,7 +854,7 @@ ngx_conf_open_file(ngx_cycle_t *cycle, n
     if (name) {
         full = *name;
 
-        if (ngx_conf_full_name(cycle, &full, 0) == NGX_ERROR) {
+        if (ngx_conf_full_name(cycle, &full, 0) != NGX_OK) {
             return NULL;
         }
 
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -52,7 +52,6 @@ ngx_listening_inet_stream_socket(ngx_con
     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;
 
     return ls;
@@ -65,7 +64,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 +92,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 +282,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, cycle->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)) {
@@ -778,12 +799,16 @@ ngx_connection_error(ngx_connection_t *c
 {
     ngx_uint_t  level;
 
-    if (err == NGX_ECONNRESET
-        && c->log_error == NGX_ERROR_IGNORE_ECONNRESET)
-    {
+    if (err == NGX_ECONNRESET && 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)
@@ -799,6 +824,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;
 
@@ -57,6 +56,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 +73,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,10 +127,8 @@ 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;
 
@@ -134,7 +136,7 @@ struct ngx_connection_s {
 
     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;
--- 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
@@ -136,7 +136,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,7 +155,7 @@ 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;
@@ -359,8 +359,9 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
             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,
@@ -876,23 +877,47 @@ 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;
@@ -1040,9 +1065,8 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx
             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",
--- 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;
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -11,65 +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;
-    ngx_uint_t          path_access;
-    time_t              time;
-    ngx_fd_t            fd;
-    ngx_err_t           rename_error;
+    ngx_uint_t                 access;
+    ngx_uint_t                 path_access;
+    time_t                     time;
+    ngx_fd_t                   fd;
+    ngx_err_t                  rename_error;
 
-    unsigned            create_path:1;
-    unsigned            delete_file:1;
-    unsigned            log_rename_error:1;
+    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;
 
 
@@ -113,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, *args;
-    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;
 
@@ -311,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;
@@ -334,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;
                 }
             }
@@ -374,8 +519,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) {
@@ -385,16 +531,24 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
             }
 
             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) {
@@ -409,11 +563,134 @@ 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);
+
+    rc = inet_pton(AF_INET6, (const char *) p, &sin6->sin6_addr);
+
+    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;
@@ -428,6 +705,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) {
@@ -464,22 +743,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;
         }
 
@@ -502,18 +781,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,102 @@
 #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;
+
+
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+    struct in6_addr           addr;
+    struct in6_addr           mask;
+} ngx_in6_cidr_t;
+
+#endif
+
+
+typedef struct {
+    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 union {
-    in_addr_t         in_addr;
+    in_addr_t                 in_addr;
 } ngx_url_addr_t;
 
 
 typedef struct {
-    struct sockaddr  *sockaddr;
-    socklen_t         socklen;
-    ngx_str_t         name;
+    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;
+    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
@@ -200,8 +200,10 @@ ngx_log_init(void)
 
     ngx_stderr_fileno = GetStdHandle(STD_ERROR_HANDLE);
 
-    ngx_stderr.fd = ngx_open_file(NGX_ERROR_LOG_PATH, NGX_FILE_RDWR,
-                                  NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND, 0);
+    ngx_stderr.fd = ngx_open_file((u_char *) NGX_ERROR_LOG_PATH,
+                                  NGX_FILE_APPEND,
+                                  NGX_FILE_CREATE_OR_OPEN,
+                                  NGX_FILE_DEFAULT_ACCESS);
 
     if (ngx_stderr.fd == NGX_INVALID_FILE) {
         ngx_message_box("nginx", MB_OK, ngx_errno,
@@ -210,14 +212,6 @@ ngx_log_init(void)
         return NULL;
     }
 
-    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;
-    }
-
 #else
 
     ngx_stderr.fd = STDERR_FILENO;
@@ -329,7 +323,7 @@ ngx_set_error_log(ngx_conf_t *cf, ngx_co
         cf->cycle->new_log->file->name = value[1];
 
         if (ngx_conf_full_name(cf->cycle, &cf->cycle->new_log->file->name, 0)
-            == NGX_ERROR)
+            != NGX_OK)
         {
             return NGX_CONF_ERROR;
         }
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -466,8 +466,7 @@ 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);
     }
 
--- a/src/core/ngx_palloc.c
+++ b/src/core/ngx_palloc.c
@@ -326,6 +326,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
@@ -82,6 +82,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_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;
         }
 
@@ -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:
 
@@ -679,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;
 }
@@ -711,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,9 @@ typedef struct {
     u_char           *end;
 
     ngx_shmtx_t       mutex;
+
+    u_char           *log_ctx;
+    u_char            zero;
 } ngx_slab_pool_t;
 
 
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -1040,18 +1040,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 +1058,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;
     }
 
--- 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);
 
@@ -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;
 
@@ -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;
--- 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
 };
 
 
@@ -198,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;
     }
 
@@ -211,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;
     }
 
@@ -242,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;
     }
 
@@ -400,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;
     }
 
@@ -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;
 
@@ -1441,6 +1433,16 @@ ngx_ssl_session_cache_init(ngx_shm_zone_
 
     ngx_queue_init(&cache->expire_queue);
 
+    len = sizeof(" in SSL session shared cache \"\"") + shm_zone->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->name);
+
     shm_zone->data = cache;
 
     return NGX_OK;
@@ -2113,8 +2115,7 @@ ngx_openssl_create_conf(ngx_cycle_t *cyc
     /*
      * set by ngx_pcalloc():
      *
-     *     oscf->engine.len = 0;
-     *     oscf->engine.data = NULL;
+     *     oscf->engine = 0;
      */
 
     return oscf;
@@ -2122,53 +2123,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
@@ -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;
 }
--- 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);
     }
 
@@ -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_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) {
--- a/src/http/modules/ngx_http_charset_filter_module.c
+++ b/src/http/modules/ngx_http_charset_filter_module.c
@@ -1421,19 +1421,21 @@ ngx_http_charset_create_main_conf(ngx_co
     }
 
     if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t))
-        == NGX_ERROR)
+        != NGX_OK)
     {
         return NGX_CONF_ERROR;
     }
 
     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;
     }
 
     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;
     }
--- 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
@@ -23,6 +23,10 @@ typedef struct {
     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;
@@ -116,6 +120,9 @@ typedef struct {
 
 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);
@@ -144,6 +151,13 @@ static char *ngx_http_fastcgi_split_path
     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);
 
@@ -169,6 +183,9 @@ static ngx_conf_bitmask_t  ngx_http_fast
 };
 
 
+ngx_module_t  ngx_http_fastcgi_module;
+
+
 static ngx_command_t  ngx_http_fastcgi_commands[] = {
 
     { ngx_string("fastcgi_pass"),
@@ -283,12 +300,58 @@ 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("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_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 },
+
+#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,
@@ -436,6 +499,11 @@ 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)
 {
@@ -486,6 +554,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;
@@ -539,7 +610,7 @@ ngx_http_fastcgi_eval(ngx_http_request_t
 
     if (u.no_port) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "no port in upstream \"%V\"", &u.url);
+                      "no port in upstream \"%V\"", &u.url);
         return NGX_ERROR;
     }
 
@@ -564,6 +635,31 @@ ngx_http_fastcgi_eval(ngx_http_request_t
 }
 
 
+#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)
 {
@@ -1271,12 +1367,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;
             }
@@ -1725,6 +1818,7 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
      *
      *     conf->upstream.bufs.num = 0;
      *     conf->upstream.next_upstream = 0;
+     *     conf->upstream.use_stale_cache = 0;
      *     conf->upstream.temp_path = NULL;
      *     conf->upstream.hide_headers_hash = { NULL, 0 };
      *     conf->upstream.uri = { 0, NULL };
@@ -1755,6 +1849,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;
 
@@ -1923,10 +2023,48 @@ 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->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;
+    }
+
+    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
+                             prev->upstream.cache_valid, NULL);
+
+#endif
 
     ngx_conf_merge_value(conf->upstream.pass_request_headers,
                               prev->upstream.pass_request_headers, 1);
@@ -2401,6 +2539,66 @@ 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;
+    }
+
+    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_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -175,6 +175,10 @@ ngx_http_geo_addr(ngx_http_request_t *r,
         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);
     }
@@ -774,7 +778,7 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht
     ngx_int_t                        rc, del;
     ngx_str_t                       *net;
     ngx_uint_t                       i;
-    ngx_inet_cidr_t                  cidrin;
+    ngx_cidr_t                       cidr;
     ngx_http_variable_value_t       *val, *old;
 
     if (ctx->tree == NULL) {
@@ -785,8 +789,8 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht
     }
 
     if (ngx_strcmp(value[0].data, "default") == 0) {
-        cidrin.addr = 0;
-        cidrin.mask = 0;
+        cidr.u.in.addr = 0;
+        cidr.u.in.mask = 0;
         net = &value[0];
 
     } else {
@@ -800,11 +804,11 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht
         }
 
         if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
-            cidrin.addr = 0xffffffff;
-            cidrin.mask = 0xffffffff;
+            cidr.u.in.addr = 0xffffffff;
+            cidr.u.in.mask = 0xffffffff;
 
         } else {
-            rc = ngx_ptocidr(net, &cidrin);
+            rc = ngx_ptocidr(net, &cidr);
 
             if (rc == NGX_ERROR) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -812,18 +816,25 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht
                 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);
             }
 
-            cidrin.addr = ntohl(cidrin.addr);
-            cidrin.mask = ntohl(cidrin.mask);
+            cidr.u.in.addr = ntohl(cidr.u.in.addr);
+            cidr.u.in.mask = ntohl(cidr.u.in.mask);
         }
 
         if (del) {
-            if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask)
+            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,
@@ -841,7 +852,7 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht
     }
 
     for (i = 2; i; i--) {
-        rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask,
+        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;
@@ -854,13 +865,13 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht
         /* 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 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");
--- 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;
@@ -84,7 +85,7 @@ struct gztrailer {
 
 static void ngx_http_gzip_filter_memory(ngx_http_request_t *r,
     ngx_http_gzip_ctx_t *ctx);
-static ngx_int_t ngx_http_gzip_filter_copy_recycled(ngx_http_gzip_ctx_t *ctx,
+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);
@@ -170,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,
@@ -257,7 +265,7 @@ ngx_http_gzip_header_filter(ngx_http_req
     ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
 
     ctx->request = r;
-    ctx->buffering = 1;
+    ctx->buffering = (conf->postpone_gzipping != 0);
 
     ngx_http_gzip_filter_memory(r, ctx);
 
@@ -301,8 +309,17 @@ ngx_http_gzip_body_filter(ngx_http_reque
 
     if (ctx->buffering) {
 
+        /*
+         * 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.
+         */
+
         if (in) {
-            switch (ngx_http_gzip_filter_copy_recycled(ctx, in)) {
+            switch (ngx_http_gzip_filter_buffer(ctx, in)) {
 
             case NGX_OK:
                 return NGX_OK;
@@ -327,7 +344,7 @@ ngx_http_gzip_body_filter(ngx_http_reque
     }
 
     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) {
             goto failed;
         }
     }
@@ -482,12 +499,13 @@ ngx_http_gzip_filter_memory(ngx_http_req
 
 
 static ngx_int_t
-ngx_http_gzip_filter_copy_recycled(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in)
+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;
+    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;
 
@@ -501,6 +519,8 @@ ngx_http_gzip_filter_copy_recycled(ngx_h
         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) {
@@ -512,25 +532,11 @@ ngx_http_gzip_filter_copy_recycled(ngx_h
         size = b->last - b->pos;
         buffered += size;
 
-        if (b->flush || b->last_buf) {
-            ctx->buffering = 0;
-
-        } else if (buffered > ctx->allocated / 2) {
-
-            /*
-             * With default memory settings zlib starts to output gzipped data
-             * only after it has got about 90K, so it makes sense to allocate
-             * zlib memory (200-400K) only after we have enough data
-             * to compress.  Although we copy recycled buffers, nevertheless
-             * for responses up to 120K this allows to allocate zlib memory,
-             * to compress and to output the response in one step
-             * using hot CPU cache.
-             */
-
+        if (b->flush || b->last_buf || buffered > conf->postpone_gzipping) {
             ctx->buffering = 0;
         }
 
-        if (ctx->buffering && size && b->recycled) {
+        if (ctx->buffering && size) {
 
             buf = ngx_create_temp_buf(r->pool, size);
             if (buf == NULL) {
@@ -1077,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;
@@ -1093,16 +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,
                               (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,
--- 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) {
@@ -331,8 +323,8 @@ ngx_http_add_header(ngx_http_request_t *
             return NGX_ERROR;
         }
 
-        h->hash = hv->value.hash;
-        h->key = hv->value.key;
+        h->hash = hv->hash;
+        h->key = hv->key;
         h->value = *value;
     }
 
@@ -414,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;
@@ -578,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;
 
@@ -595,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;
     }
 
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -198,7 +198,7 @@ ngx_http_index_handler(ngx_http_request_
 
             path.len = e.pos - path.data;
 
-            *e.pos++ = '\0';
+            *e.pos = '\0';
         }
 
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
--- a/src/http/modules/ngx_http_limit_req_module.c
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -224,11 +224,6 @@ ngx_http_limit_req_handler(ngx_http_requ
         node = ngx_slab_alloc_locked(ctx->shpool, n);
         if (node == NULL) {
             ngx_shmtx_unlock(&ctx->shpool->mutex);
-
-            ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
-                          "could not allocate memory in zone \"%V\"",
-                          &lrcf->shm_zone->name);
-
             return NGX_HTTP_SERVICE_UNAVAILABLE;
         }
     }
@@ -457,6 +452,7 @@ ngx_http_limit_req_init_zone(ngx_shm_zon
 {
     ngx_http_limit_req_ctx_t  *octx = data;
 
+    size_t                     len;
     ngx_rbtree_node_t         *sentinel;
     ngx_http_limit_req_ctx_t  *ctx;
 
@@ -500,6 +496,16 @@ ngx_http_limit_req_init_zone(ngx_shm_zon
 
     ngx_queue_init(ctx->queue);
 
+    len = sizeof(" in limit_req zone \"\"") + shm_zone->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->name);
+
     return NGX_OK;
 }
 
--- a/src/http/modules/ngx_http_limit_zone_module.c
+++ b/src/http/modules/ngx_http_limit_zone_module.c
@@ -210,11 +210,6 @@ ngx_http_limit_zone_handler(ngx_http_req
     node = ngx_slab_alloc_locked(shpool, n);
     if (node == NULL) {
         ngx_shmtx_unlock(&shpool->mutex);
-
-        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
-                      "could not allocate memory in zone \"%V\"",
-                      &lzcf->shm_zone->name);
-
         return NGX_HTTP_SERVICE_UNAVAILABLE;
     }
 
@@ -321,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;
@@ -356,6 +352,15 @@ 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->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->name);
+
     return NGX_OK;
 }
 
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -863,7 +863,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
@@ -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_not_modified_filter_module.c
+++ b/src/http/modules/ngx_http_not_modified_filter_module.c
@@ -83,6 +83,7 @@ ngx_int_t ngx_http_not_modified_header_f
     }
 
     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);
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -32,6 +32,7 @@ struct ngx_http_proxy_redirect_s {
 
 
 typedef struct {
+    ngx_str_t                      key_start;
     ngx_str_t                      schema;
     ngx_str_t                      host_header;
     ngx_str_t                      port;
@@ -89,6 +90,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);
@@ -116,6 +120,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);
@@ -123,6 +129,10 @@ 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);
+#endif
 
 static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data);
 
@@ -135,8 +145,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 =
@@ -157,6 +166,9 @@ static ngx_conf_bitmask_t  ngx_http_prox
 };
 
 
+ngx_module_t  ngx_http_proxy_module;
+
+
 static ngx_command_t  ngx_http_proxy_commands[] = {
 
     { ngx_string("proxy_pass"),
@@ -306,12 +318,51 @@ 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_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 },
+
+#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,
@@ -455,6 +506,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)
 {
@@ -502,6 +558,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;
@@ -537,11 +596,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)
 {
-    u_char     *p;
-    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)
@@ -571,70 +631,162 @@ 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 (u.uri.len && u.uri.data[0] == '?') {
-        p = ngx_pnalloc(r->pool, u.uri.len + 1);
+    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, u.uri.data, u.uri.len);
-
-        u.uri.len++;
-        u.uri.data = p - 1;
+        ngx_memcpy(p, url.uri.data, url.uri.len);
+
+        url.uri.len++;
+        url.uri.data = p - 1;
     }
 
-    if (ngx_http_proxy_set_vars(r->pool, &u, &ctx->vars) != NGX_OK) {
+    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 (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;
+    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 {
-        r->upstream->resolved->host = u.host;
-        r->upstream->resolved->port = (in_port_t) (u.no_port ? u.default_port:
-                                                               u.port);
-        r->upstream->resolved->no_port = u.no_port;
+        u->resolved->host = url.host;
+        u->resolved->port = (in_port_t) (url.no_port ? port : url.port);
+        u->resolved->no_port = url.no_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;
+    }
+
+    *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;
@@ -675,12 +827,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) ?
@@ -691,10 +843,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) {
@@ -980,6 +1140,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");
 
@@ -996,8 +1167,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,
@@ -1638,6 +1812,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
      *
      *     conf->upstream.bufs.num = 0;
      *     conf->upstream.next_upstream = 0;
+     *     conf->upstream.use_stale_cache = 0;
      *     conf->upstream.temp_path = NULL;
      *     conf->upstream.hide_headers_hash = { NULL, 0 };
      *     conf->upstream.uri = { 0, NULL };
@@ -1675,6 +1850,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;
 
@@ -1702,16 +1883,11 @@ 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_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,
@@ -1850,10 +2026,49 @@ 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->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;
+    }
+
+    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
+                             prev->upstream.cache_valid, NULL);
+
+#endif
 
     if (conf->method.len == 0) {
         conf->method = prev->method;
@@ -1984,6 +2199,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;
@@ -1994,31 +2230,31 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     }
 
     if (conf->headers_set_hash.buckets) {
-        return NGX_CONF_OK;
+        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;
     }
 
 
@@ -2034,7 +2270,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;
@@ -2052,7 +2288,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;
@@ -2067,7 +2303,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)
@@ -2084,7 +2320,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;
@@ -2102,7 +2338,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)
@@ -2116,7 +2352,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;
@@ -2136,14 +2372,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)
@@ -2157,7 +2393,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;
@@ -2169,14 +2405,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;
@@ -2184,7 +2420,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;
@@ -2202,10 +2438,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;
 }
 
 
@@ -2298,16 +2534,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->vars.schema.len = add;
     plcf->vars.schema.data = url->data;
-    plcf->location = clcf->name;
+    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
@@ -2473,6 +2709,38 @@ 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;
+    }
+
+    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;
+}
+
+#endif
+
+
 static char *
 ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data)
 {
@@ -2563,12 +2831,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) {
@@ -2586,14 +2855,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_range_filter_module.c
+++ b/src/http/modules/ngx_http_range_filter_module.c
@@ -187,7 +187,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;
     }
@@ -199,6 +199,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);
@@ -708,6 +709,7 @@ static ngx_int_t
 ngx_http_range_multipart_body(ngx_http_request_t *r,
     ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
 {
+    off_t              body_start;
     ngx_buf_t         *b, *buf;
     ngx_uint_t         i;
     ngx_chain_t       *out, *hcl, *rcl, *dcl, **ll;
@@ -717,6 +719,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++) {
 
         /*
@@ -777,8 +785,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;
 }
--- 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;
@@ -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_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -397,7 +397,7 @@ 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;
         }
     }
@@ -2060,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;
         }
 
--- 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;
                 }
@@ -609,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";
@@ -623,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;
     }
 
@@ -684,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
@@ -388,7 +394,28 @@ ngx_http_userid_set_uid(ngx_http_request
                     return NGX_ERROR;
                 }
 
-                ctx->uid_set[0] = htonl(r->in_addr);
+                c = r->connection;
+
+                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);
--- a/src/http/modules/ngx_http_xslt_filter_module.c
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -38,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;
 
 
@@ -867,30 +861,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) {
 
@@ -1073,8 +1062,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;
@@ -1142,7 +1131,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;
@@ -1155,22 +1144,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;
--- 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.34';
+our $VERSION = '0.7.47';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -19,11 +19,17 @@ static ngx_int_t ngx_http_init_phase_han
 
 static ngx_int_t ngx_http_init_server_lists(ngx_conf_t *cf,
     ngx_array_t *servers, ngx_array_t *in_ports);
+static ngx_int_t ngx_http_add_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;
 
@@ -351,8 +367,6 @@ 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) {
         return NGX_CONF_ERROR;
     }
@@ -360,8 +374,6 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
 
     /* optimize the lists of ports, addresses and server names */
 
-    /* AF_INET only */
-
     if (ngx_http_optimize_servers(cf, cmcf, &in_ports) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -1099,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;
@@ -1122,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) {
-
-                        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) {
+        for (i = 0; i < cscfp[s]->listen.nelts; i++) {
+
+            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;
         }
     }
 
@@ -1215,6 +1143,140 @@ 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 in_port list */
+
+        return ngx_http_add_addresses(cf, cscf, &port[i], listen);
+    }
+
+    /* add a port to the in_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)
@@ -1222,60 +1284,62 @@ ngx_http_init_server_lists(ngx_conf_t *c
 
 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;
-
-    if (in_port->addrs.elts == NULL) {
-        if (ngx_array_init(&in_port->addrs, cf->temp_pool, 4,
-                           sizeof(ngx_http_conf_in_addr_t))
+    ngx_http_conf_addr_t  *addr;
+
+    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;
-
-    return ngx_http_add_names(cf, cscf, in_addr);
+    addr->listen_conf = &listen->conf;
+
+    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)
         {
@@ -1293,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;
         }
@@ -1307,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_uint_t                s, p, a;
+    ngx_http_conf_port_t     *port;
+    ngx_http_conf_addr_t     *addr;
+    ngx_http_server_name_t   *name;
+
+    port = ports->elts;
+    for (p = 0; p < ports->nelts; p++) {
+
+        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 a default server for given address:port
+         */
+
+        addr = port[p].addrs.elts;
+        for (a = 0; a < port[p].addrs.nelts; a++) {
+
+            name = addr[a].names.elts;
+            for (s = 0; s < addr[a].names.nelts; s++) {
+
+                if (addr[a].core_srv_conf == name[s].core_srv_conf
+#if (NGX_PCRE)
+                    && name[s].captures == 0
+#endif
+                    )
+                {
+                    continue;
+                }
+
+                if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) {
+                    return NGX_ERROR;
+                }
+
+                break;
+            }
+        }
+
+        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, p, a;
+    ngx_uint_t                 s;
     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;
+
+    regex = 0;
 #endif
 
-    in_port = in_ports->elts;
-    for (p = 0; p < in_ports->nelts; p++) {
-
-        ngx_sort(in_port[p].addrs.elts, (size_t) in_port[p].addrs.nelts,
-                 sizeof(ngx_http_conf_in_addr_t), ngx_http_cmp_conf_in_addrs);
-
-        /*
-         * check whether all name-based servers have
-         * the same configuraiton as the default server
-         */
-
-        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;
-                }
-            }
-
-            /*
-             * 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:
-
-            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 (NGX_PCRE)
-            regex = 0;
-#endif
-
-            name = in_addr[a].names.elts;
-
-            for (s = 0; s < in_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;
+    }
+
+    name = addr->names.elts;
+
+    for (s = 0; s < addr->names.nelts; s++) {
 
 #if (NGX_PCRE)
-                if (name[s].regex) {
-                    regex++;
-                    continue;
-                }
+        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);
-                    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);
-                }
-            }
-
-            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 (ha.keys.nelts) {
-                hash.hash = &in_addr[a].hash;
-                hash.temp_pool = NULL;
-
-                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_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;
-
-                if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,
-                                           ha.dns_wc_head.nelts)
-                    != NGX_OK)
-                {
-                    goto failed;
-                }
-
-                in_addr[a].wc_head = (ngx_hash_wildcard_t *) hash.hash;
-            }
-
-            if (ha.dns_wc_tail.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);
-
-                hash.hash = NULL;
-                hash.temp_pool = ha.temp_pool;
-
-                if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,
-                                           ha.dns_wc_tail.nelts)
-                    != NGX_OK)
-                {
-                    goto failed;
-                }
-
-                in_addr[a].wc_tail = (ngx_hash_wildcard_t *) hash.hash;
-            }
-
-            ngx_destroy_pool(ha.temp_pool);
+        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, 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);
+        }
+    }
+
+    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 (ha.keys.nelts) {
+        hash.hash = &addr->hash;
+        hash.temp_pool = NULL;
+
+        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_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;
+
+        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) {
+
+        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;
+
+        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,
+                                   ha.dns_wc_tail.nelts)
+            != NGX_OK)
+        {
+            goto failed;
+        }
+
+        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 (in_addr[a].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;
+    if (regex == 0) {
+        return NGX_OK;
+    }
+
+    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 < addr->names.nelts; s++) {
+        if (name[s].regex) {
+            addr->regex[i++] = name[s];
         }
     }
 
+#endif
+
     return NGX_OK;
 
 failed:
@@ -1499,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;
-
-    first = (ngx_http_conf_in_addr_t *) one;
-    second = (ngx_http_conf_in_addr_t *) two;
-
-    if (first->addr == INADDR_ANY) {
-        /* the INADDR_ANY must be the last resort, shift it to the end */
+    ngx_http_conf_addr_t  *first, *second;
+
+    first = (ngx_http_conf_addr_t *) one;
+    second = (ngx_http_conf_addr_t *) two;
+
+    if (first->wildcard) {
+        /* a wildcard address must be the last resort, shift it to the end */
         return 1;
     }
 
@@ -1540,171 +1606,299 @@ 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;
-
-    in_addr = in_port->addrs.elts;
-    last = in_port->addrs.nelts;
+    ngx_http_port_t           *hport;
+    ngx_http_conf_addr_t      *addr;
+
+    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;
-
-    while (a < last) {
-
-        if (!bind_all && !in_addr[a].bind) {
-            a++;
+    i = 0;
+
+    while (i < last) {
+
+        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;
-
-        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;
+        ls->servers = hport;
+
+        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
-
-            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) {
+        switch (ls->sockaddr->sa_family) {
+
+#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;
+    struct sockaddr           *sa;
+    ngx_http_core_loc_conf_t  *clcf;
+    ngx_http_core_srv_conf_t  *cscf;
+    u_char                     text[NGX_SOCKADDR_STRLEN];
+
+    ls = ngx_array_push(&cf->cycle->listening);
+    if (ls == NULL) {
+        return NULL;
+    }
+
+    ngx_memzero(ls, sizeof(ngx_listening_t));
+
+    sa = ngx_palloc(cf->pool, addr->socklen);
+    if (sa == NULL) {
+        return NULL;
+    }
+
+    ngx_memcpy(sa, addr->sockaddr, addr->socklen);
+
+    ls->sockaddr = sa;
+    ls->socklen = addr->socklen;
+
+    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;
+    }
+
+    ngx_memcpy(ls->addr_text.data, text, ls->addr_text.len);
+
+    ls->fd = (ngx_socket_t) -1;
+    ls->type = SOCK_STREAM;
+
+    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->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->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 = 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.core_srv_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.core_srv_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
@@ -78,6 +76,8 @@ ngx_int_t ngx_http_parse_multi_header_li
     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);
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,122 @@
 #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
-
-
-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;
+/**/
+#define NGX_HTTP_CACHE_STALE         1
+#define NGX_HTTP_CACHE_AGED          2
+#define NGX_HTTP_CACHE_THE_SAME      3
+/**/
 
-    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;
+#define NGX_HTTP_CACHE_KEY_LEN       16
 
 
 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_uint_t                       status;
+    time_t                           valid;
+} ngx_http_cache_valid_t;
+
+
+/* ngx_http_file_cache_node_t takes exactly 64 bytes on FreeBSD/i386 */
+
+typedef struct {
+    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;
+                                     /* 7 unused bits */
+    unsigned                         exists:1;
+
+    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                       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;
-
-
-#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);
+    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;
 
 
-#if 0
+struct ngx_http_file_cache_s {
+    ngx_rbtree_t                    *rbtree;
+    ngx_queue_t                     *queue;
+    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_atomic_t                    *cold;
+    off_t                           *size;
 
-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);
 
 
 #endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -119,6 +119,11 @@ static ngx_conf_enum_t  ngx_http_core_if
 };
 
 
+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[] = {
@@ -347,7 +352,7 @@ 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,
@@ -542,7 +547,7 @@ static ngx_command_t  ngx_http_core_comm
       NULL },
 
     { ngx_string("try_files"),
-      NGX_HTTP_LOC_CONF|NGX_CONF_2MORE,
+      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE,
       ngx_http_core_try_files,
       NGX_HTTP_LOC_CONF_OFFSET,
       0,
@@ -1037,7 +1042,7 @@ ngx_http_core_try_files_phase(ngx_http_r
 {
     size_t                        len, root, alias, reserve, allocated;
     u_char                       *p, *name;
-    ngx_str_t                     path;
+    ngx_str_t                     path, args;
     ngx_uint_t                    test_dir;
     ngx_http_try_file_t          *tf;
     ngx_open_file_info_t          of;
@@ -1086,12 +1091,8 @@ ngx_http_core_try_files_phase(ngx_http_r
             len = tf->name.len;
         }
 
-        reserve = len - r->uri.len;
-
         /* 16 bytes are preallocation */
-        reserve = reserve < 16 ? 16 : reserve + 16;
-
-        reserve += alias;
+        reserve = ngx_abs((ssize_t) (len - r->uri.len)) + alias + 16;
 
         if (reserve > allocated) {
 
@@ -1126,7 +1127,7 @@ ngx_http_core_try_files_phase(ngx_http_r
 
             path.len = e.pos - path.data;
 
-            *e.pos++ = '\0';
+            *e.pos = '\0';
 
             if (alias && ngx_strncmp(name, clcf->name.data, alias) == 0) {
                 ngx_memcpy(name, name + alias, len - alias);
@@ -1138,8 +1139,8 @@ ngx_http_core_try_files_phase(ngx_http_r
 
         tf++;
 
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "try to use file: \"%s\"", name);
+        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) {
 
@@ -1150,7 +1151,9 @@ ngx_http_core_try_files_phase(ngx_http_r
                 (void) ngx_http_named_location(r, &path);
 
             } else {
-                (void) ngx_http_internal_redirect(r, &path, NULL);
+                ngx_http_split_args(r, &path, &args);
+
+                (void) ngx_http_internal_redirect(r, &path, &args);
             }
 
             return NGX_OK;
@@ -1197,6 +1200,11 @@ ngx_http_core_try_files_phase(ngx_http_r
             ngx_memcpy(p, name, path.len);
         }
 
+        if (ngx_http_set_exten(r) != NGX_OK) {
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return NGX_OK;
+        }
+
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "try file uri: \"%V\"", &r->uri);
 
@@ -1336,7 +1344,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;
 
@@ -1370,12 +1378,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;
@@ -1393,6 +1415,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);
@@ -1550,7 +1575,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);
@@ -1656,13 +1681,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) {
@@ -1672,6 +1695,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)
@@ -1679,13 +1712,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);
@@ -1769,34 +1808,60 @@ 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 */
+    socklen_t             len;
+    ngx_uint_t            addr;
+    ngx_connection_t     *c;
+    u_char                sa[NGX_SOCKADDRLEN];
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    ngx_uint_t            i;
+    struct sockaddr_in6  *sin6;
+#endif
 
     c = r->connection;
 
-    if (r->in_addr == 0) {
-        len = sizeof(struct sockaddr_in);
-        if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) {
+    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;
         }
 
-        r->in_addr = sin.sin_addr.s_addr;
-
-    } else {
-        sin.sin_family = c->sockaddr->sa_family;
-        sin.sin_addr.s_addr = r->in_addr;
+        c->local_sockaddr = ngx_palloc(r->connection->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((struct sockaddr *) &sin, s->data,
-                           NGX_INET_ADDRSTRLEN);
+    s->len = ngx_sock_ntop(c->local_sockaddr, s->data, s->len, 0);
 
     return NGX_OK;
 }
@@ -2000,7 +2065,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;
     }
@@ -2055,10 +2120,6 @@ ngx_http_subrequest(ngx_http_request_t *
         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;
@@ -2139,6 +2200,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);
@@ -2537,6 +2602,7 @@ ngx_http_core_regex_location(ngx_conf_t 
     }
 
     clcf->name = *regex;
+    clcf->captures = (ngx_regex_capture_count(clcf->regex) > 0);
 
     return NGX_OK;
 
@@ -2592,7 +2658,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;
         }
 
@@ -2724,15 +2790,16 @@ 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;
     }
 
     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;
     }
@@ -2756,6 +2823,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 */
@@ -2768,18 +2836,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) {
@@ -2792,6 +2867,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;
@@ -2943,7 +3019,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;
             }
         }
@@ -3099,10 +3175,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);
@@ -3154,8 +3233,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)
 {
@@ -3196,17 +3273,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;
@@ -3305,6 +3383,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;
@@ -3394,6 +3511,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];
@@ -3420,6 +3538,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
@@ -3472,18 +3591,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")
@@ -3516,31 +3623,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;
 }
 
@@ -3694,13 +3813,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,
@@ -3742,29 +3861,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) {
+        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) {
@@ -3803,21 +3925,8 @@ 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;
-
-        } else {
-            err->uri = uri;
-            err->args.len = 0;
-            err->args.data = NULL;
-        }
-
-        err->uri_lengths = uri_lengths;
-        err->uri_values = uri_values;
+        err->value = cv;
+        err->args = args;
     }
 
     return NGX_CONF_OK;
@@ -3859,6 +3968,7 @@ ngx_http_core_try_files(ngx_conf_t *cf, 
         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);
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -40,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;
@@ -55,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;
@@ -151,6 +153,8 @@ typedef struct {
     /* server ctx */
     ngx_http_conf_ctx_t        *ctx;
 
+    ngx_http_virtual_names_t   *virtual_names;
+
     ngx_str_t                   server_name;
 
     size_t                      connection_pool_size;
@@ -173,35 +177,48 @@ 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;
 
-    ngx_http_virtual_names_t  *virtual_names;
-
 #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;
@@ -219,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;
@@ -239,10 +258,8 @@ 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;
 
 
@@ -259,6 +276,8 @@ struct ngx_http_core_loc_conf_s {
 
 #if (NGX_PCRE)
     ngx_regex_t  *regex;
+
+    unsigned      captures:1;
 #endif
 
     unsigned      noname:1;   /* "if () {}" block or limit_except */
@@ -274,7 +293,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;
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -7,253 +7,1568 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_http.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
+#include <ngx_md5.h>
 
 
-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);
-
-    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 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_open(ngx_http_cache_t *c)
-{
-    ssize_t                   n;
-    ngx_err_t                 err;
-    ngx_http_cache_header_t  *h;
+static u_char  ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' };
+
 
-    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;
+static ngx_int_t
+ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)
+{
+    ngx_http_file_cache_t  *ocache = data;
 
-        if (err == NGX_ENOENT || err == NGX_ENOTDIR) {
-            return NGX_DECLINED;
-        }
+    size_t                  len;
+    ngx_rbtree_node_t      *sentinel;
+    ngx_http_file_cache_t  *cache;
+
+    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->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->rbtree = ocache->rbtree;
+        cache->queue = ocache->queue;
+        cache->shpool = ocache->shpool;
+        cache->cold = ocache->cold;
+        cache->size = ocache->size;
+        cache->bsize = ocache->bsize;
+
+        cache->max_size /= cache->bsize;
+
+        return NGX_OK;
+    }
+
+    cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+    cache->rbtree = ngx_slab_alloc(cache->shpool, sizeof(ngx_rbtree_t));
+    if (cache->rbtree == NULL) {
+        return NGX_ERROR;
+    }
+
+    sentinel = ngx_slab_alloc(cache->shpool, sizeof(ngx_rbtree_node_t));
+    if (sentinel == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_rbtree_init(cache->rbtree, sentinel,
+                    ngx_http_file_cache_rbtree_insert_value);
+
+    cache->queue = ngx_slab_alloc(cache->shpool, sizeof(ngx_queue_t));
+    if (cache->queue == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_queue_init(cache->queue);
+
+    cache->cold = ngx_slab_alloc(cache->shpool, sizeof(ngx_atomic_t));
+    if (cache->cold == NULL) {
+        return NGX_ERROR;
+    }
+
+    *cache->cold = 1;
+
+    cache->size = ngx_slab_alloc(cache->shpool, sizeof(off_t));
+    if (cache->size == NULL) {
+        return NGX_ERROR;
+    }
+
+    *cache->size = 0;
+
+    cache->bsize = ngx_fs_bsize(cache->path->name.data);
+
+    cache->max_size /= cache->bsize;
+
+    len = sizeof(" in cache keys zone \"\"") + shm_zone->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->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_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http file cache exists: %i u:%ui e:%d",
+                   rc, c->uses, 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->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;
 
-        ngx_strncmp(h->key, c->key_data, h->key_len) != 0))
+    if (cold) {
+
+        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;
+
+            *cache->size += (c->length + cache->bsize - 1) / cache->bsize;
+        }
+
+        ngx_shmtx_unlock(&cache->shpool->mutex);
     }
 
-#endif
+    now = ngx_time();
 
-    c->buf->last += n;
+    if (c->valid_sec < now) {
 
-    if (c->expires < ngx_time()) {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http file cache expired");
+        c->uses = c->min_uses;
+
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http file cache expired: %T %T", c->valid_sec, now);
+
         return NGX_HTTP_CACHE_STALE;
     }
 
     /* TODO: NGX_HTTP_CACHE_AGED */
 
-    /* STUB */ return NGX_DECLINED;
+    return NGX_OK;
+}
+
+
+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);
+
+        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->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->queue, &fcn->queue);
+
+    c->uniq = fcn->uniq;
+    c->uses = fcn->uses;
+    c->error = fcn->error;
+    c->node = fcn;
+
+failed:
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
+
+    return rc;
+}
+
+
+static ngx_http_file_cache_node_t *
+ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)
+{
+    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->rbtree->root;
+    sentinel = cache->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;
+
+            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 (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;
+        }
+
+        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;
+
+    size = size - (c->node->length + cache->bsize - 1) / cache->bsize;
+
+    c->node->length = c->length;
+
+    *cache->size += size;
+
+    if (rc == NGX_OK) {
+        c->node->exists = 1;
+    }
+
+    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;
+    }
+
+    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);
+            }
+        }
+    }
+}
+
+
+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 60;
+    }
+
+    ngx_memcpy(name, path->name.data, path->name.len);
+
+    wait = 60;
+    tries = 0;
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+
+    for (q = ngx_queue_last(cache->queue);
+         q != ngx_queue_sentinel(cache->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->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;
+}
+
+
+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 60;
+    }
+
+    ngx_memcpy(name, path->name.data, path->name.len);
+
+    now = ngx_time();
+
+    ngx_shmtx_lock(&cache->shpool->mutex);
+
+    for ( ;; ) {
+
+        if (ngx_queue_empty(cache->queue)) {
+            wait = 60;
+            break;
+        }
+
+        q = ngx_queue_last(cache->queue);
+
+        fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
+
+        wait = fcn->expire - now;
+
+        if (wait > 0) {
+            wait = wait > 60 ? 60 : 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->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->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)
+{
+    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->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->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->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 60;
+        }
+
+        *cache->cold = 0;
+
+        ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+                      "http file cache: %V %.3fM, bsize: %uz",
+                      &cache->path->name,
+                      ((double) *cache->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->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));
+
+    fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 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;
+    }
+
+    c.file.fd = fd;
+    c.file.name = *name;
+    c.file.log = ctx->log;
+
+    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 ((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;
+    }
+
+    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->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->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->queue, &fcn->queue);
+
+    ngx_shmtx_unlock(&cache->shpool->mutex);
 
     return NGX_OK;
 }
 
 
-#if 0
+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;
+}
 
 
-int ngx_http_cache_update_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx,
-                               ngx_str_t *temp_file)
+time_t
+ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)
 {
-    int        retry;
-    ngx_err_t  err;
+    ngx_uint_t               i;
+    ngx_http_cache_valid_t  *valid;
 
-    retry = 0;
+    if (cache_valid == NULL) {
+        return 0;
+    }
 
-    for ( ;; ) {
-        if (ngx_rename_file(temp_file->data, ctx->file.name.data) == NGX_OK) {
-            return NGX_OK;
+    valid = cache_valid->elts;
+    for (i = 0; i < cache_valid->nelts; i++) {
+
+        if (valid[i].status == 0) {
+            return valid[i].valid;
         }
 
-        err = ngx_errno;
-
-#if (NGX_WIN32)
-        if (err == NGX_EEXIST) {
-            if (ngx_win32_rename_file(temp_file, &ctx->file.name, r->pool)
-                                                                  == NGX_ERROR)
-            {
-                return NGX_ERROR;
-            }
+        if (valid[i].status == status) {
+            return valid[i].valid;
         }
-#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;
-    }
+    return 0;
 }
 
 
-#endif
+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) {
+
+            n = 0;
+            p = value[i].data + 7;
+            last = value[i].data + value[i].len;
+
+            while (p < last) {
+
+                if (*p > '0' && *p < '6') {
+
+                    cache->path->level[n] = *p++ - '0';
+                    cache->path->len += cache->path->level[n] + 1;
+
+                    if (p == last) {
+                        break;
+                    }
+
+                    if (*p++ == ':') {
+
+                        if (n > 2) {
+                            goto invalid_levels;
+                        }
+
+                        if (cache->path->level[n] == 0) {
+                            goto invalid_levels;
+                        }
+
+                        n++;
+
+                        continue;
+                    }
+                }
+
+                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) {
+                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;
+    }
 
 
-ngx_int_t ngx_http_cache_cleaner_handler(ngx_gc_t *gc, ngx_str_t *name,
-                                         ngx_dir_t *dir)
+    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)
 {
-    int               rc;
-    ngx_buf_t         buf;
-    ngx_http_cache_t  c;
-    u_char            data[sizeof(ngx_http_cache_header_t)];
-
-    ngx_memzero(&c, sizeof(ngx_http_cache_t));
-
-    c.file.fd = NGX_INVALID_FILE;
-    c.file.name = *name;
-    c.file.log = gc->log;
+    char  *p = conf;
 
-    c.header_size = sizeof(ngx_http_cache_header_t);
-    c.buf = &buf;
-    c.log = gc->log;
-    c.key_len = 0;
+    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);
 
-    buf.memory = 1;
-    buf.temporary = 1;
-    buf.pos = data;
-    buf.last = data;
-    buf.start = data;
-    buf.end = data + sizeof(ngx_http_cache_header_t);
+    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;
+        }
+    }
 
-    rc = ngx_http_file_cache_open(&c);
+    value = cf->args->elts;
+    n = cf->args->nelts - 1;
 
-    /* TODO: NGX_AGAIN */
-
-    if (rc != NGX_ERROR&& rc != NGX_DECLINED && rc != NGX_HTTP_CACHE_STALE) {
-        return NGX_OK;
+    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 (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);
-        return NGX_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;
     }
 
-    gc->deleted++;
-    gc->freed += ngx_de_size(dir);
+    for (i = 1; i < n; i++) {
+
+        if (ngx_strcmp(value[i].data, "any") == 0) {
+
+            status = 0;
+
+        } else {
 
-    return NGX_OK;
+            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
@@ -155,14 +155,17 @@ ngx_http_header_filter(ngx_http_request_
     size_t                     len;
     ngx_str_t                  host;
     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_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;
 
@@ -290,6 +293,7 @@ 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) {
@@ -297,31 +301,42 @@ ngx_http_header_filter(ngx_http_request_
             }
         }
 
-#if (NGX_HTTP_SSL)
-        if (r->connection->ssl) {
-            len += sizeof("Location: https://") - 1
-                   + host.len
-                   + r->headers_out.location->value.len + 2;
+        switch (r->connection->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 *) 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;
+        }
 
-        } 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 (r->connection->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;
-            }
+        if (port) {
+            len += sizeof(":65535") - 1;
         }
 
     } else {
         host.len = 0;
         host.data = NULL;
+        port = 0;
     }
 
     if (r->chunked) {
@@ -473,21 +488,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,
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -1523,3 +1523,39 @@ ngx_http_arg(ngx_http_request_t *r, u_ch
 
     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_request.c
+++ b/src/http/ngx_http_request.c
@@ -232,13 +232,19 @@ 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);
@@ -292,58 +298,82 @@ 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_http_server_addr(r, NULL) != 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 = (ngx_http_in6_addr_t *) 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 = (ngx_http_in6_addr_t *) 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;
-
     /* 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 +387,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 +402,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;
@@ -415,7 +445,7 @@ 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);
         return;
@@ -775,7 +805,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 +814,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;
@@ -1580,11 +1610,15 @@ ngx_http_find_virtual_server(ngx_http_re
 {
     u_char                    *server;
     ngx_uint_t                 hash;
+    ngx_http_virtual_names_t  *vn;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
     u_char                     buf[32];
 
-    if (r->virtual_names == NULL) {
+    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+    vn = cscf->virtual_names;
+
+    if (vn == NULL) {
         return NGX_DECLINED;
     }
 
@@ -1600,7 +1634,7 @@ ngx_http_find_virtual_server(ngx_http_re
 
     hash = ngx_hash_strlow(server, host, len);
 
-    cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, server, len);
+    cscf = ngx_hash_find_combined(&vn->names, hash, server, len);
 
     if (cscf) {
         goto found;
@@ -1608,7 +1642,8 @@ ngx_http_find_virtual_server(ngx_http_re
 
 #if (NGX_PCRE)
 
-    if (r->virtual_names->nregex) {
+    if (vn->nregex) {
+        size_t                   ncaptures;
         ngx_int_t                n;
         ngx_uint_t               i;
         ngx_str_t                name;
@@ -1617,11 +1652,33 @@ ngx_http_find_virtual_server(ngx_http_re
         name.len = len;
         name.data = server;
 
-        sn = r->virtual_names->regex;
-
-        for (i = 0; i < r->virtual_names->nregex; i++) {
-
-            n = ngx_regex_exec(sn[i].regex, &name, NULL, 0);
+        ncaptures = 0;
+
+        sn = vn->regex;
+
+        for (i = 0; i < vn->nregex; i++) {
+
+            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;
@@ -1639,6 +1696,9 @@ ngx_http_find_virtual_server(ngx_http_re
 
             cscf = sn[i].core_srv_conf;
 
+            r->ncaptures = ncaptures;
+            r->captures_data = server;
+
             goto found;
         }
     }
@@ -2359,8 +2419,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;
         }
--- 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
@@ -347,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;
@@ -384,17 +387,18 @@ struct ngx_http_request_s {
     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;
     ngx_http_handler_pt               content_handler;
     ngx_uint_t                        access_code;
 
     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 */
@@ -441,6 +445,9 @@ struct ngx_http_request_s {
     unsigned                          subrequest_in_memory:1;
     unsigned                          waited:1;
 
+#if (NGX_HTTP_CACHE)
+    unsigned                          cached:1;
+#endif
     unsigned                          gzip:2;
 
     unsigned                          proxy:1;
--- 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;
@@ -265,52 +364,14 @@ ngx_http_script_compile(ngx_http_script_
 
         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:
 
@@ -387,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)
 {
@@ -425,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)
 {
@@ -461,6 +654,50 @@ ngx_http_script_copy_code(ngx_http_scrip
 }
 
 
+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;
+}
+
+
 size_t
 ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e)
 {
@@ -518,62 +755,26 @@ ngx_http_script_copy_var_code(ngx_http_s
 }
 
 
-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)
-{
-    u_char                               *p;
-    ngx_http_script_copy_capture_code_t  *code;
-
-    code = (ngx_http_script_copy_capture_code_t *) e->ip;
-
-    e->ip += sizeof(ngx_http_script_copy_capture_code_t);
+    *code = (uintptr_t) ngx_http_script_mark_args_code;
 
-    p = e->pos;
-
-    if (code->n < e->ncaptures) {
-        if ((e->is_args || e->quote)
-            && (e->request->quoted_uri || e->request->plus_in_uri))
-        {
-            e->pos = (u_char *) ngx_escape_uri(p,
-                                &e->line.data[e->captures[code->n]],
-                                e->captures[code->n + 1] - e->captures[code->n],
-                                NGX_ESCAPE_ARGS);
-        } else {
-            e->pos = ngx_copy(p,
-                              &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_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                   "http script capture: \"%*s\"", e->pos - p, p);
+    *code = (uintptr_t) ngx_http_script_start_args_code;
+
+    return NGX_OK;
 }
 
 
@@ -599,7 +800,6 @@ ngx_http_script_start_args_code(ngx_http
 }
 
 
-
 #if (NGX_PCRE)
 
 void
@@ -628,7 +828,18 @@ 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 || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
@@ -637,7 +848,7 @@ ngx_http_script_regex_start_code(ngx_htt
                           &code->name, &e->line);
         }
 
-        e->ncaptures = 0;
+        r->ncaptures = 0;
 
         if (code->test) {
             if (code->negative_test) {
@@ -674,7 +885,8 @@ ngx_http_script_regex_start_code(ngx_htt
                       "\"%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) {
@@ -725,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 {
@@ -734,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;
@@ -874,9 +1084,193 @@ 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->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->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->prefix ? sizeof(NGX_CONF_PREFIX) : ngx_cycle->root.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->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)
 {
@@ -1133,8 +1527,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)) {
--- 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                   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
@@ -432,9 +432,8 @@ ngx_http_special_response_handler(ngx_ht
 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_int_t                  overwrite;
-    ngx_str_t                 *uri, *args, u, a;
+    ngx_str_t                  uri, args;
     ngx_table_elt_t           *location;
     ngx_http_core_loc_conf_t  *clcf;
 
@@ -448,67 +447,29 @@ ngx_http_send_error_page(ngx_http_reques
 
     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 (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {
+        return NGX_ERROR;
+    }
 
-                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;
-                }
-            }
-        }
+    if (err_page->value.lengths) {
+        ngx_http_split_args(r, &uri, &args);
 
     } else {
-        uri = &err_page->uri;
-        args = &err_page->args;
+        args = err_page->args;
     }
 
-    if (uri->data[0] == '/') {
+    if (uri.data[0] == '/') {
 
         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);
@@ -522,7 +483,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;
 
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -9,6 +9,13 @@
 #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);
+#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);
@@ -29,6 +36,8 @@ static ngx_int_t ngx_http_upstream_test_
 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 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,
@@ -80,10 +89,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);
@@ -139,8 +153,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,
@@ -185,7 +198,7 @@ ngx_http_upstream_header_t  ngx_http_ups
     { 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"),
@@ -360,6 +373,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;
@@ -491,6 +523,136 @@ 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;
+
+    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 = r->upstream->conf->cache_min_uses;
+    c->body_start = r->upstream->conf->buffer_size;
+    c->file_cache = u->conf->cache->data;
+
+    rc = ngx_http_file_cache_open(r);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http upstream cache: %i u:%ui", rc, c->uses);
+
+    if (rc == NGX_OK) {
+
+        rc = ngx_http_upstream_cache_send(r, u);
+
+        if (rc != NGX_HTTP_UPSTREAM_INVALID_HEADER) {
+            return rc;
+        }
+
+    } else if (rc == NGX_ERROR) {
+
+        return NGX_ERROR;
+
+    } else if (rc == NGX_HTTP_CACHE_STALE) {
+
+        u->stale_cache = 1;
+        u->buffer.start = NULL;
+
+    } else if (rc == 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;
+        }
+
+    } else if (rc == NGX_AGAIN) {
+
+        u->cacheable = 0;
+
+    } else {
+
+        /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */
+
+        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;
+
+    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)
 {
@@ -860,7 +1022,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);
@@ -969,22 +1131,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;
 }
 
@@ -1115,15 +1273,9 @@ ngx_http_upstream_send_request_handler(n
 static void
 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_upstream_header_t     *hh;
-    ngx_http_upstream_main_conf_t  *umcf;
+    ssize_t            n;
+    ngx_int_t          rc;
+    ngx_connection_t  *c;
 
     c = u->peer.connection;
 
@@ -1166,9 +1318,10 @@ ngx_http_upstream_process_header(ngx_htt
             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
@@ -1257,124 +1410,10 @@ ngx_http_upstream_process_header(ngx_htt
         }
     }
 
-    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;
@@ -1443,9 +1482,12 @@ ngx_http_upstream_test_next(ngx_http_req
 
 #if (NGX_HTTP_CACHE)
 
-        if (u->peer.tries == 0 && u->stale && (u->conf->use_stale & un->mask)) {
+        if (u->peer.tries == 0
+            && u->stale_cache
+            && (u->conf->cache_use_stale & un->mask))
+        {
             ngx_http_upstream_finalize_request(r, u,
-                                              ngx_http_send_cached_response(r));
+                                           ngx_http_upstream_cache_send(r, u));
             return NGX_OK;
         }
 
@@ -1557,6 +1599,138 @@ ngx_http_upstream_test_connect(ngx_conne
 }
 
 
+static ngx_int_t
+ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+    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) {
+
+        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)
@@ -1637,8 +1811,6 @@ ngx_http_upstream_send_response(ngx_http
     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);
@@ -1651,18 +1823,8 @@ ngx_http_upstream_send_response(ngx_http
     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;
@@ -1738,28 +1900,38 @@ 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;
+
+        valid = ngx_http_file_cache_valid(u->conf->cache_valid,
+                                          u->headers_in.status_n);
+        if (valid) {
+
+            now = ngx_time();
+
+            r->cache->valid_sec = now + 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;
+        }
     }
 
 #endif
@@ -1789,7 +1961,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 {
@@ -2241,23 +2413,21 @@ ngx_http_upstream_process_request(ngx_ht
             }
         }
 
-#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);
             }
         }
 
@@ -2438,12 +2608,12 @@ ngx_http_upstream_next(ngx_http_request_
 
 #if (NGX_HTTP_CACHE)
 
-            if (u->stale && (u->conf->use_stale & ft_type)) {
+            if (u->stale_cache && (u->conf->cache_use_stale & ft_type)) {
+
                 ngx_http_upstream_finalize_request(r, u,
-                                             ngx_http_send_cached_response(r));
+                                           ngx_http_upstream_cache_send(r, u));
                 return;
             }
-
 #endif
 
             ngx_http_upstream_finalize_request(r, u, status);
@@ -2557,25 +2727,41 @@ 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 (r->cache) {
+        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;
     }
@@ -2827,6 +3013,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->cached) {
+        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)
 {
@@ -2916,6 +3129,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
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -39,21 +39,21 @@
 
 
 typedef struct {
-    ngx_msec_t                      bl_time;
-    ngx_uint_t                      bl_state;
+    ngx_msec_t                       bl_time;
+    ngx_uint_t                       bl_state;
 
-    ngx_uint_t                      status;
-    time_t                          response_sec;
-    ngx_uint_t                      response_msec;
+    ngx_uint_t                       status;
+    time_t                           response_sec;
+    ngx_uint_t                       response_msec;
     off_t                           response_length;
 
-    ngx_str_t                      *peer;
+    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;
 
@@ -66,21 +66,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;
 
 
@@ -93,67 +93,75 @@ 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_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_bufs_t                      bufs;
+    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                      buffering;
-    ngx_flag_t                      pass_request_headers;
-    ngx_flag_t                      pass_request_body;
+    ngx_hash_t                       hide_headers_hash;
+    ngx_array_t                     *hide_headers;
+    ngx_array_t                     *pass_headers;
 
-    ngx_flag_t                      ignore_client_abort;
-    ngx_flag_t                      intercept_errors;
-    ngx_flag_t                      cyclic_temp_file;
+#if (NGX_HTTP_CACHE)
+    ngx_shm_zone_t                  *cache;
 
-    ngx_path_t                     *temp_path;
+    ngx_uint_t                       cache_min_uses;
+    ngx_uint_t                       cache_use_stale;
 
-    ngx_hash_t                      hide_headers_hash;
-    ngx_array_t                    *hide_headers;
-    ngx_array_t                    *pass_headers;
+    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_ssl_t                       *ssl;
     ngx_flag_t                      ssl_session_reuse;
 #endif
 
@@ -161,62 +169,62 @@ typedef struct {
 
 
 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                      no_port; /* unsigned  no_port:1 */
+    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;
+    ngx_uint_t                       naddrs;
+    in_addr_t                       *addrs;
 
-    struct sockaddr                *sockaddr;
-    socklen_t                       socklen;
+    struct sockaddr                 *sockaddr;
+    socklen_t                        socklen;
 
-    ngx_resolver_ctx_t             *ctx;
+    ngx_resolver_ctx_t              *ctx;
 } ngx_http_upstream_resolved_t;
 
 
@@ -225,63 +233,69 @@ typedef void (*ngx_http_upstream_handler
 
 
 struct ngx_http_upstream_s {
-    ngx_http_upstream_handler_pt    read_event_handler;
-    ngx_http_upstream_handler_pt    write_event_handler;
+    ngx_http_upstream_handler_pt     read_event_handler;
+    ngx_http_upstream_handler_pt     write_event_handler;
 
-    ngx_peer_connection_t           peer;
+    ngx_peer_connection_t            peer;
 
-    ngx_event_pipe_t               *pipe;
+    ngx_event_pipe_t                *pipe;
 
-    ngx_chain_t                    *request_bufs;
+    ngx_chain_t                     *request_bufs;
 
-    ngx_output_chain_ctx_t          output;
-    ngx_chain_writer_ctx_t          writer;
+    ngx_output_chain_ctx_t           output;
+    ngx_chain_writer_ctx_t           writer;
 
-    ngx_http_upstream_conf_t       *conf;
+    ngx_http_upstream_conf_t        *conf;
 
-    ngx_http_upstream_headers_in_t  headers_in;
+    ngx_http_upstream_headers_in_t   headers_in;
 
-    ngx_http_upstream_resolved_t   *resolved;
+    ngx_http_upstream_resolved_t    *resolved;
 
-    ngx_buf_t                       buffer;
-    size_t                          length;
+    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                         stale_cache:1;
+#endif
 
-    unsigned                        buffering:1;
+    unsigned                         buffering:1;
 
-    unsigned                        request_sent:1;
-    unsigned                        header_sent:1;
+    unsigned                         request_sent:1;
+    unsigned                         header_sent:1;
 };
 
 
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -332,7 +332,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;
         }
@@ -828,17 +829,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;
 }
@@ -862,8 +883,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;
@@ -875,16 +899,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;
@@ -896,15 +927,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_http_server_addr(r, &s) != 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;
@@ -920,11 +957,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_http_server_addr(r, NULL) != 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;
 }
@@ -1003,9 +1073,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;
         }
 
@@ -1044,9 +1112,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;
         }
     }
@@ -1250,6 +1316,8 @@ 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;
@@ -1260,7 +1328,10 @@ ngx_http_variable_sent_location(ngx_http
         return NGX_OK;
     }
 
-    return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
+    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);
 }
--- a/src/mail/ngx_mail_core_module.c
+++ b/src/mail/ngx_mail_core_module.c
@@ -305,6 +305,11 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx
         return NGX_CONF_ERROR;
     }
 
+    if (u.family != AF_INET) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "listen supports IPv4 only");
+        return NGX_CONF_ERROR;
+    }
+
     cmcf = ngx_mail_conf_get_module_main_conf(cf, ngx_mail_core_module);
 
     imls = cmcf->listen.elts;
--- 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_errno.h
+++ b/src/os/unix/ngx_errno.h
@@ -44,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
@@ -64,9 +64,10 @@ typedef struct {
 #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_APPEND          O_WRONLY|O_APPEND
 
 #define NGX_FILE_DEFAULT_ACCESS  0644
+#define NGX_FILE_OWNER_ACCESS    0600
 
 
 #define ngx_close_file           close
@@ -200,10 +201,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))
@@ -258,4 +274,7 @@ ngx_int_t ngx_directio_off(ngx_fd_t fd);
 #endif
 
 
+size_t ngx_fs_bsize(u_char *name);
+
+
 #endif /* _NGX_FILES_H_INCLUDED_ */
--- 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_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>
--- a/src/os/unix/ngx_posix_config.h
+++ b/src/os/unix/ngx_posix_config.h
@@ -44,6 +44,12 @@
 #include <grp.h>
 #include <dirent.h>
 #include <glob.h>
+#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_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_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>