changeset 396:77df96611112

Merge with current.
author Maxim Dounin <mdounin@mdounin.ru>
date Tue, 05 Aug 2008 02:12:51 +0400
parents 1d9bef53cd8e (current diff) 040b8c84d040 (diff)
children 5c4b468a4f90
files auto/modules auto/sources src/http/modules/ngx_http_range_filter_module.c src/http/ngx_http_request.h
diffstat 63 files changed, 2971 insertions(+), 420 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags
+++ b/.hgtags
@@ -193,3 +193,5 @@ 984bb0b1399bffe332ad1fe5cd8d4d7862fa18d8
 12defd37f57883e4d59ef36837d03c9b68c3c3ad NGINX_0_7_4
 1172e6d6f40f26a5130a15f5b7adbd902216faf2 NGINX_0_7_5
 6de24473fa708acc3b2f13611f5c44b0e3a292c9 NGINX_0_7_6
+0b6053502c552a3021db417cb2dc6caac7ba1bdd NGINX_0_7_7
+34fb3a5735483bd22e77f90f305103307a813fc4 NGINX_0_7_8
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,33 @@
 
+Changes with nginx 0.7.7                                         30 Jul 2008
+
+    *) Change: now the EAGAIN error returned by connect() is not considered 
+       as temporary error.
+
+    *) Change: now the $ssl_client_cert variable value is a certificate 
+       with TAB character intended before each line except first one; an 
+       unchanged certificate is available in the $ssl_client_raw_cert 
+       variable.
+
+    *) Feature: the "ask" parameter in the "ssl_verify_client" directive.
+
+    *) Feature: byte-range processing improvements.
+       Thanks to Maxim Dounin.
+
+    *) Feature: the "directio" directive.
+
+    *) Feature: MacOSX 1.5 sendfile() support.
+
+    *) Bugfix: now in MacOSX and Cygwin locations are tested in case 
+       insensitive mode; however, the compare is provided by single-byte 
+       locales only.
+
+    *) Bugfix: mail proxy SSL connections hanged, if select, poll, or 
+       /dev/poll methods were used.
+
+    *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module.
+
+
 Changes with nginx 0.7.6                                         07 Jul 2008
 
     *) Bugfix: now if variables are used in the "access_log" directive a 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,34 @@
 
+Изменения в nginx 0.7.7                                           30.07.2008
+
+    *) Изменение: теперь ошибка EAGAIN при вызове connect() не считается 
+       временной.
+
+    *) Изменение: значением переменной $ssl_client_cert теперь является 
+       сертификат, перед каждой строкой которого, кроме первой, вставляется 
+       символ табуляции; неизменённый сертификат доступен через переменную 
+       $ssl_client_raw_cert.
+
+    *) Добавление: параметр ask директивы ssl_verify_client.
+
+    *) Добавление: улучшения в обработке byte-range.
+       Спасибо Максиму Дунину.
+
+    *) Добавление: директива directio.
+
+    *) Добавление: поддержка sendfile() в MacOSX 1.5.
+
+    *) Исправление: в MacOSX и Cygwin при проверке location'ов теперь 
+       делается сравнение без учёта регистра символов; однако, сравнение 
+       ограничено только однобайтными locale'ями.
+
+    *) Исправление: соединения почтового прокси-сервера зависали в режиме 
+       SSL, если использовались методы select, poll или /dev/poll.
+
+    *) Исправление: ошибки при использовании кодировки UTF-8 в 
+       ngx_http_autoindex_module.
+
+
 Изменения в nginx 0.7.6                                           07.07.2008
 
     *) Исправление: теперь при использовании переменных в директиве 
--- a/auto/feature
+++ b/auto/feature
@@ -19,7 +19,9 @@ if test -n "$ngx_feature_name"; then
 fi
 
 if test -n "$ngx_feature_path"; then
-    ngx_feature_inc_path="-I $ngx_feature_path"
+    for ngx_temp in $ngx_feature_path; do
+        ngx_feature_inc_path="$ngx_feature_inc_path -I $ngx_temp"
+    done
 fi
 
 cat << END > $NGX_AUTOTEST.c
--- a/auto/lib/conf
+++ b/auto/lib/conf
@@ -41,6 +41,10 @@ if [ $USE_ZLIB = YES ]; then
     . auto/lib/zlib/conf
 fi
 
+if [ $USE_LIBXSLT = YES ]; then
+    . auto/lib/libxslt/conf
+fi
+
 if [ $USE_PERL = YES ]; then
     . auto/lib/perl/conf
 fi
new file mode 100644
--- /dev/null
+++ b/auto/lib/libxslt/conf
@@ -0,0 +1,78 @@
+
+# Copyright (C) Igor Sysoev
+
+
+    ngx_feature="libxslt"
+    ngx_feature_name=
+    ngx_feature_run=no
+    ngx_feature_incs="#include <libxml/parser.h>
+                      #include <libxml/tree.h>
+                      #include <libxslt/xslt.h>
+                      #include <libxslt/xsltInternals.h>
+                      #include <libxslt/transform.h>
+                      #include <libxslt/xsltutils.h>"
+    ngx_feature_path="/usr/include/libxml2"
+    ngx_feature_libs="-lxml2 -lxslt"
+    ngx_feature_test="xmlParserCtxtPtr    ctxt = NULL;
+                      xsltStylesheetPtr   sheet = NULL;
+                      xmlDocPtr           doc;
+                      doc = xmlParseChunk(ctxt, NULL, 0, 0);
+                      xsltApplyStylesheet(sheet, doc, NULL);"
+    . auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+    # FreeBSD port
+
+    ngx_feature="libxslt in /usr/local/"
+    ngx_feature_path="/usr/local/include/libxml2 /usr/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lxml2 -lxslt"
+    else
+        ngx_feature_libs="-L/usr/local/lib -lxml2 -lxslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # NetBSD port
+
+    ngx_feature="libxslt in /usr/pkg/"
+    ngx_feature_path="/usr/pkg/include/libxml2 /usr/pkg/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lxml2 -lxslt"
+    else
+        ngx_feature_libs="-L/usr/pkg/lib -lxml2 -lxslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # MacPorts
+
+    ngx_feature="libxslt in /opt/local/"
+    ngx_feature_path="/opt/local/include/libxml2 /opt/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lxml2 -lxslt"
+    else
+        ngx_feature_libs="-L/opt/local/lib -lxml2 -lxslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+    CORE_INCS="$CORE_INCS $ngx_feature_path"
+    CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+fi
--- a/auto/lib/pcre/conf
+++ b/auto/lib/pcre/conf
@@ -114,7 +114,7 @@ else
 
         if [ $ngx_found = no ]; then
 
-            # Linux package
+            # RedHat RPM, Solaris package
 
             ngx_feature="PCRE library in /usr/include/pcre/"
             ngx_feature_path="/usr/include/pcre"
--- a/auto/modules
+++ b/auto/modules
@@ -76,6 +76,9 @@ fi
 
 
 # the module order is important
+#     ngx_http_static_module
+#     ngx_http_gzip_static_module
+#     ngx_http_dav_module
 #     ngx_http_autoindex_module
 #     ngx_http_index_module
 #
@@ -92,6 +95,8 @@ fi
 #     ngx_http_postpone_filter
 #     ngx_http_charset_filter
 #     ngx_http_ssi_filter
+#         ngx_http_xslt_filter
+#         ngx_http_sub_filter
 #         ngx_http_addition_filter
 #         ngx_http_userid_filter
 #         ngx_http_headers_filter
@@ -131,6 +136,12 @@ if [ $HTTP_SSI = YES ]; then
     HTTP_SRCS="$HTTP_SRCS $HTTP_SSI_SRCS"
 fi
 
+if [ $HTTP_XSLT = YES ]; then
+    USE_LIBXSLT=YES
+    HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_XSLT_FILTER_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_XSLT_SRCS"
+fi
+
 if [ $HTTP_SUB = YES ]; then
     HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SUB_FILTER_MODULE"
     HTTP_SRCS="$HTTP_SRCS $HTTP_SUB_SRCS"
--- a/auto/options
+++ b/auto/options
@@ -56,6 +56,7 @@ HTTP_SSL=NO
 HTTP_SSI=YES
 HTTP_POSTPONE=NO
 HTTP_REALIP=NO
+HTTP_XSLT=NO
 HTTP_SUB=NO
 HTTP_ADDITION=NO
 HTTP_DAV=NO
@@ -115,6 +116,8 @@ ZLIB_ASM=NO
 USE_PERL=NO
 NGX_PERL=perl
 
+USE_LIBXSLT=NO
+
 NGX_GOOGLE_PERFTOOLS=NO
 
 NGX_CPU_CACHE_LINE=
@@ -162,6 +165,7 @@ do
         --with-http_ssl_module)          HTTP_SSL=YES               ;;
         --with-http_realip_module)       HTTP_REALIP=YES            ;;
         --with-http_addition_module)     HTTP_ADDITION=YES          ;;
+        --with-http_xslt_module)         HTTP_XSLT=YES              ;;
         --with-http_sub_module)          HTTP_SUB=YES               ;;
         --with-http_dav_module)          HTTP_DAV=YES               ;;
         --with-http_flv_module)          HTTP_FLV=YES               ;;
@@ -276,6 +280,7 @@ cat << END
   --with-http_ssl_module             enable ngx_http_ssl_module
   --with-http_realip_module          enable ngx_http_realip_module
   --with-http_addition_module        enable ngx_http_addition_module
+  --with-http_xslt_module            enable ngx_http_xslt_module
   --with-http_sub_module             enable ngx_http_sub_module
   --with-http_dav_module             enable ngx_http_dav_module
   --with-http_flv_module             enable ngx_http_flv_module
--- a/auto/os/conf
+++ b/auto/os/conf
@@ -18,6 +18,10 @@ case "$NGX_PLATFORM" in
         . auto/os/solaris
     ;;
 
+    Darwin:*)
+        . auto/os/darwin
+    ;;
+
     win32)
         . auto/os/win32
     ;;
@@ -36,24 +40,6 @@ case "$NGX_PLATFORM" in
 '
     ;;
 
-    Darwin:*)
-        have=NGX_DARWIN . auto/have_headers
-        have=NGX_HAVE_INHERITED_NONBLOCK . auto/have
-        CORE_INCS="$UNIX_INCS"
-        CORE_DEPS="$UNIX_DEPS $POSIX_DEPS"
-        CORE_SRCS="$UNIX_SRCS"
-
-        ngx_feature="atomic(3)"
-        ngx_feature_name=NGX_DARWIN_ATOMIC
-        ngx_feature_run=no
-        ngx_feature_incs="#include <libkern/OSAtomic.h>"
-        ngx_feature_path=
-        ngx_feature_libs=
-        ngx_feature_test="int32_t  lock, n;
-                          n = OSAtomicCompareAndSwap32Barrier(0, 1, lock)"
-        . auto/feature
-    ;;
-
     HP-UX:*)
         # HP/UX
         have=NGX_HPUX . auto/have_headers
new file mode 100644
--- /dev/null
+++ b/auto/os/darwin
@@ -0,0 +1,115 @@
+
+# Copyright (C) Igor Sysoev
+
+
+have=NGX_DARWIN . auto/have_headers
+
+CORE_INCS="$UNIX_INCS"
+CORE_DEPS="$UNIX_DEPS $DARWIN_DEPS"
+CORE_SRCS="$UNIX_SRCS $DARWIN_SRCS"
+
+
+
+ngx_spacer='
+'
+
+# kqueue
+
+echo " + kqueue found"
+have=NGX_HAVE_KQUEUE . auto/have
+have=NGX_HAVE_CLEAR_EVENT . auto/have
+EVENT_MODULES="$EVENT_MODULES $KQUEUE_MODULE"
+CORE_SRCS="$CORE_SRCS $KQUEUE_SRCS"
+EVENT_FOUND=YES
+NGX_KQUEUE_CHECKED=YES
+
+ngx_feature="kqueue's EVFILT_TIMER"
+ngx_feature_name="NGX_HAVE_TIMER_EVENT"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/event.h>
+                  #include <sys/time.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int      kq;
+                  struct kevent    kev;
+                  struct timespec  ts;
+
+                  if ((kq = kqueue()) == -1) return 1;
+
+                  kev.ident = 0;
+                  kev.filter = EVFILT_TIMER;
+                  kev.flags = EV_ADD|EV_ENABLE;
+                  kev.fflags = 0;
+                  kev.data = 1000;
+                  kev.udata = 0;
+
+                  ts.tv_sec = 0;
+                  ts.tv_nsec = 0;
+
+                  if (kevent(kq, &kev, 1, &kev, 1, &ts) == -1) return 1;
+
+                  if (kev.flags & EV_ERROR) return 1;"
+
+. auto/feature
+
+
+ngx_feature="Darwin 64-bit kqueue millisecond timeout bug"
+ngx_feature_name=NGX_DARWIN_KEVENT_BUG
+ngx_feature_run=bug
+ngx_feature_incs="#include <sys/event.h>
+                  #include <sys/time.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int  kq;
+                  struct kevent    kev;
+                  struct timespec  ts;
+                  struct timeval   tv, tv0;
+
+                  kq = kqueue();
+
+                  ts.tv_sec = 0;
+                  ts.tv_nsec = 999000000;
+
+                  gettimeofday(&tv, 0);
+                  kevent(kq, NULL, 0, &kev, 1, &ts);
+                  gettimeofday(&tv0, 0);
+                  timersub(&tv0, &tv, &tv);
+
+                  if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;"
+
+. auto/feature
+
+
+# sendfile()
+
+CC_AUX_FLAGS="$CC_AUX_FLAGS"
+ngx_feature="sendfile()"
+ngx_feature_name="NGX_HAVE_SENDFILE"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/types.h>
+                  #include <sys/socket.h>
+                  #include <sys/uio.h>
+                  #include <sys/errno.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int s = 0, fd = 1;
+                  off_t n; off_t off = 0;
+                  n = sendfile(s, fd, off, &n, NULL, 0);
+                  if (n == -1 && errno == ENOSYS) return 1"
+. auto/feature
+
+if [ $ngx_found = yes ]; then
+    have=NGX_HAVE_SENDFILE . auto/have
+    CORE_SRCS="$CORE_SRCS $DARWIN_SENDFILE_SRCS"
+fi
+
+
+ngx_feature="atomic(3)"
+ngx_feature_name=NGX_DARWIN_ATOMIC
+ngx_feature_run=no
+ngx_feature_incs="#include <libkern/OSAtomic.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int32_t  lock, n;
+                  n = OSAtomicCompareAndSwap32Barrier(0, 1, lock)"
+. auto/feature
--- a/auto/os/features
+++ b/auto/os/features
@@ -122,36 +122,6 @@ if test -z "$NGX_KQUEUE_CHECKED"; then
                   if (kev.flags & EV_ERROR) return 1;"
 
         . auto/feature
-
-
-        if [ "$NGX_SYSTEM" = "Darwin" ]; then
-
-            ngx_feature="Darwin 64-bit kqueue millisecond timeout bug"
-            ngx_feature_name=NGX_DARWIN_KEVENT_BUG
-            ngx_feature_run=bug
-            ngx_feature_incs="#include <sys/event.h>
-#include <sys/time.h>"
-            ngx_feature_path=
-            ngx_feature_libs=
-            ngx_feature_test="int  kq;
-                  struct kevent    kev;
-                  struct timespec  ts;
-                  struct timeval   tv, tv0;
-
-                  kq = kqueue();
-
-                  ts.tv_sec = 0;
-                  ts.tv_nsec = 999000000;
-
-                  gettimeofday(&tv, 0);
-                  kevent(kq, NULL, 0, &kev, 1, &ts);
-                  gettimeofday(&tv0, 0);
-                  timersub(&tv0, &tv, &tv);
-
-                  if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;"
-
-            . auto/feature
-        fi
     fi
 fi
 
@@ -200,3 +170,34 @@ if [ $ngx_found = no ]; then
         CRYPT_LIB="-lcrypt"
     fi
 fi
+
+
+ngx_feature="O_DIRECT"
+ngx_feature_name="NGX_HAVE_O_DIRECT"
+ngx_feature_run=no
+ngx_feature_incs="#include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="fcntl(0, F_SETFL, O_DIRECT);"
+. auto/feature
+
+
+ngx_feature="F_NOCACHE"
+ngx_feature_name="NGX_HAVE_F_NOCACHE"
+ngx_feature_run=no
+ngx_feature_incs="#include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="fcntl(0, F_NOCACHE, 1);"
+. auto/feature
+
+
+ngx_feature="directio()"
+ngx_feature_name="NGX_HAVE_DIRECTIO"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/types.h>
+                  #include <sys/fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="directio(0, DIRECTIO_ON);"
+. auto/feature
--- a/auto/sources
+++ b/auto/sources
@@ -198,6 +198,11 @@ SOLARIS_SRCS=src/os/unix/ngx_solaris_ini
 SOLARIS_SENDFILEV_SRCS=src/os/unix/ngx_solaris_sendfilev_chain.c
 
 
+DARWIN_DEPS="src/os/unix/ngx_darwin_config.h src/os/unix/ngx_darwin.h"
+DARWIN_SRCS=src/os/unix/ngx_darwin_init.c
+DARWIN_SENDFILE_SRCS=src/os/unix/ngx_darwin_sendfile_chain.c
+
+
 WIN32_INCS="$CORE_INCS $EVENT_INCS src/os/win32"
 
 WIN32_DEPS="$CORE_DEPS $EVENT_DEPS \
@@ -328,6 +333,10 @@ HTTP_SSI_DEPS=src/http/modules/ngx_http_
 HTTP_SSI_SRCS=src/http/modules/ngx_http_ssi_filter_module.c
 
 
+HTTP_XSLT_FILTER_MODULE=ngx_http_xslt_filter_module
+HTTP_XSLT_SRCS=src/http/modules/ngx_http_xslt_filter_module.c
+
+
 HTTP_SUB_FILTER_MODULE=ngx_http_sub_filter_module
 HTTP_SUB_SRCS=src/http/modules/ngx_http_sub_filter_module.c
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.7.6"
+#define NGINX_VERSION      "0.7.8"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_config.h
+++ b/src/core/ngx_config.h
@@ -29,6 +29,10 @@
 #include <ngx_solaris_config.h>
 
 
+#elif (NGX_DARWIN)
+#include <ngx_darwin_config.h>
+
+
 #elif (NGX_WIN32)
 #include <ngx_win32_config.h>
 
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -390,9 +390,7 @@ found:
         elt->value = names[n].value;
         elt->len = (u_char) names[n].key.len;
 
-        for (i = 0; i < names[n].key.len; i++) {
-            elt->name[i] = ngx_tolower(names[n].key.data[i]);
-        }
+        ngx_strlow(elt->name, names[n].key.data, names[n].key.len);
 
         test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
     }
@@ -622,6 +620,24 @@ ngx_hash_key_lc(u_char *data, size_t len
 }
 
 
+ngx_uint_t
+ngx_hash_strlow(u_char *dst, u_char *src, size_t n)
+{
+    ngx_uint_t  key;
+
+    key = 0;
+
+    while (n--) {
+        *dst = ngx_tolower(*src);
+        key = ngx_hash(key, *dst);
+        dst++;
+        src++;
+    }
+
+    return key;
+}
+
+
 ngx_int_t
 ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)
 {
@@ -796,12 +812,7 @@ wildcard:
 
     /* wildcard hash */
 
-    k = 0;
-
-    for (i = skip; i < last; i++) {
-        key->data[i] = ngx_tolower(key->data[i]);
-        k = ngx_hash(k, key->data[i]);
-    }
+    k = ngx_hash_strlow(&key->data[skip], &key->data[skip], last - skip);
 
     k %= ha->hsize;
 
--- a/src/core/ngx_hash.h
+++ b/src/core/ngx_hash.h
@@ -110,6 +110,8 @@ ngx_int_t ngx_hash_wildcard_init(ngx_has
 #define ngx_hash(key, c)   ((ngx_uint_t) key * 31 + c)
 ngx_uint_t ngx_hash_key(u_char *data, size_t len);
 ngx_uint_t ngx_hash_key_lc(u_char *data, size_t len);
+ngx_uint_t ngx_hash_strlow(u_char *dst, u_char *src, size_t n);
+
 
 ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type);
 ngx_int_t ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key,
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -497,6 +497,13 @@ ngx_open_and_stat_file(u_char *name, ngx
 
     } else {
         of->fd = fd;
+
+        if (of->directio <= ngx_file_size(&fi)) {
+            if (ngx_directio(fd) == -1) {
+                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                              ngx_directio_n " \"%s\" failed", name);
+            }
+        }
     }
 
 done:
--- a/src/core/ngx_open_file_cache.h
+++ b/src/core/ngx_open_file_cache.h
@@ -17,6 +17,7 @@ typedef struct {
     ngx_file_uniq_t          uniq;
     time_t                   mtime;
     off_t                    size;
+    off_t                    directio;
     ngx_err_t                err;
 
     time_t                   valid;
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -32,6 +32,7 @@ ngx_output_chain(ngx_output_chain_ctx_t 
     size_t        size;
     ngx_int_t     rc, last;
     ngx_uint_t    recycled;
+    ngx_buf_t    *b;
     ngx_chain_t  *cl, *out, **last_out;
 
     if (ctx->in == NULL && ctx->busy == NULL) {
@@ -161,13 +162,29 @@ ngx_output_chain(ngx_output_chain_ctx_t 
                         }
                     }
 
-                    ctx->buf = ngx_create_temp_buf(ctx->pool, size);
-                    if (ctx->buf == NULL) {
+                    b = ngx_calloc_buf(ctx->pool);
+                    if (b == NULL) {
                         return NGX_ERROR;
                     }
 
-                    ctx->buf->tag = ctx->tag;
-                    ctx->buf->recycled = recycled;
+                    /*
+                     * allocate block aligned to a disk sector size
+                     * to enable O_DIRECT
+                     */
+
+                    b->start = ngx_pmemalign(ctx->pool, size, 512);
+                    if (b->start == NULL) {
+                        return NGX_ERROR;
+                    }
+
+                    b->pos = b->start;
+                    b->last = b->start;
+                    b->end = b->last + size;
+                    b->temporary = 1;
+                    b->tag = ctx->tag;
+                    b->recycled = recycled;
+
+                    ctx->buf = b;
                     ctx->allocated++;
                 }
             }
--- a/src/core/ngx_palloc.c
+++ b/src/core/ngx_palloc.c
@@ -195,17 +195,35 @@ ngx_palloc_large(ngx_pool_t *pool, size_
     void              *p;
     ngx_pool_large_t  *large;
 
-#if 0
-    p = ngx_memalign(ngx_pagesize, size, pool->log);
-    if (p == NULL) {
-        return NULL;
-    }
-#else
     p = ngx_alloc(size, pool->log);
     if (p == NULL) {
         return NULL;
     }
-#endif
+
+    large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
+    if (large == NULL) {
+        ngx_free(p);
+        return NULL;
+    }
+
+    large->alloc = p;
+    large->next = pool->large;
+    pool->large = large;
+
+    return p;
+}
+
+
+void *
+ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
+{
+    void              *p;
+    ngx_pool_large_t  *large;
+
+    p = ngx_memalign(alignment, size, pool->log);
+    if (p == NULL) {
+        return NULL;
+    }
 
     large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
     if (large == NULL) {
--- a/src/core/ngx_palloc.h
+++ b/src/core/ngx_palloc.h
@@ -77,6 +77,7 @@ void ngx_destroy_pool(ngx_pool_t *pool);
 void *ngx_palloc(ngx_pool_t *pool, size_t size);
 void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
 void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
+void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
 ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);
 
 
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -8,6 +8,17 @@
 #include <ngx_core.h>
 
 
+void
+ngx_strlow(u_char *dst, u_char *src, size_t n)
+{
+    while (n--) {
+        *dst = ngx_tolower(*src);
+        dst++;
+        src++;
+    }
+}
+
+
 u_char *
 ngx_cpystrn(u_char *dst, u_char *src, size_t n)
 {
@@ -952,16 +963,16 @@ ngx_decode_base64(ngx_str_t *dst, ngx_st
 
 
 /*
- * ngx_utf_decode() decodes two and more bytes UTF sequences only
+ * ngx_utf8_decode() decodes two and more bytes UTF sequences only
  * the return values:
  *    0x80 - 0x10ffff         valid character
- *    0x10ffff - 0xfffffffd   invalid sequence
+ *    0x110000 - 0xfffffffd   invalid sequence
  *    0xfffffffe              incomplete sequence
  *    0xffffffff              error
  */
 
 uint32_t
-ngx_utf_decode(u_char **p, size_t n)
+ngx_utf8_decode(u_char **p, size_t n)
 {
     size_t    len;
     uint32_t  u, i, valid;
@@ -1018,31 +1029,26 @@ ngx_utf_decode(u_char **p, size_t n)
 
 
 size_t
-ngx_utf_length(u_char *p, size_t n)
+ngx_utf8_length(u_char *p, size_t n)
 {
-    u_char      c;
-    size_t      len;
-    ngx_uint_t  i;
+    u_char  c, *last;
+    size_t  len;
 
-    for (len = 0, i = 0; i < n; len++, i++) {
+    last = p + n;
 
-        c = p[i];
+    for (len = 0; p < last; len++) {
+
+        c = *p;
 
         if (c < 0x80) {
+            p++;
             continue;
         }
 
-        if (c >= 0xc0) {
-            for (c <<= 1; c & 0x80; c <<= 1) {
-                i++;
-            }
-
-            continue;
+        if (ngx_utf8_decode(&p, n) > 0x10ffff) {
+            /* invalid UTF-8 */
+            return n;
         }
-
-        /* invalid utf */
-
-        return n;
     }
 
     return len;
@@ -1050,36 +1056,45 @@ ngx_utf_length(u_char *p, size_t n)
 
 
 u_char *
-ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n)
+ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len)
 {
-    u_char  c;
+    u_char  c, *next;
 
     if (n == 0) {
         return dst;
     }
 
-    for ( /* void */ ; --n; dst++, src++) {
+    while (--n) {
 
         c = *src;
         *dst = c;
 
         if (c < 0x80) {
-            if (*dst != '\0') {
+
+            if (c != '\0') {
+                dst++;
+                src++;
+                len--;
+
                 continue;
             }
 
             return dst;
         }
 
-        if (c >= 0xc0) {
-            for (c <<= 1; c & 0x80; c <<= 1) {
-               *++dst = *++src;
-            }
+        next = src;
 
-            continue;
+        if (ngx_utf8_decode(&next, len) > 0x10ffff) {
+            /* invalid UTF-8 */
+            break;
         }
 
-        /* invalid utf */
+        len--;
+
+        while (src < next) {
+            *++dst = *++src;
+            len--;
+        }
     }
 
     *dst = '\0';
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -43,6 +43,8 @@ typedef struct {
 #define ngx_tolower(c)      (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)
 #define ngx_toupper(c)      (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)
 
+void ngx_strlow(u_char *dst, u_char *src, size_t n);
+
 
 #define ngx_strncmp(s1, s2, n)  strncmp((const char *) s1, (const char *) s2, n)
 
@@ -151,9 +153,9 @@ u_char *ngx_hex_dump(u_char *dst, u_char
 void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src);
 ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src);
 
-uint32_t ngx_utf_decode(u_char **p, size_t n);
-size_t ngx_utf_length(u_char *p, size_t n);
-u_char *ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n);
+uint32_t ngx_utf8_decode(u_char **p, size_t n);
+size_t ngx_utf8_length(u_char *p, size_t n);
+u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len);
 
 
 #define NGX_ESCAPE_URI         0
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -139,11 +139,22 @@ ngx_event_connect_peer(ngx_peer_connecti
     if (rc == -1) {
         err = ngx_socket_errno;
 
-        /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */
 
-        if (err != NGX_EINPROGRESS && err != NGX_EAGAIN) {
-
+        if (err != NGX_EINPROGRESS
+#if (NGX_WIN32)
+            /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */
+            && err != NGX_EAGAIN
+#endif
+            )
+        {
             if (err == NGX_ECONNREFUSED
+#if (NGX_LINUX)
+                /*
+                 * Linux returns EAGAIN instead of ECONNREFUSED
+                 * for unix sockets if listen queue is full
+                 */
+                || err == NGX_EAGAIN
+#endif
                 || err == NGX_ENETDOWN
                 || err == NGX_ENETUNREACH
                 || err == NGX_EHOSTDOWN
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -505,6 +505,9 @@ ngx_ssl_handshake(ngx_connection_t *c)
 
     if (n == 1) {
 
+        c->read->ready = 0;
+        c->write->ready = 1;
+
         if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
             return NGX_ERROR;
         }
@@ -1884,7 +1887,7 @@ ngx_ssl_get_cipher_name(ngx_connection_t
 
 
 ngx_int_t
-ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
 {
     size_t   len;
     BIO     *bio;
@@ -1934,6 +1937,50 @@ failed:
 
 
 ngx_int_t
+ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+    u_char      *p;
+    size_t       len;
+    ngx_uint_t   i;
+    ngx_str_t    cert;
+
+    if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (cert.len == 0) {
+        s->len = 0;
+        return NGX_OK;
+    }
+
+    len = cert.len - 1;
+
+    for (i = 0; i < cert.len - 1; i++) {
+        if (cert.data[i] == LF) {
+            len++;
+        }
+    }
+
+    s->len = len;
+    s->data = ngx_pnalloc(pool, len);
+    if (s->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    p = s->data;
+
+    for (i = 0; i < len; i++) {
+        *p++ = cert.data[i];
+        if (cert.data[i] == LF) {
+            *p++ = '\t';
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
 {
     char       *p;
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -121,6 +121,8 @@ ngx_int_t ngx_ssl_get_protocol(ngx_conne
     ngx_str_t *s);
 ngx_int_t ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
+ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
 ngx_int_t ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
 ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool,
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -135,7 +135,7 @@ ngx_http_autoindex_handler(ngx_http_requ
 {
     u_char                         *last, *filename, scale;
     off_t                           length;
-    size_t                          len, copy, allocated, root;
+    size_t                          len, utf_len, allocated, root;
     ngx_tm_t                        tm;
     ngx_err_t                       err;
     ngx_buf_t                      *b;
@@ -329,7 +329,7 @@ ngx_http_autoindex_handler(ngx_http_requ
                                            NGX_ESCAPE_HTML);
 
         if (r->utf8) {
-            entry->utf_len = ngx_utf_length(entry->name.data, entry->name.len);
+            entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len);
         } else {
             entry->utf_len = len;
         }
@@ -412,15 +412,16 @@ ngx_http_autoindex_handler(ngx_http_requ
 
         len = entry[i].utf_len;
 
-        if (entry[i].name.len - len) {
+        if (entry[i].name.len != len) {
             if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
-                copy = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;
+                utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;
 
             } else {
-                copy = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
+                utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
             }
 
-            b->last = ngx_utf_cpystrn(b->last, entry[i].name.data, copy);
+            b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,
+                                       utf_len, entry[i].name.len + 1);
             last = b->last;
 
         } else {
--- a/src/http/modules/ngx_http_charset_filter_module.c
+++ b/src/http/modules/ngx_http_charset_filter_module.c
@@ -642,7 +642,7 @@ ngx_http_charset_recode_from_utf8(ngx_po
                 size = buf->last - src;
 
                 saved = src;
-                n = ngx_utf_decode(&saved, size);
+                n = ngx_utf8_decode(&saved, size);
 
                 if (n == 0xfffffffe) {
                     /* incomplete UTF-8 symbol */
@@ -710,7 +710,7 @@ ngx_http_charset_recode_from_utf8(ngx_po
     }
 
     saved = ctx->saved;
-    n = ngx_utf_decode(&saved, i);
+    n = ngx_utf8_decode(&saved, i);
 
     c = '\0';
 
@@ -818,7 +818,7 @@ recode:
 
         len = buf->last - src;
 
-        n = ngx_utf_decode(&src, len);
+        n = ngx_utf8_decode(&src, len);
 
         if (n < 0x10000) {
 
@@ -1270,7 +1270,7 @@ ngx_http_charset_map(ngx_conf_t *cf, ngx
 
         p = &table->src2dst[src * NGX_UTF_LEN] + 1;
 
-        n = ngx_utf_decode(&p, i);
+        n = ngx_utf8_decode(&p, i);
 
         if (n > 0xffff) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -1120,9 +1120,7 @@ ngx_http_fastcgi_process_header(ngx_http
                     ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
 
                 } else {
-                    for (i = 0; i < h->key.len; i++) {
-                        h->lowcase_key[i] = ngx_tolower(h->key.data[i]);
-                    }
+                    ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
                 }
 
                 hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
@@ -1167,6 +1165,13 @@ ngx_http_fastcgi_process_header(ngx_http
                     u->headers_in.status_n = status;
                     u->headers_in.status_line = *status_line;
 
+                } else if (u->headers_in.location) {
+                    u->headers_in.status_n = 302;
+                    u->headers_in.status_line.len =
+                                           sizeof("302 Moved Temporarily") - 1;
+                    u->headers_in.status_line.data =
+                                           (u_char *) "302 Moved Temporarily"; 
+
                 } else {
                     u->headers_in.status_n = 200;
                     u->headers_in.status_line.len = sizeof("200 OK") - 1;
--- a/src/http/modules/ngx_http_flv_module.c
+++ b/src/http/modules/ngx_http_flv_module.c
@@ -107,6 +107,7 @@ ngx_http_flv_handler(ngx_http_request_t 
 
     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+    of.directio = clcf->directio;
     of.valid = clcf->open_file_cache_valid;
     of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
@@ -212,9 +213,6 @@ ngx_http_flv_handler(ngx_http_request_t 
 
         out[0].buf = b;
         out[0].next = &out[1];
-
-    } else {
-        r->allow_ranges = 1;
     }
 
 
@@ -228,6 +226,8 @@ ngx_http_flv_handler(ngx_http_request_t 
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
+    r->allow_ranges = 1;
+
     rc = ngx_http_send_header(r);
 
     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
--- a/src/http/modules/ngx_http_gzip_static_module.c
+++ b/src/http/modules/ngx_http_gzip_static_module.c
@@ -121,6 +121,7 @@ ngx_http_gzip_static_handler(ngx_http_re
 
     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+    of.directio = clcf->directio;
     of.valid = clcf->open_file_cache_valid;
     of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -210,6 +210,7 @@ ngx_http_index_handler(ngx_http_request_
 
         ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+        of.directio = clcf->directio;
         of.valid = clcf->open_file_cache_valid;
         of.min_uses = clcf->open_file_cache_min_uses;
         of.errors = clcf->open_file_cache_errors;
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -106,7 +106,7 @@ ngx_http_map_variable(ngx_http_request_t
 
     size_t                      len;
     u_char                     *name;
-    ngx_uint_t                  key, i;
+    ngx_uint_t                  key;
     ngx_http_variable_value_t  *vv, *value;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -135,11 +135,7 @@ ngx_http_map_variable(ngx_http_request_t
         return NGX_ERROR;
     }
 
-    key = 0;
-    for (i = 0; i < len; i++) {
-        name[i] = ngx_tolower(vv->data[i]);
-        key = ngx_hash(key, name[i]);
-    }
+    key = ngx_hash_strlow(name, vv->data, len);
 
     value = ngx_hash_find_combined(&map->hash, key, name, len);
 
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -1196,7 +1196,6 @@ static ngx_int_t
 ngx_http_proxy_process_header(ngx_http_request_t *r)
 {
     ngx_int_t                       rc;
-    ngx_uint_t                      i;
     ngx_table_elt_t                *h;
     ngx_http_upstream_header_t     *hh;
     ngx_http_upstream_main_conf_t  *umcf;
@@ -1237,9 +1236,7 @@ ngx_http_proxy_process_header(ngx_http_r
                 ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
 
             } else {
-                for (i = 0; i < h->key.len; i++) {
-                    h->lowcase_key[i] = ngx_tolower(h->key.data[i]);
-                }
+                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
             }
 
             hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
--- a/src/http/modules/ngx_http_range_filter_module.c
+++ b/src/http/modules/ngx_http_range_filter_module.c
@@ -58,6 +58,22 @@ typedef struct {
 } ngx_http_range_filter_ctx_t;
 
 
+ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx);
+static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx);
+static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx);
+static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r);
+static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
+static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in,
+    ngx_http_output_body_filter_pt ngx_http_next_body_filter);
+static ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in,
+    ngx_http_output_body_filter_pt ngx_http_next_body_filter);
+
 static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf);
 static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf);
 static ngx_int_t ngx_http_range_late_filter_init(ngx_conf_t *cf);
@@ -164,15 +180,8 @@ static ngx_http_output_body_filter_pt   
 static ngx_int_t
 ngx_http_range_header_filter(ngx_http_request_t *r)
 {
-    u_char                       *p;
-    size_t                        len;
-    off_t                         start, end;
     time_t                        if_range;
     ngx_int_t                     rc;
-    ngx_uint_t                    suffix, i;
-    ngx_atomic_uint_t             boundary;
-    ngx_table_elt_t              *content_range;
-    ngx_http_range_t             *range;
     ngx_http_range_filter_ctx_t  *ctx;
 
     if (r->http_version < NGX_HTTP_VERSION_10
@@ -218,8 +227,54 @@ ngx_http_range_header_filter(ngx_http_re
         return NGX_ERROR;
     }
 
-    rc = 0;
-    range = NULL;
+    rc = ngx_http_range_parse(r, ctx);
+
+    if (rc == NGX_OK) {
+
+        ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
+
+        r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
+
+        if (ctx->ranges.nelts == 1) {
+            return ngx_http_range_singlepart_header(r, ctx);
+        }
+
+        return ngx_http_range_multipart_header(r, ctx);
+    }
+
+    if (rc == NGX_HTTP_RANGE_NOT_SATISFIABLE) {
+        return ngx_http_range_not_satisfiable(r);
+    }
+
+    /* rc == NGX_ERROR */
+
+    return rc;
+
+next_filter:
+
+    r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
+    if (r->headers_out.accept_ranges == NULL) {
+        return NGX_ERROR;
+    }
+
+    r->headers_out.accept_ranges->hash = 1;
+    r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1;
+    r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges";
+    r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1;
+    r->headers_out.accept_ranges->value.data = (u_char *) "bytes";
+
+    return ngx_http_next_header_filter(r);
+}
+
+
+ngx_int_t
+ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx)
+{
+    u_char            *p;
+    off_t              start, end;
+    ngx_uint_t         suffix;
+    ngx_http_range_t  *range;
+
     p = r->headers_in.range->value.data + 6;
 
     for ( ;; ) {
@@ -231,8 +286,7 @@ ngx_http_range_header_filter(ngx_http_re
 
         if (*p != '-') {
             if (*p < '0' || *p > '9') {
-                rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-                break;
+                return NGX_HTTP_RANGE_NOT_SATISFIABLE;
             }
 
             while (*p >= '0' && *p <= '9') {
@@ -242,13 +296,11 @@ ngx_http_range_header_filter(ngx_http_re
             while (*p == ' ') { p++; }
 
             if (*p++ != '-') {
-                rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-                break;
+                return NGX_HTTP_RANGE_NOT_SATISFIABLE;
             }
 
             if (start >= r->headers_out.content_length_n) {
-                rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-                break;
+                return NGX_HTTP_RANGE_NOT_SATISFIABLE;
             }
 
             while (*p == ' ') { p++; }
@@ -263,7 +315,7 @@ ngx_http_range_header_filter(ngx_http_re
                 range->end = r->headers_out.content_length_n;
 
                 if (*p++ != ',') {
-                    break;
+                    return NGX_OK;
                 }
 
                 continue;
@@ -275,8 +327,7 @@ ngx_http_range_header_filter(ngx_http_re
         }
 
         if (*p < '0' || *p > '9') {
-            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-            break;
+            return NGX_HTTP_RANGE_NOT_SATISFIABLE;
         }
 
         while (*p >= '0' && *p <= '9') {
@@ -286,8 +337,7 @@ ngx_http_range_header_filter(ngx_http_re
         while (*p == ' ') { p++; }
 
         if (*p != ',' && *p != '\0') {
-            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-            break;
+            return NGX_HTTP_RANGE_NOT_SATISFIABLE;
         }
 
         if (suffix) {
@@ -296,8 +346,7 @@ ngx_http_range_header_filter(ngx_http_re
         }
 
         if (start > end) {
-            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
-            break;
+            return NGX_HTTP_RANGE_NOT_SATISFIABLE;
         }
 
         range = ngx_array_push(&ctx->ranges);
@@ -319,86 +368,65 @@ ngx_http_range_header_filter(ngx_http_re
         }
 
         if (*p++ != ',') {
-            break;
+            return NGX_OK;
         }
     }
+}
+
+
+static ngx_int_t
+ngx_http_range_singlepart_header(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx)
+{
+    ngx_table_elt_t   *content_range;
+    ngx_http_range_t  *range;
+
+    content_range = ngx_list_push(&r->headers_out.headers);
+    if (content_range == NULL) {
+        return NGX_ERROR;
+    }
 
-    if (rc) {
-
-        /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */
-
-        r->headers_out.status = rc;
-
-        content_range = ngx_list_push(&r->headers_out.headers);
-        if (content_range == NULL) {
-            return NGX_ERROR;
-        }
-
-        r->headers_out.content_range = content_range;
+    r->headers_out.content_range = content_range;
 
-        content_range->hash = 1;
-        content_range->key.len = sizeof("Content-Range") - 1;
-        content_range->key.data = (u_char *) "Content-Range";
+    content_range->hash = 1;
+    content_range->key.len = sizeof("Content-Range") - 1;
+    content_range->key.data = (u_char *) "Content-Range";
 
-        content_range->value.data = ngx_pnalloc(r->pool,
-                                       sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
-        if (content_range->value.data == NULL) {
-            return NGX_ERROR;
-        }
-
-        content_range->value.len = ngx_sprintf(content_range->value.data,
-                                               "bytes */%O",
-                                               r->headers_out.content_length_n)
-                                   - content_range->value.data;
-
-        ngx_http_clear_content_length(r);
-
-        return rc;
+    content_range->value.data = ngx_pnalloc(r->pool,
+                                    sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
+    if (content_range->value.data == NULL) {
+        return NGX_ERROR;
     }
 
-    ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
-
-    r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
-
-    if (ctx->ranges.nelts == 1) {
+    /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
 
-        content_range = ngx_list_push(&r->headers_out.headers);
-        if (content_range == NULL) {
-            return NGX_ERROR;
-        }
-
-        r->headers_out.content_range = content_range;
-
-        content_range->hash = 1;
-        content_range->key.len = sizeof("Content-Range") - 1;
-        content_range->key.data = (u_char *) "Content-Range";
+    range = ctx->ranges.elts;
 
-        content_range->value.data =
-              ngx_pnalloc(r->pool, sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
-        if (content_range->value.data == NULL) {
-            return NGX_ERROR;
-        }
-
-        /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
+    content_range->value.len = ngx_sprintf(content_range->value.data,
+                                           "bytes %O-%O/%O",
+                                           range->start, range->end - 1,
+                                           r->headers_out.content_length_n)
+                               - content_range->value.data;
 
-        content_range->value.len = ngx_sprintf(content_range->value.data,
-                                               "bytes %O-%O/%O",
-                                               range->start, range->end - 1,
-                                               r->headers_out.content_length_n)
-                                   - content_range->value.data;
+    r->headers_out.content_length_n = range->end - range->start;
 
-        r->headers_out.content_length_n = range->end - range->start;
-
-        if (r->headers_out.content_length) {
-            r->headers_out.content_length->hash = 0;
-            r->headers_out.content_length = NULL;
-        }
-
-        return ngx_http_next_header_filter(r);
+    if (r->headers_out.content_length) {
+        r->headers_out.content_length->hash = 0;
+        r->headers_out.content_length = NULL;
     }
 
+    return ngx_http_next_header_filter(r);
+}
 
-    /* TODO: what if no content_type ?? */
+
+static ngx_int_t
+ngx_http_range_multipart_header(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx)
+{
+    size_t              len;
+    ngx_uint_t          i;
+    ngx_http_range_t   *range;
+    ngx_atomic_uint_t   boundary;
 
     len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
           + sizeof(CRLF "Content-Type: ") - 1
@@ -436,7 +464,7 @@ ngx_http_range_header_filter(ngx_http_re
 
         r->headers_out.charset.len = 0;
 
-    } else {
+    } else if (r->headers_out.content_type.len) {
         ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
                                            CRLF "--%0muA" CRLF
                                            "Content-Type: %V" CRLF
@@ -444,6 +472,13 @@ ngx_http_range_header_filter(ngx_http_re
                                            boundary,
                                            &r->headers_out.content_type)
                                    - ctx->boundary_header.data;
+
+    } else {
+        ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
+                                           CRLF "--%0muA" CRLF
+                                           "Content-Range: bytes ",
+                                           boundary)
+                                   - ctx->boundary_header.data;
     }
 
     r->headers_out.content_type.data =
@@ -498,21 +533,41 @@ ngx_http_range_header_filter(ngx_http_re
     }
 
     return ngx_http_next_header_filter(r);
+}
 
-next_filter:
 
-    r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
-    if (r->headers_out.accept_ranges == NULL) {
+static ngx_int_t
+ngx_http_range_not_satisfiable(ngx_http_request_t *r)
+{
+    ngx_table_elt_t  *content_range;
+
+    r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
+
+    content_range = ngx_list_push(&r->headers_out.headers);
+    if (content_range == NULL) {
         return NGX_ERROR;
     }
 
-    r->headers_out.accept_ranges->hash = 1;
-    r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1;
-    r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges";
-    r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1;
-    r->headers_out.accept_ranges->value.data = (u_char *) "bytes";
+    r->headers_out.content_range = content_range;
+
+    content_range->hash = 1;
+    content_range->key.len = sizeof("Content-Range") - 1;
+    content_range->key.data = (u_char *) "Content-Range";
 
-    return ngx_http_next_header_filter(r);
+    content_range->value.data = ngx_pnalloc(r->pool,
+                                       sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
+    if (content_range->value.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    content_range->value.len = ngx_sprintf(content_range->value.data,
+                                           "bytes */%O",
+                                           r->headers_out.content_length_n)
+                               - content_range->value.data;
+
+    ngx_http_clear_content_length(r);
+
+    return NGX_HTTP_RANGE_NOT_SATISFIABLE;
 }
 
 
@@ -520,11 +575,6 @@ static ngx_int_t
 ngx_http_range_body_generic_filter(ngx_http_request_t *r, ngx_chain_t *in,
     ngx_http_output_body_filter_pt ngx_http_next_body_filter)
 {
-    off_t                         start, last;
-    ngx_buf_t                    *b, *buf;
-    ngx_uint_t                    i;
-    ngx_chain_t                  *out, *cl, *hcl, *rcl, *dcl, **ll;
-    ngx_http_range_t             *range;
     ngx_http_range_filter_ctx_t  *ctx;
 
     if (in == NULL) {
@@ -537,25 +587,88 @@ ngx_http_range_body_generic_filter(ngx_h
         return ngx_http_next_body_filter(r, in);
     }
 
-    buf = in->buf;
+    if (ctx->ranges.nelts == 1) {
+        return ngx_http_range_singlepart_body(r, ctx, in,
+                                              ngx_http_next_body_filter);
+    }
+
+    /*
+     * multipart ranges are supported only if whole body is in a single buffer
+     */
 
     if (ngx_buf_special(in->buf)) {
         return ngx_http_next_body_filter(r, in);
     }
 
-    range = ctx->ranges.elts;
+    if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    return ngx_http_range_multipart_body(r, ctx, in,
+                                         ngx_http_next_body_filter);
+}
+
 
-    if (ctx->ranges.nelts > 1) {
-        goto multipart;
+static ngx_int_t
+ngx_http_range_test_overlapped(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
+{
+    off_t              start, last;
+    ngx_buf_t         *buf;
+    ngx_uint_t         i;
+    ngx_http_range_t  *range;
+
+    if (ctx->offset) {
+        goto overlapped;
     }
 
-    /*
-     * the optimized version for the responses
-     * that are passed in the single buffer
-     */
+    buf = in->buf;
+
+    if (!buf->last_buf) {
+
+        if (buf->in_file) {
+            start = buf->file_pos + ctx->offset;
+            last = buf->file_last + ctx->offset;
+
+        } else {
+            start = buf->pos - buf->start + ctx->offset;
+            last = buf->last - buf->start + ctx->offset;
+        }
+
+        range = ctx->ranges.elts;
+        for (i = 0; i < ctx->ranges.nelts; i++) {
+            if (start > range[i].start || last < range[i].end) {
+                 goto overlapped;
+            }
+        }
+    }
+
+    ctx->offset = ngx_buf_size(buf);
+
+    return NGX_OK;
+
+overlapped:
+
+    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                  "range in overlapped buffers");
+
+    return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_range_singlepart_body(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in,
+    ngx_http_output_body_filter_pt ngx_http_next_body_filter)
+{
+    off_t              start, last;
+    ngx_buf_t         *buf;
+    ngx_chain_t       *out, *cl, **ll;
+    ngx_http_range_t  *range;
 
     out = NULL;
     ll = &out;
+    range = ctx->ranges.elts;
 
     for (cl = in; cl; cl = cl->next) {
 
@@ -567,44 +680,44 @@ ngx_http_range_body_generic_filter(ngx_h
         ctx->offset = last;
 
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "range body filter: %O-%O", start, last);
+                       "http range body buf: %O-%O", start, last);
 
         if (ngx_buf_special(buf)) {
-            /* pass anyway */
-            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "range body filter: pass special");
             *ll = cl;
             ll = &cl->next;
             continue;
         }
 
         if (range->end <= start || range->start >= last) {
-            /* skip buffer */
+
             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "range body filter: skip");
+                           "http range body skip");
+
             buf->pos = buf->last;
             continue;
         }
 
         if (range->start > start) {
+
             if (buf->in_file) {
                 buf->file_pos += range->start - start;
             }
+
             if (ngx_buf_in_memory(buf)) {
                 buf->pos += (size_t) (range->start - start);
             }
         }
 
         if (range->end <= last) {
+
             if (buf->in_file) {
                 buf->file_last -= last - range->end;
             }
+
             if (ngx_buf_in_memory(buf)) {
                 buf->last -= (size_t) (last - range->end);
             }
 
-            /* we are done */
-
             buf->last_buf = 1;
             *ll = cl;
             cl->next = NULL;
@@ -621,34 +734,22 @@ ngx_http_range_body_generic_filter(ngx_h
     }
 
     return ngx_http_next_body_filter(r, out);
-
-multipart:
+}
 
-    if (ctx->offset) {
-        goto overlapped;
-    }
-
-    if (!buf->last_buf) {
-
-        if (buf->in_file) {
-            start = buf->file_pos + ctx->offset;
-            last = buf->file_last + ctx->offset;
 
-        } else {
-            start = buf->pos - buf->start + ctx->offset;
-            last = buf->last - buf->start + ctx->offset;
-        }
-
-        for (i = 0; i < ctx->ranges.nelts; i++) {
-            if (start > range[i].start || last < range[i].end) {
-                 goto overlapped;
-            }
-        }
-    }
-
-    ctx->offset = ngx_buf_size(buf);
+static ngx_int_t
+ngx_http_range_multipart_body(ngx_http_request_t *r,
+    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in,
+    ngx_http_output_body_filter_pt ngx_http_next_body_filter)
+{
+    ngx_buf_t         *b, *buf;
+    ngx_uint_t         i;
+    ngx_chain_t       *out, *hcl, *rcl, *dcl, **ll;
+    ngx_http_range_t  *range;
 
     ll = &out;
+    buf = in->buf;
+    range = ctx->ranges.elts;
 
     for (i = 0; i < ctx->ranges.nelts; i++) {
 
@@ -764,13 +865,6 @@ multipart:
     *ll = hcl;
 
     return ngx_http_next_body_filter(r, out);
-
-overlapped:
-
-    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                  "range in overlapped buffers");
-
-    return NGX_ERROR;
 }
 
 
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -1605,7 +1605,7 @@ ngx_http_ssi_evaluate_string(ngx_http_re
     size_t                     *size, len, prefix, part_len;
     ngx_str_t                   var, *val;
     ngx_int_t                   key;
-    ngx_uint_t                  i, j, n, bracket, quoted;
+    ngx_uint_t                  i, n, bracket, quoted;
     ngx_array_t                 lengths, values;
     ngx_http_variable_value_t  *vv;
 
@@ -1731,12 +1731,7 @@ ngx_http_ssi_evaluate_string(ngx_http_re
                 goto invalid_variable;
             }
 
-            key = 0;
-
-            for (j = 0; j < var.len; j++) {
-                var.data[j] = ngx_tolower(var.data[j]);
-                key = ngx_hash(key, var.data[j]);
-            }
+            key = ngx_hash_strlow(var.data, var.data, var.len);
 
             val = ngx_http_ssi_get_variable(r, &var, key);
 
@@ -2025,12 +2020,7 @@ ngx_http_ssi_include(ngx_http_request_t 
     }
 
     if (set) {
-        key = 0;
-
-        for (i = 0; i < set->len; i++) {
-            set->data[i] = ngx_tolower(set->data[i]);
-            key = ngx_hash(key, set->data[i]);
-        }
+        key = ngx_hash_strlow(set->data, set->data, set->len);
 
         psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
         if (psr == NULL) {
@@ -2141,7 +2131,6 @@ ngx_http_ssi_echo(ngx_http_request_t *r,
     u_char                     *p;
     uintptr_t                   len;
     ngx_int_t                   key;
-    ngx_uint_t                  i;
     ngx_buf_t                  *b;
     ngx_str_t                  *var, *value, *enc, text;
     ngx_chain_t                *cl;
@@ -2152,12 +2141,7 @@ ngx_http_ssi_echo(ngx_http_request_t *r,
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "ssi echo \"%V\"", var);
 
-    key = 0;
-
-    for (i = 0; i < var->len; i++) {
-        var->data[i] = ngx_tolower(var->data[i]);
-        key = ngx_hash(key, var->data[i]);
-    }
+    key = ngx_hash_strlow(var->data, var->data, var->len);
 
     value = ngx_http_ssi_get_variable(r, var, key);
 
@@ -2310,7 +2294,6 @@ ngx_http_ssi_set(ngx_http_request_t *r, 
     ngx_str_t **params)
 {
     ngx_int_t            key, rc;
-    ngx_uint_t           i;
     ngx_str_t           *name, *value, *vv;
     ngx_http_ssi_var_t  *var;
     ngx_http_ssi_ctx_t  *mctx;
@@ -2337,12 +2320,7 @@ ngx_http_ssi_set(ngx_http_request_t *r, 
         return rc;
     }
 
-    key = 0;
-
-    for (i = 0; i < name->len; i++) {
-        name->data[i] = ngx_tolower(name->data[i]);
-        key = ngx_hash(key, name->data[i]);
-    }
+    key = ngx_hash_strlow(name->data, name->data, name->len);
 
     vv = ngx_http_ssi_get_variable(r, name, key);
 
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -13,9 +13,9 @@ typedef ngx_int_t (*ngx_ssl_variable_han
     ngx_pool_t *pool, ngx_str_t *s);
 
 
-#define NGX_DEFLAUT_CERTIFICATE      "cert.pem"
-#define NGX_DEFLAUT_CERTIFICATE_KEY  "cert.pem"
-#define NGX_DEFLAUT_CIPHERS  "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
+#define NGX_DEFAULT_CERTIFICATE      "cert.pem"
+#define NGX_DEFAULT_CERTIFICATE_KEY  "cert.pem"
+#define NGX_DEFAULT_CIPHERS  "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
 
 
 static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r,
@@ -49,6 +49,14 @@ static ngx_conf_bitmask_t  ngx_http_ssl_
 };
 
 
+static ngx_conf_enum_t  ngx_http_ssl_verify[] = {
+    { ngx_string("off"), 0 },
+    { ngx_string("on"), 1 },
+    { ngx_string("ask"), 2 },
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_command_t  ngx_http_ssl_commands[] = {
 
     { ngx_string("ssl"),
@@ -95,10 +103,10 @@ static ngx_command_t  ngx_http_ssl_comma
 
     { ngx_string("ssl_verify_client"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
-      ngx_conf_set_flag_slot,
+      ngx_conf_set_enum_slot,
       NGX_HTTP_SRV_CONF_OFFSET,
       offsetof(ngx_http_ssl_srv_conf_t, verify),
-      NULL },
+      &ngx_http_ssl_verify },
 
     { ngx_string("ssl_verify_depth"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
@@ -185,6 +193,10 @@ static ngx_http_variable_t  ngx_http_ssl
     { ngx_string("ssl_client_cert"), NULL, ngx_http_ssl_variable,
       (uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
+    { ngx_string("ssl_client_raw_cert"), NULL, ngx_http_ssl_variable,
+      (uintptr_t) ngx_ssl_get_raw_certificate,
+      NGX_HTTP_VAR_CHANGEABLE, 0 },
+
     { ngx_string("ssl_client_s_dn"), NULL, ngx_http_ssl_variable,
       (uintptr_t) ngx_ssl_get_subject_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
@@ -307,9 +319,9 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t 
      */
 
     sscf->enable = NGX_CONF_UNSET;
+    sscf->prefer_server_ciphers = NGX_CONF_UNSET;
     sscf->verify = NGX_CONF_UNSET;
     sscf->verify_depth = NGX_CONF_UNSET;
-    sscf->prefer_server_ciphers = NGX_CONF_UNSET;
     sscf->builtin_session_cache = NGX_CONF_UNSET;
     sscf->session_timeout = NGX_CONF_UNSET;
 
@@ -341,21 +353,21 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
                          (NGX_CONF_BITMASK_SET
                           |NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1));
 
-    ngx_conf_merge_value(conf->verify, prev->verify, 0);
-    ngx_conf_merge_value(conf->verify_depth, prev->verify_depth, 1);
+    ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
+    ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
 
     ngx_conf_merge_str_value(conf->certificate, prev->certificate,
-                         NGX_DEFLAUT_CERTIFICATE);
+                         NGX_DEFAULT_CERTIFICATE);
 
     ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key,
-                         NGX_DEFLAUT_CERTIFICATE_KEY);
+                         NGX_DEFAULT_CERTIFICATE_KEY);
 
     ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
 
     ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
                          "");
 
-    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFLAUT_CIPHERS);
+    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
 
     conf->ssl.log = cf->log;
@@ -402,6 +414,13 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
     }
 
     if (conf->verify) {
+
+        if (conf->client_certificate.len == 0) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "no ssl_client_certificate for ssl_client_verify");
+            return NGX_CONF_ERROR;
+        }
+
         if (ngx_ssl_client_certificate(cf, &conf->ssl,
                                        &conf->client_certificate,
                                        conf->verify_depth)
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -22,8 +22,8 @@ typedef struct {
 
     ngx_uint_t                      protocols;
 
-    ngx_int_t                       verify;
-    ngx_int_t                       verify_depth;
+    ngx_uint_t                      verify;
+    ngx_uint_t                      verify_depth;
 
     ssize_t                         builtin_session_cache;
 
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -98,6 +98,7 @@ ngx_http_static_handler(ngx_http_request
 
     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+    of.directio = clcf->directio;
     of.valid = clcf->open_file_cache_valid;
     of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
--- a/src/http/modules/ngx_http_sub_filter_module.c
+++ b/src/http/modules/ngx_http_sub_filter_module.c
@@ -632,7 +632,6 @@ ngx_http_sub_filter(ngx_conf_t *cf, ngx_
 
     ngx_str_t                  *value;
     ngx_int_t                   n;
-    ngx_uint_t                  i;
     ngx_http_script_compile_t   sc;
 
     if (slcf->match.len) {
@@ -641,11 +640,9 @@ ngx_http_sub_filter(ngx_conf_t *cf, ngx_
 
     value = cf->args->elts;
 
-    slcf->match = value[1];
+    ngx_strlow(value[1].data, value[1].data, value[1].len);
 
-    for (i = 0; i < value[1].len; i++) {
-        value[1].data[i] = ngx_tolower(value[1].data[i]);
-    }
+    slcf->match = value[1];
 
     n = ngx_http_script_variables_count(&value[2]);
 
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -0,0 +1,1109 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+
+
+#ifndef NGX_HTTP_XSLT_REUSE_DTD
+#define NGX_HTTP_XSLT_REUSE_DTD  1
+#endif
+
+
+typedef struct {
+    ngx_array_t         *lengths;
+    ngx_array_t         *values;
+} ngx_http_xslt_param_t;
+
+
+typedef struct {
+    xsltStylesheetPtr    stylesheet;
+    ngx_array_t          params;       /* ngx_http_xslt_param_t */
+} ngx_http_xslt_sheet_t;
+
+
+typedef struct {
+    xmlDtdPtr            dtd;
+    ngx_array_t          sheets;        /* ngx_http_xslt_sheet_t */
+    ngx_hash_t           types_hash;
+    ngx_array_t         *keys;
+} ngx_http_xslt_filter_conf_t;
+
+
+typedef struct {
+    xmlDocPtr            doc;
+    xmlParserCtxtPtr     ctxt;
+    xmlSAXHandler       *sax;
+    ngx_http_request_t  *request;
+    ngx_array_t          params;
+    unsigned             done:1;
+    unsigned             html:1;
+} ngx_http_xslt_filter_ctx_t;
+
+
+static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,
+    ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
+static ngx_int_t ngx_http_xslt_filter_internal_error(ngx_http_request_t *r);
+static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r,
+    ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
+
+
+static void ngx_http_xslt_sax_start_document(void *data);
+static void ngx_http_xslt_sax_end_document(void *data);
+static void ngx_http_xslt_sax_internal_subset(void *data, const xmlChar *name,
+    const xmlChar *externalId, const xmlChar *systemId);
+static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
+    const xmlChar *externalId, const xmlChar *systemId);
+static void ngx_http_xslt_sax_entity_decl(void *data, const xmlChar *name,
+    int type, const xmlChar *publicId, const xmlChar *systemId,
+    xmlChar *content);
+static void ngx_http_xslt_sax_attribute_decl(void *data, const xmlChar *elem,
+    const xmlChar *fullname, int type, int def, const xmlChar *defaultValue,
+    xmlEnumerationPtr tree);
+static void ngx_http_xslt_sax_element_decl(void *data, const xmlChar * name,
+    int type, xmlElementContentPtr content);
+static void ngx_http_xslt_sax_notation_decl(void *data, const xmlChar *name,
+    const xmlChar *publicId, const xmlChar *systemId);
+static void ngx_http_xslt_sax_unparsed_entity_decl(void *data,
+    const xmlChar *name, const xmlChar *publicId, const xmlChar *systemId,
+    const xmlChar *notationName);
+static void ngx_http_xslt_sax_start_element(void *data,
+    const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI,
+    int nb_namespaces, const xmlChar **namespaces, int nb_attributes,
+    int nb_defaulted, const xmlChar **attributes);
+static void ngx_http_xslt_sax_end_element(void *data,
+    const xmlChar * localname ATTRIBUTE_UNUSED,
+    const xmlChar * prefix ATTRIBUTE_UNUSED,
+    const xmlChar * URI ATTRIBUTE_UNUSED);
+static void ngx_http_xslt_sax_characters(void *data, const xmlChar *p, int len);
+static void ngx_http_xslt_sax_cdata_block(void *data, const xmlChar *p,
+    int len);
+static xmlEntityPtr ngx_http_xslt_sax_get_entity(void *data,
+    const xmlChar *name);
+static xmlEntityPtr ngx_http_xslt_sax_get_parameter_entity(void *data,
+    const xmlChar *name);
+static xmlParserInputPtr ngx_http_xslt_sax_resolve_entity(void *data,
+    const xmlChar *publicId, const xmlChar *systemId);
+static void ngx_http_xslt_sax_reference(void *data, const xmlChar *name);
+static void ngx_http_xslt_sax_comment(void *data, const xmlChar *value);
+static void ngx_http_xslt_sax_processing_instruction(void *data,
+    const xmlChar *target, const xmlChar *pidata);
+static int ngx_http_xslt_sax_is_standalone(void *data);
+static int ngx_http_xslt_sax_has_internal_subset(void *data);
+static int ngx_http_xslt_sax_has_external_subset(void *data);
+static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...);
+
+
+static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
+    ngx_http_xslt_filter_ctx_t *ctx);
+static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r,
+    ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params);
+static void ngx_http_xslt_cleanup(void *data);
+
+static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static void ngx_http_xslt_cleanup_stylesheet(void *data);
+static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf);
+static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent,
+    void *child);
+static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf);
+
+
+ngx_str_t  ngx_http_xslt_default_types[] = {
+    ngx_string("text/xml"),
+    ngx_null_string
+};
+
+
+static ngx_command_t  ngx_http_xslt_filter_commands[] = {
+
+    { ngx_string("xml_entities"),
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
+      ngx_http_xslt_entities,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("xslt_stylesheet"),
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_1MORE,
+      ngx_http_xslt_stylesheet,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("xslt_types"),
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_1MORE,
+      ngx_http_types_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_xslt_filter_conf_t, keys),
+      &ngx_http_xslt_default_types[0] },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_xslt_filter_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    ngx_http_xslt_filter_init,             /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_xslt_filter_create_conf,      /* create location configuration */
+    ngx_http_xslt_filter_merge_conf        /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_xslt_filter_module = {
+    NGX_MODULE_V1,
+    &ngx_http_xslt_filter_module_ctx,      /* module context */
+    ngx_http_xslt_filter_commands,         /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_xslt_header_filter(ngx_http_request_t *r)
+{
+    ngx_http_xslt_filter_ctx_t   *ctx;
+    ngx_http_xslt_filter_conf_t  *conf;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "xslt filter header");
+
+    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
+        return ngx_http_next_header_filter(r);
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
+
+    if (conf->sheets.nelts == 0
+        || ngx_http_test_content_type(r, &conf->types_hash) == NULL)
+    {
+        return ngx_http_next_header_filter(r);
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
+
+    if (ctx) {
+        return ngx_http_next_header_filter(r);
+    }
+
+    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);
+
+    r->main_filter_need_in_memory = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    ngx_chain_t                 *cl;
+    ngx_http_xslt_filter_ctx_t  *ctx;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "xslt filter body");
+
+    if (in == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
+
+    if (ctx == NULL || ctx->done) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    for (cl = in; cl; cl = cl->next) {
+
+        if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {
+
+            if (ctx->ctxt->myDoc){
+
+#if (NGX_HTTP_XSLT_REUSE_DTD)
+                ctx->ctxt->myDoc->extSubset = NULL;
+#endif
+                xmlFreeDoc(ctx->ctxt->myDoc);
+            }
+
+            xmlFreeParserCtxt(ctx->ctxt);
+
+            return ngx_http_xslt_send(r, ctx, NULL);
+        }
+
+        if (cl->buf->last_buf) {
+
+            ctx->doc = ctx->ctxt->myDoc;
+
+#if (NGX_HTTP_XSLT_REUSE_DTD)
+            ctx->doc->extSubset = NULL;
+#endif
+
+            xmlFreeParserCtxt(ctx->ctxt);
+
+            if (ctx->ctxt->wellFormed) {
+                return ngx_http_xslt_send(r, ctx,
+                                       ngx_http_xslt_apply_stylesheet(r, ctx));
+            }
+
+            xmlFreeDoc(ctx->doc);
+
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "not well formed XML document");
+
+            return ngx_http_xslt_send(r, ctx, NULL);
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
+    ngx_buf_t *b)
+{
+    ngx_int_t            rc;
+    ngx_chain_t          out;
+    ngx_pool_cleanup_t  *cln;
+
+    ctx->done = 1;
+
+    if (b == NULL) {
+        return ngx_http_xslt_filter_internal_error(r);
+    }
+
+    cln = ngx_pool_cleanup_add(r->pool, 0);
+
+    if (cln == NULL) {
+        ngx_free(b->pos);
+        return ngx_http_special_response_handler(r,
+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
+    }
+
+    if (ctx->html) {
+        r->headers_out.content_type_len = sizeof("text/html") - 1;
+        r->headers_out.content_type.len = sizeof("text/html") - 1;
+        r->headers_out.content_type.data = (u_char *) "text/html";
+    }
+
+    r->headers_out.content_length_n = b->last - b->pos;
+
+    if (r->headers_out.content_length) {
+        r->headers_out.content_length->hash = 0;
+        r->headers_out.content_length = NULL;
+    }
+
+    r->allow_ranges = 1;
+
+    rc = ngx_http_next_header_filter(r);
+
+    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+        ngx_free(b->pos);
+        return rc;
+    }
+
+    cln->handler = ngx_http_xslt_cleanup;
+    cln->data = b->pos;
+
+    out.buf = b;
+    out.next = NULL;
+
+    return ngx_http_next_body_filter(r, &out);
+}
+
+
+static ngx_int_t
+ngx_http_xslt_filter_internal_error(ngx_http_request_t *r)
+{
+    ngx_int_t  rc;
+
+    /* clear the modules contexts */
+    ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
+
+    rc = ngx_http_special_response_handler(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+    /* NGX_ERROR resets any pending data */
+
+    return (rc == NGX_OK) ? NGX_ERROR : rc;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
+    ngx_buf_t *b)
+{
+    int                err;
+    xmlSAXHandler     *sax;
+    xmlParserCtxtPtr   ctxt;
+
+    if (ctx->ctxt == NULL) {
+
+        ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
+        if (ctxt == NULL) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "xmlCreatePushParserCtxt() failed");
+            return NGX_ERROR;
+        }
+
+        ctx->sax = ngx_palloc(r->pool, sizeof(xmlSAXHandler));
+        if (ctx->sax == NULL) {
+            return NGX_ERROR;
+        }
+
+        sax = ctxt->sax;
+
+        ngx_memcpy(ctx->sax, sax, sizeof(xmlSAXHandler));
+
+        sax->startDocument = ngx_http_xslt_sax_start_document;
+        sax->endDocument = ngx_http_xslt_sax_end_document;
+
+        sax->internalSubset = ngx_http_xslt_sax_internal_subset;
+        sax->externalSubset = ngx_http_xslt_sax_external_subset;
+        sax->entityDecl = ngx_http_xslt_sax_entity_decl;
+        sax->attributeDecl = ngx_http_xslt_sax_attribute_decl;
+        sax->elementDecl = ngx_http_xslt_sax_element_decl;
+        sax->notationDecl = ngx_http_xslt_sax_notation_decl;
+        sax->unparsedEntityDecl = ngx_http_xslt_sax_unparsed_entity_decl;
+        sax->setDocumentLocator = NULL;
+
+        sax->startElementNs = ngx_http_xslt_sax_start_element;
+        sax->endElementNs = ngx_http_xslt_sax_end_element;
+
+        sax->characters = ngx_http_xslt_sax_characters;
+        sax->ignorableWhitespace  = ngx_http_xslt_sax_characters;
+        sax->cdataBlock = ngx_http_xslt_sax_cdata_block;
+        sax->getEntity = ngx_http_xslt_sax_get_entity;
+        sax->resolveEntity = ngx_http_xslt_sax_resolve_entity;
+        sax->getParameterEntity = ngx_http_xslt_sax_get_parameter_entity;
+        sax->reference = ngx_http_xslt_sax_reference;
+        sax->comment = ngx_http_xslt_sax_comment;
+        sax->processingInstruction = ngx_http_xslt_sax_processing_instruction;
+
+        sax->isStandalone = ngx_http_xslt_sax_is_standalone;
+        sax->hasInternalSubset = ngx_http_xslt_sax_has_internal_subset;
+        sax->hasExternalSubset = ngx_http_xslt_sax_has_external_subset;
+
+        sax->warning = NULL;
+        sax->error = ngx_http_xslt_sax_error;
+        sax->fatalError = ngx_http_xslt_sax_error;
+
+        ctxt->userData = ctx;
+
+        ctxt->replaceEntities = 1;
+        ctxt->loadsubset = 1;
+
+        ctx->ctxt = ctxt;
+        ctx->request = r;
+    }
+
+    err = xmlParseChunk(ctx->ctxt, (char *) b->pos,
+                        (int) (b->last - b->pos), b->last_buf);
+
+    if (err == 0) {
+        b->pos = b->last;
+        return NGX_OK;
+    }
+
+    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                  "xmlParseChunk() failed, error:%d", err);
+
+    return NGX_ERROR;
+}
+
+
+static void
+ngx_http_xslt_sax_start_document(void *data)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->startDocument(ctx->ctxt);
+}
+
+
+static void
+ngx_http_xslt_sax_end_document(void *data)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->endDocument(ctx->ctxt);
+}
+
+
+static void
+ngx_http_xslt_sax_internal_subset(void *data, const xmlChar *name,
+    const xmlChar *externalId, const xmlChar *systemId)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->internalSubset(ctx->ctxt, name, externalId, systemId);
+}
+
+
+static void
+ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
+    const xmlChar *externalId, const xmlChar *systemId)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    xmlDocPtr                     doc;
+    xmlDtdPtr                     dtd;
+    ngx_http_request_t           *r;
+    ngx_http_xslt_filter_conf_t  *conf;
+
+    r = ctx->request;
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
+
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "xslt filter extSubset: \"%s\" \"%s\" \"%s\"",
+                   name ? name : (xmlChar *) "",
+                   externalId ? externalId : (xmlChar *) "",
+                   systemId ? systemId : (xmlChar *) "");
+
+    doc = ctx->ctxt->myDoc;
+
+#if (NGX_HTTP_XSLT_REUSE_DTD)
+
+    dtd = conf->dtd;
+
+#else
+
+    dtd = xmlCopyDtd(conf->dtd);
+    if (dtd == NULL) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "xmlCopyDtd() failed");
+        return;
+    }
+
+    dtd->name = xmlStrdup(name);
+
+    if (doc->children == NULL) {
+        xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
+
+    } else {
+        xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);
+    }
+
+#endif
+
+    doc->extSubset = dtd;
+}
+
+
+static void
+ngx_http_xslt_sax_entity_decl(void *data, const xmlChar *name, int type,
+    const xmlChar *publicId, const xmlChar *systemId, xmlChar *content)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->entityDecl(ctx->ctxt, name, type, publicId, systemId, content);
+}
+
+
+static void
+ngx_http_xslt_sax_attribute_decl(void *data, const xmlChar *elem,
+    const xmlChar *fullname, int type, int def, const xmlChar *defaultValue,
+    xmlEnumerationPtr tree)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->attributeDecl(ctx->ctxt, elem, fullname, type, def, defaultValue,
+                            tree);
+}
+
+
+static void
+ngx_http_xslt_sax_element_decl(void *data, const xmlChar * name, int type,
+    xmlElementContentPtr content)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->elementDecl(ctx->ctxt, name, type, content);
+}
+
+
+static void
+ngx_http_xslt_sax_notation_decl(void *data, const xmlChar *name,
+    const xmlChar *publicId, const xmlChar *systemId)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->notationDecl(ctx->ctxt, name, publicId, systemId);
+}
+
+
+static void
+ngx_http_xslt_sax_unparsed_entity_decl(void *data, const xmlChar *name,
+    const xmlChar *publicId, const xmlChar *systemId,
+    const xmlChar *notationName)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->unparsedEntityDecl(ctx->ctxt, name, publicId, systemId,
+                                 notationName);
+}
+
+
+static void
+ngx_http_xslt_sax_start_element(void *data, const xmlChar *localname,
+    const xmlChar *prefix, const xmlChar *URI, int nb_namespaces,
+    const xmlChar **namespaces, int nb_attributes, int nb_defaulted,
+    const xmlChar **attributes)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->startElementNs(ctx->ctxt, localname, prefix, URI, nb_namespaces,
+        namespaces, nb_attributes, nb_defaulted, attributes);
+}
+
+
+static void
+ngx_http_xslt_sax_end_element(void *data,
+    const xmlChar * localname ATTRIBUTE_UNUSED,
+    const xmlChar * prefix ATTRIBUTE_UNUSED,
+    const xmlChar * URI ATTRIBUTE_UNUSED)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->endElementNs(ctx->ctxt, localname, prefix, URI);
+}
+
+
+static void
+ngx_http_xslt_sax_characters(void *data, const xmlChar *p, int len)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->characters(ctx->ctxt, p, len);
+}
+
+
+static void
+ngx_http_xslt_sax_cdata_block(void *data, const xmlChar *p, int len)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->cdataBlock(ctx->ctxt, p, len);
+}
+
+
+static xmlEntityPtr
+ngx_http_xslt_sax_get_entity(void *data, const xmlChar *name)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->getEntity(ctx->ctxt, name);
+}
+
+
+static xmlEntityPtr
+ngx_http_xslt_sax_get_parameter_entity(void *data, const xmlChar *name)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->getParameterEntity(ctx->ctxt, name);
+}
+
+
+static xmlParserInputPtr
+ngx_http_xslt_sax_resolve_entity(void *data, const xmlChar *publicId,
+    const xmlChar *systemId)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->resolveEntity(ctx->ctxt, publicId, systemId);
+}
+
+
+static void
+ngx_http_xslt_sax_reference(void *data, const xmlChar *name)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->reference(ctx->ctxt, name);
+}
+
+
+static void
+ngx_http_xslt_sax_comment(void *data, const xmlChar *value)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->comment(ctx->ctxt, value);
+}
+
+
+static void
+ngx_http_xslt_sax_processing_instruction(void *data, const xmlChar *target,
+    const xmlChar *pidata)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    ctx->sax->processingInstruction(ctx->ctxt, target, pidata);
+}
+
+
+static int
+ngx_http_xslt_sax_is_standalone(void *data)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->isStandalone(ctx->ctxt);
+}
+
+
+static int
+ngx_http_xslt_sax_has_internal_subset(void *data)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->hasInternalSubset(ctx->ctxt);
+}
+
+
+static int
+ngx_http_xslt_sax_has_external_subset(void *data)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    return ctx->sax->hasExternalSubset(ctx->ctxt);
+}
+
+
+static void ngx_cdecl
+ngx_http_xslt_sax_error(void *data, const char *msg, ...)
+{
+    ngx_http_xslt_filter_ctx_t *ctx = data;
+
+    size_t    n;
+    va_list   args;
+    u_char    buf[NGX_MAX_ERROR_STR];
+
+    buf[0] = '\0';
+
+    va_start(args, msg);
+    n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);
+    va_end(args);
+
+    while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
+
+    ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
+                  "libxml2 error: \"%*s\"", n, buf);
+}
+
+
+static ngx_buf_t *
+ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
+    ngx_http_xslt_filter_ctx_t *ctx)
+{
+    int                           len, rc;
+    ngx_buf_t                    *b;
+    ngx_uint_t                    i;
+    xmlChar                      *buf;
+    xmlDocPtr                     doc, res;
+    ngx_http_xslt_sheet_t        *sheet;
+    ngx_http_xslt_filter_conf_t  *conf;
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
+    sheet = conf->sheets.elts;
+    doc = ctx->doc;
+
+    /* preallocate array for 4 params */
+
+    if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))
+        != NGX_OK)
+    {
+        xmlFreeDoc(doc);
+        return NULL;
+    }
+
+    for (i = 0; i < conf->sheets.nelts; i++) {
+
+        if (ngx_http_xslt_params(r, ctx, &sheet[i].params) != NGX_OK) {
+            xmlFreeDoc(doc);
+            return NULL;
+        }
+
+        res = xsltApplyStylesheet(sheet[i].stylesheet, doc, ctx->params.elts);
+
+        xmlFreeDoc(doc);
+
+        if (res == NULL) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "xsltApplyStylesheet() failed");
+            return NULL;
+        }
+
+        doc = res;
+
+        /* reset array elements */
+        ctx->params.nelts = 0;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "xslt filter doc type: %d", doc->type);
+
+    ctx->html = (doc->type == XML_HTML_DOCUMENT_NODE) ? 1 : 0;
+
+    rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);
+
+    xmlFreeDoc(doc);
+
+    if (rc != 0) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "xsltSaveResultToString() failed");
+        return NULL;
+    }
+
+    if (len == 0) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "xsltSaveResultToString() returned zero-length result");
+        return NULL;
+    }
+
+    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+    if (b == NULL) {
+        ngx_free(buf);
+        return NULL;
+    }
+
+    b->pos = buf;
+    b->last = buf + len;
+    b->memory = 1;
+    b->last_buf = 1;
+
+    return b;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
+    ngx_array_t *params)
+{
+    u_char                 *p, *last, *value, *dst, *src, **s;
+    size_t                  len;
+    ngx_uint_t              i;
+    ngx_str_t               string;
+    ngx_http_xslt_param_t  *param;
+
+    param = params->elts;
+
+    for (i = 0; i < params->nelts; i++) {
+
+        if (ngx_http_script_run(r, &string, param[i].lengths->elts, 1,
+                                param[i].values->elts)
+            == NULL)
+        {
+            return NGX_ERROR;
+        }
+
+        last = string.data + string.len - 1;
+        *last = '\0';
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "xslt filter param: \"%s\"", string.data);
+
+        p = string.data;
+
+        while (p && *p) {
+
+            value = p;
+            p = (u_char *) ngx_strchr(p, '=');
+            if (p == NULL) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                "invalid libxslt parameter \"%s\"", value);
+                return NGX_ERROR;
+            }
+            *p++ = '\0';
+
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "xslt filter param name: \"%s\"", value);
+
+            s = ngx_array_push(&ctx->params);
+            if (s == NULL) {
+                return NGX_ERROR;
+            }
+
+            *s = value;
+
+            value = p;
+            p = (u_char *) ngx_strchr(p, ':');
+
+            if (p) {
+                len = p - value;
+                *p++ = '\0';
+
+            } else {
+                len = last - value;
+            }
+
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "xslt filter param value: \"%s\"", value);
+
+            dst = value;
+            src = value;
+
+            ngx_unescape_uri(&dst, &src, len, 0);
+
+            *dst = '\0';
+
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "xslt filter param unescaped: \"%s\"", value);
+
+            s = ngx_array_push(&ctx->params);
+            if (s == NULL) {
+                return NGX_ERROR;
+            }
+
+            *s = value;
+        }
+    }
+
+    s = ngx_array_push(&ctx->params);
+    if (s == NULL) {
+        return NGX_ERROR;
+    }
+
+    *s = NULL;
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_http_xslt_cleanup(void *data)
+{
+    ngx_free(data);
+}
+
+
+static char *
+ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_xslt_filter_conf_t *xlcf = conf;
+
+    ngx_str_t  *value;
+
+    if (xlcf->dtd) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);
+
+    if (xlcf->dtd == NULL) {
+        ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+
+static char *
+ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_xslt_filter_conf_t *xlcf = conf;
+
+    ngx_str_t                  *value;
+    ngx_uint_t                  i, n;
+    ngx_pool_cleanup_t         *cln;
+    ngx_http_xslt_sheet_t      *sheet;
+    ngx_http_xslt_param_t      *param;
+    ngx_http_script_compile_t   sc;
+
+    value = cf->args->elts;
+
+    if (xlcf->sheets.elts == NULL) {
+        if (ngx_array_init(&xlcf->sheets, cf->pool, 1,
+                           sizeof(ngx_http_xslt_sheet_t))
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    sheet = ngx_array_push(&xlcf->sheets);
+    if (sheet == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));
+
+    if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    sheet->stylesheet = xsltParseStylesheetFile(value[1].data);
+    if (sheet->stylesheet == NULL) {
+        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+                           "xsltParseStylesheetFile(\"%s\") failed",
+                           value[1].data);
+        return NGX_CONF_ERROR;
+    }
+
+    cln->handler = ngx_http_xslt_cleanup_stylesheet;
+    cln->data = sheet->stylesheet;
+
+    n = cf->args->nelts;
+
+    if (n == 2) {
+        return NGX_CONF_OK;
+    }
+
+    if (ngx_array_init(&sheet->params, cf->pool, n - 2,
+                       sizeof(ngx_http_xslt_param_t))
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    for (i = 2; i < n; i++) {
+
+        param = ngx_array_push(&sheet->params);
+        if (param == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        param->lengths = NULL;
+        param->values = NULL;
+
+        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+        sc.cf = cf;
+        sc.source = &value[i];
+        sc.lengths = &param->lengths;
+        sc.values = &param->values;
+        sc.variables = ngx_http_script_variables_count(&value[i]);
+        sc.complete_lengths = 1;
+        sc.complete_values = 1;
+
+        if (ngx_http_script_compile(&sc) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static void
+ngx_http_xslt_cleanup_stylesheet(void *data)
+{
+    xsltStylesheetPtr  stylesheet = data;
+
+    xsltFreeStylesheet(stylesheet);
+}
+
+
+
+static void *
+ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
+{
+    ngx_http_xslt_filter_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_conf_t));
+    if (conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     conf->dtd
+     *     conf->sheets
+     */
+
+    return conf;
+}
+
+
+static char *
+ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_xslt_filter_conf_t *prev = parent;
+    ngx_http_xslt_filter_conf_t *conf = child;
+
+    if (conf->dtd == NULL) {
+        conf->dtd = prev->dtd;
+    }
+
+    if (conf->sheets.nelts == 0) {
+        conf->sheets = prev->sheets;
+    }
+
+    if (ngx_http_merge_types(cf, conf->keys, &conf->types_hash, prev->keys,
+                             &prev->types_hash, ngx_http_xslt_default_types)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_filter_init(ngx_conf_t *cf)
+{
+    xmlInitParser();
+
+    ngx_http_next_header_filter = ngx_http_top_header_filter;
+    ngx_http_top_header_filter = ngx_http_xslt_header_filter;
+
+    ngx_http_next_body_filter = ngx_http_top_body_filter;
+    ngx_http_top_body_filter = ngx_http_xslt_body_filter;
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
+{
+    xsltCleanupGlobals();
+    xmlCleanupParser();
+}
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.7.6';
+our $VERSION = '0.7.8';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -247,11 +247,7 @@ header_in(r, key)
         XSRETURN_UNDEF;
     }
 
-    hash = 0;
-    for (i = 0; i < len; i++) {
-        lowcase_key[i] = ngx_tolower(p[i]);
-        hash = ngx_hash(hash, lowcase_key[i]);
-    }
+    hash = ngx_hash_strlow(lowcase_key, p, len);
 
     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
 
@@ -652,6 +648,7 @@ sendfile(r, filename, offset = -1, bytes
 
     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+    of.directio = clcf->directio;
     of.valid = clcf->open_file_cache_valid;
     of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
@@ -832,11 +829,7 @@ variable(r, name, value = NULL)
         XSRETURN_UNDEF;
     }
 
-    hash = 0;
-    for (i = 0; i < len; i++) {
-        lowcase[i] = ngx_tolower(p[i]);
-        hash = ngx_hash(hash, lowcase[i]);
-    }
+    hash = ngx_hash_strlow(lowcase, p, len);
 
     var.len = len;
     var.data = lowcase;
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -1259,7 +1259,7 @@ static ngx_int_t
 ngx_http_add_names(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
     ngx_http_conf_in_addr_t *in_addr)
 {
-    ngx_uint_t               i, n;
+    ngx_uint_t               i;
     ngx_http_server_name_t  *server_names, *name;
 
     if (in_addr->names.elts == NULL) {
@@ -1275,10 +1275,8 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h
 
     for (i = 0; i < cscf->server_names.nelts; i++) {
 
-        for (n = 0; n < server_names[i].name.len; n++) {
-            server_names[i].name.data[n] =
-                                     ngx_tolower(server_names[i].name.data[n]);
-        }
+        ngx_strlow(server_names[i].name.data, server_names[i].name.data,
+                   server_names[i].name.len);
 
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0,
                        "name: %V", &server_names[i].name);
@@ -1689,3 +1687,135 @@ ngx_http_init_listening(ngx_conf_t *cf, 
 
     return NGX_OK;
 }
+
+
+char *
+ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    char  *p = conf;
+
+    ngx_array_t     **types;
+    ngx_str_t        *value, *default_type;
+    ngx_uint_t        i, n, hash;
+    ngx_hash_key_t   *type;
+
+    types = (ngx_array_t **) (p + cmd->offset);
+
+    default_type = cmd->post;
+
+    if (*types == NULL) {
+        *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t));
+        if (*types == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        if (default_type) {
+            type = ngx_array_push(*types);
+            if (type == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            type->key = *default_type;
+            type->key_hash = ngx_hash_key(default_type->data,
+                                          default_type->len);
+            type->value = (void *) 4;
+        }
+    }
+
+    value = cf->args->elts;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len);
+        value[i].data[value[i].len] = '\0';
+
+        type = (*types)->elts;
+        for (n = 0; n < (*types)->nelts; n++) {
+
+            if (ngx_strcmp(value[i].data, type[n].key.data) == 0) {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                                   "duplicate MIME type \"%V\"", &value[i]);
+                continue;
+            }
+        }
+
+        type = ngx_array_push(*types);
+        if (type == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        type->key = value[i];
+        type->key_hash = hash;
+        type->value = (void *) 4;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+char *
+ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t *keys, ngx_hash_t *types_hash,
+    ngx_array_t *prev_keys, ngx_hash_t *prev_types_hash, 
+    ngx_str_t *default_types)
+{
+    ngx_hash_init_t  hash;
+
+    if (keys == NULL) {
+
+        if (prev_keys) {
+            *types_hash = *prev_types_hash;
+            return NGX_CONF_OK;
+        }
+
+        if (ngx_http_set_default_types(cf, &keys, default_types)
+            != NGX_CONF_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    hash.hash = types_hash;
+    hash.key = NULL;
+    hash.max_size = 2048;
+    hash.bucket_size = 64;
+    hash.name = "test_types_hash";
+    hash.pool = cf->pool;
+    hash.temp_pool = NULL;
+
+    if (ngx_hash_init(&hash, keys->elts, keys->nelts) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+
+}
+
+
+char *
+ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,
+    ngx_str_t *default_type)
+{
+    ngx_hash_key_t  *type;
+
+    *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t));
+    if (*types == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    while (default_type->len) {
+
+        type = ngx_array_push(*types);
+        if (type == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        type->key = *default_type;
+        type->key_hash = ngx_hash_key(default_type->data,
+                                      default_type->len);
+        type->value = (void *) 4;
+
+        default_type++;
+    }
+
+    return NGX_CONF_OK;
+}
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -107,6 +107,14 @@ ngx_int_t ngx_http_discard_request_body(
 void ngx_http_block_reading(ngx_http_request_t *r);
 
 
+char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t *keys,
+    ngx_hash_t *types_hash, ngx_array_t *prev_keys, ngx_hash_t *prev_types_hash,
+    ngx_str_t *default_types);
+char *ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,
+    ngx_str_t *default_type);
+
+
 extern ngx_module_t  ngx_http_module;
 
 
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -54,6 +54,8 @@ static char *ngx_http_core_server_name(n
 static char *ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -353,6 +355,13 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, sendfile_max_chunk),
       NULL },
 
+    { ngx_string("directio"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_http_core_directio,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("tcp_nopush"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -1204,7 +1213,7 @@ ngx_http_core_find_static_location(ngx_h
 
         n = (len <= (size_t) node->len) ? len : node->len;
 
-        rc = ngx_memcmp(uri, node->name, n);
+        rc = ngx_filename_cmp(uri, node->name, n);
 
         if (rc != 0) {
             node = (rc < 0) ? node->left : node->right;
@@ -1293,10 +1302,48 @@ ngx_http_core_send_continue(ngx_http_req
 }
 
 
+void *
+ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash)
+{
+    u_char       c, *p;
+    ngx_uint_t   i, hash;
+
+    if (r->headers_out.content_type.len == 0) {
+        return NULL;
+    }
+
+    if (r->headers_out.content_type_lowcase == NULL) {
+
+        p = ngx_pnalloc(r->pool, r->headers_out.content_type_len);
+
+        if (p == NULL) {
+            return NULL;
+        }
+
+        r->headers_out.content_type_lowcase = p;
+
+        hash = 0;
+
+        for (i = 0; i < r->headers_out.content_type_len; i++) {
+            c = ngx_tolower(r->headers_out.content_type.data[i]);
+            hash = ngx_hash(hash, c);
+            *p++ = c;
+        }
+
+        r->headers_out.content_type_hash = hash;
+    }
+
+    return ngx_hash_find(types_hash,
+                         r->headers_out.content_type_hash,
+                         r->headers_out.content_type_lowcase,
+                         r->headers_out.content_type_len);
+}
+
+
 ngx_int_t
 ngx_http_set_content_type(ngx_http_request_t *r)
 {
-    u_char                     c, *p, *exten;
+    u_char                     c, *exten;
     ngx_str_t                 *type;
     ngx_uint_t                 i, hash;
     ngx_http_core_loc_conf_t  *clcf;
@@ -1316,19 +1363,12 @@ ngx_http_set_content_type(ngx_http_reque
 
             if (c >= 'A' && c <= 'Z') {
 
-                p = ngx_pnalloc(r->pool, r->exten.len);
-                if (p == NULL) {
+                exten = ngx_pnalloc(r->pool, r->exten.len);
+                if (exten == NULL) {
                     return NGX_HTTP_INTERNAL_SERVER_ERROR;
                 }
 
-                hash = 0;
-                exten = p;
-
-                for (i = 0; i < r->exten.len; i++) {
-                    c = ngx_tolower(r->exten.data[i]);
-                    hash = ngx_hash(hash, c);
-                    *p++ = c;
-                }
+                hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len);
 
                 r->exten.data = exten;
 
@@ -2307,7 +2347,7 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c
     ngx_http_core_loc_conf_t *lcf = conf;
 
     ngx_str_t       *value, *content_type, *old, file;
-    ngx_uint_t       i, n;
+    ngx_uint_t       i, n, hash;
     ngx_hash_key_t  *type;
 
     value = cf->args->elts;
@@ -2333,9 +2373,7 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c
 
     for (i = 1; i < cf->args->nelts; i++) {
 
-        for (n = 0; n < value[i].len; n++) {
-            value[i].data[n] = ngx_tolower(value[i].data[n]);
-        }
+        hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len);
 
         type = lcf->types->elts;
         for (n = 0; n < lcf->types->nelts; n++) {
@@ -2359,7 +2397,7 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c
         }
 
         type->key = value[i];
-        type->key_hash = ngx_hash_key(value[i].data, value[i].len);
+        type->key_hash = hash;
         type->value = content_type;
     }
 
@@ -2586,6 +2624,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t
     lcf->client_body_in_file_only = NGX_CONF_UNSET;
     lcf->sendfile = NGX_CONF_UNSET;
     lcf->sendfile_max_chunk = NGX_CONF_UNSET_SIZE;
+    lcf->directio = NGX_CONF_UNSET;
     lcf->tcp_nopush = NGX_CONF_UNSET;
     lcf->tcp_nodelay = NGX_CONF_UNSET;
     lcf->send_timeout = NGX_CONF_UNSET_MSEC;
@@ -2774,6 +2813,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
     ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
     ngx_conf_merge_size_value(conf->sendfile_max_chunk,
                               prev->sendfile_max_chunk, 0);
+    ngx_conf_merge_off_value(conf->directio, prev->directio,
+                              NGX_MAX_OFF_T_VALUE);
     ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0);
     ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1);
 
@@ -3340,6 +3381,33 @@ ngx_http_core_limit_except(ngx_conf_t *c
 
 
 static char *
+ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_core_loc_conf_t *clcf = conf;
+
+    ngx_str_t  *value;
+
+    if (clcf->directio != NGX_CONF_UNSET) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        clcf->directio = NGX_MAX_OFF_T_VALUE;
+        return NGX_CONF_OK;
+    }
+
+    clcf->directio = ngx_parse_offset(&value[1]);
+    if (clcf->directio == (off_t) NGX_ERROR) {
+        return "invalid value";
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
 ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_core_loc_conf_t *lcf = conf;
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -265,6 +265,7 @@ struct ngx_http_core_loc_conf_s {
     ngx_str_t     default_type;
 
     off_t         client_max_body_size;    /* client_max_body_size */
+    off_t         directio;                /* directio */
 
     size_t        client_body_buffer_size; /* client_body_buffer_size */
     size_t        send_lowat;              /* send_lowat */
@@ -372,6 +373,8 @@ ngx_int_t ngx_http_core_post_access_phas
 ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph);
 
+
+void *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash);
 ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);
 ngx_int_t ngx_http_set_exten(ngx_http_request_t *r);
 u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name,
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -647,6 +647,7 @@ ngx_http_process_request_line(ngx_event_
 
             r->request_line.len = r->request_end - r->request_start;
             r->request_line.data = r->request_start;
+            *r->request_end = '\0';
 
 
             if (r->args_start) {
@@ -818,7 +819,6 @@ ngx_http_process_request_headers(ngx_eve
     ssize_t                     n;
     ngx_int_t                   rc, rv;
     ngx_str_t                   header;
-    ngx_uint_t                  i;
     ngx_table_elt_t            *h;
     ngx_connection_t           *c;
     ngx_http_header_t          *hh;
@@ -928,9 +928,7 @@ ngx_http_process_request_headers(ngx_eve
                 ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
 
             } else {
-                for (i = 0; i < h->key.len; i++) {
-                    h->lowcase_key[i] = ngx_tolower(h->key.data[i]);
-                }
+                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
             }
 
             hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
@@ -1446,7 +1444,7 @@ ngx_http_process_request(ngx_http_reques
 
         sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
 
-        if (sscf->verify) {
+        if (sscf->verify == 1) {
             rc = SSL_get_verify_result(c->ssl->connection);
 
             if (rc != X509_V_OK) {
@@ -1552,8 +1550,8 @@ ngx_http_validate_host(u_char *host, siz
 static ngx_int_t
 ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len)
 {
-    u_char                    *server, ch;
-    ngx_uint_t                 i, hash;
+    u_char                    *server;
+    ngx_uint_t                 hash;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
     u_char                     buf[32];
@@ -1572,16 +1570,7 @@ ngx_http_find_virtual_server(ngx_http_re
         }
     }
 
-    hash = 0;
-
-    for (i = 0; i < len; i++) {
-        ch = host[i];
-
-        ch = ngx_tolower(ch);
-        server[i] = ch;
-
-        hash = ngx_hash(hash, ch);
-    }
+    hash = ngx_hash_strlow(server, host, len);
 
     cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, server, len);
 
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -244,6 +244,8 @@ typedef struct {
     size_t                            content_type_len;
     ngx_str_t                         content_type;
     ngx_str_t                         charset;
+    u_char                           *content_type_lowcase;
+    ngx_uint_t                        content_type_hash;
 
     ngx_array_t                       cache_control;
 
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -996,6 +996,7 @@ ngx_http_script_file_code(ngx_http_scrip
 
     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+    of.directio = clcf->directio;
     of.valid = clcf->open_file_cache_valid;
     of.min_uses = clcf->open_file_cache_min_uses;
     of.errors = clcf->open_file_cache_errors;
@@ -1015,69 +1016,69 @@ ngx_http_script_file_code(ngx_http_scrip
         case ngx_http_script_file_dir:
         case ngx_http_script_file_exists:
         case ngx_http_script_file_exec:
-             goto false;
+             goto false_value;
 
         case ngx_http_script_file_not_plain:
         case ngx_http_script_file_not_dir:
         case ngx_http_script_file_not_exists:
         case ngx_http_script_file_not_exec:
-             goto true;
+             goto true_value;
         }
 
-        goto false;
+        goto false_value;
     }
 
     switch (code->op) {
     case ngx_http_script_file_plain:
         if (of.is_file) {
-             goto true;
+             goto true_value;
         }
-        goto false;
+        goto false_value;
 
     case ngx_http_script_file_not_plain:
         if (of.is_file) {
-            goto false;
+            goto false_value;
         }
-        goto true;
+        goto true_value;
 
     case ngx_http_script_file_dir:
         if (of.is_dir) {
-             goto true;
+             goto true_value;
         }
-        goto false;
+        goto false_value;
 
     case ngx_http_script_file_not_dir:
         if (of.is_dir) {
-            goto false;
+            goto false_value;
         }
-        goto true;
+        goto true_value;
 
     case ngx_http_script_file_exists:
         if (of.is_file || of.is_dir || of.is_link) {
-             goto true;
+             goto true_value;
         }
-        goto false;
+        goto false_value;
 
     case ngx_http_script_file_not_exists:
         if (of.is_file || of.is_dir || of.is_link) {
-            goto false;
+            goto false_value;
         }
-        goto true;
+        goto true_value;
 
     case ngx_http_script_file_exec:
         if (of.is_exec) {
-             goto true;
+             goto true_value;
         }
-        goto false;
+        goto false_value;
 
     case ngx_http_script_file_not_exec:
         if (of.is_exec) {
-            goto false;
+            goto false_value;
         }
-        goto true;
+        goto true_value;
     }
 
-false:
+false_value:
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http script file op false");
@@ -1085,7 +1086,7 @@ false:
     *value = ngx_http_variable_null_value;
     return;
 
-true:
+true_value:
 
     *value = ngx_http_variable_true_value;
     return;
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -137,7 +137,8 @@ ngx_http_upstream_header_t  ngx_http_ups
                  ngx_http_upstream_copy_header_line, 0, 0 },
 
     { ngx_string("Location"),
-                 ngx_http_upstream_ignore_header_line, 0,
+                 ngx_http_upstream_process_header_line,
+                 offsetof(ngx_http_upstream_headers_in_t, location),
                  ngx_http_upstream_rewrite_location, 0, 0 },
 
     { ngx_string("Refresh"),
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -26,6 +26,8 @@ static ngx_int_t ngx_http_variable_unkno
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 
 static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
@@ -277,9 +279,7 @@ ngx_http_add_variable(ngx_conf_t *cf, ng
         return NULL;
     }
 
-    for (i = 0; i < name->len; i++) {
-        v->name.data[i] = ngx_tolower(name->data[i]);
-    }
+    ngx_strlow(v->name.data, name->data, name->len);
 
     v->set_handler = NULL;
     v->get_handler = NULL;
@@ -344,9 +344,7 @@ ngx_http_get_variable_index(ngx_conf_t *
         return NGX_ERROR;
     }
 
-    for (i = 0; i < name->len; i++) {
-        v->name.data[i] = ngx_tolower(name->data[i]);
-    }
+    ngx_strlow(v->name.data, name->data, name->len);
 
     v->set_handler = NULL;
     v->get_handler = NULL;
@@ -481,6 +479,15 @@ ngx_http_get_variable(ngx_http_request_t
         return NULL;
     }
 
+    if (ngx_strncmp(name->data, "arg_", 4) == 0) {
+
+        if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) {
+            return vv;
+        }
+
+        return NULL;
+    }
+
     vv->not_found = 1;
 
     if (nowarn == 0) {
@@ -712,6 +719,62 @@ ngx_http_variable_unknown_header(ngx_htt
 
 
 static ngx_int_t
+ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    ngx_str_t *name = (ngx_str_t *) data;
+
+    u_char  *p, *arg;
+    size_t   len;
+
+    if (r->args.len == 0) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    len = name->len - 1 - (sizeof("arg_") - 1);
+    arg = name->data + sizeof("arg_") - 1;
+
+    for (p = r->args.data; *p && *p != ' '; p++) {
+
+        /*
+         * although r->args.data is not null-terminated by itself,
+         * however, there is null in the end of request line
+         */
+
+        p = ngx_strcasestrn(p, (char *) arg, len);
+
+        if (p == NULL) {
+            v->not_found = 1;
+            return NGX_OK;
+        }
+
+        if ((p == r->args.data || *(p - 1) == '&') && *(p + len + 1) == '=') {
+
+            v->data = p + len + 2;
+
+            p = (u_char *) ngx_strchr(p, '&');
+
+            if (p == NULL) {
+                p = r->args.data + r->args.len;
+            }
+
+            v->len = p - v->data;
+            v->valid = 1;
+            v->no_cacheable = 0;
+            v->not_found = 0;
+
+            return NGX_OK;
+        }
+    }
+
+    v->not_found = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v,
     uintptr_t data)
 {
@@ -1396,6 +1459,13 @@ ngx_http_variables_init_vars(ngx_conf_t 
             continue;
         }
 
+        if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) {
+            v[i].get_handler = ngx_http_variable_argument;
+            v[i].data = (uintptr_t) &v[i].name;
+
+            continue;
+        }
+
         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                       "unknown \"%V\" variable", &v[i].name);
 
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -9,9 +9,9 @@
 #include <ngx_mail.h>
 
 
-#define NGX_DEFLAUT_CERTIFICATE      "cert.pem"
-#define NGX_DEFLAUT_CERTIFICATE_KEY  "cert.pem"
-#define NGX_DEFLAUT_CIPHERS  "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
+#define NGX_DEFAULT_CERTIFICATE      "cert.pem"
+#define NGX_DEFAULT_CERTIFICATE_KEY  "cert.pem"
+#define NGX_DEFAULT_CIPHERS  "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
 
 
 static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf);
@@ -214,14 +214,14 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, 
                           |NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1));
 
     ngx_conf_merge_str_value(conf->certificate, prev->certificate,
-                         NGX_DEFLAUT_CERTIFICATE);
+                         NGX_DEFAULT_CERTIFICATE);
 
     ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key,
-                         NGX_DEFLAUT_CERTIFICATE_KEY);
+                         NGX_DEFAULT_CERTIFICATE_KEY);
 
     ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
 
-    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFLAUT_CIPHERS);
+    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
 
     conf->ssl.log = cf->log;
--- a/src/os/unix/ngx_alloc.h
+++ b/src/os/unix/ngx_alloc.h
@@ -21,8 +21,8 @@ void *ngx_calloc(size_t size, ngx_log_t 
 /*
  * Linux has memalign() or posix_memalign()
  * Solaris has memalign()
- * FreeBSD has not memalign() or posix_memalign() but its malloc() alignes
- * allocations bigger than page size at the page boundary.
+ * FreeBSD 7.0 has posix_memalign(), besides, early version's malloc()
+ * aligns allocations bigger than page size at the page boundary
  */
 
 #if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN)
--- a/src/os/unix/ngx_atomic.h
+++ b/src/os/unix/ngx_atomic.h
@@ -21,13 +21,8 @@
 
 #include <libkern/OSAtomic.h>
 
-/* "bool" conflicts with perl's CORE/handy.h
- * "true" and "false" conflict with nginx, and of course we can rename them,
- * but we need to undef "bool" anyway
- */
+/* "bool" conflicts with perl's CORE/handy.h */
 #undef bool
-#undef true
-#undef false
 
 
 #define NGX_HAVE_ATOMIC_OPS  1
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_darwin.h
@@ -0,0 +1,19 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_DARWIN_H_INCLUDED_
+#define _NGX_DARWIN_H_INCLUDED_
+
+
+ngx_chain_t *ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,
+    off_t limit);
+
+extern int       ngx_darwin_kern_osreldate;
+extern int       ngx_darwin_hw_ncpu;
+extern u_long    ngx_darwin_net_inet_tcp_sendspace;
+
+
+#endif /* _NGX_DARWIN_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_darwin_config.h
@@ -0,0 +1,87 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_DARWIN_CONFIG_H_INCLUDED_
+#define _NGX_DARWIN_CONFIG_H_INCLUDED_
+
+
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stddef.h>             /* offsetof() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+#include <glob.h>
+
+#include <sys/filio.h>          /* FIONBIO */
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>        /* TCP_NODELAY */
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#include <sys/sysctl.h>
+#include <xlocale.h>
+
+
+#ifndef IOV_MAX
+#define IOV_MAX   64
+#endif
+
+
+#include <ngx_auto_config.h>
+
+
+#if (NGX_HAVE_POLL)
+#include <poll.h>
+#endif
+
+
+#if (NGX_HAVE_KQUEUE)
+#include <sys/event.h>
+#endif
+
+
+#define NGX_LISTEN_BACKLOG  -1
+
+
+#ifndef NGX_HAVE_INHERITED_NONBLOCK
+#define NGX_HAVE_INHERITED_NONBLOCK  1
+#endif
+
+
+#ifndef NGX_HAVE_CASELESS_FILESYSTEM
+#define NGX_HAVE_CASELESS_FILESYSTEM  1
+#endif
+
+
+#define NGX_HAVE_OS_SPECIFIC_INIT    1
+
+
+extern char **environ;
+
+
+#endif /* _NGX_DARWIN_CONFIG_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_darwin_init.c
@@ -0,0 +1,155 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+char    ngx_darwin_kern_ostype[16];
+char    ngx_darwin_kern_osrelease[128];
+int     ngx_darwin_hw_ncpu;
+int     ngx_darwin_kern_ipc_somaxconn;
+u_long  ngx_darwin_net_inet_tcp_sendspace;
+
+
+static ngx_os_io_t ngx_darwin_io = {
+    ngx_unix_recv,
+    ngx_readv_chain,
+    ngx_udp_unix_recv,
+    ngx_unix_send,
+#if (NGX_HAVE_SENDFILE)
+    ngx_darwin_sendfile_chain,
+    NGX_IO_SENDFILE
+#else
+    ngx_writev_chain,
+    0
+#endif
+};
+
+
+typedef struct {
+    char        *name;
+    void        *value;
+    size_t       size;
+    ngx_uint_t   exists;
+} sysctl_t;
+
+
+sysctl_t sysctls[] = {
+    { "hw.ncpu",
+      &ngx_darwin_hw_ncpu,
+      sizeof(ngx_darwin_hw_ncpu), 0 },
+
+    { "net.inet.tcp.sendspace",
+      &ngx_darwin_net_inet_tcp_sendspace,
+      sizeof(ngx_darwin_net_inet_tcp_sendspace), 0 },
+
+    { "kern.ipc.somaxconn",
+      &ngx_darwin_kern_ipc_somaxconn,
+      sizeof(ngx_darwin_kern_ipc_somaxconn), 0 },
+
+    { NULL, NULL, 0, 0 }
+};
+
+
+ngx_int_t
+ngx_os_specific_init(ngx_log_t *log)
+{
+    int         somaxconn;
+    size_t      size;
+    ngx_err_t   err;
+    ngx_uint_t  i;
+
+    size = sizeof(ngx_darwin_kern_ostype);
+    if (sysctlbyname("kern.ostype",
+                     ngx_darwin_kern_ostype, &size, NULL, 0) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "sysctlbyname(kern.ostype) failed");
+
+        if (ngx_errno != NGX_ENOMEM) {
+            return NGX_ERROR;
+        }
+
+        ngx_darwin_kern_ostype[size - 1] = '\0';
+    }
+
+    size = sizeof(ngx_darwin_kern_osrelease);
+    if (sysctlbyname("kern.osrelease",
+                     ngx_darwin_kern_osrelease, &size, NULL, 0) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "sysctlbyname(kern.osrelease) failed");
+
+        if (ngx_errno != NGX_ENOMEM) {
+            return NGX_ERROR;
+        }
+
+        ngx_darwin_kern_osrelease[size - 1] = '\0';
+    }
+
+
+    for (i = 0; sysctls[i].name; i++) {
+        size = sysctls[i].size;
+
+        if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0)
+            == 0)
+        {
+            sysctls[i].exists = 1;
+            continue;
+        }
+
+        err = ngx_errno;
+
+        if (err == NGX_ENOENT) {
+            continue;
+        }
+
+        ngx_log_error(NGX_LOG_ALERT, log, err,
+                      "sysctlbyname(%s) failed", sysctls[i].name);
+        return NGX_ERROR;
+    }
+
+    ngx_ncpu = ngx_darwin_hw_ncpu;
+
+    somaxconn = 32676;
+
+    if (ngx_darwin_kern_ipc_somaxconn > somaxconn) {
+        ngx_log_error(NGX_LOG_ALERT, log, 0,
+                      "sysctl kern.ipc.somaxconn must be no more than %d",
+                      somaxconn);
+        return NGX_ERROR;
+    }
+
+    ngx_tcp_nodelay_and_tcp_nopush = 1;
+
+    ngx_os_io = ngx_darwin_io;
+
+    return NGX_OK;
+}
+
+
+void
+ngx_os_specific_status(ngx_log_t *log)
+{
+    u_long      value;
+    ngx_uint_t  i;
+
+    ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
+                  ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease);
+
+    for (i = 0; sysctls[i].name; i++) {
+        if (sysctls[i].exists) {
+            if (sysctls[i].size == sizeof(long)) {
+                value = *(long *) sysctls[i].value;
+
+            } else {
+                value = *(int *) sysctls[i].value;
+            }
+
+            ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l",
+                          sysctls[i].name, value);
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_darwin_sendfile_chain.c
@@ -0,0 +1,363 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+/*
+ * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same
+ * old bug as early FreeBSD sendfile() syscall:
+ * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
+ *
+ * Besides sendfile() has another bug: if one calls sendfile()
+ * with both a header and a trailer, then sendfile() ignores a file part
+ * at all and sends only the header and the trailer together.
+ * For this reason we send a trailer only if there is no a header.
+ *
+ * Although sendfile() allows to pass a header or a trailer,
+ * it may send the header or the trailer and a part of the file
+ * in different packets.  And FreeBSD workaround (TCP_NOPUSH option)
+ * does not help.
+ */
+
+
+#if (IOV_MAX > 64)
+#define NGX_HEADERS   64
+#define NGX_TRAILERS  64
+#else
+#define NGX_HEADERS   IOV_MAX
+#define NGX_TRAILERS  IOV_MAX
+#endif
+
+
+ngx_chain_t *
+ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
+{
+    int              rc;
+    u_char          *prev;
+    off_t            size, send, prev_send, aligned, sent, fprev;
+    off_t            header_size, file_size;
+    ngx_uint_t       eintr, eagain, complete;
+    ngx_err_t        err;
+    ngx_buf_t       *file;
+    ngx_array_t      header, trailer;
+    ngx_event_t     *wev;
+    ngx_chain_t     *cl;
+    struct sf_hdtr   hdtr;
+    struct iovec    *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS];
+
+    wev = c->write;
+
+    if (!wev->ready) {
+        return in;
+    }
+
+#if (NGX_HAVE_KQUEUE)
+
+    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
+        (void) ngx_connection_error(c, wev->kq_errno,
+                               "kevent() reported about an closed connection");
+        wev->error = 1;
+        return NGX_CHAIN_ERROR;
+    }
+
+#endif
+
+    /* the maximum limit size is the maximum size_t value - the page size */
+
+    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
+        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
+    }
+
+    send = 0;
+    eagain = 0;
+
+    header.elts = headers;
+    header.size = sizeof(struct iovec);
+    header.nalloc = NGX_HEADERS;
+    header.pool = c->pool;
+
+    trailer.elts = trailers;
+    trailer.size = sizeof(struct iovec);
+    trailer.nalloc = NGX_TRAILERS;
+    trailer.pool = c->pool;
+
+    for ( ;; ) {
+        file = NULL;
+        file_size = 0;
+        header_size = 0;
+        eintr = 0;
+        complete = 0;
+        prev_send = send;
+
+        header.nelts = 0;
+        trailer.nelts = 0;
+
+        /* create the header iovec and coalesce the neighbouring bufs */
+
+        prev = NULL;
+        iov = NULL;
+
+        for (cl = in;
+             cl && header.nelts < IOV_MAX && send < limit;
+             cl = cl->next)
+        {
+            if (ngx_buf_special(cl->buf)) {
+                continue;
+            }
+
+            if (!ngx_buf_in_memory_only(cl->buf)) {
+                break;
+            }
+
+            size = cl->buf->last - cl->buf->pos;
+
+            if (send + size > limit) {
+                size = limit - send;
+            }
+
+            if (prev == cl->buf->pos) {
+                iov->iov_len += (size_t) size;
+
+            } else {
+                iov = ngx_array_push(&header);
+                if (iov == NULL) {
+                    return NGX_CHAIN_ERROR;
+                }
+
+                iov->iov_base = (void *) cl->buf->pos;
+                iov->iov_len = (size_t) size;
+            }
+
+            prev = cl->buf->pos + (size_t) size;
+            header_size += size;
+            send += size;
+        }
+
+
+        if (cl && cl->buf->in_file && send < limit) {
+            file = cl->buf;
+
+            /* coalesce the neighbouring file bufs */
+
+            do {
+                size = cl->buf->file_last - cl->buf->file_pos;
+
+                if (send + size > limit) {
+                    size = limit - send;
+
+                    aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
+                               & ~((off_t) ngx_pagesize - 1);
+
+                    if (aligned <= cl->buf->file_last) {
+                        size = aligned - cl->buf->file_pos;
+                    }
+                }
+
+                file_size += size;
+                send += size;
+                fprev = cl->buf->file_pos + size;
+                cl = cl->next;
+
+            } while (cl
+                     && cl->buf->in_file
+                     && send < limit
+                     && file->file->fd == cl->buf->file->fd
+                     && fprev == cl->buf->file_pos);
+        }
+
+        if (file && header.nelts == 0) {
+
+            /* create the tailer iovec and coalesce the neighbouring bufs */
+
+            prev = NULL;
+            iov = NULL;
+
+            while (cl && header.nelts < IOV_MAX && send < limit) {
+
+                if (ngx_buf_special(cl->buf)) {
+                    cl = cl->next;
+                    continue;
+                }
+
+                if (!ngx_buf_in_memory_only(cl->buf)) {
+                    break;
+                }
+
+                size = cl->buf->last - cl->buf->pos;
+
+                if (send + size > limit) {
+                    size = limit - send;
+                }
+
+                if (prev == cl->buf->pos) {
+                    iov->iov_len += (size_t) size;
+
+                } else {
+                    iov = ngx_array_push(&trailer);
+                    if (iov == NULL) {
+                        return NGX_CHAIN_ERROR;
+                    }
+
+                    iov->iov_base = (void *) cl->buf->pos;
+                    iov->iov_len = (size_t) size;
+                }
+
+                prev = cl->buf->pos + (size_t) size;
+                send += size;
+                cl = cl->next;
+            }
+        }
+
+        if (file) {
+
+            /*
+             * sendfile() returns EINVAL if sf_hdtr's count is 0,
+             * but corresponding pointer is not NULL
+             */
+
+            hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL;
+            hdtr.hdr_cnt = header.nelts;
+            hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL;
+            hdtr.trl_cnt = trailer.nelts;
+
+            sent = header_size + file_size;
+
+            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "sendfile: @%O %O h:%O",
+                           file->file_pos, sent, header_size);
+
+            rc = sendfile(file->file->fd, c->fd, file->file_pos,
+                          &sent, &hdtr, 0);
+
+            if (rc == -1) {
+                err = ngx_errno;
+
+                if (err == NGX_EAGAIN || err == NGX_EINTR) {
+                    if (err == NGX_EINTR) {
+                        eintr = 1;
+
+                    } else {
+                        eagain = 1;
+                    }
+
+                    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
+                                   "sendfile() sent only %O bytes", sent);
+
+                } else {
+                    wev->error = 1;
+                    (void) ngx_connection_error(c, err, "sendfile() failed");
+                    return NGX_CHAIN_ERROR;
+                }
+            }
+
+            if (rc == 0 && sent == 0) {
+
+                /*
+                 * if rc and sent equal to zero, then someone
+                 * has truncated the file, so the offset became beyond
+                 * the end of the file
+                 */
+
+                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                              "sendfile() reported that \"%s\" was truncated",
+                              file->file->name.data);
+
+                return NGX_CHAIN_ERROR;
+            }
+
+            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "sendfile: %d, @%O %O:%O",
+                           rc, file->file_pos, sent, file_size + header_size);
+
+        } else {
+            rc = writev(c->fd, header.elts, header.nelts);
+
+            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "writev: %d of %uz", rc, send);
+
+            if (rc == -1) {
+                err = ngx_errno;
+
+                if (err == NGX_EAGAIN || err == NGX_EINTR) {
+                    if (err == NGX_EINTR) {
+                        eintr = 1;
+                    }
+
+                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+                                   "writev() not ready");
+
+                } else {
+                    wev->error = 1;
+                    ngx_connection_error(c, err, "writev() failed");
+                    return NGX_CHAIN_ERROR;
+                }
+            }
+
+            sent = rc > 0 ? rc : 0;
+        }
+
+        if (send - prev_send == sent) {
+            complete = 1;
+        }
+
+        c->sent += sent;
+
+        for (cl = in; cl; cl = cl->next) {
+
+            if (ngx_buf_special(cl->buf)) {
+                continue;
+            }
+
+            if (sent == 0) {
+                break;
+            }
+
+            size = ngx_buf_size(cl->buf);
+
+            if (sent >= size) {
+                sent -= size;
+
+                if (ngx_buf_in_memory(cl->buf)) {
+                    cl->buf->pos = cl->buf->last;
+                }
+
+                if (cl->buf->in_file) {
+                    cl->buf->file_pos = cl->buf->file_last;
+                }
+
+                continue;
+            }
+
+            if (ngx_buf_in_memory(cl->buf)) {
+                cl->buf->pos += (size_t) sent;
+            }
+
+            if (cl->buf->in_file) {
+                cl->buf->file_pos += sent;
+            }
+
+            break;
+        }
+
+        if (eintr) {
+            continue;
+        }
+
+        if (!complete) {
+            wev->ready = 0;
+            return cl;
+        }
+
+        if (send >= limit || cl == NULL) {
+            return cl;
+        }
+
+        in = cl;
+    }
+}
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -351,3 +351,22 @@ ngx_unlock_fd(ngx_fd_t fd)
 
     return 0;
 }
+
+
+#if (NGX_HAVE_O_DIRECT)
+
+ngx_int_t
+ngx_directio(ngx_fd_t fd)
+{
+    int  flags;
+
+    flags = fcntl(fd, F_GETFL);
+
+    if (flags == -1) {
+        return -1;
+    }
+
+    return fcntl(fd, F_SETFL, flags | O_DIRECT);
+}
+
+#endif
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -19,6 +19,8 @@
 
 #ifdef __CYGWIN__
 
+#define NGX_HAVE_CASELESS_FILESYSTEM  1
+
 #define ngx_open_file(name, mode, create, access)                            \
     open((const char *) name, mode|create|O_BINARY, access)
 
@@ -103,6 +105,16 @@ ngx_int_t ngx_set_file_time(u_char *name
 #define ngx_file_uniq(sb)        (sb)->st_ino
 
 
+#if (NGX_HAVE_CASELESS_FILESYSTEM)
+
+#define ngx_filename_cmp(s1, s2, n)  strncasecmp((char *) s1, (char *) s2, n)
+
+#else
+
+#define ngx_filename_cmp         ngx_memcmp
+
+#endif
+
 
 #define ngx_getcwd(buf, size)    (getcwd(buf, size) != NULL)
 #define ngx_getcwd_n             "getcwd()"
@@ -177,4 +189,27 @@ ngx_err_t ngx_unlock_fd(ngx_fd_t fd);
 #define ngx_unlock_fd_n          "fcntl(F_SETLK, F_UNLCK)"
 
 
+#if (NGX_HAVE_O_DIRECT)
+
+ngx_int_t ngx_directio(ngx_fd_t fd);
+#define ngx_directio_n           "fcntl(O_DIRECT)"
+
+#elif (NGX_HAVE_F_NOCACHE)
+
+#define ngx_directio(fd)         fcntl(fd, F_NOCACHE, 1)
+#define ngx_directio_n           "fcntl(F_NOCACHE)"
+
+#elif (NGX_HAVE_DIRECTIO)
+
+#define ngx_directio(fd)         directio(fd, DIRECTIO_ON)
+#define ngx_directio_n           "directio(DIRECTIO_ON)"
+
+#else
+
+#define ngx_directio(fd)         0
+#define ngx_directio_n           "ngx_directio_n"
+
+#endif
+
+
 #endif /* _NGX_FILES_H_INCLUDED_ */
--- a/src/os/unix/ngx_os.h
+++ b/src/os/unix/ngx_os.h
@@ -66,6 +66,10 @@ extern ngx_uint_t   ngx_tcp_nodelay_and_
 
 #elif (NGX_SOLARIS)
 #include <ngx_solaris.h>
+
+
+#elif (NGX_DARWIN)
+#include <ngx_darwin.h>
 #endif