nginx-0.3.42-RELEASE import

    *) 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.
diff --git a/src/http/modules/ngx_http_rewrite_module.c b/src/http/modules/ngx_http_rewrite_module.c
index 37266a4..c557314 100644
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -395,7 +395,7 @@
     regex->size = sc.size;
     regex->args = sc.args;
 
-    if (sc.variables == 0) {
+    if (sc.variables == 0 && !sc.dup_capture) {
         regex->lengths = NULL;
     }
 
diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c
index 15cb0c3..dba3639 100644
--- 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_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) {
diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
index 5c1e0a0..a15b3e3 100644
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -725,8 +725,6 @@
             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;
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 4a9c208..487fd26 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -1328,6 +1328,7 @@
     ngx_http_update_location_config(r);
 
     r->internal = 1;
+    r->method = NGX_HTTP_GET;
 
     ngx_http_handler(r);
 
@@ -2528,6 +2529,7 @@
 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 }
 };
 
diff --git a/src/http/ngx_http_postpone_filter_module.c b/src/http/ngx_http_postpone_filter_module.c
index 20a78a0..76deaa4 100644
--- a/src/http/ngx_http_postpone_filter_module.c
+++ b/src/http/ngx_http_postpone_filter_module.c
@@ -89,6 +89,22 @@
             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;
         }
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 615ae75..d801e2c 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -201,11 +201,11 @@
 }
 
 
-static
-void ngx_http_init_request(ngx_event_t *rev)
+static void
+ngx_http_init_request(ngx_event_t *rev)
 {
-    ngx_uint_t                  i;
     socklen_t                   len;
+    ngx_uint_t                  i;
     struct sockaddr_in          sin;
     ngx_connection_t           *c;
     ngx_http_request_t         *r;
@@ -274,7 +274,7 @@
 
     /* AF_INET only */
 
-    hip = c->servers;
+    hip = c->listening->servers;
     hia = hip->addrs;
 
     r->port = hip->port;
@@ -311,7 +311,7 @@
             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 @@
         || 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;
         }
diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c
index 5a52456..9826ece 100644
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -91,6 +91,14 @@
 
             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 @@
 
                 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 @@
                 }
 
                 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;
diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h
index 3ef4d42..c4aa408 100644
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -47,6 +47,7 @@
 
     ngx_uint_t                  variables;
     ngx_uint_t                  ncaptures;
+    ngx_uint_t                  captures_mask;
     ngx_uint_t                  size;
 
     void                       *main;
@@ -54,6 +55,7 @@
     unsigned                    compile_args:1;
     unsigned                    complete_lengths:1;
     unsigned                    complete_values:1;
+    unsigned                    dup_capture:1;
 
     unsigned                    args:1;
 } ngx_http_script_compile_t;
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 997038c..45e586f 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -170,6 +170,10 @@
                  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 @@
         return;
     }
 
+    r->connection->log->action = "SSL handshaking to upstream";
+
     rc = ngx_ssl_handshake(c);
 
     if (rc == NGX_AGAIN) {
@@ -852,7 +858,6 @@
                    "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;
     }
diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
index 69bfe3a..0f162a7 100644
--- 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_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 @@
  *     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 @@
       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 @@
 
 
 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)
 {