changeset 324:7cf404023f50 NGINX_0_5_32

nginx 0.5.32 *) Change: now nginx tries to set the "worker_priority", "worker_rlimit_nofile", "worker_rlimit_core", and "worker_rlimit_sigpending" without super-user privileges. *) Change: now nginx escapes space and "%" in request to a mail proxy authentication server. *) Change: now nginx escapes "%" in $memcached_key variable. *) Change: the special make target "upgrade1" was defined for online upgrade of 0.1.x versions. *) Feature: the "add_header Last-Modified ..." directive changes the "Last-Modified" response header line. *) Feature: the mail proxy supports AUTHENTICATE in IMAP mode. Thanks to Maxim Dounin. *) Feature: the mail proxy supports STARTTLS in SMTP mode. Thanks to Maxim Dounin. *) Bugfix: nginx did not close directory file on HEAD request if autoindex was used. Thanks to Arkadiusz Patyk. *) Bugfix: the "proxy_hide_header" and "fastcgi_hide_header" directives did not hide response header lines whose name was longer than 32 characters. Thanks to Manlio Perillo. *) Bugfix: active connection counter always increased if mail proxy was used. *) Bugfix: if backend returned response header only using non-buffered proxy, then nginx closed backend connection on timeout. *) Bugfix: nginx did not support several "Connection" request header lines. *) Bugfix: a charset set by the "charset" directive was not appended to the "Content-Type" header set by $r->send_http_header(). *) Bugfix: a segmentation fault might occur in worker process if /dev/poll method was used. *) Bugfix: nginx did not work on FreeBSD/sparc64. *) Bugfix: a segmentation fault occurred in worker process if invalid address was set in the "auth_http" directive. *) Bugfix: now nginx uses default listen backlog value 511 on all platforms except FreeBSD. Thanks to Jiang Hong. *) Bugfix: now Solaris sendfilev() is not used to transfer the client request body to FastCGI-server via the unix domain socket. *) Bugfix: if the same host without specified port was used as backend for HTTP and HTTPS, then nginx used only one port - 80 or 443. *) Bugfix: the "proxy_ignore_client_abort" and "fastcgi_ignore_client_abort" directives did not work; bug appeared in 0.5.13.
author Igor Sysoev <http://sysoev.ru>
date Mon, 24 Sep 2007 00:00:00 +0400
parents 85aeb2da6e4c
children 5bb1b28ddeaa
files CHANGES CHANGES.ru auto/init conf/mime.types src/core/nginx.h src/core/ngx_conf_file.c src/core/ngx_conf_file.h src/core/ngx_connection.c src/core/ngx_inet.c src/core/ngx_string.c src/core/ngx_string.h src/event/modules/ngx_devpoll_module.c src/event/modules/ngx_kqueue_module.c src/event/ngx_event.c src/event/ngx_event.h src/event/ngx_event_connect.c src/event/ngx_event_openssl.c src/http/modules/ngx_http_access_module.c src/http/modules/ngx_http_autoindex_module.c src/http/modules/ngx_http_empty_gif_module.c src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_geo_module.c src/http/modules/ngx_http_headers_filter_module.c src/http/modules/ngx_http_limit_zone_module.c src/http/modules/ngx_http_log_module.c src/http/modules/ngx_http_map_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_realip_module.c src/http/modules/perl/nginx.pm src/http/modules/perl/nginx.xs src/http/ngx_http.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_request.c src/http/ngx_http_script.c src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h src/http/ngx_http_upstream_round_robin.c src/http/ngx_http_write_filter_module.c src/mail/ngx_mail.c src/mail/ngx_mail.h src/mail/ngx_mail_auth_http_module.c src/mail/ngx_mail_core_module.c src/mail/ngx_mail_handler.c src/mail/ngx_mail_parse.c src/os/unix/ngx_freebsd_config.h src/os/unix/ngx_linux_config.h src/os/unix/ngx_posix_config.h src/os/unix/ngx_process_cycle.c src/os/unix/ngx_solaris_config.h
diffstat 50 files changed, 1412 insertions(+), 456 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,71 @@
 
+Changes with nginx 0.5.32                                        24 Sep 2007
+
+    *) Change: now nginx tries to set the "worker_priority", 
+       "worker_rlimit_nofile", "worker_rlimit_core", and 
+       "worker_rlimit_sigpending" without super-user privileges.
+
+    *) Change: now nginx escapes space and "%" in request to a mail proxy 
+       authentication server.
+
+    *) Change: now nginx escapes "%" in $memcached_key variable.
+
+    *) Change: the special make target "upgrade1" was defined for online 
+       upgrade of 0.1.x versions.
+
+    *) Feature: the "add_header Last-Modified ..." directive changes the 
+       "Last-Modified" response header line.
+
+    *) Feature: the mail proxy supports AUTHENTICATE in IMAP mode.
+       Thanks to Maxim Dounin.
+
+    *) Feature: the mail proxy supports STARTTLS in SMTP mode.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: nginx did not close directory file on HEAD request if 
+       autoindex was used.
+       Thanks to Arkadiusz Patyk.
+
+    *) Bugfix: the "proxy_hide_header" and "fastcgi_hide_header" directives 
+       did not hide response header lines whose name was longer than 32 
+       characters.
+       Thanks to Manlio Perillo.
+
+    *) Bugfix: active connection counter always increased if mail proxy was 
+       used.
+
+    *) Bugfix: if backend returned response header only using non-buffered 
+       proxy, then nginx closed backend connection on timeout.
+
+    *) Bugfix: nginx did not support several "Connection" request header 
+       lines.
+
+    *) Bugfix: a charset set by the "charset" directive was not appended to 
+       the "Content-Type" header set by $r->send_http_header().
+
+    *) Bugfix: a segmentation fault might occur in worker process if 
+       /dev/poll method was used.
+
+    *) Bugfix: nginx did not work on FreeBSD/sparc64.
+
+    *) Bugfix: a segmentation fault occurred in worker process if invalid 
+       address was set in the "auth_http" directive.
+
+    *) Bugfix: now nginx uses default listen backlog value 511 on all 
+       platforms except FreeBSD.
+       Thanks to Jiang Hong.
+
+    *) Bugfix: now Solaris sendfilev() is not used to transfer the client 
+       request body to FastCGI-server via the unix domain socket.
+
+    *) Bugfix: if the same host without specified port was used as backend 
+       for HTTP and HTTPS, then nginx used only one port - 80 or 443.
+
+    *) Bugfix: the "proxy_ignore_client_abort" and 
+       "fastcgi_ignore_client_abort" directives did not work; bug appeared 
+       in 0.5.13.
+
+
 Changes with nginx 0.5.31                                        15 Aug 2007
 
     *) Feature: named locations.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,75 @@
 
+Изменения в nginx 0.5.32                                          24.09.2007
+
+    *) Изменение: теперь nginx пытается установить директивы 
+       worker_priority, worker_rlimit_nofile, worker_rlimit_core, 
+       worker_rlimit_sigpending без привилегий root'а.
+
+    *) Изменение: теперь nginx экранирует символы пробела и "%" при 
+       передаче запроса серверу аутентификации почтового прокси-сервера.
+
+    *) Изменение: теперь nginx экранирует символ "%" в переменной 
+       $memcached_key.
+
+    *) Изменение: для обновления на лету версий 0.1.x создан специальный 
+       сценарий make upgrade1.
+
+    *) Добавление: директива "add_header Last-Modified ..." меняет строку 
+       "Last-Modified" в заголовке ответа.
+
+    *) Добавление: почтовый прокси-сервер поддерживает AUTHENTICATE в 
+       режиме IMAP.
+       Спасибо Максиму Дунину.
+
+    *) Добавление: почтовый прокси-сервер поддерживает STARTTLS в режиме 
+       SMTP.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: nginx не закрывал файл каталога для запроса HEAD, если 
+       использовался autoindex
+       Спасибо Arkadiusz Patyk.
+
+    *) Исправление: директивы proxy_hide_header и fastcgi_hide_header не 
+       скрывали строки заголовка ответа с именем больше 32 символов.
+       Спасибо Manlio Perillo.
+
+    *) Исправление: счётчик активных соединений всегда рос при 
+       использовании почтового прокси-сервера.
+
+    *) Исправление: если бэкенд возвращал только заголовок ответа при 
+       небуферизированном проксировании, то nginx закрывал соединение с 
+       бэкендом по таймауту.
+
+    *) Исправление: nginx не поддерживал несколько строк "Connection" в 
+       заголовке запроса.
+
+    *) Исправление: В строку заголовка ответа "Content-Type", указанную в 
+       методе $r->send_http_header(), не добавлялась кодировка, указанная в 
+       директиве charset.
+
+    *) Исправление: при использовании метода /dev/poll в рабочем процессе 
+       мог произойти segmentation fault.
+
+    *) Исправление: nginx не работал на FreeBSD/sparc64.
+
+    *) Исправление: если в директиве auth_http был задан неправильный 
+       адрес, то в рабочем процессе происходил segmentation fault.
+
+    *) Исправление: теперь по умолчанию nginx использует значение 511 для 
+       listen backlog на всех платформах, кроме FreeBSD.
+       Спасибо Jiang Hong.
+
+    *) Исправление: sendfilev() в Solaris теперь не используется при 
+       передаче тела запроса FastCGI-серверу через unix domain сокет.
+
+    *) Исправление: при использовании одного хоста в качестве бэкендов для 
+       протоколов HTTP и HTTPS без явного указания портов, nginx 
+       использовал только один порт - 80 или 443.
+
+    *) Исправление: директивы proxy_ignore_client_abort и 
+       fastcgi_ignore_client_abort не работали; ошибка появилась в 0.5.13.
+
+
 Изменения в nginx 0.5.31                                          15.08.2007
 
     *) Добавление: именованные location'ы.
--- a/auto/init
+++ b/auto/init
@@ -55,15 +55,24 @@ clean:
 upgrade:
 	$NGX_SBIN_PATH -t
 
-	# upgrade compatibility from 0.1.x to 0.2.x
+	kill -USR2 \`cat $NGX_PID_PATH\`
+	sleep 1
+	test -f $NGX_PID_PATH.oldbin
+
+	kill -QUIT \`cat $NGX_PID_PATH.oldbin\`
+
+upgrade1:
+	# upgrade 0.1.x to 0.2+
+
+	$NGX_SBIN_PATH -t
+
 	cp $NGX_PID_PATH $NGX_PID_PATH.oldbin
 
 	kill -USR2 \`cat $NGX_PID_PATH\`
 	sleep 1
 	test -f $NGX_PID_PATH.oldbin
 
-	# upgrade compatibility from 0.1.x to 0.2.x
 	cp $NGX_PID_PATH $NGX_PID_PATH.newbin
 
-	kill -WINCH \`cat $NGX_PID_PATH.oldbin\`
+	kill -QUIT \`cat $NGX_PID_PATH.oldbin\`
 END
--- a/conf/mime.types
+++ b/conf/mime.types
@@ -20,6 +20,7 @@ types {
     image/x-icon                          ico;
     image/x-jng                           jng;
     image/x-ms-bmp                        bmp;
+    image/svg+xml                         svg;
 
     application/java-archive              jar war ear;
     application/mac-binhex40              hqx;
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.5.31"
+#define NGINX_VERSION      "0.5.32"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -366,7 +366,7 @@ not_allowed:
 invalid:
 
     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                       "invalid number arguments in \"%s\" directive",
+                       "invalid number of arguments in \"%s\" directive",
                        name->data);
 
     return NGX_ERROR;
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -257,8 +257,8 @@ char *ngx_conf_check_num_bounds(ngx_conf
     }
 
 #define ngx_conf_merge_ptr_value(conf, prev, default)                        \
-    if (conf == NULL) {                                                      \
-        conf = (prev == NULL) ? default : prev;                              \
+    if (conf == NGX_CONF_UNSET_PTR) {                                        \
+        conf = (prev == NGX_CONF_UNSET_PTR) ? default : prev;                \
     }
 
 #define ngx_conf_merge_uint_value(conf, prev, default)                       \
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -123,7 +123,7 @@ ngx_set_inherited_sockets(ngx_cycle_t *c
                                           ntohs(sin->sin_port))
                               - ls[i].addr_text.data;
 
-        ls[i].backlog = -1;
+        ls[i].backlog = NGX_LISTEN_BACKLOG;
 
         olen = sizeof(int);
 
@@ -735,7 +735,7 @@ ngx_close_connection(ngx_connection_t *c
         /* we use ngx_cycle->log because c->log was in c->pool */
 
         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
-                      ngx_close_socket_n " failed");
+                      ngx_close_socket_n " %d failed", fd);
     }
 }
 
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -214,7 +214,13 @@ ngx_ptocidr(ngx_str_t *text, void *cidr)
 
     in_cidr->mask = htonl((ngx_uint_t) (0 - (1 << (32 - m))));
 
-    return NGX_OK;
+    if (in_cidr->addr == (in_cidr->addr & in_cidr->mask)) {
+        return NGX_OK;
+    }
+
+    in_cidr->addr &= in_cidr->mask;
+
+    return NGX_DONE;
 }
 
 
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -59,8 +59,9 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t 
  *    %P                        ngx_pid_t
  *    %M                        ngx_msec_t
  *    %r                        rlim_t
- *    %p                        pointer
- *    %V                        pointer to ngx_str_t
+ *    %p                        void *
+ *    %V                        ngx_str_t *
+ *    %v                        ngx_variable_value_t *
  *    %s                        null-terminated string
  *    %Z                        '\0'
  *    %N                        '\n'
@@ -117,7 +118,8 @@ ngx_vsnprintf(u_char *buf, size_t max, c
     uint64_t               ui64;
     ngx_msec_t             ms;
     ngx_uint_t             width, sign, hexadecimal, max_width;
-    ngx_variable_value_t  *v;
+    ngx_str_t             *v;
+    ngx_variable_value_t  *vv;
     static u_char          hex[] = "0123456789abcdef";
     static u_char          HEX[] = "0123456789ABCDEF";
 
@@ -188,7 +190,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c
             switch (*fmt) {
 
             case 'V':
-                v = va_arg(args, ngx_variable_value_t *);
+                v = va_arg(args, ngx_str_t *);
 
                 len = v->len;
                 len = (buf + len < last) ? len : (size_t) (last - buf);
@@ -198,6 +200,17 @@ ngx_vsnprintf(u_char *buf, size_t max, c
 
                 continue;
 
+            case 'v':
+                vv = va_arg(args, ngx_variable_value_t *);
+
+                len = vv->len;
+                len = (buf + len < last) ? len : (size_t) (last - buf);
+
+                buf = ngx_cpymem(buf, vv->data, len);
+                fmt++;
+
+                continue;
+
             case 's':
                 p = va_arg(args, u_char *);
 
@@ -1019,7 +1032,7 @@ ngx_escape_uri(u_char *dst, u_char *src,
         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
     };
 
-                    /* " ", """, "%", "'", %00-%1F, %7F-%FF */
+                    /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */
 
     static uint32_t   html[] = {
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
@@ -1039,13 +1052,13 @@ ngx_escape_uri(u_char *dst, u_char *src,
         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
     };
 
-                    /* " ", """, "'", %00-%1F, %7F-%FF */
+                    /* " ", """, "%", "'", %00-%1F, %7F-%FF */
 
     static uint32_t   refresh[] = {
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
 
                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
-        0x00000085, /* 0000 0000 0000 0000  0000 0000 1000 0101 */
+        0x000000a5, /* 0000 0000 0000 0000  0000 0000 1010 0101 */
 
                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
@@ -1059,13 +1072,13 @@ ngx_escape_uri(u_char *dst, u_char *src,
         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
     };
 
-                    /* " ", %00-%1F */
+                    /* " ", "%", %00-%1F */
 
     static uint32_t   memcached[] = {
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
 
                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
-        0x00000001, /* 0000 0000 0000 0000  0000 0000 0000 0001 */
+        0x00000021, /* 0000 0000 0000 0000  0000 0000 0010 0001 */
 
                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
@@ -1079,7 +1092,10 @@ ngx_escape_uri(u_char *dst, u_char *src,
         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
     };
 
-    static uint32_t  *map[] = { uri, args, html, refresh, memcached };
+                    /* mail_auth is the same as memcached */
+
+    static uint32_t  *map[] =
+        { uri, args, html, refresh, memcached, memcached };
 
 
     escape = map[type];
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -155,6 +155,7 @@ u_char *ngx_utf_cpystrn(u_char *dst, u_c
 #define NGX_ESCAPE_HTML       2
 #define NGX_ESCAPE_REFRESH    3
 #define NGX_ESCAPE_MEMCACHED  4
+#define NGX_ESCAPE_MAIL_AUTH  5
 
 #define NGX_UNESCAPE_URI      1
 
--- a/src/event/modules/ngx_devpoll_module.c
+++ b/src/event/modules/ngx_devpoll_module.c
@@ -15,6 +15,7 @@
 
 #define POLLREMOVE   0x0800
 #define DP_POLL      0xD001
+#define DP_ISPOLLED  0xD002
 
 struct dvpoll {
     struct pollfd  *dp_fds;
@@ -252,10 +253,16 @@ ngx_devpoll_del_event(ngx_event_t *ev, i
     ev->active = 0;
 
     if (flags & NGX_CLOSE_EVENT) {
+        e = (event == POLLIN) ? c->write : c->read;
+
+        if (e) {
+            e->active = 0;
+        }
+
         return NGX_OK;
     }
 
-    /* restore the paired event if it exists */
+    /* restore the pair event if it exists */
 
     if (event == POLLIN) {
         e = c->write;
@@ -327,13 +334,15 @@ ngx_int_t
 ngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
     ngx_uint_t flags)
 {
-    int                 events, revents;
+    int                 events, revents, rc;
     size_t              n;
+    ngx_fd_t            fd;
     ngx_err_t           err;
     ngx_int_t           i;
     ngx_uint_t          level;
     ngx_event_t        *rev, *wev, **queue;
     ngx_connection_t   *c;
+    struct pollfd       pfd;
     struct dvpoll       dvp;
 
     /* NGX_TIMER_INFINITE == INFTIM */
@@ -398,34 +407,77 @@ ngx_devpoll_process_events(ngx_cycle_t *
     ngx_mutex_lock(ngx_posted_events_mutex);
 
     for (i = 0; i < events; i++) {
-        c = ngx_cycle->files[event_list[i].fd];
+
+        fd = event_list[i].fd;
+        revents = event_list[i].revents;
+
+        c = ngx_cycle->files[fd];
+
+        if (c == NULL || c->fd == -1) {
+
+            pfd.fd = fd;
+            pfd.events = 0;
+            pfd.revents = 0;
+
+            rc = ioctl(dp, DP_ISPOLLED, &pfd);
+
+            switch (rc) {
+
+            case -1:
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                    "ioctl(DP_ISPOLLED) failed for socket %d, event",
+                    fd, revents);
+                break;
 
-        if (c->fd == -1) {
-            if (c->read->closed) {
-                continue;
+            case 0:
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                    "phantom event %04Xd for closed and removed socket %d",
+                    revents, fd);
+                break;
+
+            default:
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                    "unexpected event %04Xd for closed and removed socket %d, ",
+                    "ioctl(DP_ISPOLLED) returned rc:%d, fd:%d, event %04Xd",
+                    revents, fd, rc, pfd.fd, pfd.revents);
+
+                pfd.fd = fd;
+                pfd.events = POLLREMOVE;
+                pfd.revents = 0;
+
+                if (write(dp, &pfd, sizeof(struct pollfd))
+                    != (ssize_t) sizeof(struct pollfd))
+                {
+                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                                  "write(/dev/poll) for %d failed, fd");
+                }
+
+                if (close(fd) == -1) {
+                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                                  "close(%d) failed", fd);
+                }
+
+                break;
             }
 
-            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unexpected event");
             continue;
         }
 
-        revents = event_list[i].revents;
-
         ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                        "devpoll: fd:%d, ev:%04Xd, rev:%04Xd",
-                       event_list[i].fd, event_list[i].events, revents);
+                       fd, event_list[i].events, revents);
 
         if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
             ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "ioctl(DP_POLL) error fd:%d ev:%04Xd rev:%04Xd",
-                          event_list[i].fd, event_list[i].events, revents);
+                          fd, event_list[i].events, revents);
         }
 
         if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
             ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                           "strange ioctl(DP_POLL) events "
                           "fd:%d ev:%04Xd rev:%04Xd",
-                          event_list[i].fd, event_list[i].events, revents);
+                          fd, event_list[i].events, revents);
         }
 
         if ((revents & (POLLERR|POLLHUP|POLLNVAL))
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -404,7 +404,7 @@ ngx_kqueue_set_event(ngx_event_t *ev, in
 
     c = ev->data;
 
-    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                    "kevent set event: %d: ft:%d fl:%04Xd",
                    c->fd, filter, flags);
 
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -1038,8 +1038,9 @@ ngx_event_debug_connection(ngx_conf_t *c
 #if (NGX_DEBUG)
     ngx_event_conf_t  *ecf = conf;
 
+    ngx_int_t           rc;
+    ngx_str_t          *value;
     ngx_event_debug_t  *dc;
-    ngx_str_t          *value;
     struct hostent     *h;
     ngx_inet_cidr_t     in_cidr;
 
@@ -1056,13 +1057,21 @@ ngx_event_debug_connection(ngx_conf_t *c
 
     if (dc->addr != INADDR_NONE) {
         dc->mask = 0xffffffff;
-        return NGX_OK;
+        return NGX_CONF_OK;
     }
 
-    if (ngx_ptocidr(&value[1], &in_cidr) == NGX_OK) {
+    rc = ngx_ptocidr(&value[1], &in_cidr);
+
+    if (rc == NGX_DONE) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "low address bits of %V are meaningless", &value[1]);
+        rc = NGX_OK;
+    }
+
+    if (rc == NGX_OK) {
         dc->mask = in_cidr.mask;
         dc->addr = in_cidr.addr;
-        return NGX_OK;
+        return NGX_CONF_OK;
     }
 
     h = gethostbyname((char *) value[1].data);
@@ -1084,7 +1093,7 @@ ngx_event_debug_connection(ngx_conf_t *c
 
 #endif
 
-    return NGX_OK;
+    return NGX_CONF_OK;
 }
 
 
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -41,7 +41,7 @@ struct ngx_event_s {
 
     unsigned         accept:1;
 
-    /* used to detect the stale events in kqueue, rt signals and epoll */
+    /* used to detect the stale events in kqueue, rtsig, and epoll */
     unsigned         instance:1;
 
     /*
@@ -247,8 +247,7 @@ extern ngx_event_actions_t   ngx_event_a
 #define NGX_USE_LOWAT_EVENT      0x00000010
 
 /*
- * The event filter requires to do i/o operation until EAGAIN:
- * epoll, rt signals.
+ * The event filter requires to do i/o operation until EAGAIN: epoll, rtsig.
  */
 #define NGX_USE_GREEDY_EVENT     0x00000020
 
@@ -258,7 +257,7 @@ extern ngx_event_actions_t   ngx_event_a
 #define NGX_USE_EPOLL_EVENT      0x00000040
 
 /*
- * No need to add or delete the event filters: rt signals.
+ * No need to add or delete the event filters: rtsig.
  */
 #define NGX_USE_RTSIG_EVENT      0x00000080
 
@@ -276,13 +275,13 @@ extern ngx_event_actions_t   ngx_event_a
 
 /*
  * The event filter has no opaque data and requires file descriptors table:
- * poll, /dev/poll, rt signals.
+ * poll, /dev/poll, rtsig.
  */
 #define NGX_USE_FD_EVENT         0x00000400
 
 /*
  * The event module handles periodic or absolute timer event by itself:
- * kqueue in FreeBSD 4.4 and NetBSD 2.0, Solaris 10's event ports.
+ * kqueue in FreeBSD 4.4, NetBSD 2.0, and MacOSX 10.4, Solaris 10's event ports.
  */
 #define NGX_USE_TIMER_EVENT      0x00000800
 
@@ -290,17 +289,26 @@ extern ngx_event_actions_t   ngx_event_a
  * All event filters on file descriptor are deleted after a notification:
  * Solaris 10's event ports.
  */
-#define NGX_USE_EVENTPORT_EVENT    0x00001000
+#define NGX_USE_EVENTPORT_EVENT  0x00001000
 
 
 
 /*
- * The event filter is deleted before the closing file.
- * Has no meaning for select, poll, kqueue, epoll.
- * /dev/poll:  we need to flush POLLREMOVE event before closing file
+ * The event filter is deleted just before the closing file.
+ * Has no meaning for select and poll.
+ * kqueue, epoll, rtsig, eventport:  allows to avoid explicit delete,
+ *                                   because filter automatically is deleted
+ *                                   on file close,
+ *
+ * /dev/poll:                        we need to flush POLLREMOVE event
+ *                                   before closing file.
  */
+#define NGX_CLOSE_EVENT    1
 
-#define NGX_CLOSE_EVENT    1
+/*
+ * disable temporarily event filter, this may avoid locks
+ * in kernel malloc()/free(): kqueue.
+ */
 #define NGX_DISABLE_EVENT  2
 
 
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -85,6 +85,8 @@ ngx_event_connect_peer(ngx_peer_connecti
     c->recv_chain = ngx_recv_chain;
     c->send_chain = ngx_send_chain;
 
+    c->sendfile = 1;
+
     c->log_error = pc->log_error;
 
     if (pc->sockaddr->sa_family != AF_INET) {
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -1588,7 +1588,7 @@ ngx_ssl_expire_sessions(ngx_ssl_session_
         }
 
         if (n++ != 0 && sess_id->expire > tp->sec) {
-            break;
+            return;
         }
 
         sess_id->next->prev = sess_id->prev;
--- a/src/http/modules/ngx_http_access_module.c
+++ b/src/http/modules/ngx_http_access_module.c
@@ -137,6 +137,7 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx
 {
     ngx_http_access_loc_conf_t *alcf = conf;
 
+    ngx_int_t                rc;
     ngx_str_t               *value;
     ngx_inet_cidr_t          in_cidr;
     ngx_http_access_rule_t  *rule;
@@ -173,12 +174,19 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx
         return NGX_CONF_OK;
     }
 
-    if (ngx_ptocidr(&value[1], &in_cidr) == NGX_ERROR) {
+    rc = ngx_ptocidr(&value[1], &in_cidr);
+
+    if (rc == NGX_ERROR) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
                            &value[1]);
         return NGX_CONF_ERROR;
     }
 
+    if (rc == NGX_DONE) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "low address bits of %V are meaningless", &value[1]);
+    }
+
     rule->mask = in_cidr.mask;
     rule->addr = in_cidr.addr;
 
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -236,6 +236,11 @@ ngx_http_autoindex_handler(ngx_http_requ
     rc = ngx_http_send_header(r);
 
     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+        if (ngx_close_dir(&dir) == NGX_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+                          ngx_close_dir_n " \"%V\" failed", &path);
+        }
+
         return rc;
     }
 
--- a/src/http/modules/ngx_http_empty_gif_module.c
+++ b/src/http/modules/ngx_http_empty_gif_module.c
@@ -128,11 +128,7 @@ ngx_http_empty_gif_handler(ngx_http_requ
     if (r->method == NGX_HTTP_HEAD) {
         r->headers_out.status = NGX_HTTP_OK;
 
-        rc = ngx_http_send_header(r);
-
-        if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
-            return rc;
-        }
+        return ngx_http_send_header(r);
     }
 
     b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -1135,7 +1135,7 @@ ngx_http_fastcgi_process_header(ngx_http
 
                 } else {
                     for (i = 0; i < h->key.len; i++) {
-                        h->lowcase_key[i] = ngx_tolower(h->lowcase_key[i]);
+                        h->lowcase_key[i] = ngx_tolower(h->key.data[i]);
                     }
                 }
 
@@ -1640,7 +1640,6 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
      *     conf->upstream.hide_headers_hash = { NULL, 0 };
      *     conf->upstream.hide_headers = NULL;
      *     conf->upstream.pass_headers = NULL;
-     *     conf->upstream.catch_stderr = NULL;
      *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
@@ -1675,6 +1674,8 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
     /* "fastcgi_cyclic_temp_file" is disabled */
     conf->upstream.cyclic_temp_file = 0;
 
+    conf->catch_stderr = NGX_CONF_UNSET_PTR;
+
     return conf;
 }
 
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -86,7 +86,7 @@ ngx_http_geo_variable(ngx_http_request_t
     *v = *vv;
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http geo: %V %V", &r->connection->addr_text, v);
+                   "http geo: %V %v", &r->connection->addr_text, v);
 
     return NGX_OK;
 }
@@ -100,8 +100,8 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
     ngx_conf_t                save;
     ngx_pool_t               *pool;
     ngx_radix_tree_t         *tree;
+    ngx_http_variable_t      *var;
     ngx_http_geo_conf_ctx_t   ctx;
-    ngx_http_variable_t  *var;
 
     value = cf->args->elts;
 
@@ -212,12 +212,20 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
         cidrin.mask = 0;
 
     } else {
-        if (ngx_ptocidr(&value[0], &cidrin) == NGX_ERROR) {
+        rc = ngx_ptocidr(&value[0], &cidrin);
+
+        if (rc == NGX_ERROR) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "invalid parameter \"%V\"", &value[0]);
             return NGX_CONF_ERROR;
         }
 
+        if (rc == NGX_DONE) {
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                               "low address bits of %V are meaningless",
+                               &value[0]);
+        }
+
         cidrin.addr = ntohl(cidrin.addr);
         cidrin.mask = ntohl(cidrin.mask);
     }
@@ -277,9 +285,8 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
                     ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask);
 
         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                           "duplicate parameter \"%V\", value: \"%V\", "
-                           "old value: \"%V\"",
-                           &value[0], var, old);
+                "duplicate parameter \"%V\", value: \"%v\", old value: \"%v\"",
+                &value[0], var, old);
 
         rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask);
 
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -9,17 +9,31 @@
 #include <ngx_http.h>
 
 
-typedef struct {
-    ngx_table_elt_t   value;
-    ngx_array_t      *lengths;
-    ngx_array_t      *values;
-} ngx_http_header_val_t;
+typedef struct ngx_http_header_val_s  ngx_http_header_val_t;
+
+typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
+    ngx_http_header_val_t *hv, ngx_str_t *value);
 
 
 typedef struct {
-    time_t            expires;
-    ngx_str_t         cache_control;
-    ngx_array_t      *headers;
+    ngx_str_t                name;
+    ngx_uint_t               offset;
+    ngx_http_set_header_pt   handler;
+} ngx_http_set_header_t;
+
+
+struct ngx_http_header_val_s {
+    ngx_table_elt_t          value;
+    ngx_uint_t               offset;
+    ngx_http_set_header_pt   handler;
+    ngx_array_t             *lengths;
+    ngx_array_t             *values;
+};
+
+
+typedef struct {
+    time_t                   expires;
+    ngx_array_t             *headers;
 } ngx_http_headers_conf_t;
 
 
@@ -29,6 +43,13 @@ typedef struct {
 #define NGX_HTTP_EXPIRES_MAX     -2147483644
 
 
+static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,
+    ngx_http_headers_conf_t *conf);
+static ngx_int_t ngx_http_add_cache_control(ngx_http_request_t *r,
+    ngx_http_header_val_t *hv, ngx_str_t *value);
+static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,
+    ngx_http_header_val_t *hv, ngx_str_t *value);
+
 static void *ngx_http_headers_create_conf(ngx_conf_t *cf);
 static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,
     void *parent, void *child);
@@ -39,6 +60,18 @@ static char *ngx_http_headers_add(ngx_co
     void *conf);
 
 
+static ngx_http_set_header_t  ngx_http_set_headers[] = {
+
+    { ngx_string("Cache-Control"), 0, ngx_http_add_cache_control },
+
+    { ngx_string("Last-Modified"),
+                 offsetof(ngx_http_headers_out_t, last_modified),
+                 ngx_http_set_last_modified },
+
+    { ngx_null_string, 0, NULL }
+};
+
+
 static ngx_command_t  ngx_http_headers_filter_commands[] = {
 
     { ngx_string("expires"),
@@ -98,13 +131,15 @@ static ngx_http_output_header_filter_pt 
 static ngx_int_t
 ngx_http_headers_filter(ngx_http_request_t *r)
 {
-    size_t                    len;
+    ngx_str_t                 value;
     ngx_uint_t                i;
-    ngx_table_elt_t          *expires, *cc, **ccp, *out;
     ngx_http_header_val_t    *h;
     ngx_http_headers_conf_t  *conf;
 
-    if (r != r->main
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
+
+    if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL)
+        || r != r->main
         || (r->headers_out.status != NGX_HTTP_OK
             && r->headers_out.status != NGX_HTTP_NO_CONTENT
             && r->headers_out.status != NGX_HTTP_MOVED_PERMANENTLY
@@ -114,124 +149,73 @@ ngx_http_headers_filter(ngx_http_request
         return ngx_http_next_header_filter(r);
     }
 
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
-
     if (conf->expires != NGX_HTTP_EXPIRES_OFF) {
-
-        expires = r->headers_out.expires;
-
-        if (expires == NULL) {
+        if (ngx_http_set_expires(r, conf) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
 
-            expires = ngx_list_push(&r->headers_out.headers);
-            if (expires == NULL) {
-                return NGX_ERROR;
-            }
-
-            r->headers_out.expires = expires;
-
-            expires->hash = 1;
-            expires->key.len = sizeof("Expires") - 1;
-            expires->key.data = (u_char *) "Expires";
-        }
+    if (conf->headers) {
+        h = conf->headers->elts;
+        for (i = 0; i < conf->headers->nelts; i++) {
 
-        len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
-        expires->value.len = len - 1;
-
-        ccp = r->headers_out.cache_control.elts;
-
-        if (ccp == NULL) {
+            if (h[i].lengths == NULL) {
+                value = h[i].value.value;
 
-            if (ngx_array_init(&r->headers_out.cache_control, r->pool,
-                               1, sizeof(ngx_table_elt_t *))
-                != NGX_OK)
-            {
-                return NGX_ERROR;
-            }
-
-            ccp = ngx_array_push(&r->headers_out.cache_control);
-            if (ccp == NULL) {
-                return NGX_ERROR;
-            }
-
-            cc = ngx_list_push(&r->headers_out.headers);
-            if (cc == NULL) {
-                return NGX_ERROR;
+            } else {
+                if (ngx_http_script_run(r, &value, h[i].lengths->elts, 0,
+                                        h[i].values->elts)
+                    == NULL)
+                {
+                    return NGX_ERROR;
+                }
             }
 
-            cc->hash = 1;
-            cc->key.len = sizeof("Cache-Control") - 1;
-            cc->key.data = (u_char *) "Cache-Control";
-
-            *ccp = cc;
-
-        } else {
-            for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
-                ccp[i]->hash = 0;
-            }
-
-            cc = ccp[0];
-        }
-
-        if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) {
-            expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
-
-            cc->value.len = sizeof("no-cache") - 1;
-            cc->value.data = (u_char *) "no-cache";
-
-        } else if (conf->expires == NGX_HTTP_EXPIRES_MAX) {
-            expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
-
-            /* 10 years */
-            cc->value.len = sizeof("max-age=315360000") - 1;
-            cc->value.data = (u_char *) "max-age=315360000";
-
-        } else {
-            expires->value.data = ngx_palloc(r->pool, len);
-            if (expires->value.data == NULL) {
+            if (h[i].handler(r, &h[i], &value) != NGX_OK) {
                 return NGX_ERROR;
             }
-
-            if (conf->expires == 0) {
-                ngx_memcpy(expires->value.data, ngx_cached_http_time.data,
-                           ngx_cached_http_time.len + 1);
-
-                cc->value.len = sizeof("max-age=0") - 1;
-                cc->value.data = (u_char *) "max-age=0";
-
-            } else {
-                ngx_http_time(expires->value.data, ngx_time() + conf->expires);
-
-                if (conf->expires < 0) {
-                    cc->value.len = sizeof("no-cache") - 1;
-                    cc->value.data = (u_char *) "no-cache";
-
-                } else {
-                    cc->value.data = ngx_palloc(r->pool, sizeof("max-age=")
-                                                         + NGX_TIME_T_LEN + 1);
-                    if (cc->value.data == NULL) {
-                        return NGX_ERROR;
-                    }
-
-                    cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T",
-                                                conf->expires)
-                                    - cc->value.data;
-                }
-            }
         }
     }
 
-    if (conf->cache_control.len) {
+    return ngx_http_next_header_filter(r);
+}
+
 
-        ccp = r->headers_out.cache_control.elts;
+static ngx_int_t
+ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
+{
+    size_t            len;
+    ngx_uint_t        i;
+    ngx_table_elt_t  *expires, *cc, **ccp;
 
-        if (ccp == NULL) {
+    expires = r->headers_out.expires;
+
+    if (expires == NULL) {
+
+        expires = ngx_list_push(&r->headers_out.headers);
+        if (expires == NULL) {
+            return NGX_ERROR;
+        }
 
-            if (ngx_array_init(&r->headers_out.cache_control, r->pool,
-                               1, sizeof(ngx_table_elt_t *))
-                != NGX_OK)
-            {
-                return NGX_ERROR;
-            }
+        r->headers_out.expires = expires;
+
+        expires->hash = 1;
+        expires->key.len = sizeof("Expires") - 1;
+        expires->key.data = (u_char *) "Expires";
+    }
+
+    len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
+    expires->value.len = len - 1;
+
+    ccp = r->headers_out.cache_control.elts;
+
+    if (ccp == NULL) {
+
+        if (ngx_array_init(&r->headers_out.cache_control, r->pool,
+                           1, sizeof(ngx_table_elt_t *))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
         }
 
         ccp = ngx_array_push(&r->headers_out.cache_control);
@@ -247,37 +231,161 @@ ngx_http_headers_filter(ngx_http_request
         cc->hash = 1;
         cc->key.len = sizeof("Cache-Control") - 1;
         cc->key.data = (u_char *) "Cache-Control";
-        cc->value = conf->cache_control;
 
         *ccp = cc;
+
+    } else {
+        for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
+            ccp[i]->hash = 0;
+        }
+
+        cc = ccp[0];
+    }
+
+    if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) {
+        expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
+
+        cc->value.len = sizeof("no-cache") - 1;
+        cc->value.data = (u_char *) "no-cache";
+
+        return NGX_OK;
+    }
+
+    if (conf->expires == NGX_HTTP_EXPIRES_MAX) {
+        expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
+
+        /* 10 years */
+        cc->value.len = sizeof("max-age=315360000") - 1;
+        cc->value.data = (u_char *) "max-age=315360000";
+
+        return NGX_OK;
+    }
+
+    expires->value.data = ngx_palloc(r->pool, len);
+    if (expires->value.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (conf->expires == 0) {
+        ngx_memcpy(expires->value.data, ngx_cached_http_time.data,
+                   ngx_cached_http_time.len + 1);
+
+        cc->value.len = sizeof("max-age=0") - 1;
+        cc->value.data = (u_char *) "max-age=0";
+
+        return NGX_OK;
     }
 
-    if (conf->headers) {
-        h = conf->headers->elts;
-        for (i = 0; i < conf->headers->nelts; i++) {
-            out = ngx_list_push(&r->headers_out.headers);
-            if (out == NULL) {
-                return NGX_ERROR;
-            }
+    ngx_http_time(expires->value.data, ngx_time() + conf->expires);
+
+    if (conf->expires < 0) {
+        cc->value.len = sizeof("no-cache") - 1;
+        cc->value.data = (u_char *) "no-cache";
+
+        return NGX_OK;
+    }
 
-            out->hash = h[i].value.hash;
-            out->key = h[i].value.key;
+    cc->value.data = ngx_palloc(r->pool,
+                                sizeof("max-age=") + NGX_TIME_T_LEN + 1);
+    if (cc->value.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", conf->expires)
+                    - cc->value.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+    ngx_str_t *value)
+{
+    ngx_table_elt_t  *h;
 
-            if (h[i].lengths == NULL) {
-                out->value = h[i].value.value;
-                continue;
-            }
+    h = ngx_list_push(&r->headers_out.headers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    h->hash = hv->value.hash;
+    h->key = hv->value.key;
+    h->value = *value;
+
+    return NGX_OK;
+}
+
 
-            if (ngx_http_script_run(r, &out->value, h[i].lengths->elts, 0,
-                                    h[i].values->elts)
-                == NULL)
-            {
-                return NGX_ERROR;
-            }
+static ngx_int_t
+ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+    ngx_str_t *value)
+{
+    ngx_table_elt_t  *cc, **ccp;
+
+    ccp = r->headers_out.cache_control.elts;
+
+    if (ccp == NULL) {
+
+        if (ngx_array_init(&r->headers_out.cache_control, r->pool,
+                           1, sizeof(ngx_table_elt_t *))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
         }
     }
 
-    return ngx_http_next_header_filter(r);
+    ccp = ngx_array_push(&r->headers_out.cache_control);
+    if (ccp == NULL) {
+        return NGX_ERROR;
+    }
+
+    cc = ngx_list_push(&r->headers_out.headers);
+    if (cc == NULL) {
+        return NGX_ERROR;
+    }
+
+    cc->hash = 1;
+    cc->key.len = sizeof("Cache-Control") - 1;
+    cc->key.data = (u_char *) "Cache-Control";
+    cc->value = *value;
+
+    *ccp = cc;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+    ngx_str_t *value)
+{
+    ngx_table_elt_t  *h, **old;
+
+    if (hv->offset) {
+        old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
+
+    } else {
+        old = NULL;
+    }
+
+    if (old == NULL || *old == NULL) {
+        h = ngx_list_push(&r->headers_out.headers);
+        if (h == NULL) {
+            return NGX_ERROR;
+        }
+
+    } else {
+        h = *old;
+    }
+
+    h->hash = hv->value.hash;
+    h->key = hv->value.key;
+    h->value = *value;
+
+    r->headers_out.last_modified_time = -1;
+
+    return NGX_OK;
 }
 
 
@@ -294,8 +402,6 @@ ngx_http_headers_create_conf(ngx_conf_t 
     /*
      * set by ngx_pcalloc():
      *
-     *     conf->cache_control.len = 0;
-     *     conf->cache_control.data = NULL;
      *     conf->headers = NULL;
      */
 
@@ -313,11 +419,7 @@ ngx_http_headers_merge_conf(ngx_conf_t *
 
     if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
         conf->expires = (prev->expires == NGX_HTTP_EXPIRES_UNSET) ?
-                                          NGX_HTTP_EXPIRES_OFF : prev->expires;
-    }
-
-    if (conf->cache_control.data == NULL) {
-        conf->cache_control = prev->cache_control;
+                            NGX_HTTP_EXPIRES_OFF : prev->expires;
     }
 
     if (conf->headers == NULL) {
@@ -406,16 +508,13 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx
 
     ngx_int_t                   n;
     ngx_str_t                  *value;
+    ngx_uint_t                  i;
     ngx_http_header_val_t      *h;
+    ngx_http_set_header_t      *sh;
     ngx_http_script_compile_t   sc;
 
     value = cf->args->elts;
 
-    if (ngx_strcasecmp(value[1].data, (u_char *) "cache-control") == 0) {
-        hcf->cache_control = value[2];
-        return NGX_CONF_OK;
-    }
-
     if (hcf->headers == NULL) {
         hcf->headers = ngx_array_create(cf->pool, 1,
                                         sizeof(ngx_http_header_val_t));
@@ -432,9 +531,22 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx
     h->value.hash = 1;
     h->value.key = value[1];
     h->value.value = value[2];
+    h->offset = 0;
+    h->handler = ngx_http_add_header;
     h->lengths = NULL;
     h->values = NULL;
 
+    sh = ngx_http_set_headers;
+    for (i = 0; sh[i].name.len; i++) {
+        if (ngx_strcasecmp(value[1].data, sh[i].name.data) != 0) {
+            continue;
+        }
+
+        h->offset = sh[i].offset;
+        h->handler = sh[i].handler;
+        break;
+    }
+
     n = ngx_http_script_variables_count(&value[2]);
 
     if (n == 0) {
--- a/src/http/modules/ngx_http_limit_zone_module.c
+++ b/src/http/modules/ngx_http_limit_zone_module.c
@@ -141,7 +141,7 @@ ngx_http_limit_zone_handler(ngx_http_req
     if (len > 255) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "the value of the \"%V\" variable "
-                      "is more than 255 bytes: \"%V\"",
+                      "is more than 255 bytes: \"%v\"",
                       &ctx->var, vv);
         return NGX_DECLINED;
     }
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -423,6 +423,11 @@ ngx_http_log_bytes_sent(ngx_http_request
 }
 
 
+/*
+ * although there is a real $body_bytes_sent variable,
+ * this log operation code function is more optimized for logging
+ */
+
 static u_char *
 ngx_http_log_body_bytes_sent(ngx_http_request_t *r, u_char *buf,
     ngx_http_log_op_t *op)
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -167,7 +167,7 @@ ngx_http_map_variable(ngx_http_request_t
     }
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http map: \"%V\" \"%V\"", vv, v);
+                   "http map: \"%v\" \"%v\"", vv, v);
 
     return NGX_OK;
 }
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -553,7 +553,7 @@ ngx_http_proxy_create_request(ngx_http_r
 
     } else {
         unparsed_uri = 0;
-        if (r->quoted_uri) {
+        if (r->quoted_uri || r->internal) {
             escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
                                         r->uri.len - loc_len, NGX_ESCAPE_URI);
         }
@@ -1141,7 +1141,7 @@ ngx_http_proxy_process_header(ngx_http_r
 
             } else {
                 for (i = 0; i < h->key.len; i++) {
-                    h->lowcase_key[i] = ngx_tolower(h->lowcase_key[i]);
+                    h->lowcase_key[i] = ngx_tolower(h->key.data[i]);
                 }
             }
 
@@ -2237,6 +2237,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
             if (port == 80) {
                 plcf->port.len = sizeof("80") - 1;
                 plcf->port.data = (u_char *) "80";
+
             } else {
                 plcf->port.len = sizeof("443") - 1;
                 plcf->port.data = (u_char *) "443";
--- a/src/http/modules/ngx_http_realip_module.c
+++ b/src/http/modules/ngx_http_realip_module.c
@@ -188,6 +188,7 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx
 {
     ngx_http_realip_loc_conf_t *rlcf = conf;
 
+    ngx_int_t                 rc;
     ngx_str_t                *value;
     ngx_inet_cidr_t           in_cidr;
     ngx_http_realip_from_t   *from;
@@ -215,12 +216,19 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx
         return NGX_CONF_OK;
     }
 
-    if (ngx_ptocidr(&value[1], &in_cidr) == NGX_ERROR) {
+    rc = ngx_ptocidr(&value[1], &in_cidr);
+
+    if (rc == NGX_ERROR) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
                            &value[1]);
         return NGX_CONF_ERROR;
     }
 
+    if (rc == NGX_DONE) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "low address bits of %V are meaningless", &value[1]);
+    }
+
     from->mask = in_cidr.mask;
     from->addr = in_cidr.addr;
 
--- 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.5.31';
+our $VERSION = '0.5.32';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -131,6 +131,8 @@ send_http_header(r, ...)
             XSRETURN_EMPTY;
         }
 
+        r->headers_out.content_type_len = r->headers_out.content_type.len;
+
     } else {
         if (ngx_http_set_content_type(r) != NGX_OK) {
             XSRETURN_EMPTY;
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -551,8 +551,8 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
 
                         if (in_addr[a].default_server) {
                             ngx_log_error(NGX_LOG_ERR, cf->log, 0,
-                                        "the duplicate default server in %V:%d",
-                                        &lscf[l].file_name, lscf[l].line);
+                                      "the duplicate default server in %s:%ui",
+                                       &lscf[l].file_name, lscf[l].line);
 
                             return NGX_CONF_ERROR;
                         }
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -620,6 +620,8 @@ ngx_int_t
 ngx_http_core_find_config_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph)
 {
+    u_char                    *p;
+    size_t                     len;
     ngx_int_t                  rc;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
@@ -680,7 +682,25 @@ ngx_http_core_find_config_phase(ngx_http
          * r->headers_out.location->key fields
          */
 
-        r->headers_out.location->value = clcf->name;
+        if (r->args.len == 0) {
+            r->headers_out.location->value = clcf->name;
+
+        } else {
+            len = clcf->name.len + 1 + r->args.len;
+            p = ngx_palloc(r->pool, len);
+
+            if (p == NULL) {
+                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return NGX_OK;
+            }
+
+            r->headers_out.location->value.len = len;
+            r->headers_out.location->value.data = p;
+
+            p = ngx_cpymem(p, clcf->name.data, clcf->name.len);
+            *p++ = '?';
+            ngx_memcpy(p, r->args.data, r->args.len);
+        }
 
         ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY);
         return NGX_OK;
@@ -2226,7 +2246,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
 #endif
         ls->family = AF_INET;
 
-        ls->conf.backlog = -1;
+        ls->conf.backlog = NGX_LISTEN_BACKLOG;
         ls->conf.rcvbuf = -1;
         ls->conf.sndbuf = -1;
     }
@@ -2575,9 +2595,9 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx
     ls->family = AF_INET;
     ls->addr = u.addr.in_addr;
     ls->port = u.port;
-    ls->file_name = cf->conf_file->file.name;
+    ls->file_name = cf->conf_file->file.name.data;
     ls->line = cf->conf_file->line;
-    ls->conf.backlog = -1;
+    ls->conf.backlog = NGX_LISTEN_BACKLOG;
     ls->conf.rcvbuf = -1;
     ls->conf.sndbuf = -1;
 
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -38,8 +38,8 @@ typedef struct {
     in_port_t                  port;
     int                        family;
 
-    ngx_str_t                  file_name;
-    ngx_int_t                  line;
+    u_char                    *file_name;
+    ngx_uint_t                 line;
 
     ngx_http_listen_conf_t     conf;
 } ngx_http_listen_t;
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -21,6 +21,8 @@ static ngx_int_t ngx_http_process_header
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_process_cookie(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 
@@ -34,6 +36,7 @@ static ngx_int_t ngx_http_set_write_hand
 static void ngx_http_writer(ngx_http_request_t *r);
 
 static void ngx_http_block_read(ngx_http_request_t *r);
+static void ngx_http_test_read(ngx_http_request_t *r);
 static void ngx_http_set_keepalive(ngx_http_request_t *r);
 static void ngx_http_keepalive_handler(ngx_event_t *ev);
 static void ngx_http_set_lingering_close(ngx_http_request_t *r);
@@ -71,11 +74,11 @@ ngx_http_header_t  ngx_http_headers_in[]
                  ngx_http_process_unique_header_line },
 
     { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),
-                 ngx_http_process_unique_header_line },
+                 ngx_http_process_connection },
 
     { ngx_string("If-Modified-Since"),
                  offsetof(ngx_http_headers_in_t, if_modified_since),
-                 ngx_http_process_header_line },
+                 ngx_http_process_unique_header_line },
 
     { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),
                  ngx_http_process_header_line },
@@ -836,7 +839,7 @@ ngx_http_process_request_headers(ngx_eve
                     ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                   "client sent too long header line: \"%V\"",
                                   &header);
-                    ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST);
+                    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
                     return;
                 }
             }
@@ -948,7 +951,7 @@ ngx_http_process_request_headers(ngx_eve
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
                       "client sent invalid header line: \"%V\\r...\"",
                       &header);
-        ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST);
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
         return;
     }
 }
@@ -1199,6 +1202,21 @@ ngx_http_process_unique_header_line(ngx_
 
 
 static ngx_int_t
+ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    if (ngx_strstr(h->value.data, "close")) {
+        r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
+
+    } else if (ngx_strstr(h->value.data, "keep-alive")) {
+        r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
 {
@@ -1294,7 +1312,7 @@ ngx_http_process_request_header(ngx_http
         return NGX_ERROR;
     }
 
-    if (r->method & (NGX_HTTP_TRACE)) {
+    if (r->method & NGX_HTTP_TRACE) {
         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                       "client sent TRACE method");
         ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
@@ -1317,26 +1335,11 @@ ngx_http_process_request_header(ngx_http
         return NGX_ERROR;
     }
 
-    if (r->headers_in.connection) {
-        if (r->headers_in.connection->value.len == 5
-            && ngx_strcasecmp(r->headers_in.connection->value.data,
-                              (u_char *) "close")
-               == 0)
-        {
-            r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
-
-        } else if (r->headers_in.connection->value.len == 10
-                   && ngx_strcasecmp(r->headers_in.connection->value.data,
-                                     (u_char *) "keep-alive")
-                      == 0)
-        {
-            r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;
-
-            if (r->headers_in.keep_alive) {
-                r->headers_in.keep_alive_n =
-                                ngx_atotm(r->headers_in.keep_alive->value.data,
-                                          r->headers_in.keep_alive->value.len);
-            }
+    if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {
+        if (r->headers_in.keep_alive) {
+            r->headers_in.keep_alive_n =
+                            ngx_atotm(r->headers_in.keep_alive->value.data,
+                                      r->headers_in.keep_alive->value.len);
         }
     }
 
@@ -1710,7 +1713,7 @@ ngx_http_set_write_handler(ngx_http_requ
 
     r->http_state = NGX_HTTP_WRITING_REQUEST_STATE;
 
-    r->read_event_handler = ngx_http_block_read;
+    r->read_event_handler = ngx_http_test_read;
     r->write_event_handler = ngx_http_writer;
 
     wev = r->connection->write;
@@ -1823,6 +1826,26 @@ ngx_http_writer(ngx_http_request_t *r)
 static void
 ngx_http_block_read(ngx_http_request_t *r)
 {
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http read blocked");
+
+    /* aio does not call this handler */
+
+    if ((ngx_event_flags & NGX_USE_LEVEL_EVENT)
+        && r->connection->read->active)
+    {
+        if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0)
+            == NGX_ERROR)
+        {
+            ngx_http_close_request(r, 0);
+        }
+    }
+}
+
+
+static void
+ngx_http_test_read(ngx_http_request_t *r)
+{
     int                n;
     char               buf[1];
     ngx_err_t          err;
@@ -1832,7 +1855,7 @@ ngx_http_block_read(ngx_http_request_t *
     c = r->connection;
     rev = c->read;
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http read blocked");
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test read");
 
 #if (NGX_HAVE_KQUEUE)
 
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -1191,7 +1191,7 @@ ngx_http_script_var_code(ngx_http_script
 
     if (value && !value->not_found) {
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                       "http script var: \"%V\"", value);
+                       "http script var: \"%v\"", value);
 
         *e->sp = *value;
         e->sp++;
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -581,7 +581,7 @@ ngx_http_upstream_connect(ngx_http_reque
     c->write->handler = ngx_http_upstream_send_request_handler;
     c->read->handler = ngx_http_upstream_process_header;
 
-    c->sendfile = r->connection->sendfile;
+    c->sendfile &= r->connection->sendfile;
 
     c->pool = r->pool;
     c->read->log = c->write->log = c->log = r->connection->log;
@@ -1288,6 +1288,7 @@ ngx_http_upstream_test_connect(ngx_conne
 
     if (ngx_event_flags & NGX_USE_KQUEUE_EVENT)  {
         if (c->write->pending_eof) {
+            c->log->action = "connecting to upstream";
             (void) ngx_connection_error(c, c->write->kq_errno,
                                     "kevent() reported that connect() failed");
             return NGX_ERROR;
@@ -1311,6 +1312,7 @@ ngx_http_upstream_test_connect(ngx_conne
         }
 
         if (err) {
+            c->log->action = "connecting to upstream";
             (void) ngx_connection_error(c, err, "connect() failed");
             return NGX_ERROR;
         }
@@ -1488,6 +1490,11 @@ ngx_http_upstream_send_response(ngx_http
                 ngx_http_upstream_finalize_request(r, u, 0);
                 return;
             }
+
+            if (u->peer.connection->read->ready) {
+                ngx_http_upstream_process_non_buffered_body(
+                                                     u->peer.connection->read);
+            }
         }
 
         return;
@@ -3201,7 +3208,7 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng
             ngx_log_error(NGX_LOG_WARN, cf->log, 0,
                           "upstream \"%V\" may not have port %d in %s:%ui",
                           &u->host, uscfp[i]->port,
-                          uscfp[i]->file_name.data, uscfp[i]->line);
+                          uscfp[i]->file_name, uscfp[i]->line);
             return NULL;
         }
 
@@ -3209,6 +3216,12 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng
             continue;
         }
 
+        if (uscfp[i]->default_port && u->default_port
+            && uscfp[i]->default_port != u->default_port)
+        {
+            continue;
+        }
+
         return uscfp[i];
     }
 
@@ -3219,7 +3232,7 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng
 
     uscf->flags = flags;
     uscf->host = u->host;
-    uscf->file_name = cf->conf_file->file.name;
+    uscf->file_name = cf->conf_file->file.name.data;
     uscf->line = cf->conf_file->line;
     uscf->port = u->port;
     uscf->default_port = u->default_port;
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -91,7 +91,7 @@ struct ngx_http_upstream_srv_conf_s {
 
     ngx_uint_t                      flags;
     ngx_str_t                       host;
-    ngx_str_t                       file_name;
+    u_char                         *file_name;
     ngx_uint_t                      line;
     in_port_t                       port;
     in_port_t                       default_port;
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -64,7 +64,7 @@ ngx_http_upstream_init_round_robin(ngx_c
     if (us->port == 0 && us->default_port == 0) {
         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                       "no port in upstream \"%V\" in %s:%ui",
-                      &us->host, us->file_name.data, us->line);
+                      &us->host, us->file_name, us->line);
         return NGX_ERROR;
     }
 
@@ -77,7 +77,7 @@ ngx_http_upstream_init_round_robin(ngx_c
         if (u.err) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                           "%s in upstream \"%V\" in %s:%ui",
-                          u.err, &us->host, us->file_name.data, us->line);
+                          u.err, &us->host, us->file_name, us->line);
         }
 
         return NGX_ERROR;
@@ -428,8 +428,8 @@ ngx_http_upstream_set_round_robin_peer_s
     rc = ngx_ssl_set_session(pc->connection, ssl_session);
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
-                  "set session: %p:%d",
-                  ssl_session, ssl_session ? ssl_session->references : 0);
+                   "set session: %p:%d",
+                   ssl_session, ssl_session ? ssl_session->references : 0);
 
     /* ngx_unlock_mutex(rrp->peers->mutex); */
 
@@ -453,7 +453,7 @@ ngx_http_upstream_save_round_robin_peer_
     }
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
-                  "save session: %p:%d", ssl_session, ssl_session->references);
+                   "save session: %p:%d", ssl_session, ssl_session->references);
 
     peer = &rrp->peers->peer[rrp->current];
 
@@ -468,7 +468,7 @@ ngx_http_upstream_save_round_robin_peer_
     if (old_ssl_session) {
 
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
-                      "old session: %p:%d",
+                       "old session: %p:%d",
                        old_ssl_session, old_ssl_session->references);
 
         /* TODO: may block */
--- a/src/http/ngx_http_write_filter_module.c
+++ b/src/http/ngx_http_write_filter_module.c
@@ -249,7 +249,11 @@ ngx_http_write_filter(ngx_http_request_t
         c->write->delayed = 1;
         ngx_add_timer(c->write, (ngx_msec_t) (sent * 1000 / r->limit_rate + 1));
 
-    } else if (c->write->ready && clcf->sendfile_max_chunk) {
+    } else if (c->write->ready
+               && clcf->sendfile_max_chunk
+               && (size_t) (c->sent - sent)
+                                >= clcf->sendfile_max_chunk - 2 * ngx_pagesize)
+    {
         c->write->delayed = 1;
         ngx_add_timer(c->write, 1);
     }
--- a/src/mail/ngx_mail.c
+++ b/src/mail/ngx_mail.c
@@ -300,7 +300,7 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma
                 return NGX_CONF_ERROR;
             }
 
-            ls->backlog = -1;
+            ls->backlog = NGX_LISTEN_BACKLOG;
             ls->rcvbuf = -1;
             ls->sndbuf = -1;
 
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -92,12 +92,15 @@ typedef struct {
     ngx_str_t               imap_starttls_only_capability;
 
     ngx_str_t               smtp_capability;
+    ngx_str_t               smtp_starttls_capability;
+    ngx_str_t               smtp_starttls_only_capability;
 
     ngx_str_t               server_name;
     ngx_str_t               smtp_server_name;
     ngx_str_t               smtp_greeting;
 
     ngx_uint_t              pop3_auth_methods;
+    ngx_uint_t              imap_auth_methods;
     ngx_uint_t              smtp_auth_methods;
 
     ngx_array_t             pop3_capabilities;
@@ -132,6 +135,10 @@ typedef enum {
 
 typedef enum {
     ngx_imap_start = 0,
+    ngx_imap_auth_login_username,
+    ngx_imap_auth_login_password,
+    ngx_imap_auth_plain,
+    ngx_imap_auth_cram_md5,
     ngx_imap_login,
     ngx_imap_user,
     ngx_imap_passwd
@@ -214,39 +221,47 @@ typedef struct {
 } ngx_mail_log_ctx_t;
 
 
-#define NGX_POP3_USER        1
-#define NGX_POP3_PASS        2
-#define NGX_POP3_CAPA        3
-#define NGX_POP3_QUIT        4
-#define NGX_POP3_NOOP        5
-#define NGX_POP3_STLS        6
-#define NGX_POP3_APOP        7
-#define NGX_POP3_AUTH        8
-#define NGX_POP3_STAT        9
-#define NGX_POP3_LIST        10
-#define NGX_POP3_RETR        11
-#define NGX_POP3_DELE        12
-#define NGX_POP3_RSET        13
-#define NGX_POP3_TOP         14
-#define NGX_POP3_UIDL        15
+#define NGX_POP3_USER          1
+#define NGX_POP3_PASS          2
+#define NGX_POP3_CAPA          3
+#define NGX_POP3_QUIT          4
+#define NGX_POP3_NOOP          5
+#define NGX_POP3_STLS          6
+#define NGX_POP3_APOP          7
+#define NGX_POP3_AUTH          8
+#define NGX_POP3_STAT          9
+#define NGX_POP3_LIST          10
+#define NGX_POP3_RETR          11
+#define NGX_POP3_DELE          12
+#define NGX_POP3_RSET          13
+#define NGX_POP3_TOP           14
+#define NGX_POP3_UIDL          15
 
 
-#define NGX_IMAP_LOGIN       1
-#define NGX_IMAP_LOGOUT      2
-#define NGX_IMAP_CAPABILITY  3
-#define NGX_IMAP_NOOP        4
-#define NGX_IMAP_STARTTLS    5
+#define NGX_IMAP_LOGIN         1
+#define NGX_IMAP_LOGOUT        2
+#define NGX_IMAP_CAPABILITY    3
+#define NGX_IMAP_NOOP          4
+#define NGX_IMAP_STARTTLS      5
 
-#define NGX_IMAP_NEXT        6
+#define NGX_IMAP_NEXT          6
+
+#define NGX_IMAP_AUTHENTICATE  7
 
 
-#define NGX_SMTP_HELO        1
-#define NGX_SMTP_EHLO        2
-#define NGX_SMTP_AUTH        3
-#define NGX_SMTP_QUIT        4
-#define NGX_SMTP_NOOP        5
-#define NGX_SMTP_MAIL        6
-#define NGX_SMTP_RSET        7
+#define NGX_SMTP_HELO          1
+#define NGX_SMTP_EHLO          2
+#define NGX_SMTP_AUTH          3
+#define NGX_SMTP_QUIT          4
+#define NGX_SMTP_NOOP          5
+#define NGX_SMTP_MAIL          6
+#define NGX_SMTP_RSET          7
+#define NGX_SMTP_RCPT          8
+#define NGX_SMTP_DATA          9
+#define NGX_SMTP_VRFY          10
+#define NGX_SMTP_EXPN          11
+#define NGX_SMTP_HELP          12
+#define NGX_SMTP_STARTTLS      13
 
 
 #define NGX_MAIL_AUTH_PLAIN     0
@@ -285,6 +300,8 @@ typedef struct {
 
 #define ngx_mail_conf_get_module_main_conf(cf, module)                       \
     ((ngx_mail_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
+#define ngx_mail_conf_get_module_srv_conf(cf, module)                        \
+    ((ngx_mail_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
 
 
 void ngx_mail_init_connection(ngx_connection_t *c);
--- a/src/mail/ngx_mail_auth_http_module.c
+++ b/src/mail/ngx_mail_auth_http_module.c
@@ -21,6 +21,9 @@ typedef struct {
     ngx_str_t                       header;
 
     ngx_array_t                    *headers;
+
+    u_char                         *file;
+    ngx_uint_t                      line;
 } ngx_mail_auth_http_conf_t;
 
 
@@ -1248,18 +1251,10 @@ ngx_mail_auth_http_create_request(ngx_ma
 static ngx_int_t
 ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped)
 {
-    u_char      ch, *p;
-    ngx_uint_t  i, n;
-
-    n = 0;
+    u_char     *p;
+    uintptr_t   n;
 
-    for (i = 0; i < text->len; i++) {
-        ch = text->data[i];
-
-        if (ch == CR || ch == LF) {
-            n++;
-        }
-    }
+    n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
 
     if (n == 0) {
         *escaped = *text;
@@ -1273,27 +1268,9 @@ ngx_mail_auth_http_escape(ngx_pool_t *po
         return NGX_ERROR;
     }
 
-    escaped->data = p;
-
-    for (i = 0; i < text->len; i++) {
-        ch = text->data[i];
+    (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
 
-        if (ch == CR) {
-            *p++ = '%';
-            *p++ = '0';
-            *p++ = 'D';
-            continue;
-        }
-
-        if (ch == LF) {
-            *p++ = '%';
-            *p++ = '0';
-            *p++ = 'A';
-            continue;
-        }
-
-        *p++ = ch;
-    }
+    escaped->data = p;
 
     return NGX_OK;
 }
@@ -1311,6 +1288,9 @@ ngx_mail_auth_http_create_conf(ngx_conf_
 
     ahcf->timeout = NGX_CONF_UNSET_MSEC;
 
+    ahcf->file = cf->conf_file->file.name.data;
+    ahcf->line = cf->conf_file->line;
+
     return ahcf;
 }
 
@@ -1330,6 +1310,14 @@ ngx_mail_auth_http_merge_conf(ngx_conf_t
         conf->peer = prev->peer;
         conf->host_header = prev->host_header;
         conf->uri = prev->uri;
+
+        if (conf->peer == NULL) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "no \"http_auth\" is defined for server in %s:%ui",
+                          conf->file, conf->line);
+
+            return NGX_CONF_ERROR;
+        }
     }
 
     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
@@ -1383,11 +1371,18 @@ ngx_mail_auth_http(ngx_conf_t *cf, ngx_c
     u.uri_part = 1;
     u.one_addr = 1;
 
+    if (ngx_strncmp(u.url.data, "http://", 7) == 0) {
+        u.url.len -= 7;
+        u.url.data += 7;
+    }
+
     if (ngx_parse_url(cf, &u) != NGX_OK) {
         if (u.err) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "%s in auth_http \"%V\"", u.err, &u.url);
         }
+
+        return NGX_CONF_ERROR;
     }
 
     ahcf->peer = u.addrs;
--- a/src/mail/ngx_mail_core_module.c
+++ b/src/mail/ngx_mail_core_module.c
@@ -54,6 +54,14 @@ static ngx_conf_bitmask_t  ngx_pop3_auth
 };
 
 
+static ngx_conf_bitmask_t  ngx_imap_auth_methods[] = {
+    { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
+    { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
+    { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_conf_bitmask_t  ngx_smtp_auth_methods[] = {
     { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
     { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
@@ -62,6 +70,14 @@ static ngx_conf_bitmask_t  ngx_smtp_auth
 };
 
 
+static ngx_str_t  ngx_imap_auth_methods_names[] = {
+    ngx_string("AUTH=PLAIN"),
+    ngx_string("AUTH=LOGIN"),
+    ngx_null_string,  /* APOP */
+    ngx_string("AUTH=CRAM-MD5")
+};
+
+
 static ngx_str_t  ngx_smtp_auth_methods_names[] = {
     ngx_string("PLAIN"),
     ngx_string("LOGIN"),
@@ -172,6 +188,13 @@ static ngx_command_t  ngx_mail_core_comm
       offsetof(ngx_mail_core_srv_conf_t, pop3_auth_methods),
       &ngx_pop3_auth_methods },
 
+    { ngx_string("imap_auth"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+      ngx_conf_set_bitmask_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_core_srv_conf_t, imap_auth_methods),
+      &ngx_imap_auth_methods },
+
     { ngx_string("smtp_auth"),
       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
       ngx_conf_set_bitmask_slot,
@@ -278,7 +301,7 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
     ngx_mail_core_srv_conf_t *prev = parent;
     ngx_mail_core_srv_conf_t *conf = child;
 
-    u_char      *p;
+    u_char      *p, *auth;
     size_t       size, stls_only_size;
     ngx_str_t   *c, *d;
     ngx_uint_t   i, m;
@@ -297,6 +320,11 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
                                  (NGX_CONF_BITMASK_SET
                                   |NGX_MAIL_AUTH_PLAIN_ENABLED));
 
+    ngx_conf_merge_bitmask_value(conf->imap_auth_methods,
+                                 prev->imap_auth_methods,
+                                 (NGX_CONF_BITMASK_SET
+                                  |NGX_MAIL_AUTH_PLAIN_ENABLED));
+
     ngx_conf_merge_bitmask_value(conf->smtp_auth_methods,
                                  prev->smtp_auth_methods,
                                  (NGX_CONF_BITMASK_SET
@@ -463,6 +491,15 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
         size += 1 + c[i].len;
     }
 
+    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+         m <<= 1, i++)
+    {
+        if (m & conf->imap_auth_methods) {
+            size += 1 + ngx_imap_auth_methods_names[i].len;
+        }
+    }
+
     p = ngx_palloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
@@ -478,6 +515,19 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
         p = ngx_cpymem(p, c[i].data, c[i].len);
     }
 
+    auth = p;
+
+    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+         m <<= 1, i++)
+    {
+        if (m & conf->imap_auth_methods) {
+            *p++ = ' ';
+            p = ngx_cpymem(p, ngx_imap_auth_methods_names[i].data,
+                           ngx_imap_auth_methods_names[i].len);
+        }
+    }
+
     *p++ = CR; *p = LF;
 
 
@@ -497,7 +547,8 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
     *p++ = CR; *p = LF;
 
 
-    size += sizeof(" LOGINDISABLED") - 1;
+    size = (auth - conf->imap_capability.data) + sizeof(CRLF) - 1
+            + sizeof(" STARTTLS LOGINDISABLED") - 1;
 
     p = ngx_palloc(cf->pool, size);
     if (p == NULL) {
@@ -507,9 +558,10 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
     conf->imap_starttls_only_capability.len = size;
     conf->imap_starttls_only_capability.data = p;
 
-    p = ngx_cpymem(p, conf->imap_starttls_capability.data,
-                   conf->imap_starttls_capability.len - (sizeof(CRLF) - 1));
-    p = ngx_cpymem(p, " LOGINDISABLED", sizeof(" LOGINDISABLED") - 1);
+    p = ngx_cpymem(p, conf->imap_capability.data,
+                   auth - conf->imap_capability.data);
+    p = ngx_cpymem(p, " STARTTLS LOGINDISABLED",
+                   sizeof(" STARTTLS LOGINDISABLED") - 1);
     *p++ = CR; *p = LF;
 
 
@@ -582,6 +634,8 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
         *p++ = CR; *p++ = LF;
     }
 
+    auth = p;
+
     *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
     *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';
 
@@ -598,6 +652,42 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
 
     *p++ = CR; *p = LF;
 
+    size += sizeof("250 STARTTLS" CRLF) - 1;
+
+    p = ngx_palloc(cf->pool, size);
+    if (p == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    conf->smtp_starttls_capability.len = size;
+    conf->smtp_starttls_capability.data = p;
+
+    p = ngx_cpymem(p, conf->smtp_capability.data,
+                   conf->smtp_capability.len);
+
+    p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
+    *p++ = CR; *p = LF;
+
+    p = conf->smtp_starttls_capability.data
+        + (auth - conf->smtp_capability.data) + 3;
+    *p = '-';
+
+    size = (auth - conf->smtp_capability.data)
+            + sizeof("250 STARTTLS" CRLF) - 1;
+
+    p = ngx_palloc(cf->pool, size);
+    if (p == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    conf->smtp_starttls_only_capability.len = size;
+    conf->smtp_starttls_only_capability.data = p;
+
+    p = ngx_cpymem(p, conf->smtp_capability.data,
+                   auth - conf->smtp_capability.data);
+
+    ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
+
     return NGX_CONF_OK;
 }
 
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -280,6 +280,9 @@ ngx_mail_init_session(ngx_connection_t *
          && (cscf->pop3_auth_methods
              & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED)))
 
+        || (s->protocol == NGX_MAIL_IMAP_PROTOCOL
+           && (cscf->imap_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED))
+
         || (s->protocol == NGX_MAIL_SMTP_PROTOCOL
            && (cscf->smtp_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)))
     {
@@ -985,7 +988,7 @@ ngx_imap_auth_state(ngx_event_t *rev)
 {
     u_char                    *p, *last, *text, *dst, *src, *end;
     ssize_t                    text_len, last_len;
-    ngx_str_t                 *arg;
+    ngx_str_t                 *arg, salt;
     ngx_int_t                  rc;
     ngx_uint_t                 tag, i;
     ngx_connection_t          *c;
@@ -1055,113 +1058,342 @@ ngx_imap_auth_state(ngx_event_t *rev)
             s->backslash = 0;
         }
 
-        switch (s->command) {
-
-        case NGX_IMAP_LOGIN:
+        switch (s->mail_state) {
+
+        case ngx_imap_start:
+
+            switch (s->command) {
+
+            case NGX_IMAP_LOGIN:
 
 #if (NGX_MAIL_SSL)
 
-            if (c->ssl == NULL) {
-                sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
-
-                if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+                if (c->ssl == NULL) {
+                    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+                    if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+                        rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                        break;
+                    }
+                }
+#endif
+
+                arg = s->args.elts;
+
+                if (s->args.nelts == 2 && arg[0].len) {
+
+                    s->login.len = arg[0].len;
+                    s->login.data = ngx_palloc(c->pool, s->login.len);
+                    if (s->login.data == NULL) {
+                        ngx_mail_session_internal_server_error(s);
+                        return;
+                    }
+
+                    ngx_memcpy(s->login.data, arg[0].data, s->login.len);
+
+                    s->passwd.len = arg[1].len;
+                    s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
+                    if (s->passwd.data == NULL) {
+                        ngx_mail_session_internal_server_error(s);
+                        return;
+                    }
+
+                    ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+                    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                                   "imap login:\"%V\" passwd:\"%V\"",
+                                   &s->login, &s->passwd);
+#else
+                    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                                   "imap login:\"%V\"", &s->login);
+#endif
+
+                    ngx_mail_do_auth(s);
+                    return;
+                }
+
+                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                break;
+
+            case NGX_IMAP_AUTHENTICATE:
+
+#if (NGX_MAIL_SSL)
+
+                if (c->ssl == NULL) {
+                    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+                    if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+                        rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                        break;
+                    }
+                }
+#endif
+
+                if (s->args.nelts != 1) {
                     rc = NGX_MAIL_PARSE_INVALID_COMMAND;
                     break;
                 }
-            }
+
+                arg = s->args.elts;
+
+                if (arg[0].len == 5) {
+
+                    if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5)
+                        == 0)
+                    {
+
+                        s->mail_state = ngx_imap_auth_login_username;
+
+                        last_len = sizeof(pop3_username) - 1;
+                        last = pop3_username;
+                        tag = 0;
+
+                        break;
+
+                    } else if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN",
+                                               5)
+                               == 0)
+                    {
+
+                        s->mail_state = ngx_imap_auth_plain;
+
+                        last_len = sizeof(pop3_next) - 1;
+                        last = pop3_next;
+                        tag = 0;
+
+                        break;
+                    }
+
+                } else if (arg[0].len == 8
+                           && ngx_strncasecmp(arg[0].data,
+                                              (u_char *) "CRAM-MD5", 8)
+                              == 0)
+                {
+                    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+                    if (!(cscf->imap_auth_methods
+                          & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)
+                        || s->args.nelts != 1)
+                    {
+                        rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                        break;
+                    }
+
+                    s->mail_state = ngx_imap_auth_cram_md5;
+
+                    last = ngx_palloc(c->pool,
+                                      sizeof("+ " CRLF) - 1
+                                      + ngx_base64_encoded_length(s->salt.len));
+                    if (last == NULL) {
+                        ngx_mail_session_internal_server_error(s);
+                        return;
+                    }
+
+                    last[0] = '+'; last[1]= ' ';
+                    salt.data = &last[2];
+                    s->salt.len -= 2;
+
+                    ngx_encode_base64(&salt, &s->salt);
+
+                    s->salt.len += 2;
+                    last_len = 2 + salt.len;
+                    last[last_len++] = CR; last[last_len++] = LF;
+                    tag = 0;
+
+                    break;
+                }
+
+                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                break;
+
+            case NGX_IMAP_CAPABILITY:
+                cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+#if (NGX_MAIL_SSL)
+
+                if (c->ssl == NULL) {
+                    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+                    if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
+                        text_len = cscf->imap_starttls_capability.len;
+                        text = cscf->imap_starttls_capability.data;
+                        break;
+                    }
+
+                    if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+                        text_len = cscf->imap_starttls_only_capability.len;
+                        text = cscf->imap_starttls_only_capability.data;
+                        break;
+                    }
+                }
 #endif
 
+                text_len = cscf->imap_capability.len;
+                text = cscf->imap_capability.data;
+                break;
+
+            case NGX_IMAP_LOGOUT:
+                s->quit = 1;
+                text = imap_bye;
+                text_len = sizeof(imap_bye) - 1;
+                break;
+
+            case NGX_IMAP_NOOP:
+                break;
+
+#if (NGX_MAIL_SSL)
+
+            case NGX_IMAP_STARTTLS:
+                if (c->ssl == NULL) {
+                    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+                    if (sslcf->starttls) {
+                        c->read->handler = ngx_mail_starttls_handler;
+                        break;
+                    }
+                }
+
+                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                break;
+#endif
+
+            default:
+                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                break;
+            }
+
+            break;
+
+        case ngx_imap_auth_login_username:
             arg = s->args.elts;
-
-            if (s->args.nelts == 2 && arg[0].len) {
-
-                s->login.len = arg[0].len;
-                s->login.data = ngx_palloc(c->pool, s->login.len);
-                if (s->login.data == NULL) {
-                    ngx_mail_session_internal_server_error(s);
-                    return;
-                }
-
-                ngx_memcpy(s->login.data, arg[0].data, s->login.len);
-
-                s->passwd.len = arg[1].len;
-                s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
-                if (s->passwd.data == NULL) {
-                    ngx_mail_session_internal_server_error(s);
-                    return;
-                }
-
-                ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
+            s->mail_state = ngx_imap_auth_login_password;
+
+            ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                           "imap auth login username: \"%V\"", &arg[0]);
+
+            s->login.data = ngx_palloc(c->pool,
+                                       ngx_base64_decoded_length(arg[0].len));
+            if (s->login.data == NULL){
+                ngx_mail_session_internal_server_error(s);
+                return;
+            }
+
+            if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                              "client sent invalid base64 encoding "
+                              "in AUTH LOGIN command");
+                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                break;
+            }
+
+            ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                           "imap auth login username: \"%V\"", &s->login);
+
+            last_len = sizeof(pop3_password) - 1;
+            last = pop3_password;
+            tag = 0;
+
+            break;
+
+        case ngx_imap_auth_login_password:
+            arg = s->args.elts;
 
 #if (NGX_DEBUG_MAIL_PASSWD)
-                ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
-                               "imap login:\"%V\" passwd:\"%V\"",
-                               &s->login, &s->passwd);
-#else
-                ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
-                               "imap login:\"%V\"", &s->login);
+            ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                           "imap auth login password: \"%V\"", &arg[0]);
 #endif
 
+            s->passwd.data = ngx_palloc(c->pool,
+                                        ngx_base64_decoded_length(arg[0].len));
+            if (s->passwd.data == NULL){
+                ngx_mail_session_internal_server_error(s);
+                return;
+            }
+
+            if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) {
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                              "client sent invalid base64 encoding "
+                              "in AUTH LOGIN command");
+                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                break;
+            }
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+            ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                           "imap auth login password: \"%V\"", &s->passwd);
+#endif
+
+            ngx_mail_do_auth(s);
+            return;
+
+        case ngx_imap_auth_plain:
+            arg = s->args.elts;
+
+            rc = ngx_mail_decode_auth_plain(s, &arg[0]);
+
+            if (rc == NGX_OK) {
                 ngx_mail_do_auth(s);
                 return;
             }
 
-            rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+            if (rc == NGX_ERROR) {
+                ngx_mail_session_internal_server_error(s);
+                return;
+            }
+
+            /* rc == NGX_MAIL_PARSE_INVALID_COMMAND */
+
             break;
 
-        case NGX_IMAP_CAPABILITY:
-            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
-
-#if (NGX_MAIL_SSL)
-
-            if (c->ssl == NULL) {
-                sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
-
-                if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
-                    text_len = cscf->imap_starttls_capability.len;
-                    text = cscf->imap_starttls_capability.data;
-                    break;
-                }
-
-                if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
-                    text_len = cscf->imap_starttls_only_capability.len;
-                    text = cscf->imap_starttls_only_capability.data;
+        case ngx_imap_auth_cram_md5:
+            arg = s->args.elts;
+
+            ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                           "imap auth cram-md5: \"%V\"", &arg[0]);
+
+            s->login.data = ngx_palloc(c->pool,
+                                       ngx_base64_decoded_length(arg[0].len));
+            if (s->login.data == NULL){
+                ngx_mail_session_internal_server_error(s);
+                return;
+            }
+
+            if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                              "client sent invalid base64 encoding "
+                              "in AUTH CRAM-MD5 command");
+                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                break;
+            }
+
+            p = s->login.data;
+            last = p + s->login.len;
+
+            while (p < last) {
+                if (*p++ == ' ') {
+                    s->login.len = p - s->login.data - 1;
+                    s->passwd.len = last - p;
+                    s->passwd.data = p;
                     break;
                 }
             }
-#endif
-
-            text_len = cscf->imap_capability.len;
-            text = cscf->imap_capability.data;
-            break;
-
-        case NGX_IMAP_LOGOUT:
-            s->quit = 1;
-            text = imap_bye;
-            text_len = sizeof(imap_bye) - 1;
-            break;
-
-        case NGX_IMAP_NOOP:
-            break;
-
-#if (NGX_MAIL_SSL)
-
-        case NGX_IMAP_STARTTLS:
-            if (c->ssl == NULL) {
-                sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
-                if (sslcf->starttls) {
-                    c->read->handler = ngx_mail_starttls_handler;
-                    break;
-                }
+
+            if (s->passwd.len != 32) {
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                              "client sent invalid CRAM-MD5 hash "
+                              "in AUTH CRAM-MD5 command");
+                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                break;
             }
 
-            rc = NGX_MAIL_PARSE_INVALID_COMMAND;
-            break;
-#endif
-
-        default:
-            rc = NGX_MAIL_PARSE_INVALID_COMMAND;
-            break;
+            ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                           "imap auth cram-md5: \"%V\" \"%V\"",
+                           &s->login, &s->passwd);
+
+            s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;
+
+            ngx_mail_do_auth(s);
+            return;
         }
 
     } else if (rc == NGX_IMAP_NEXT) {
@@ -1171,6 +1403,8 @@ ngx_imap_auth_state(ngx_event_t *rev)
     }
 
     if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
+        s->mail_state = ngx_imap_start;
+        s->state = 0;
         last = imap_invalid_command;
         last_len = sizeof(imap_invalid_command) - 1;
     }
@@ -1209,9 +1443,18 @@ ngx_imap_auth_state(ngx_event_t *rev)
 
     if (rc != NGX_IMAP_NEXT) {
         s->args.nelts = 0;
-        s->buffer->pos = s->buffer->start;
-        s->buffer->last = s->buffer->start;
-        s->tag.len = 0;
+
+        if (s->state) {
+            /* preserve tag */
+            s->arg_start = s->buffer->start + s->tag.len;
+            s->buffer->pos = s->arg_start;
+            s->buffer->last = s->arg_start;
+
+        } else {
+            s->buffer->pos = s->buffer->start;
+            s->buffer->last = s->buffer->start;
+            s->tag.len = 0;
+        }
     }
 
     ngx_mail_send(c->write);
@@ -1229,6 +1472,9 @@ ngx_smtp_auth_state(ngx_event_t *rev)
     ngx_connection_t          *c;
     ngx_mail_session_t        *s;
     ngx_mail_core_srv_conf_t  *cscf;
+#if (NGX_MAIL_SSL)
+    ngx_mail_ssl_conf_t       *sslcf;
+#endif
 
     c = rev->data;
     s = c->data;
@@ -1295,6 +1541,26 @@ ngx_smtp_auth_state(ngx_event_t *rev)
 
                 } else {
                     s->esmtp = 1;
+
+#if (NGX_MAIL_SSL)
+
+                    if (c->ssl == NULL) {
+                        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+                        if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
+                            size = cscf->smtp_starttls_capability.len;
+                            text = cscf->smtp_starttls_capability.data;
+                            break;
+                        }
+
+                        if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+                            size = cscf->smtp_starttls_only_capability.len;
+                            text = cscf->smtp_starttls_only_capability.data;
+                            break;
+                        }
+                    }
+#endif
+
                     size = cscf->smtp_capability.len;
                     text = cscf->smtp_capability.data;
                 }
@@ -1303,6 +1569,18 @@ ngx_smtp_auth_state(ngx_event_t *rev)
 
             case NGX_SMTP_AUTH:
 
+#if (NGX_MAIL_SSL)
+
+                if (c->ssl == NULL) {
+                    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+                    if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+                        rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                        break;
+                    }
+                }
+#endif
+
                 if (s->args.nelts == 0) {
                     text = smtp_invalid_argument;
                     size = sizeof(smtp_invalid_argument) - 1;
@@ -1453,6 +1731,38 @@ ngx_smtp_auth_state(ngx_event_t *rev)
                 text = smtp_ok;
                 size = sizeof(smtp_ok) - 1;
                 break;
+
+#if (NGX_MAIL_SSL)
+
+            case NGX_SMTP_STARTTLS:
+                if (c->ssl == NULL) {
+                    sslcf = ngx_mail_get_module_srv_conf(s,
+                                                         ngx_mail_ssl_module);
+                    if (sslcf->starttls) {
+                        c->read->handler = ngx_mail_starttls_handler;
+
+                        /*
+                         * RFC3207 requires us to discard any knowledge
+                         * obtained from client before STARTTLS.
+                         */
+
+                        s->smtp_helo.len = 0;
+                        s->smtp_helo.data = NULL;
+
+                        text = smtp_ok;
+                        size = sizeof(smtp_ok) - 1;
+
+                        break;
+                    }
+                }
+
+                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                break;
+#endif
+
+            default:
+                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+                break;
             }
 
             break;
@@ -1790,6 +2100,10 @@ ngx_mail_close_connection(ngx_connection
 
 #endif
 
+#if (NGX_STAT_STUB)
+    ngx_atomic_fetch_add(ngx_stat_active, -1);
+#endif
+
     c->destroyed = 1;
 
     pool = c->pool;
--- a/src/mail/ngx_mail_parse.c
+++ b/src/mail/ngx_mail_parse.c
@@ -354,6 +354,27 @@ ngx_int_t ngx_imap_parse_command(ngx_mai
                     }
                     break;
 
+                case 12:
+                    if ((c[0] == 'A'|| c[0] == 'a')
+                        && (c[1] == 'U'|| c[1] == 'u')
+                        && (c[2] == 'T'|| c[2] == 't')
+                        && (c[3] == 'H'|| c[3] == 'h')
+                        && (c[4] == 'E'|| c[4] == 'e')
+                        && (c[5] == 'N'|| c[5] == 'n')
+                        && (c[6] == 'T'|| c[6] == 't')
+                        && (c[7] == 'I'|| c[7] == 'i')
+                        && (c[8] == 'C'|| c[8] == 'c')
+                        && (c[9] == 'A'|| c[9] == 'a')
+                        && (c[10] == 'T'|| c[10] == 't')
+                        && (c[11] == 'E'|| c[11] == 'e'))
+                    {
+                        s->command = NGX_IMAP_AUTHENTICATE;
+
+                    } else {
+                        goto invalid;
+                    }
+                    break;
+
                 default:
                     goto invalid;
                 }
@@ -413,6 +434,10 @@ ngx_int_t ngx_imap_parse_command(ngx_mai
             break;
 
         case sw_argument:
+            if (ch == ' ' && s->quoted) {
+                break;
+            }
+
             switch (ch) {
             case '"':
                 if (!s->quoted) {
@@ -573,7 +598,7 @@ done:
         s->literal_len = 0;
     }
 
-    s->state = sw_start;
+    s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;
 
     return NGX_OK;
 
@@ -646,10 +671,43 @@ ngx_int_t ngx_smtp_parse_command(ngx_mai
                     {
                         s->command = NGX_SMTP_RSET;
 
+                    } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T')
+                    {
+                        s->command = NGX_SMTP_RCPT;
+
+                    } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y')
+                    {
+                        s->command = NGX_SMTP_VRFY;
+
+                    } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N')
+                    {
+                        s->command = NGX_SMTP_EXPN;
+
+                    } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P')
+                    {
+                        s->command = NGX_SMTP_HELP;
+
                     } else {
                         goto invalid;
                     }
+#if (NGX_MAIL_SSL)
+                } else if (p - c == 8) {
 
+                    if ((c[0] == 'S'|| c[0] == 's')
+                        && (c[1] == 'T'|| c[1] == 't')
+                        && (c[2] == 'A'|| c[2] == 'a')
+                        && (c[3] == 'R'|| c[3] == 'r')
+                        && (c[4] == 'T'|| c[4] == 't')
+                        && (c[5] == 'T'|| c[5] == 't')
+                        && (c[6] == 'L'|| c[6] == 'l')
+                        && (c[7] == 'S'|| c[7] == 's'))
+                    {
+                        s->command = NGX_SMTP_STARTTLS;
+
+                    } else {
+                        goto invalid;
+                    }
+#endif
                 } else {
                     goto invalid;
                 }
--- a/src/os/unix/ngx_freebsd_config.h
+++ b/src/os/unix/ngx_freebsd_config.h
@@ -77,6 +77,9 @@
 #endif
 
 
+#define NGX_LISTEN_BACKLOG        -1
+
+
 #if (defined SO_ACCEPTFILTER && !defined NGX_HAVE_DEFERRED_ACCEPT)
 #define NGX_HAVE_DEFERRED_ACCEPT  1
 #endif
--- a/src/os/unix/ngx_linux_config.h
+++ b/src/os/unix/ngx_linux_config.h
@@ -78,6 +78,9 @@ extern ssize_t sendfile(int s, int fd, i
 #endif
 
 
+#define NGX_LISTEN_BACKLOG        511
+
+
 #if defined TCP_DEFER_ACCEPT && !defined NGX_HAVE_DEFERRED_ACCEPT
 #define NGX_HAVE_DEFERRED_ACCEPT  1
 #endif
--- a/src/os/unix/ngx_posix_config.h
+++ b/src/os/unix/ngx_posix_config.h
@@ -88,6 +88,9 @@
 #endif
 
 
+#define NGX_LISTEN_BACKLOG  511
+
+
 #if (__FreeBSD__) && (__FreeBSD_version < 400017)
 
 #include <sys/param.h>          /* ALIGN() */
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -62,6 +62,11 @@ u_long         cpu_affinity;
 static u_char  master_process[] = "master process";
 
 
+static ngx_cycle_t      ngx_exit_cycle;
+static ngx_log_t        ngx_exit_log;
+static ngx_open_file_t  ngx_exit_log_file;
+
+
 void
 ngx_master_process_cycle(ngx_cycle_t *cycle)
 {
@@ -649,13 +654,21 @@ ngx_master_process_exit(ngx_cycle_t *cyc
     }
 
     /*
-     * we do not destroy cycle->pool here because a signal handler
-     * that uses cycle->log can be called at this point
+     * Copy ngx_cycle->log related data to the special static exit cycle,
+     * log, and log file structures enough to allow a signal handler to log.
+     * The handler may be called when standard ngx_cycle->log allocated from
+     * ngx_cycle->pool is already destroyed.
      */
 
-#if 0
+    ngx_exit_log_file.fd = ngx_cycle->log->file->fd;
+
+    ngx_exit_log = *ngx_cycle->log;
+    ngx_exit_log.file = &ngx_exit_log_file;
+
+    ngx_exit_cycle.log = &ngx_exit_log;
+    ngx_cycle = &ngx_exit_cycle;
+
     ngx_destroy_pool(cycle->pool);
-#endif
 
     exit(0);
 }
@@ -792,49 +805,49 @@ ngx_worker_process_init(ngx_cycle_t *cyc
 
     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
 
-    if (geteuid() == 0) {
-        if (priority && ccf->priority != 0) {
-            if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {
-                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                              "setpriority(%d) failed", ccf->priority);
-            }
+    if (priority && ccf->priority != 0) {
+        if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "setpriority(%d) failed", ccf->priority);
         }
+    }
 
-        if (ccf->rlimit_nofile != NGX_CONF_UNSET) {
-            rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;
-            rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile;
+    if (ccf->rlimit_nofile != NGX_CONF_UNSET) {
+        rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;
+        rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile;
 
-            if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
-                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                              "setrlimit(RLIMIT_NOFILE, %i) failed",
-                              ccf->rlimit_nofile);
-            }
+        if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "setrlimit(RLIMIT_NOFILE, %i) failed",
+                          ccf->rlimit_nofile);
         }
+    }
 
-        if (ccf->rlimit_core != NGX_CONF_UNSET_SIZE) {
-            rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;
-            rlmt.rlim_max = (rlim_t) ccf->rlimit_core;
+    if (ccf->rlimit_core != NGX_CONF_UNSET_SIZE) {
+        rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;
+        rlmt.rlim_max = (rlim_t) ccf->rlimit_core;
 
-            if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {
-                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                              "setrlimit(RLIMIT_CORE, %i) failed",
-                              ccf->rlimit_core);
-            }
+        if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "setrlimit(RLIMIT_CORE, %i) failed",
+                          ccf->rlimit_core);
         }
+    }
 
 #ifdef RLIMIT_SIGPENDING
-        if (ccf->rlimit_sigpending != NGX_CONF_UNSET) {
-            rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending;
-            rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending;
+    if (ccf->rlimit_sigpending != NGX_CONF_UNSET) {
+        rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending;
+        rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending;
 
-            if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) {
-                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                              "setrlimit(RLIMIT_SIGPENDING, %i) failed",
-                              ccf->rlimit_sigpending);
-            }
+        if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "setrlimit(RLIMIT_SIGPENDING, %i) failed",
+                          ccf->rlimit_sigpending);
         }
+    }
 #endif
 
+    if (geteuid() == 0) {
         if (setgid(ccf->group) == -1) {
             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                           "setgid(%d) failed", ccf->group);
@@ -996,13 +1009,23 @@ ngx_worker_process_exit(ngx_cycle_t *cyc
     }
 
     /*
-     * we do not destroy cycle->pool here because a signal handler
-     * that uses cycle->log can be called at this point
+     * Copy ngx_cycle->log related data to the special static exit cycle,
+     * log, and log file structures enough to allow a signal handler to log.
+     * The handler may be called when standard ngx_cycle->log allocated from
+     * ngx_cycle->pool is already destroyed.
      */
 
-#if 0
+    ngx_exit_log_file.fd = ngx_cycle->log->file->fd;
+
+    ngx_exit_log = *ngx_cycle->log;
+    ngx_exit_log.file = &ngx_exit_log_file;
+
+    ngx_exit_cycle.log = &ngx_exit_log;
+    ngx_cycle = &ngx_exit_cycle;
+
     ngx_destroy_pool(cycle->pool);
-#endif
+
+    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, "exit");
 
     exit(0);
 }
--- a/src/os/unix/ngx_solaris_config.h
+++ b/src/os/unix/ngx_solaris_config.h
@@ -82,6 +82,9 @@
 #endif
 
 
+#define NGX_LISTEN_BACKLOG           511
+
+
 #ifndef NGX_HAVE_INHERITED_NONBLOCK
 #define NGX_HAVE_INHERITED_NONBLOCK  1
 #endif