nginx-0.3.3-RELEASE import

    *) Change: the "bl" and "af" parameters of the "listen" directive was
       renamed to the "backlog" and "accept_filter".

    *) Feature: the "rcvbuf" and "sndbuf" parameters of the "listen"
       directive.

    *) Change: the "$msec" log parameter does not require now the
       additional the gettimeofday() system call.

    *) Feature: the -t switch now tests the "listen" directives.

    *) Bugfix: if the invalid address was specified in the "listen"
       directive, then after the -HUP signal nginx left an open socket in
       the CLOSED state.

    *) Bugfix: the mime type may be incorrectly set to default value for
       index file with variable in the name; the bug had appeared in 0.3.0.

    *) Feature: the "timer_resolution" directive.

    *) Feature: the millisecond "$upstream_response_time" log parameter.

    *) Bugfix: a temporary file with client request body now is removed
       just after the response header was transferred to a client.

    *) Bugfix: OpenSSL 0.9.6 compatibility.

    *) Bugfix: the SSL certificate and key file paths could not be relative.

    *) Bugfix: the "ssl_prefer_server_ciphers" directive did not work in
       the ngx_imap_ssl_module.

    *) Bugfix: the "ssl_protocols" directive allowed to specify the single
       protocol only.
diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c
index 70f7974..3623e2a 100644
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -144,6 +144,7 @@
     ngx_dir_t                       dir;
     ngx_uint_t                      i, level;
     ngx_pool_t                     *pool;
+    ngx_time_t                     *tp;
     ngx_chain_t                     out;
     ngx_array_t                     entries;
     ngx_http_autoindex_entry_t     *entry;
@@ -372,6 +373,8 @@
     b->last = ngx_cpymem(b->last, "<hr><pre><a href=\"../\">../</a>" CRLF,
                          sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1);
 
+    tp = ngx_timeofday();
+
     for (i = 0; i < entries.nelts; i++) {
         b->last = ngx_cpymem(b->last, "<a href=\"", sizeof("<a href=\"") - 1);
 
@@ -428,7 +431,7 @@
 
         *b->last++ = ' ';
 
-        ngx_gmtime(entry[i].mtime + ngx_gmtoff * 60 * alcf->localtime, &tm);
+        ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm);
 
         b->last = ngx_sprintf(b->last, "%02d-%s-%d %02d:%02d ",
                               tm.ngx_tm_mday,
diff --git a/src/http/modules/ngx_http_geo_module.c b/src/http/modules/ngx_http_geo_module.c
index eedc1f7..6d94a06 100644
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -123,6 +123,7 @@
     }
 
     tree = ngx_radix_tree_create(cf->pool, -1);
+
     if (tree == NULL) {
         return NGX_CONF_ERROR;
     }
diff --git a/src/http/modules/ngx_http_index_module.c b/src/http/modules/ngx_http_index_module.c
index 01b6d0c..b7daa6d 100644
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -126,7 +126,8 @@
     ngx_uint_t                    i;
     ngx_http_index_t             *index;
     ngx_http_index_ctx_t         *ctx;
-    ngx_pool_cleanup_file_t      *cln;
+    ngx_pool_cleanup_t           *cln;
+    ngx_pool_cleanup_file_t      *clnf;
     ngx_http_script_code_pt       code;
     ngx_http_script_engine_t      e;
     ngx_http_core_loc_conf_t     *clcf;
@@ -180,9 +181,9 @@
             e.ip = index[i].lengths->elts;
             e.request = r;
 
-            /* 1 byte for terminating '\0' and 4 bytes is preallocation */
+            /* 1 byte for terminating '\0' */
 
-            len = 1 + 4;
+            len = 1;
 
             while (*(uintptr_t *) e.ip) {
                 lcode = *(ngx_http_script_len_code_pt *) e.ip;
@@ -190,6 +191,10 @@
             }
 
             ctx->index.len = len;
+
+            /* 16 bytes are preallocation */
+
+            len += 16;
         }
 
         if (len > ctx->path.len) {
@@ -228,6 +233,11 @@
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                        "open index \"%s\"", ctx->path.data);
 
+        cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
+        if (cln == NULL) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
         fd = ngx_open_file(ctx->path.data, NGX_FILE_RDONLY, NGX_FILE_OPEN);
 
         if (fd == (ngx_fd_t) NGX_AGAIN) {
@@ -268,19 +278,12 @@
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
+        cln->handler = ngx_pool_cleanup_file;
+        clnf = cln->data;
 
-        cln = ngx_palloc(r->pool, sizeof(ngx_pool_cleanup_file_t));
-        if (cln == NULL) {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR; 
-        }
-
-        cln->fd = fd;
-        cln->name = ctx->path.data;
-        cln->log = r->pool->log;
-
-        if (ngx_pool_cleanup_add(r->pool, ngx_pool_cleanup_file, cln) == NULL) {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
-        }
+        clnf->fd = fd;
+        clnf->name = ctx->path.data;
+        clnf->log = r->pool->log;
 
         clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c
index 5f9ee3a..af82b0e 100644
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -1968,6 +1968,7 @@
 {
     ngx_http_ssi_ctx_t         *ctx;
     ngx_http_variable_value_t  *vv;
+    ngx_time_t                 *tp;
     struct tm                   tm;
     char                        buf[NGX_HTTP_SSI_DATE_LEN];
 
@@ -1976,12 +1977,14 @@
         return NULL;
     }
 
+    tp = ngx_timeofday();
+
     ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
 
     if (ctx->timefmt.len == sizeof("%s") - 1
         && ctx->timefmt.data[0] == '%' && ctx->timefmt.data[1] == 's')
     {
-        vv->value = ngx_time() + (gmt ? 0 : ngx_gmtoff);
+        vv->value = tp->sec + (gmt ? 0 : tp->gmtoff);
 
         vv->text.data = ngx_palloc(r->pool, NGX_TIME_T_LEN);
         if (vv->text.data == NULL) {
@@ -1994,12 +1997,12 @@
     }
 
     if (gmt) {
-        ngx_libc_gmtime(&tm);
+        ngx_libc_gmtime(tp->sec, &tm);
     } else {
-        ngx_libc_localtime(&tm);
+        ngx_libc_localtime(tp->sec, &tm);
     }
 
-    vv->value = ngx_time() + (gmt ? 0 : ngx_gmtoff);
+    vv->value = tp->sec + (gmt ? 0 : tp->gmtoff);
 
     vv->text.len = strftime(buf, NGX_HTTP_SSI_DATE_LEN,
                             (char *) ctx->timefmt.data, &tm);
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index bb9a55f..09bf63d 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -17,6 +17,15 @@
 static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
     void *parent, void *child);
 
+#if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE)
+
+static char *ngx_http_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
+static char  ngx_http_ssl_openssl097[] = "OpenSSL 0.9.7 and higher";
+
+#endif
+
 
 static ngx_conf_bitmask_t  ngx_http_ssl_protocols[] = {
     { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
@@ -26,6 +35,7 @@
 };
 
 
+
 static ngx_command_t  ngx_http_ssl_commands[] = {
 
     { ngx_string("ssl"),
@@ -50,14 +60,14 @@
       NULL },
 
     { ngx_string("ssl_protocols"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
       ngx_conf_set_bitmask_slot,
       NGX_HTTP_SRV_CONF_OFFSET,
       offsetof(ngx_http_ssl_srv_conf_t, protocols),
       &ngx_http_ssl_protocols },
 
     { ngx_string("ssl_ciphers"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_str_slot,
       NGX_HTTP_SRV_CONF_OFFSET,
       offsetof(ngx_http_ssl_srv_conf_t, ciphers),
@@ -65,10 +75,14 @@
 
     { ngx_string("ssl_prefer_server_ciphers"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
       ngx_conf_set_flag_slot,
       NGX_HTTP_SRV_CONF_OFFSET,
       offsetof(ngx_http_ssl_srv_conf_t, prefer_server_ciphers),
       NULL },
+#else
+      ngx_http_ssl_nosupported, 0, 0, ngx_http_ssl_openssl097 },
+#endif
 
       ngx_null_command
 };
@@ -144,6 +158,8 @@
     ngx_http_ssl_srv_conf_t *prev = parent;
     ngx_http_ssl_srv_conf_t *conf = child;
 
+    ngx_pool_cleanup_t  *cln;
+
     ngx_conf_merge_value(conf->enable, prev->enable, 0);
 
     if (conf->enable == 0) {
@@ -172,29 +188,37 @@
         return NGX_CONF_ERROR;
     }
 
-    if (ngx_pool_cleanup_add(cf->pool, ngx_ssl_cleanup_ctx, &conf->ssl) == NULL)
-    {
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    if (ngx_ssl_certificate(&conf->ssl, conf->certificate.data,
-                            conf->certificate_key.data) != NGX_OK)
+    cln->handler = ngx_ssl_cleanup_ctx;
+    cln->data = &conf->ssl;
+
+    if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
+                            &conf->certificate_key) != NGX_OK)
     {
         return NGX_CONF_ERROR;
     }
 
     if (SSL_CTX_set_cipher_list(conf->ssl.ctx,
-                                (const char *) conf->ciphers.data) == 0)
+                                (const char *) conf->ciphers.data)
+        == 0)
     {
         ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
                       "SSL_CTX_set_cipher_list(\"%V\") failed",
                       &conf->ciphers);
     }
 
+#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
+
     if (conf->prefer_server_ciphers) {
         SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
     }
 
+#endif
+
     /* a temporary 512-bit RSA key is required for export versions of MSIE */
     if (ngx_ssl_generate_rsa512_key(&conf->ssl) != NGX_OK) {
         return NGX_CONF_ERROR;
@@ -207,3 +231,18 @@
 
     return NGX_CONF_OK;
 }
+
+
+#if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE)
+
+static char *
+ngx_http_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "\"%V\" directive is available only in %s,",
+                       &cmd->name, cmd->post);
+
+    return NGX_CONF_ERROR;
+}
+
+#endif
diff --git a/src/http/modules/ngx_http_static_module.c b/src/http/modules/ngx_http_static_module.c
index c5d5af8..0e995f5 100644
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -82,7 +82,8 @@
     ngx_buf_t                 *b;
     ngx_chain_t                out;
     ngx_file_info_t            fi;
-    ngx_pool_cleanup_file_t   *cln;
+    ngx_pool_cleanup_t        *cln;
+    ngx_pool_cleanup_file_t   *clnf;
     ngx_http_core_loc_conf_t  *clcf;
 
     if (r->uri.data[r->uri.len - 1] == '/') {
@@ -119,6 +120,11 @@
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                    "http filename: \"%s\"", path.data);
 
+    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
+    if (cln == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
     fd = ngx_open_file(path.data, NGX_FILE_RDONLY, NGX_FILE_OPEN);
 
     if (fd == NGX_INVALID_FILE) {
@@ -223,18 +229,12 @@
 
     log->action = "sending response to client";
 
-    cln = ngx_palloc(r->pool, sizeof(ngx_pool_cleanup_file_t));
-    if (cln == NULL) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
+    cln->handler = ngx_pool_cleanup_file;
+    clnf = cln->data;
 
-    cln->fd = fd;
-    cln->name = path.data;
-    cln->log = r->pool->log;
-
-    if (ngx_pool_cleanup_add(r->pool, ngx_pool_cleanup_file, cln) == NULL) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
+    clnf->fd = fd;
+    clnf->name = path.data;
+    clnf->log = r->pool->log;
 
     r->headers_out.status = NGX_HTTP_OK;
     r->headers_out.content_length_n = ngx_file_size(&fi);
diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
index dcb3956..867bf31 100644
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -659,6 +659,8 @@
 #endif
 
             ls->backlog = in_addr[a].conf.backlog;
+            ls->rcvbuf = in_addr[a].conf.rcvbuf;
+            ls->sndbuf = in_addr[a].conf.sndbuf;
 
 #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
             ls->accept_filter = in_addr[a].conf.accept_filter;
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 4c6cfc0..21224d3 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -152,11 +152,7 @@
       NULL },
 
     { ngx_string("listen"),
-#if 0
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
-#else
       NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
-#endif
       ngx_http_core_listen,
       NGX_HTTP_SRV_CONF_OFFSET,
       0,
@@ -2010,7 +2006,7 @@
     ngx_http_core_srv_conf_t *scf = conf;
 
     char                 *err;
-    ngx_str_t            *value;
+    ngx_str_t            *value, size;
     ngx_uint_t            n;
     struct hostent       *h;
     ngx_http_listen_t    *ls;
@@ -2050,6 +2046,8 @@
     ls->file_name = cf->conf_file->file.name;
     ls->line = cf->conf_file->line;
     ls->conf.backlog = -1;
+    ls->conf.rcvbuf = -1;
+    ls->conf.sndbuf = -1;
 
     if (inet_upstream.host.len) {
         inet_upstream.host.data[inet_upstream.host.len] = '\0';
@@ -2100,8 +2098,8 @@
             continue;
         }
 
-        if (ngx_strncmp(value[n].data, "bl=", 3) == 0) {
-            ls->conf.backlog = ngx_atoi(value[n].data + 3, value[n].len - 3);
+        if (ngx_strncmp(value[n].data, "backlog=", 8) == 0) {
+            ls->conf.backlog = ngx_atoi(value[n].data + 8, value[n].len - 8);
             ls->conf.bind = 1;
 
             if (ls->conf.backlog == NGX_ERROR || ls->conf.backlog == 0) {
@@ -2113,9 +2111,41 @@
             continue;
         }
 
-        if (ngx_strncmp(value[n].data, "af=", 3) == 0) {
+        if (ngx_strncmp(value[n].data, "rcvbuf=", 7) == 0) {
+            size.len = value[n].len - 7;
+            size.data = value[n].data + 7;
+
+            ls->conf.rcvbuf = ngx_parse_size(&size);
+            ls->conf.bind = 1;
+
+            if (ls->conf.rcvbuf == NGX_ERROR) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid rcvbuf \"%V\"", &value[n]);
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[n].data, "sndbuf=", 7) == 0) {
+            size.len = value[n].len - 7;
+            size.data = value[n].data + 7;
+
+            ls->conf.sndbuf = ngx_parse_size(&size);
+            ls->conf.bind = 1;
+
+            if (ls->conf.sndbuf == NGX_ERROR) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid sndbuf \"%V\"", &value[n]);
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[n].data, "accept_filter=", 14) == 0) {
 #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
-            ls->conf.accept_filter = (char *) &value[n].data[3];
+            ls->conf.accept_filter = (char *) &value[n].data[14];
             ls->conf.bind = 1;
 #else
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
index 4de8007..cf415ef 100644
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -18,6 +18,8 @@
     unsigned                   bind:1;
 
     int                        backlog;
+    int                        rcvbuf;
+    int                        sndbuf;
 
 #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
     char                      *accept_filter;
diff --git a/src/http/ngx_http_log_module.c b/src/http/ngx_http_log_module.c
index 3caa2e9..617d46a 100644
--- a/src/http/ngx_http_log_module.c
+++ b/src/http/ngx_http_log_module.c
@@ -309,11 +309,11 @@
 static u_char *
 ngx_http_log_msec(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
 {
-    struct timeval  tv;
+    ngx_time_t  *tp;
 
-    ngx_gettimeofday(&tv);
+    tp = ngx_timeofday();
 
-    return ngx_sprintf(buf, "%l.%03l", tv.tv_sec, tv.tv_usec / 1000);
+    return ngx_sprintf(buf, "%T.%03M", tp->sec, tp->msec);
 }
 
 
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 2fb1764..9071ae5 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -163,15 +163,8 @@
         /* the deferred accept(), rtsig, aio, iocp */
 
         if (ngx_accept_mutex) {
-            if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) {
 
-                ngx_http_close_connection(c);
-                return;
-            }
-
-            ngx_post_event(rev); 
-
-            ngx_mutex_unlock(ngx_posted_events_mutex);
+            ngx_post_event(rev, &ngx_posted_events);
 
 #if (NGX_STAT_STUB)
             ngx_atomic_fetch_add(ngx_stat_reading, 1);
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index a1c6a2e..6d1bd49 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -66,6 +66,8 @@
     uintptr_t data);
 static u_char *ngx_http_upstream_log_status(ngx_http_request_t *r,
     u_char *buf, ngx_http_log_op_t *op);
+static u_char *ngx_http_upstream_log_response_time(ngx_http_request_t *r,
+    u_char *buf, ngx_http_log_op_t *op);
 
 static u_char *ngx_http_upstream_log_error(ngx_http_request_t *r, u_char *buf,
     size_t len);
@@ -204,6 +206,8 @@
     { ngx_string("upstream_status"), 0, NULL,
                                         ngx_http_upstream_log_status_getlen,
                                         ngx_http_upstream_log_status },
+    { ngx_string("upstream_response_time"), NGX_TIME_T_LEN + 4, NULL, NULL,
+                                        ngx_http_upstream_log_response_time },
     { ngx_null_string, 0, NULL, NULL, NULL }
 };
 
@@ -217,6 +221,7 @@
 void
 ngx_http_upstream_init(ngx_http_request_t *r)
 {
+    ngx_time_t                *tp;
     ngx_connection_t          *c;
     ngx_http_upstream_t       *u;
     ngx_http_core_loc_conf_t  *clcf;
@@ -286,6 +291,10 @@
 
     ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));
 
+    tp = ngx_timeofday();
+
+    u->state->response_time = tp->sec * 1000 + tp->msec;
+
     ngx_http_upstream_connect(r, u);
 }
 
@@ -978,6 +987,8 @@
     ngx_list_part_t                *part;
     ngx_table_elt_t                *h;
     ngx_event_pipe_t               *p;
+    ngx_pool_cleanup_t             *cl;
+    ngx_pool_cleanup_file_t        *clf;
     ngx_http_core_loc_conf_t       *clcf;
     ngx_http_upstream_header_t     *hh;
     ngx_http_upstream_main_conf_t  *umcf;
@@ -1033,6 +1044,20 @@
 
     u->header_sent = 1;
 
+    if (r->request_body->temp_file) {
+        for (cl = r->pool->cleanup; cl; cl = cl->next) {
+            if (cl->handler == ngx_pool_cleanup_file) {
+                clf = cl->data;
+
+                if (clf->fd == r->request_body->temp_file->file.fd) {
+                    cl->handler(clf);
+                    cl->handler = NULL;
+                    break;
+                }
+            }
+        }
+    }
+
     /* TODO: preallocate event_pipe bufs, look "Content-Length" */
 
 #if 0
@@ -1404,9 +1429,17 @@
 ngx_http_upstream_finalize_request(ngx_http_request_t *r,
     ngx_http_upstream_t *u, ngx_int_t rc)
 {
+    ngx_time_t  *tp;
+
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "finalize http upstream request: %i", rc);
 
+    if (u->state->response_time) {
+        tp = ngx_timeofday();
+        u->state->response_time = tp->sec * 1000 + tp->msec
+                                  - u->state->response_time;
+    }
+
     u->finalize_request(r, rc);
 
     if (u->peer.connection) {
@@ -1419,8 +1452,7 @@
 
     u->peer.connection = NULL;
 
-    if (u->header_sent
-        && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE))
+    if (u->header_sent && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE))
     {
         rc = 0;
     }
@@ -1783,6 +1815,44 @@
 
 
 static u_char *
+ngx_http_upstream_log_response_time(ngx_http_request_t *r, u_char *buf,
+    ngx_http_log_op_t *op)
+{
+    ngx_uint_t                  i;
+    ngx_http_upstream_t        *u;
+    ngx_http_upstream_state_t  *state;
+
+    u = r->upstream;
+
+    if (u == NULL) {
+        *buf = '-';
+        return buf + 1;
+    }
+
+    i = 0;
+    state = u->states.elts;
+
+    for ( ;; ) {
+        if (state[i].status == 0) {
+            *buf++ = '-';
+
+        } else {
+            buf = ngx_sprintf(buf, "%d.%03d",
+                               state[i].response_time / 1000,
+                               state[i].response_time % 1000);
+        }
+
+        if (++i == u->states.nelts) {
+            return buf;
+        }
+
+        *buf++ = ',';
+        *buf++ = ' ';
+    }
+}
+
+
+static u_char *
 ngx_http_upstream_log_error(ngx_http_request_t *r, u_char *buf, size_t len)
 {
     u_char                 *p;
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
index dd7b3c1..637a38d 100644
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -29,12 +29,12 @@
 
 
 typedef struct {
-    time_t                          bl_time;
+    ngx_msec_t                      bl_time;
     ngx_uint_t                      bl_state;
 
     ngx_uint_t                      status;
-    time_t                          time;
-    
+    ngx_msec_t                      response_time;
+
     ngx_str_t                      *peer;
 } ngx_http_upstream_state_t;