changeset 190:3689cd4e3228 NGINX_0_3_42

nginx 0.3.42 *) Feature: the "bind" option of the "listen" directive in IMAP/POP3 proxy. *) Bugfix: if the same capture in the "rewrite" directive was used more then once. *) Bugfix: the $sent_http_content_type, $sent_http_content_length, $sent_http_last_modified, $sent_http_connection, $sent_http_keep_alive, and $sent_http_transfer_encoding variables were not written to access log. *) Bugfix: the $sent_http_cache_control returned value of the single "Cache-Control" response header line.
author Igor Sysoev <http://sysoev.ru>
date Wed, 26 Apr 2006 00:00:00 +0400
parents fca7768a4aca
children 219b4d22ddd9
files CHANGES CHANGES.ru auto/modules src/core/nginx.c src/core/nginx.h src/core/ngx_connection.h src/event/ngx_event.c src/event/ngx_event_accept.c src/http/modules/ngx_http_rewrite_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/ngx_http.c src/http/ngx_http_core_module.c src/http/ngx_http_postpone_filter_module.c src/http/ngx_http_request.c src/http/ngx_http_script.c src/http/ngx_http_script.h src/http/ngx_http_upstream.c src/http/ngx_http_variables.c src/imap/ngx_imap.c src/imap/ngx_imap.h src/imap/ngx_imap_core_module.c src/imap/ngx_imap_handler.c
diffstat 22 files changed, 721 insertions(+), 91 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,21 @@
 
+Changes with nginx 0.3.42                                        26 Apr 2006
+
+    *) Feature: the "bind" option of the "listen" directive in IMAP/POP3 
+       proxy.
+
+    *) Bugfix: if the same capture in the "rewrite" directive was used more 
+       then once.
+
+    *) Bugfix: the $sent_http_content_type, $sent_http_content_length, 
+       $sent_http_last_modified, $sent_http_connection, 
+       $sent_http_keep_alive, and $sent_http_transfer_encoding variables 
+       were not written to access log.
+
+    *) Bugfix: the $sent_http_cache_control returned value of the single 
+       "Cache-Control" response header line.
+
+
 Changes with nginx 0.3.41                                        21 Apr 2006
 
     *) Feature: the -v switch.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,20 @@
 
+Изменения в nginx 0.3.42                                          26.04.2006
+
+    *) Добавление: параметр bind в директиве listen в IMAP/POP3 прокси.
+
+    *) Исправление: ошибки при использовании в директиве rewrite одного и 
+       того же выделения более одного раза.
+
+    *) Исправление: в лог не записывались переменные 
+       $sent_http_content_type, $sent_http_content_length, 
+       $sent_http_last_modified, $sent_http_connection, 
+       $sent_http_keep_alive и $sent_http_transfer_encoding.
+
+    *) Исправление: переменная $sent_http_cache_control возвращала 
+       содержимое только одной строки "Cache-Control" в заголовке ответа.
+
+
 Изменения в nginx 0.3.41                                          21.04.2006
 
     *) Добавление: ключ -v.
--- a/auto/modules
+++ b/auto/modules
@@ -61,6 +61,12 @@ if [ $HTTP_SSI = YES ]; then
     HTTP_POSTPONE=YES
 fi
 
+
+if [ $HTTP_ADDITION = YES ]; then
+    HTTP_POSTPONE=YES
+fi
+
+
 # the module order is important
 #     ngx_http_autoindex_module
 #     ngx_http_index_module
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -239,7 +239,9 @@ main(int argc, char *const *argv)
         ngx_write_fd(ngx_stderr_fileno, "built by " NGX_COMPILER CRLF,
                      sizeof("built by " NGX_COMPILER CRLF) - 1);
 #endif
-        return 0;
+        if (!ngx_test_config) {
+            return 0;
+        }
     }
 
     if (ngx_test_config) {
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VER          "nginx/0.3.41"
+#define NGINX_VER          "nginx/0.3.42"
 
 #define NGINX_VAR          "NGINX"
 #define NGX_OLDPID_EXT     ".oldbin"
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -33,7 +33,6 @@ struct ngx_listening_s {
     /* handler of accepted connection */
     ngx_connection_handler_pt   handler;
 
-    void               *ctx;      /* ngx_http_conf_ctx_t, for example */
     void               *servers;  /* array of ngx_http_in_addr_t, for example */
 
     ngx_log_t           log;
@@ -113,10 +112,6 @@ struct ngx_connection_s {
 
     off_t               sent;
 
-    void               *ctx;
-    void               *servers;
-
-
     ngx_log_t          *log;
 
     ngx_pool_t         *pool;
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -432,8 +432,10 @@ ngx_event_module_init(ngx_cycle_t *cycle
 
     ecf = (*cf)[ngx_event_core_module.ctx_index];
 
-    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
-                  "using the \"%s\" event method", ecf->name);
+    if (!ngx_test_config) {
+        ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
+                      "using the \"%s\" event method", ecf->name);
+    }
 
     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
 
@@ -738,9 +740,6 @@ ngx_event_process_init(ngx_cycle_t *cycl
         c->listening = &ls[i];
         ls[i].connection = c;
 
-        c->ctx = ls[i].ctx;
-        c->servers = ls[i].servers;
-
         rev = c->read;
 
         rev->log = c->log;
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -156,9 +156,6 @@ ngx_event_accept(ngx_event_t *ev)
 
         c->unexpected_eof = 1;
 
-        c->ctx = lc->ctx;
-        c->servers = lc->servers;
-
         rev = c->read;
         wev = c->write;
 
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -395,7 +395,7 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com
     regex->size = sc.size;
     regex->args = sc.args;
 
-    if (sc.variables == 0) {
+    if (sc.variables == 0 && !sc.dup_capture) {
         regex->lengths = NULL;
     }
 
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -734,6 +734,19 @@ ngx_http_ssi_output(ngx_http_request_t *
     ngx_buf_t    *b;
     ngx_chain_t  *cl;
 
+#if 1
+    b = NULL;
+    for (cl = ctx->out; cl; cl = cl->next) {
+        if (cl->buf == b) {
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                          "the same buf was used in ssi");
+            ngx_debug_point();
+            return NGX_ERROR;
+        }
+        b = cl->buf;
+    }
+#endif
+
     rc = ngx_http_next_body_filter(r, ctx->out);
 
     if (ctx->busy == NULL) {
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -725,8 +725,6 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
             ls->deferred_accept = in_addr[a].listen_conf->deferred_accept;
 #endif
 
-            ls->ctx = ctx;
-
             hip = ngx_palloc(cf->pool, sizeof(ngx_http_in_port_t));
             if (hip == NULL) {
                 return NGX_CONF_ERROR;
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -1328,6 +1328,7 @@ ngx_http_internal_redirect(ngx_http_requ
     ngx_http_update_location_config(r);
 
     r->internal = 1;
+    r->method = NGX_HTTP_GET;
 
     ngx_http_handler(r);
 
@@ -2528,6 +2529,7 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c
 static ngx_http_method_name_t  ngx_methods_names[] = {
    { "GET",  (uint32_t) ~NGX_HTTP_GET },
    { "HEAD", (uint32_t) ~NGX_HTTP_HEAD },
+   { "POST", (uint32_t) ~NGX_HTTP_POST },
    { NULL, 0 }
 };
 
--- a/src/http/ngx_http_postpone_filter_module.c
+++ b/src/http/ngx_http_postpone_filter_module.c
@@ -89,6 +89,22 @@ ngx_http_postpone_filter(ngx_http_reques
             return NGX_ERROR;
         }
 
+#if 1
+        {
+        ngx_chain_t  *cl;
+        ngx_buf_t    *b = NULL;
+        for (cl = pr->out; cl; cl = cl->next) {
+            if (cl->buf == b) {
+                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                              "the same buf was used in postponed");
+                ngx_debug_point();
+                return NGX_ERROR;
+            }
+            b = cl->buf;
+        }
+        }
+#endif
+
         if (r != r->connection->data || r->postponed->request) {
             return NGX_AGAIN;
         }
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -201,11 +201,11 @@ ngx_http_init_connection(ngx_connection_
 }
 
 
-static
-void ngx_http_init_request(ngx_event_t *rev)
+static void
+ngx_http_init_request(ngx_event_t *rev)
 {
+    socklen_t                   len;
     ngx_uint_t                  i;
-    socklen_t                   len;
     struct sockaddr_in          sin;
     ngx_connection_t           *c;
     ngx_http_request_t         *r;
@@ -274,7 +274,7 @@ void ngx_http_init_request(ngx_event_t *
 
     /* AF_INET only */
 
-    hip = c->servers;
+    hip = c->listening->servers;
     hia = hip->addrs;
 
     r->port = hip->port;
@@ -311,7 +311,7 @@ void ngx_http_init_request(ngx_event_t *
             r->in_addr = sin.sin_addr.s_addr;
         }
 
-        /* the last in_port->addrs address is "*" */
+        /* the last address is "*" */
 
         for ( /* void */ ; i < hip->naddrs - 1; i++) {
             if (hia[i].addr == r->in_addr) {
@@ -1405,6 +1405,12 @@ ngx_http_finalize_request(ngx_http_reque
         || rc == NGX_HTTP_REQUEST_TIME_OUT
         || r->connection->error)
     {
+        if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST
+            && r->headers_out.status == 0)
+        {
+            r->headers_out.status = NGX_HTTP_CLIENT_CLOSED_REQUEST;
+        }
+
         if (ngx_http_post_action(r) == NGX_OK) {
             return;
         }
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -91,6 +91,14 @@ ngx_http_script_compile(ngx_http_script_
 
             if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {
 
+                n = sc->source->data[i] - '0';
+
+                if (sc->captures_mask & (1 << n)) {
+                    sc->dup_capture = 1;
+                }
+
+                sc->captures_mask |= 1 << n;
+
                 copy_capture = ngx_http_script_add_code(*sc->lengths,
                                    sizeof(ngx_http_script_copy_capture_code_t),
                                    NULL);
@@ -100,7 +108,8 @@ ngx_http_script_compile(ngx_http_script_
 
                 copy_capture->code = (ngx_http_script_code_pt)
                                          ngx_http_script_copy_capture_len_code;
-                copy_capture->n = 2 * (sc->source->data[i] - '0');
+                copy_capture->n = 2 * n;
+
 
                 copy_capture = ngx_http_script_add_code(*sc->values,
                                    sizeof(ngx_http_script_copy_capture_code_t),
@@ -110,14 +119,12 @@ ngx_http_script_compile(ngx_http_script_
                 }
 
                 copy_capture->code = ngx_http_script_copy_capture_code;
-                copy_capture->n = sc->source->data[i] - '0';
+                copy_capture->n = 2 * n;
 
-                if (sc->ncaptures < copy_capture->n) {
-                    sc->ncaptures = copy_capture->n;
+                if (sc->ncaptures < n) {
+                    sc->ncaptures = n;
                 }
 
-                copy_capture->n *= 2;
-
                 i++;
 
                 continue;
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -47,6 +47,7 @@ typedef struct {
 
     ngx_uint_t                  variables;
     ngx_uint_t                  ncaptures;
+    ngx_uint_t                  captures_mask;
     ngx_uint_t                  size;
 
     void                       *main;
@@ -54,6 +55,7 @@ typedef struct {
     unsigned                    compile_args:1;
     unsigned                    complete_lengths:1;
     unsigned                    complete_values:1;
+    unsigned                    dup_capture:1;
 
     unsigned                    args:1;
 } ngx_http_script_compile_t;
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -170,6 +170,10 @@ ngx_http_upstream_header_t  ngx_http_ups
                  ngx_http_upstream_ignore_header_line, 0,
                  ngx_http_upstream_ignore_header_line, 0, 0 },
 
+    { ngx_string("Keep-Alive"),
+                 ngx_http_upstream_ignore_header_line, 0,
+                 ngx_http_upstream_ignore_header_line, 0, 0 },
+
     { ngx_string("X-Pad"),
                  ngx_http_upstream_ignore_header_line, 0,
                  ngx_http_upstream_ignore_header_line, 0, 0 },
@@ -626,6 +630,8 @@ ngx_http_upstream_ssl_init_connection(ng
         return;
     }
 
+    r->connection->log->action = "SSL handshaking to upstream";
+
     rc = ngx_ssl_handshake(c);
 
     if (rc == NGX_AGAIN) {
@@ -852,7 +858,6 @@ ngx_http_upstream_send_request_handler(n
                    "http upstream send request handler");
 
     if (wev->timedout) {
-        c->log->action = "sending request to upstream";
         ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
         return;
     }
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -49,6 +49,19 @@ static ngx_int_t ngx_http_variable_body_
 static ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 
+static ngx_int_t ngx_http_variable_sent_content_type(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_content_length(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_last_modified(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_connection(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_keep_alive(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+
 
 /*
  * TODO:
@@ -59,6 +72,13 @@ static ngx_int_t ngx_http_variable_reque
  *     Apache SSI: DOCUMENT_NAME, LAST_MODIFIED, USER_NAME (file owner)
  */
 
+/*
+ * the $http_host, $http_user_agent, $http_referer, $http_via,
+ * and $http_x_forwarded_for variables may be handled by generic
+ * ngx_http_variable_unknown_header_in(), but for perfomance reasons
+ * they are handled using dedicated entries
+ */
+
 static ngx_http_variable_t  ngx_http_core_variables[] = {
 
     { ngx_string("http_host"), NULL, ngx_http_variable_header,
@@ -146,6 +166,27 @@ static ngx_http_variable_t  ngx_http_cor
       ngx_http_variable_request_completion,
       0, 0, 0 },
 
+    { ngx_string("sent_http_content_type"), NULL,
+      ngx_http_variable_sent_content_type, 0, 0, 0 },
+
+    { ngx_string("sent_http_content_length"), NULL,
+      ngx_http_variable_sent_content_length, 0, 0, 0 },
+
+    { ngx_string("sent_http_last_modified"), NULL,
+      ngx_http_variable_sent_last_modified, 0, 0, 0 },
+
+    { ngx_string("sent_http_connection"), NULL,
+      ngx_http_variable_sent_connection, 0, 0, 0 },
+
+    { ngx_string("sent_http_keep_alive"), NULL,
+      ngx_http_variable_sent_keep_alive, 0, 0, 0 },
+
+    { ngx_string("sent_http_transfer_encoding"), NULL,
+      ngx_http_variable_sent_transfer_encoding, 0, 0, 0 },
+
+    { ngx_string("sent_http_cache_control"), NULL, ngx_http_variable_headers,
+      offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 },
+
     { ngx_string("limit_rate"), ngx_http_variable_request_set_size,
       ngx_http_variable_request,
       offsetof(ngx_http_request_t, limit_rate),
@@ -847,6 +888,178 @@ ngx_http_variable_body_bytes_sent(ngx_ht
 
 
 static ngx_int_t
+ngx_http_variable_sent_content_type(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    if (r->headers_out.content_type.len) {
+        v->len = r->headers_out.content_type.len;
+        v->valid = 1;
+        v->no_cachable = 0;
+        v->not_found = 0;
+        v->data = r->headers_out.content_type.data;
+
+    } else {
+        v->not_found = 1;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_content_length(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    u_char  *p;
+
+    if (r->headers_out.content_length) {
+        v->len = r->headers_out.content_length->value.len;
+        v->valid = 1;
+        v->no_cachable = 0;
+        v->not_found = 0;
+        v->data = r->headers_out.content_length->value.data;
+
+        return NGX_OK;
+    }
+
+    if (r->headers_out.content_length_n >= 0) {
+        p = ngx_palloc(r->pool, NGX_OFF_T_LEN);
+        if (p == NULL) {
+            return NGX_ERROR;
+        }
+
+        v->len = ngx_sprintf(p, "%O", r->headers_out.content_length_n) - p;
+        v->valid = 1;
+        v->no_cachable = 0;
+        v->not_found = 0;
+        v->data = p;
+
+        return NGX_OK;
+    }
+
+    v->not_found = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_last_modified(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    u_char  *p;
+
+    if (r->headers_out.last_modified) {
+        v->len = r->headers_out.last_modified->value.len;
+        v->valid = 1;
+        v->no_cachable = 0;
+        v->not_found = 0;
+        v->data = r->headers_out.last_modified->value.data;
+
+        return NGX_OK;
+    }
+
+    if (r->headers_out.last_modified_time >= 0) {
+        p = ngx_palloc(r->pool,
+                   sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT") - 1);
+        if (p == NULL) {
+            return NGX_ERROR;
+        }
+
+        v->len = ngx_http_time(p, r->headers_out.last_modified_time) - p;
+        v->valid = 1;
+        v->no_cachable = 0;
+        v->not_found = 0;
+        v->data = p;
+
+        return NGX_OK;
+    }
+
+    v->not_found = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_connection(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    size_t   len;
+    char    *p;
+
+    if (r->keepalive) {
+        len = sizeof("keep-alive") - 1;
+        p = "keep-alive";
+
+    } else {
+        len = sizeof("close") - 1;
+        p = "close";
+    }
+
+    v->len = len;
+    v->valid = 1;
+    v->no_cachable = 0;
+    v->not_found = 0;
+    v->data = (u_char *) p;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_keep_alive(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    u_char                    *p;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    if (r->keepalive) {
+        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+        if (clcf->keepalive_header) {
+
+            p = ngx_palloc(r->pool, sizeof("timeout=") - 1 + NGX_TIME_T_LEN);
+            if (p == NULL) {
+                return NGX_ERROR;
+            }
+
+            v->len = ngx_sprintf(p, "timeout=%T", clcf->keepalive_header) - p;
+            v->valid = 1;
+            v->no_cachable = 0;
+            v->not_found = 0;
+            v->data = p;
+
+            return NGX_OK;
+        }
+    }
+
+    v->not_found = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    if (r->chunked) {
+        v->len = sizeof("chunked") - 1;
+        v->valid = 1;
+        v->no_cachable = 0;
+        v->not_found = 0;
+        v->data = (u_char *) "chunked";
+
+    } else {
+        v->not_found = 1;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_request_completion(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
--- a/src/imap/ngx_imap.c
+++ b/src/imap/ngx_imap.c
@@ -11,6 +11,8 @@
 
 
 static char *ngx_imap_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static int ngx_libc_cdecl ngx_imap_cmp_conf_in_addrs(const void *one,
+    const void *two);
 
 
 ngx_uint_t  ngx_imap_max_module;
@@ -56,10 +58,18 @@ static char *
 ngx_imap_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     char                        *rv;
-    ngx_uint_t                   m, mi, s;
+    u_char                      *text;
+    size_t                       len;
+    ngx_uint_t                   i, a, l, m, mi, s, p, last, bind_all, done;
     ngx_conf_t                   pcf;
+    ngx_array_t                  in_ports;
+    ngx_listening_t             *ls;
+    ngx_imap_listen_t           *imls;
     ngx_imap_module_t           *module;
+    ngx_imap_in_port_t          *imip;
     ngx_imap_conf_ctx_t         *ctx;
+    ngx_imap_conf_in_port_t     *in_port;
+    ngx_imap_conf_in_addr_t     *in_addr;
     ngx_imap_core_srv_conf_t   **cscfp;
     ngx_imap_core_main_conf_t   *cmcf;
 
@@ -191,5 +201,203 @@ ngx_imap_block(ngx_conf_t *cf, ngx_comma
 
     *cf = pcf;
 
+
+    if (ngx_array_init(&in_ports, cf->temp_pool, 4,
+                       sizeof(ngx_imap_conf_in_port_t))
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    imls = cmcf->listen.elts;
+
+    for (l = 0; l < cmcf->listen.nelts; l++) {
+
+        /* AF_INET only */
+
+        in_port = in_ports.elts;
+        for (p = 0; p < in_ports.nelts; p++) {
+            if (in_port[p].port == imls[l].port) {
+                in_port = &in_port[p];
+                goto found;
+            }
+        }
+
+        in_port = ngx_array_push(&in_ports);
+        if (in_port == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        in_port->port = imls[l].port;
+
+        if (ngx_array_init(&in_port->addrs, cf->temp_pool, 2,
+                           sizeof(ngx_imap_conf_in_addr_t))
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+
+    found:
+
+        in_addr = ngx_array_push(&in_port->addrs);
+        if (in_addr == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        in_addr->addr = imls[l].addr;
+        in_addr->ctx = imls[l].ctx;
+        in_addr->bind = imls[l].bind;
+    }
+
+    /* optimize the lists of ports and addresses */
+
+    /* AF_INET only */
+
+    in_port = in_ports.elts;
+    for (p = 0; p < in_ports.nelts; p++) {
+
+        ngx_qsort(in_port[p].addrs.elts, (size_t) in_port[p].addrs.nelts,
+                  sizeof(ngx_imap_conf_in_addr_t), ngx_imap_cmp_conf_in_addrs);
+
+        in_addr = in_port[p].addrs.elts;
+        last = in_port[p].addrs.nelts;
+
+        /*
+         * if there is the binding to the "*:port" then we need to bind()
+         * to the "*:port" only and ignore the other bindings
+         */
+
+        if (in_addr[last - 1].addr == INADDR_ANY) {
+            in_addr[last - 1].bind = 1;
+            bind_all = 0;
+
+        } else {
+            bind_all = 1;
+        }
+
+        for (a = 0; a < last; /* void */ ) {
+
+            if (!bind_all && !in_addr[a].bind) {
+                a++;
+                continue;
+            }
+
+            ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr,
+                                                  in_port[p].port);
+            if (ls == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            ls->backlog = -1;
+            ls->rcvbuf = -1;
+            ls->sndbuf = -1;
+
+            ls->addr_ntop = 1;
+            ls->handler = ngx_imap_init_connection;
+            ls->pool_size = 256;
+
+            /* STUB */
+            ls->log = *cf->cycle->new_log;
+            ls->log.data = &ls->addr_text;
+            ls->log.handler = ngx_accept_log_error;
+            /**/
+
+            imip = ngx_palloc(cf->pool, sizeof(ngx_imap_in_port_t));
+            if (imip == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            ls->servers = imip;
+
+            in_addr = in_port[p].addrs.elts;
+
+            if (in_addr[a].bind && in_addr[a].addr != INADDR_ANY) {
+                imip->naddrs = 1;
+                done = 0;
+
+            } else if (in_port[p].addrs.nelts > 1
+                       && in_addr[last - 1].addr == INADDR_ANY)
+            {
+                imip->naddrs = last;
+                done = 1;
+
+            } else {
+                imip->naddrs = 1;
+                done = 0;
+            }
+
+#if 0
+            ngx_log_error(NGX_LOG_ALERT, cf->log, 0,
+                          "%ui: %V %d %ui %ui",
+                          a, &ls->addr_text, in_addr[a].bind,
+                          imip->naddrs, last);
+#endif
+
+            imip->addrs = ngx_pcalloc(cf->pool,
+                                    imip->naddrs * sizeof(ngx_imap_in_addr_t));
+            if (imip->addrs == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            for (i = 0; i < imip->naddrs; i++) {
+                imip->addrs[i].addr = in_addr[i].addr;
+                imip->addrs[i].ctx = in_addr[i].ctx;
+
+                text = ngx_palloc(cf->pool,
+                                  INET_ADDRSTRLEN - 1 + sizeof(":65535") - 1);
+                if (text == NULL) {
+                    return NGX_CONF_ERROR;
+                }
+
+                len = ngx_inet_ntop(AF_INET, &in_addr[i].addr, text,
+                                    INET_ADDRSTRLEN);
+
+                len = ngx_sprintf(text + len, ":%d", in_port[p].port) - text;
+
+                imip->addrs[i].addr_text.len = len;
+                imip->addrs[i].addr_text.data = text;
+            }
+
+            if (done) {
+                break;
+            }
+
+            in_addr++;
+            in_port[p].addrs.elts = in_addr;
+            last--;
+
+            a = 0;
+        }
+    }
+
     return NGX_CONF_OK;
 }
+
+
+static int ngx_libc_cdecl
+ngx_imap_cmp_conf_in_addrs(const void *one, const void *two)
+{
+    ngx_imap_conf_in_addr_t  *first, *second;
+
+    first = (ngx_imap_conf_in_addr_t *) one;
+    second = (ngx_imap_conf_in_addr_t *) two;
+
+    if (first->addr == INADDR_ANY) {
+        /* the INADDR_ANY must be the last resort, shift it to the end */
+        return 1;
+    }
+
+    if (first->bind && !second->bind) {
+        /* shift explicit bind()ed addresses to the start */
+        return -1;
+    }
+
+    if (!first->bind && second->bind) {
+        /* shift explicit bind()ed addresses to the start */
+        return 1;
+    }
+
+    /* do not sort by default */
+
+    return 0;
+}
--- a/src/imap/ngx_imap.h
+++ b/src/imap/ngx_imap.h
@@ -26,7 +26,46 @@ typedef struct {
 
 
 typedef struct {
-    ngx_array_t             servers;         /* ngx_imap_core_srv_conf_t */
+    in_addr_t               addr;
+    in_port_t               port;
+    int                     family;
+
+    /* server ctx */
+    ngx_imap_conf_ctx_t    *ctx;
+
+    unsigned                bind:1;
+} ngx_imap_listen_t;
+
+
+typedef struct {
+    in_addr_t               addr;
+    ngx_imap_conf_ctx_t    *ctx;
+    ngx_str_t               addr_text;
+} ngx_imap_in_addr_t;
+
+
+typedef struct {
+    ngx_imap_in_addr_t     *addrs;       /* array of ngx_imap_in_addr_t */
+    ngx_uint_t              naddrs;
+} ngx_imap_in_port_t;
+
+
+typedef struct {
+    in_port_t               port;
+    ngx_array_t             addrs;       /* array of ngx_imap_conf_in_addr_t */
+} ngx_imap_conf_in_port_t;
+
+
+typedef struct {
+    in_addr_t               addr;
+    ngx_imap_conf_ctx_t    *ctx;
+    unsigned                bind:1;
+} ngx_imap_conf_in_addr_t;
+
+
+typedef struct {
+    ngx_array_t             servers;     /* ngx_imap_core_srv_conf_t */
+    ngx_array_t             listen;      /* ngx_imap_listen_t */
 } ngx_imap_core_main_conf_t;
 
 
@@ -52,7 +91,7 @@ typedef struct {
     ngx_array_t             imap_capabilities;
 
     /* server ctx */
-    ngx_imap_conf_ctx_t  *ctx;
+    ngx_imap_conf_ctx_t    *ctx;
 } ngx_imap_core_srv_conf_t;
 
 
@@ -109,6 +148,7 @@ typedef struct {
     unsigned                quoted:1;
     unsigned                backslash:1;
     unsigned                no_sync_literal:1;
+    unsigned                starttls:1;
 
     ngx_str_t               login;
     ngx_str_t               passwd;
@@ -116,6 +156,8 @@ typedef struct {
     ngx_str_t               tag;
     ngx_str_t               tagged_line;
 
+    ngx_str_t              *addr_text;
+
     ngx_uint_t              command;
     ngx_array_t             args;
 
@@ -184,6 +226,9 @@ typedef struct {
     (s)->main_conf[module.ctx_index]
 #define ngx_imap_get_module_srv_conf(s, module)  (s)->srv_conf[module.ctx_index]
 
+#define ngx_imap_conf_get_module_main_conf(cf, module)                       \
+    ((ngx_imap_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
+
 
 void ngx_imap_init_connection(ngx_connection_t *c);
 void ngx_imap_send(ngx_event_t *wev);
--- a/src/imap/ngx_imap_core_module.c
+++ b/src/imap/ngx_imap_core_module.c
@@ -55,7 +55,7 @@ static ngx_command_t  ngx_imap_core_comm
       NULL },
 
     { ngx_string("listen"),
-      NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
+      NGX_IMAP_SRV_CONF|NGX_CONF_TAKE12,
       ngx_imap_core_listen,
       0,
       0,
@@ -143,7 +143,14 @@ ngx_imap_core_create_main_conf(ngx_conf_
     }
 
     if (ngx_array_init(&cmcf->servers, cf->pool, 4,
-                       sizeof(ngx_imap_core_srv_conf_t *)) == NGX_ERROR)
+                       sizeof(ngx_imap_core_srv_conf_t *))
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_imap_listen_t))
+        != NGX_OK)
     {
         return NGX_CONF_ERROR;
     }
@@ -419,12 +426,14 @@ ngx_imap_core_server(ngx_conf_t *cf, ngx
 static char *
 ngx_imap_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    char                 *err;
-    ngx_str_t            *value;
-    in_addr_t             in_addr;
-    struct hostent       *h;
-    ngx_listening_t      *ls;
-    ngx_inet_upstream_t   inet_upstream;
+    char                       *err;
+    ngx_str_t                  *value;
+    in_addr_t                   in_addr;
+    ngx_uint_t                  i;
+    struct hostent             *h;
+    ngx_imap_listen_t          *imls;
+    ngx_inet_upstream_t         inet_upstream;
+    ngx_imap_core_main_conf_t  *cmcf;
 
     value = cf->args->elts;
 
@@ -469,29 +478,46 @@ ngx_imap_core_listen(ngx_conf_t *cf, ngx
         in_addr = INADDR_ANY;
     }
 
+    cmcf = ngx_imap_conf_get_module_main_conf(cf, ngx_imap_core_module);
 
-    ls = ngx_listening_inet_stream_socket(cf, in_addr, inet_upstream.port);
-    if (ls == NULL) {
+    imls = cmcf->listen.elts;
+
+    for (i = 0; i < cmcf->listen.nelts; i++) {
+
+        if (imls[i].addr != in_addr || imls[i].port != inet_upstream.port) {
+            continue;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "duplicate \"%V\" address and port pair",
+                           &inet_upstream.url);
         return NGX_CONF_ERROR;
     }
 
-    ls->backlog = -1;
-    ls->rcvbuf = -1;
-    ls->sndbuf = -1;
+    imls = ngx_array_push(&cmcf->listen);
+    if (imls == NULL) {
+        return NGX_CONF_ERROR;
+    }
 
-    ls->addr_ntop = 1;
-    ls->handler = ngx_imap_init_connection;
-    ls->pool_size = 256;
+    ngx_memzero(imls, sizeof(ngx_imap_listen_t));
+
+    imls->addr = in_addr;
+    imls->port = inet_upstream.port;
+    imls->family = AF_INET;
+    imls->ctx = cf->ctx;
 
-    ls->ctx = cf->ctx;
+    if (cf->args->nelts == 2) {
+        return NGX_CONF_OK;
+    }
 
-    /* STUB */
-    ls->log = *cf->cycle->new_log;
-    ls->log.data = &ls->addr_text;
-    ls->log.handler = ngx_accept_log_error;
-    /**/
+    if (ngx_strcmp(value[2].data, "bind") == 0) {
+        imls->bind = 1;
+        return NGX_CONF_OK;
+    }
 
-    return NGX_CONF_OK;
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "the invalid \"%V\" parameter", &value[2]);
+    return NGX_CONF_ERROR;
 }
 
 
--- a/src/imap/ngx_imap_handler.c
+++ b/src/imap/ngx_imap_handler.c
@@ -44,35 +44,103 @@ static u_char  imap_invalid_command[] = 
 void
 ngx_imap_init_connection(ngx_connection_t *c)
 {
-    ngx_imap_log_ctx_t   *lctx;
+    in_addr_t             in_addr;
+    socklen_t             len;
+    ngx_uint_t            i;
+    struct sockaddr_in    sin;
+    ngx_imap_log_ctx_t   *ctx;
+    ngx_imap_in_port_t   *imip;
+    ngx_imap_in_addr_t   *imia;
+    ngx_imap_session_t   *s;
 #if (NGX_IMAP_SSL)
-    ngx_imap_conf_ctx_t  *ctx;
     ngx_imap_ssl_conf_t  *sslcf;
 #endif
 
-    ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client %V connected to %V",
-                  c->number, &c->addr_text, &c->listening->addr_text);
+
+    /* find the server configuration for the address:port */
+
+    /* AF_INET only */
+
+    imip = c->listening->servers;
+    imia = imip->addrs;
+
+    i = 0;
+
+    if (imip->naddrs > 1) {
+
+        /*
+         * There are several addresses on this port and one of them
+         * is the "*:port" wildcard so getsockname() is needed to determine
+         * the server address.
+         *
+         * AcceptEx() already gave this address.
+         */
+
+#if (NGX_WIN32)
+        if (c->local_sockaddr) {
+            in_addr =
+                   ((struct sockaddr_in *) c->local_sockaddr)->sin_addr.s_addr;
 
-    lctx = ngx_palloc(c->pool, sizeof(ngx_imap_log_ctx_t));
-    if (lctx == NULL) {
+        } else
+#endif
+        {
+            len = sizeof(struct sockaddr_in);
+            if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) {
+                ngx_connection_error(c, ngx_socket_errno,
+                                     "getsockname() failed");
+                ngx_imap_close_connection(c);
+                return;
+            }
+
+            in_addr = sin.sin_addr.s_addr;
+        }
+
+        /* the last address is "*" */
+
+        for ( /* void */ ; i < imip->naddrs - 1; i++) {
+            if (in_addr == imia[i].addr) {
+                break;
+            }
+        }
+    }
+
+
+    s = ngx_pcalloc(c->pool, sizeof(ngx_imap_session_t));
+    if (s == NULL) {
         ngx_imap_close_connection(c);
         return;
     }
 
-    lctx->client = &c->addr_text;
-    lctx->session = NULL;
+    s->main_conf = imia[i].ctx->main_conf;
+    s->srv_conf = imia[i].ctx->srv_conf;
+
+    s->addr_text = &imia[i].addr_text;
+
+    c->data = s;
+    s->connection = c;
+
+    ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client %V connected to %V",
+                  c->number, &c->addr_text, s->addr_text);
+
+    ctx = ngx_palloc(c->pool, sizeof(ngx_imap_log_ctx_t));
+    if (ctx == NULL) {
+        ngx_imap_close_connection(c);
+        return;
+    }
+
+    ctx->client = &c->addr_text;
+    ctx->session = s;
 
     c->log->connection = c->number;
     c->log->handler = ngx_imap_log_error;
-    c->log->data = lctx;
+    c->log->data = ctx;
     c->log->action = "sending client greeting line";
 
     c->log_error = NGX_ERROR_INFO;
 
 #if (NGX_IMAP_SSL)
 
-    ctx = c->ctx;
-    sslcf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_ssl_module);
+    sslcf = ngx_imap_get_module_srv_conf(s, ngx_imap_ssl_module);
 
     if (sslcf->enable) {
         ngx_imap_ssl_init_connection(&sslcf->ssl, c);
@@ -96,6 +164,7 @@ ngx_imap_starttls_handler(ngx_event_t *r
 
     c = rev->data;
     s = c->data;
+    s->starttls = 1;
 
     c->log->action = "in starttls state";
 
@@ -108,7 +177,7 @@ ngx_imap_starttls_handler(ngx_event_t *r
 static void
 ngx_imap_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)
 {
-    ngx_imap_conf_ctx_t       *ctx;
+    ngx_imap_session_t        *s;
     ngx_imap_core_srv_conf_t  *cscf;
 
     if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) {
@@ -118,8 +187,9 @@ ngx_imap_ssl_init_connection(ngx_ssl_t *
 
     if (ngx_ssl_handshake(c) == NGX_AGAIN) {
 
-        ctx = c->ctx;
-        cscf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_core_module);
+        s = c->data;
+
+        cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
 
         ngx_add_timer(c->read, cscf->timeout);
 
@@ -135,9 +205,13 @@ ngx_imap_ssl_init_connection(ngx_ssl_t *
 static void
 ngx_imap_ssl_handshake_handler(ngx_connection_t *c)
 {
+    ngx_imap_session_t  *s;
+
     if (c->ssl->handshaked) {
 
-        if (c->data) {
+        s = c->data;
+
+        if (s->starttls) {
             c->read->handler = ngx_imap_init_protocol;
             c->write->handler = ngx_imap_send;
 
@@ -160,24 +234,14 @@ static void
 ngx_imap_init_session(ngx_connection_t *c)
 {
     ngx_imap_session_t        *s;
-    ngx_imap_log_ctx_t        *lctx;
-    ngx_imap_conf_ctx_t       *ctx;
     ngx_imap_core_srv_conf_t  *cscf;
 
     c->read->handler = ngx_imap_init_protocol;
     c->write->handler = ngx_imap_send;
 
-    s = ngx_pcalloc(c->pool, sizeof(ngx_imap_session_t));
-    if (s == NULL) {
-        ngx_imap_close_connection(c);
-        return;
-    }
+    s = c->data;
 
-    ctx = c->ctx;
-    cscf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_core_module);
-
-    c->data = s;
-    s->connection = c;
+    cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
 
     s->protocol = cscf->protocol;
 
@@ -187,14 +251,8 @@ ngx_imap_init_session(ngx_connection_t *
         return;
     }
 
-    s->main_conf = ctx->main_conf;
-    s->srv_conf = ctx->srv_conf;
-
     s->out = greetings[s->protocol];
 
-    lctx = c->log->data;
-    lctx->session = s;
-
     ngx_add_timer(c->read, cscf->timeout);
 
     if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
@@ -896,8 +954,7 @@ ngx_imap_log_error(ngx_log_t *log, u_cha
         return p;
     }
 
-    p = ngx_snprintf(buf, len, ", server: %V",
-                     &s->connection->listening->addr_text);
+    p = ngx_snprintf(buf, len, ", server: %V", s->addr_text);
     len -= p - buf;
     buf = p;