changeset 435:e7dbea1ee115 NGINX_0_7_25

nginx 0.7.25 *) Change: in subrequest processing. *) Change: now POSTs without "Content-Length" header line are allowed. *) Bugfix: now the "limit_req" and "limit_conn" directives log a prohibition reason. *) Bugfix: in the "delete" parameter of the "geo" directive.
author Igor Sysoev <http://sysoev.ru>
date Mon, 08 Dec 2008 00:00:00 +0300
parents f64d9e30046c
children 135cffea8f75
files CHANGES CHANGES.ru src/core/nginx.h src/core/ngx_radix_tree.c src/http/modules/ngx_http_addition_filter_module.c src/http/modules/ngx_http_autoindex_module.c src/http/modules/ngx_http_geo_module.c src/http/modules/ngx_http_limit_req_module.c src/http/modules/ngx_http_limit_zone_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/modules/ngx_http_static_module.c src/http/modules/perl/nginx.pm src/http/ngx_http.h src/http/ngx_http_copy_filter_module.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_request.h src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h
diffstat 20 files changed, 484 insertions(+), 426 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,16 @@
 
+Changes with nginx 0.7.25                                        08 Dec 2008
+
+    *) Change: in subrequest processing.
+
+    *) Change: now POSTs without "Content-Length" header line are allowed.
+
+    *) Bugfix: now the "limit_req" and "limit_conn" directives log a 
+       prohibition reason.
+
+    *) Bugfix: in the "delete" parameter of the "geo" directive.
+
+
 Changes with nginx 0.7.24                                        01 Dec 2008
 
     *) Feature: the "if_modified_since" directive.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,17 @@
 
+Изменения в nginx 0.7.25                                          08.12.2008
+
+    *) Изменение: в обработке подзапросов.
+
+    *) Изменение: теперь разрешаются POST'ы без строки "Content-Length" в 
+       заголовке запроса.
+
+    *) Исправление: теперь директивы limit_req и limit_conn указывают 
+       причину запрета запроса.
+
+    *) Исправление: в параметре delete директивы geo.
+
+
 Изменения в nginx 0.7.24                                          01.12.2008
 
     *) Добавление: директива if_modified_since.
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.7.24"
+#define NGINX_VERSION      "0.7.25"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_radix_tree.c
+++ b/src/core/ngx_radix_tree.c
@@ -42,13 +42,13 @@ ngx_radix_tree_create(ngx_pool_t *pool, 
     }
 
     /*
-     * The preallocation the first nodes: 0, 1, 00, 01, 10, 11, 000, 001, etc.
-     * increases the TLB hits even if for the first lookup iterations.
-     * On the 32-bit platforms the 7 preallocated bits takes continuous 4K,
-     * 8 - 8K, 9 - 16K, etc.  On the 64-bit platforms the 6 preallocated bits
+     * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc.
+     * increases TLB hits even if for first lookup iterations.
+     * On 32-bit platforms the 7 preallocated bits takes continuous 4K,
+     * 8 - 8K, 9 - 16K, etc.  On 64-bit platforms the 6 preallocated bits
      * takes continuous 4K, 7 - 8K, 8 - 16K, etc.  There is no sense to
      * to preallocate more than one page, because further preallocation
-     * distributes the only bit per page.  Instead, the random insertion
+     * distributes the only bit per page.  Instead, a random insertion
      * may distribute several bits per page.
      *
      * Thus, by default we preallocate maximum
--- a/src/http/modules/ngx_http_addition_filter_module.c
+++ b/src/http/modules/ngx_http_addition_filter_module.c
@@ -151,10 +151,10 @@ ngx_http_addition_body_filter(ngx_http_r
         ctx->before_body_sent = 1;
 
         if (conf->before_body.len) {
-            rc = ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0);
-
-            if (rc == NGX_ERROR || rc == NGX_DONE) {
-                return rc;
+            if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0)
+                != NGX_OK)
+            {
+                return NGX_ERROR;
             }
         }
     }
@@ -180,10 +180,10 @@ ngx_http_addition_body_filter(ngx_http_r
         return rc;
     }
 
-    rc = ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0);
-
-    if (rc == NGX_ERROR || rc == NGX_DONE) {
-        return rc;
+    if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
     }
 
     ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -299,6 +299,11 @@ ngx_http_autoindex_handler(ngx_http_requ
                 if (err != NGX_ENOENT) {
                     ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                                   ngx_de_info_n " \"%s\" failed", filename);
+
+                    if (err == NGX_EACCES) {
+                        continue;
+                    }
+
                     return ngx_http_autoindex_error(r, &dir, &path);
                 }
 
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -666,8 +666,9 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht
             {
                 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                                    "no network \"%V\" to delete", net);
-                return NGX_CONF_OK;
             }
+
+            return NGX_CONF_OK;
         }
     }
 
--- a/src/http/modules/ngx_http_limit_req_module.c
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -38,8 +38,8 @@ typedef struct {
 
 
 static void ngx_http_limit_req_delay(ngx_http_request_t *r);
-static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lzcf,
-    ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lzp);
+static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf,
+    ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lrp);
 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
     ngx_uint_t n);
 
@@ -115,20 +115,20 @@ ngx_http_limit_req_handler(ngx_http_requ
     ngx_rbtree_node_t          *node;
     ngx_http_variable_value_t  *vv;
     ngx_http_limit_req_ctx_t   *ctx;
-    ngx_http_limit_req_node_t  *lz;
-    ngx_http_limit_req_conf_t  *lzcf;
+    ngx_http_limit_req_node_t  *lr;
+    ngx_http_limit_req_conf_t  *lrcf;
 
     if (r->main->limit_req_set) {
         return NGX_DECLINED;
     }
 
-    lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
+    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
 
-    if (lzcf->shm_zone == NULL) {
+    if (lrcf->shm_zone == NULL) {
         return NGX_DECLINED;
     }
 
-    ctx = lzcf->shm_zone->data;
+    ctx = lrcf->shm_zone->data;
 
     vv = ngx_http_get_indexed_variable(r, ctx->index);
 
@@ -158,14 +158,14 @@ ngx_http_limit_req_handler(ngx_http_requ
 
     ngx_http_limit_req_expire(ctx, 1);
 
-    rc = ngx_http_limit_req_lookup(lzcf, hash, vv->data, len, &lz);
+    rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &lr);
 
-    if (lz) {
-        ngx_queue_remove(&lz->queue);
+    if (lr) {
+        ngx_queue_remove(&lr->queue);
 
-        ngx_queue_insert_head(ctx->queue, &lz->queue);
+        ngx_queue_insert_head(ctx->queue, &lr->queue);
 
-        excess = lz->excess;
+        excess = lr->excess;
 
     } else {
         excess = 0;
@@ -178,8 +178,8 @@ ngx_http_limit_req_handler(ngx_http_requ
         ngx_shmtx_unlock(&ctx->shpool->mutex);
 
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "limiting requests, excess: %ui.%03ui",
-                      excess / 1000, excess % 1000);
+                      "limiting requests, excess: %ui.%03ui by zone \"%V\"",
+                      excess / 1000, excess % 1000, &lrcf->shm_zone->name);
 
         return NGX_HTTP_SERVICE_UNAVAILABLE;
     }
@@ -187,13 +187,13 @@ ngx_http_limit_req_handler(ngx_http_requ
     if (rc == NGX_AGAIN) {
         ngx_shmtx_unlock(&ctx->shpool->mutex);
 
-        if (lzcf->nodelay) {
+        if (lrcf->nodelay) {
             return NGX_DECLINED;
         }
 
         ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
-                      "delaying request, excess: %ui.%03ui",
-                      excess / 1000, excess % 1000);
+                      "delaying request, excess: %ui.%03ui, by zone \"%V\"",
+                      excess / 1000, excess % 1000, &lrcf->shm_zone->name);
 
         if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -224,24 +224,29 @@ ngx_http_limit_req_handler(ngx_http_requ
         node = ngx_slab_alloc_locked(ctx->shpool, n);
         if (node == NULL) {
             ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+                          "could not allocate memory in zone \"%V\"",
+                          &lrcf->shm_zone->name);
+
             return NGX_HTTP_SERVICE_UNAVAILABLE;
         }
     }
 
-    lz = (ngx_http_limit_req_node_t *) &node->color;
+    lr = (ngx_http_limit_req_node_t *) &node->color;
 
     node->key = hash;
-    lz->len = (u_char) len;
+    lr->len = (u_char) len;
 
     tp = ngx_timeofday();
-    lz->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+    lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
 
-    lz->excess = 0;
-    ngx_memcpy(lz->data, vv->data, len);
+    lr->excess = 0;
+    ngx_memcpy(lr->data, vv->data, len);
 
     ngx_rbtree_insert(ctx->rbtree, node);
 
-    ngx_queue_insert_head(ctx->queue, &lz->queue);
+    ngx_queue_insert_head(ctx->queue, &lr->queue);
 
 done:
 
@@ -274,7 +279,7 @@ ngx_http_limit_req_rbtree_insert_value(n
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
 {
     ngx_rbtree_node_t          **p;
-    ngx_http_limit_req_node_t   *lzn, *lznt;
+    ngx_http_limit_req_node_t   *lrn, *lrnt;
 
     for ( ;; ) {
 
@@ -288,10 +293,10 @@ ngx_http_limit_req_rbtree_insert_value(n
 
         } else { /* node->key == temp->key */
 
-            lzn = (ngx_http_limit_req_node_t *) &node->color;
-            lznt = (ngx_http_limit_req_node_t *) &temp->color;
+            lrn = (ngx_http_limit_req_node_t *) &node->color;
+            lrnt = (ngx_http_limit_req_node_t *) &temp->color;
 
-            p = (ngx_memn2cmp(lzn->data, lznt->data, lzn->len, lznt->len) < 0)
+            p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)
                 ? &temp->left : &temp->right;
         }
 
@@ -311,8 +316,8 @@ ngx_http_limit_req_rbtree_insert_value(n
 
 
 static ngx_int_t
-ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lzcf, ngx_uint_t hash,
-    u_char *data, size_t len, ngx_http_limit_req_node_t **lzp)
+ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash,
+    u_char *data, size_t len, ngx_http_limit_req_node_t **lrp)
 {
     ngx_int_t                   rc, excess;
     ngx_time_t                 *tp;
@@ -320,9 +325,9 @@ ngx_http_limit_req_lookup(ngx_http_limit
     ngx_msec_int_t              ms;
     ngx_rbtree_node_t          *node, *sentinel;
     ngx_http_limit_req_ctx_t   *ctx;
-    ngx_http_limit_req_node_t  *lz;
+    ngx_http_limit_req_node_t  *lr;
 
-    ctx = lzcf->shm_zone->data;
+    ctx = lrcf->shm_zone->data;
 
     node = ctx->rbtree->root;
     sentinel = ctx->rbtree->sentinel;
@@ -342,29 +347,29 @@ ngx_http_limit_req_lookup(ngx_http_limit
         /* hash == node->key */
 
         do {
-            lz = (ngx_http_limit_req_node_t *) &node->color;
+            lr = (ngx_http_limit_req_node_t *) &node->color;
 
-            rc = ngx_memn2cmp(data, lz->data, len, (size_t) lz->len);
+            rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len);
 
             if (rc == 0) {
 
                 tp = ngx_timeofday();
 
                 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
-                ms = (ngx_msec_int_t) (now - lz->last);
+                ms = (ngx_msec_int_t) (now - lr->last);
 
-                excess = lz->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
+                excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
 
                 if (excess < 0) {
                     excess = 0;
                 }
 
-                lz->excess = excess;
-                lz->last = now;
+                lr->excess = excess;
+                lr->last = now;
 
-                *lzp = lz;
+                *lrp = lr;
 
-                if ((ngx_uint_t) excess > lzcf->burst) {
+                if ((ngx_uint_t) excess > lrcf->burst) {
                     return NGX_BUSY;
                 }
 
@@ -382,7 +387,7 @@ ngx_http_limit_req_lookup(ngx_http_limit
         break;
     }
 
-    *lzp = NULL;
+    *lrp = NULL;
 
     return NGX_DECLINED;
 }
@@ -397,7 +402,7 @@ ngx_http_limit_req_expire(ngx_http_limit
     ngx_queue_t                *q;
     ngx_msec_int_t              ms;
     ngx_rbtree_node_t          *node;
-    ngx_http_limit_req_node_t  *lz;
+    ngx_http_limit_req_node_t  *lr;
 
     tp = ngx_timeofday();
 
@@ -417,18 +422,18 @@ ngx_http_limit_req_expire(ngx_http_limit
 
         q = ngx_queue_last(ctx->queue);
 
-        lz = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
+        lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
 
         if (n++ != 0) {
 
-            ms = (ngx_msec_int_t) (now - lz->last);
+            ms = (ngx_msec_int_t) (now - lr->last);
             ms = ngx_abs(ms);
 
             if (ms < 60000) {
                 return;
             }
 
-            excess = lz->excess - ctx->rate * ms / 1000;
+            excess = lr->excess - ctx->rate * ms / 1000;
 
             if (excess > 0) {
                 return;
@@ -438,7 +443,7 @@ ngx_http_limit_req_expire(ngx_http_limit
         ngx_queue_remove(q);
 
         node = (ngx_rbtree_node_t *)
-                   ((u_char *) lz - offsetof(ngx_rbtree_node_t, color));
+                   ((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
 
         ngx_rbtree_delete(ctx->rbtree, node);
 
@@ -513,7 +518,7 @@ ngx_http_limit_req_create_conf(ngx_conf_
      * set by ngx_pcalloc():
      *
      *     conf->shm_zone = NULL;
-     *     conf->burst = 0.0;
+     *     conf->burst = 0;
      *     conf->nodelay = 0;
      */
 
@@ -671,13 +676,13 @@ ngx_http_limit_req_zone(ngx_conf_t *cf, 
 static char *
 ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    ngx_http_limit_req_conf_t  *lzcf = conf;
+    ngx_http_limit_req_conf_t  *lrcf = conf;
 
     ngx_int_t    burst;
     ngx_str_t   *value, s;
     ngx_uint_t   i;
 
-    if (lzcf->shm_zone) {
+    if (lrcf->shm_zone) {
         return "is duplicate";
     }
 
@@ -692,9 +697,9 @@ ngx_http_limit_req(ngx_conf_t *cf, ngx_c
             s.len = value[i].len - 5;
             s.data = value[i].data + 5;
 
-            lzcf->shm_zone = ngx_shared_memory_add(cf, &s, 0,
+            lrcf->shm_zone = ngx_shared_memory_add(cf, &s, 0,
                                                    &ngx_http_limit_req_module);
-            if (lzcf->shm_zone == NULL) {
+            if (lrcf->shm_zone == NULL) {
                 return NGX_CONF_ERROR;
             }
 
@@ -714,7 +719,7 @@ ngx_http_limit_req(ngx_conf_t *cf, ngx_c
         }
 
         if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) {
-            lzcf->nodelay = 1;
+            lrcf->nodelay = 1;
             continue;
         }
 
@@ -723,21 +728,21 @@ ngx_http_limit_req(ngx_conf_t *cf, ngx_c
         return NGX_CONF_ERROR;
     }
 
-    if (lzcf->shm_zone == NULL) {
+    if (lrcf->shm_zone == NULL) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "\"%V\" must have \"zone\" parameter",
                            &cmd->name);
         return NGX_CONF_ERROR;
     }
 
-    if (lzcf->shm_zone->data == NULL) {
+    if (lrcf->shm_zone->data == NULL) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "unknown limit_req_zone \"%V\"",
-                           &lzcf->shm_zone->name);
+                           &lrcf->shm_zone->name);
         return NGX_CONF_ERROR;
     }
 
-    lzcf->burst = burst * 1000;
+    lrcf->burst = burst * 1000;
 
     return NGX_CONF_OK;
 }
--- a/src/http/modules/ngx_http_limit_zone_module.c
+++ b/src/http/modules/ngx_http_limit_zone_module.c
@@ -189,6 +189,10 @@ ngx_http_limit_zone_handler(ngx_http_req
 
                 ngx_shmtx_unlock(&shpool->mutex);
 
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "limiting connections by zone \"%V\"",
+                              &lzcf->shm_zone->name);
+
                 return NGX_HTTP_SERVICE_UNAVAILABLE;
             }
 
@@ -206,6 +210,11 @@ ngx_http_limit_zone_handler(ngx_http_req
     node = ngx_slab_alloc_locked(shpool, n);
     if (node == NULL) {
         ngx_shmtx_unlock(&shpool->mutex);
+
+        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+                      "could not allocate memory in zone \"%V\"",
+                      &lzcf->shm_zone->name);
+
         return NGX_HTTP_SERVICE_UNAVAILABLE;
     }
 
@@ -462,6 +471,10 @@ ngx_http_limit_conn(ngx_conf_t *cf, ngx_
     ngx_int_t   n;
     ngx_str_t  *value;
 
+    if (lzcf->shm_zone) {
+        return "is duplicate";
+    }
+
     value = cf->args->elts;
 
     lzcf->shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -375,7 +375,6 @@ ngx_http_ssi_body_filter(ngx_http_reques
     ngx_uint_t                 i, index;
     ngx_chain_t               *cl, **ll;
     ngx_table_elt_t           *param;
-    ngx_http_request_t        *pr;
     ngx_http_ssi_ctx_t        *ctx, *mctx;
     ngx_http_ssi_block_t      *bl;
     ngx_http_ssi_param_t      *prm;
@@ -403,44 +402,37 @@ ngx_http_ssi_body_filter(ngx_http_reques
         }
     }
 
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http ssi filter \"%V?%V\"", &r->uri, &r->args);
+
     if (ctx->wait) {
-        if (r->connection->data != r) {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http ssi filter \"%V\" wait", &r->uri);
+
+        if (r != r->connection->data) {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http ssi filter wait \"%V?%V\" non-active",
+                           &ctx->wait->uri, &ctx->wait->args);
+
             return NGX_AGAIN;
         }
 
-        for (pr = ctx->wait->parent; pr; pr = pr->parent) {
-            if (pr == r) {
-                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                               "http ssi filter \"%V\" flush", &r->uri);
-
-                rc = ngx_http_next_body_filter(r, NULL);
-
-                if (ctx->wait->done) {
-                    ctx->wait = NULL;
-                }
-
-                if (rc == NGX_ERROR || rc == NGX_AGAIN) {
-                    return rc;
-                }
-
-                break;
-            }
-        }
-
-        if (ctx->wait == r) {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http ssi filter \"%V\" continue", &r->uri);
+        if (ctx->wait->done) {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http ssi filter wait \"%V?%V\" done",
+                           &ctx->wait->uri, &ctx->wait->args);
+
             ctx->wait = NULL;
+
+        } else {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http ssi filter wait \"%V?%V\"",
+                           &ctx->wait->uri, &ctx->wait->args);
+
+            return ngx_http_next_body_filter(r, NULL);
         }
     }
 
     slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http ssi filter \"%V\"", &r->uri);
-
     while (ctx->in || ctx->buf) {
 
         if (ctx->buf == NULL ){
@@ -788,16 +780,12 @@ ngx_http_ssi_body_filter(ngx_http_reques
                     }
                 }
 
-                if (cmd->flush) {
-
-                    if (ctx->out) {
-                        rc = ngx_http_ssi_output(r, ctx);
-
-                    } else {
-                        rc = ngx_http_next_body_filter(r, NULL);
-                    }
-
-                    if (rc == NGX_ERROR) {
+                if (cmd->flush && ctx->out) {
+
+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                                   "ssi flush");
+
+                    if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) {
                         return NGX_ERROR;
                     }
                 }
@@ -2001,6 +1989,10 @@ ngx_http_ssi_include(ngx_http_request_t 
         }
     }
 
+    if (wait) {
+        flags |= NGX_HTTP_SUBREQUEST_WAITED;
+    }
+
     if (set) {
         key = ngx_hash_strlow(set->data, set->data, set->len);
 
@@ -2033,16 +2025,10 @@ ngx_http_ssi_include(ngx_http_request_t 
             psr->data = &var->value;
         }
 
-        flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY;
+        flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED;
     }
 
-    rc = ngx_http_subrequest(r, uri, &args, &sr, psr, flags);
-
-    if (rc == NGX_DONE) {
-        return NGX_DONE;
-    }
-
-    if (rc == NGX_ERROR) {
+    if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) {
         return NGX_HTTP_SSI_ERROR;
     }
 
@@ -2050,17 +2036,17 @@ ngx_http_ssi_include(ngx_http_request_t 
         return NGX_OK;
     }
 
-    if (rc == NGX_AGAIN) {
-        if (ctx->wait == NULL) {
-            ctx->wait = sr;
-
-        } else {
-            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "only one subrequest may be waited at the same time");
-        }
+    if (ctx->wait == NULL) {
+        ctx->wait = sr;
+
+        return NGX_AGAIN;
+
+    } else {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "only one subrequest may be waited at the same time");
     }
 
-    return rc;
+    return NGX_OK;
 }
 
 
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -217,7 +217,7 @@ ngx_http_static_handler(ngx_http_request
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    if (r != r->main && of.size == 0) {
+    if (of.size == 0) {
         return ngx_http_send_header(r);
     }
 
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.7.24';
+our $VERSION = '0.7.25';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -80,11 +80,14 @@ ngx_int_t ngx_http_parse_multi_header_li
 ngx_int_t ngx_http_find_server_conf(ngx_http_request_t *r);
 void ngx_http_update_location_config(ngx_http_request_t *r);
 void ngx_http_handler(ngx_http_request_t *r);
+void ngx_http_run_posted_requests(ngx_connection_t *c);
+ngx_int_t ngx_http_post_request(ngx_http_request_t *r);
 void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
 
 void ngx_http_empty_handler(ngx_event_t *wev);
 void ngx_http_request_empty_handler(ngx_http_request_t *r);
 
+
 #define NGX_HTTP_LAST   1
 #define NGX_HTTP_FLUSH  2
 
--- a/src/http/ngx_http_copy_filter_module.c
+++ b/src/http/ngx_http_copy_filter_module.c
@@ -117,10 +117,6 @@ ngx_http_copy_filter(ngx_http_request_t 
             r->buffered |= NGX_HTTP_COPY_BUFFERED;
         }
 
-        if (r != r->main) {
-            r->out = ctx->in;
-        }
-
         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args);
     }
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -1831,7 +1831,6 @@ ngx_http_subrequest(ngx_http_request_t *
 {
     ngx_connection_t              *c;
     ngx_http_request_t            *sr;
-    ngx_http_log_ctx_t            *ctx;
     ngx_http_core_srv_conf_t      *cscf;
     ngx_http_postponed_request_t  *pr, *p;
 
@@ -1896,6 +1895,7 @@ ngx_http_subrequest(ngx_http_request_t *
 
     sr->zero_in_uri = (flags & NGX_HTTP_ZERO_IN_URI) != 0;
     sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;
+    sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0;
 
     sr->unparsed_uri = r->unparsed_uri;
     sr->method_name = ngx_http_core_get_method;
@@ -1909,7 +1909,7 @@ ngx_http_subrequest(ngx_http_request_t *
     sr->parent = r;
     sr->post_subrequest = ps;
     sr->read_event_handler = ngx_http_request_empty_handler;
-    sr->write_event_handler = ngx_http_request_empty_handler;
+    sr->write_event_handler = ngx_http_handler;
 
     if (c->data == r) {
         c->data = sr;
@@ -1940,39 +1940,18 @@ ngx_http_subrequest(ngx_http_request_t *
         r->postponed = pr;
     }
 
-    ctx = c->log->data;
-    ctx->current_request = sr;
-
     sr->internal = 1;
-    sr->fast_subrequest = 1;
 
     sr->discard_body = r->discard_body;
     sr->main_filter_need_in_memory = r->main_filter_need_in_memory;
 
     sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
 
-    ngx_http_handler(sr);
-
-    if (!c->destroyed) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http subrequest done \"%V?%V\"", uri, &sr->args);
-
-        r->main->subrequests++;
-
-        *psr = sr;
-
-        if (sr->fast_subrequest) {
-            sr->fast_subrequest = 0;
-
-            if (sr->done) {
-                return NGX_OK;
-            }
-        }
-
-        return NGX_AGAIN;
-    }
-
-    return NGX_DONE;
+    r->main->subrequests++;
+
+    *psr = sr;
+
+    return ngx_http_post_request(sr);
 }
 
 
--- a/src/http/ngx_http_postpone_filter_module.c
+++ b/src/http/ngx_http_postpone_filter_module.c
@@ -9,8 +9,8 @@
 #include <ngx_http.h>
 
 
-static ngx_int_t
-    ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r,
+    ngx_chain_t *in);
 static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf);
 
 
@@ -51,181 +51,119 @@ static ngx_http_output_body_filter_pt   
 static ngx_int_t
 ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    ngx_int_t                      rc;
-    ngx_chain_t                   *out;
-    ngx_http_postponed_request_t  *pr, **ppr;
+    ngx_connection_t              *c;
+    ngx_http_postponed_request_t  *pr;
 
-    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+    c = r->connection;
+
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in);
 
-    if (r != r->connection->data || (r->postponed && in)) {
-
-        if (r->postponed) {
-            for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }
-
-            ppr = pr->request ? &pr->next : NULL;
-
-        } else {
-            ppr = &r->postponed;
-#if (NGX_SUPPRESS_WARN)
-            pr = NULL;
-#endif
-        }
+    if (r != c->data) {
 
-        if (ppr) {
-            pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
-            if (pr == NULL) {
-                return NGX_ERROR;
-            }
-
-            *ppr = pr;
-
-            pr->request = NULL;
-            pr->out = NULL;
-            pr->next = NULL;
-        }
-
-        if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_ERROR) {
-            return NGX_ERROR;
+        if (in) {
+            ngx_http_postpone_filter_add(r, in);
+            return NGX_OK;
         }
 
-#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 %p %p",
-                               b, b->pos);
-                ngx_debug_point();
-                return NGX_ERROR;
-            }
-            b = cl->buf;
-        }
-        }
+#if 0
+        /* TODO: SSI may pass NULL */
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                      "http postpone filter NULL inactive request",
+                      &r->uri, &r->args);
 #endif
 
-        if (r != r->connection->data || r->postponed->request) {
-            return NGX_AGAIN;
-        }
-    }
-
-    if (r->postponed) {
-        out = r->postponed->out;
-        if (out) {
-            r->postponed = r->postponed->next;
-        }
-
-    } else {
-        out = in;
-    }
-
-    rc = NGX_OK;
-
-    if (out
-        || (r->connection->buffered
-            & (NGX_HTTP_LOWLEVEL_BUFFERED|NGX_LOWLEVEL_BUFFERED)))
-    {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "http postpone filter out \"%V?%V\"", &r->uri, &r->args);
-
-        if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) {
-
-            rc = ngx_http_next_filter(r->main, out);
-
-            if (rc == NGX_ERROR) {
-                /* NGX_ERROR may be returned by any filter */
-                r->connection->error = 1;
-            }
-        }
+        return NGX_OK;
     }
 
     if (r->postponed == NULL) {
-        return rc;
+
+        if (in || c->buffered) {
+            return ngx_http_next_filter(r->main, in);
+        }
+
+        return NGX_OK;
+    }
+
+    if (in) {
+        ngx_http_postpone_filter_add(r, in);
     }
 
-    rc = ngx_http_postpone_filter_output_postponed_request(r);
+    do {
+        pr = r->postponed;
+
+        if (pr->request) {
+
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http postpone filter wake \"%V?%V\"",
+                           &pr->request->uri, &pr->request->args);
+
+            r->postponed = pr->next;
+
+            c->data = pr->request;
+
+            return ngx_http_post_request(pr->request);
+        }
 
-    if (rc == NGX_ERROR) {
-        /* NGX_ERROR may be returned by any filter */
-        r->connection->error = 1;
-    }
+        if (pr->out == NULL) {
+            ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                          "http postpone filter NULL output",
+                          &r->uri, &r->args);
+
+        } else {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http postpone filter output \"%V?%V\"",
+                           &r->uri, &r->args);
 
-    return rc;
+            if (ngx_http_next_filter(r->main, pr->out) == NGX_ERROR) {
+                return NGX_ERROR;
+            }
+        }
+
+        r->postponed = pr->next;
+
+    } while (r->postponed);
+
+    return NGX_OK;
 }
 
 
 static ngx_int_t
-ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r)
+ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    ngx_int_t                      rc;
-    ngx_chain_t                   *out;
-    ngx_http_log_ctx_t            *ctx;
-    ngx_http_postponed_request_t  *pr;
+    ngx_http_postponed_request_t  *pr, **ppr;
 
-    for ( ;; ) {
-        pr = r->postponed;
+    if (r->postponed) {
+        for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }
 
-        if (pr == NULL) {
-            break;
+        if (pr->request == NULL) {
+            goto found;
         }
 
-        if (pr->request) {
-
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http postpone filter handle \"%V?%V\"",
-                           &pr->request->uri, &pr->request->args);
-
-            ctx = r->connection->log->data;
-            ctx->current_request = pr->request;
-
-            if (!pr->request->done) {
-                r->connection->data = pr->request;
-                return NGX_AGAIN;
-            }
-
-            rc = ngx_http_postpone_filter_output_postponed_request(pr->request);
-
-            if (rc == NGX_AGAIN || rc == NGX_ERROR) {
-                return rc;
-            }
+        ppr = &pr->next;
 
-            r->postponed = r->postponed->next;
-            pr = r->postponed;
-        }
-
-        if (pr == NULL) {
-            break;
-        }
-
-        out = pr->out;
+    } else {
+        ppr = &r->postponed;
+    }
 
-        if (out) {
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http postpone filter out postponed \"%V?%V\"",
-                           &r->uri, &r->args);
-
-            if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) {
-                if (ngx_http_next_filter(r->main, out) == NGX_ERROR) {
-                    return NGX_ERROR;
-                }
-            }
-        }
-
-        r->postponed = r->postponed->next;
+    pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
+    if (pr == NULL) {
+        return NGX_ERROR;
     }
 
-    if (r != r->main && r->out) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "http postpone filter out again \"%V?%V\"",
-                       &r->uri, &r->args);
+    *ppr = pr;
 
-        r->connection->data = r;
-        return NGX_AGAIN;
+    pr->request = NULL;
+    pr->out = NULL;
+    pr->next = NULL;
+
+found:
+
+    if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) {
+        return NGX_OK;
     }
 
-    return NGX_OK;
+    return NGX_ERROR;
 }
 
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -38,6 +38,7 @@ static ngx_int_t ngx_http_find_virtual_s
 static void ngx_http_request_handler(ngx_event_t *ev);
 static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);
 static void ngx_http_writer(ngx_http_request_t *r);
+static void ngx_http_request_finalizer(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);
@@ -1411,9 +1412,7 @@ ngx_http_process_request_header(ngx_http
         }
     }
 
-    if (r->method & (NGX_HTTP_POST|NGX_HTTP_PUT)
-        && r->headers_in.content_length_n == -1)
-    {
+    if (r->method & NGX_HTTP_PUT && r->headers_in.content_length_n == -1) {
         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                   "client sent %V method without \"Content-Length\" header",
                   &r->method_name);
@@ -1524,7 +1523,7 @@ ngx_http_process_request(ngx_http_reques
 
     ngx_http_handler(r);
 
-    return;
+    ngx_http_run_posted_requests(c);
 }
 
 
@@ -1677,12 +1676,73 @@ ngx_http_request_handler(ngx_event_t *ev
     ctx = c->log->data;
     ctx->current_request = r;
 
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http run request: \"%V?%V\"", &r->uri, &r->args);
+
     if (ev->write) {
         r->write_event_handler(r);
 
     } else {
         r->read_event_handler(r);
     }
+
+    ngx_http_run_posted_requests(c);
+}
+
+
+void
+ngx_http_run_posted_requests(ngx_connection_t *c)
+{
+    ngx_http_request_t         *r;
+    ngx_http_log_ctx_t         *ctx;
+    ngx_http_posted_request_t  *pr;
+
+    for ( ;; ) {
+
+        if (c->destroyed) {
+            return;
+        }
+
+        r = c->data;
+        pr = r->main->posted_requests;
+
+        if (pr == NULL) {
+            return;
+        }
+
+        r->main->posted_requests = pr->next;
+
+        r = pr->request;
+
+        ctx = c->log->data;
+        ctx->current_request = r;
+
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "http posted request: \"%V?%V\"", &r->uri, &r->args);
+
+        r->write_event_handler(r);
+    }
+}
+
+
+ngx_int_t
+ngx_http_post_request(ngx_http_request_t *r)
+{
+    ngx_http_posted_request_t  *pr, **p;
+
+    pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
+    if (pr == NULL) {
+        return NGX_ERROR;
+    }
+
+    pr->request = r;
+    pr->next = NULL;
+
+    for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }
+
+    *p = pr;
+
+    return NGX_OK;
 }
 
 
@@ -1691,7 +1751,6 @@ ngx_http_finalize_request(ngx_http_reque
 {
     ngx_connection_t          *c;
     ngx_http_request_t        *pr;
-    ngx_http_log_ctx_t        *ctx;
     ngx_http_core_loc_conf_t  *clcf;
 
     if (rc == NGX_DONE) {
@@ -1701,9 +1760,9 @@ ngx_http_finalize_request(ngx_http_reque
 
     c = r->connection;
 
-    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                   "http finalize request: %d, \"%V?%V\"",
-                   rc, &r->uri, &r->args);
+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http finalize request: %d, \"%V?%V\" %d",
+                   rc, &r->uri, &r->args, r == c->data);
 
     if (rc == NGX_DECLINED) {
         r->content_handler = NULL;
@@ -1759,88 +1818,93 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
-    if (r != r->main && !r->logged) {
-
-        if (clcf->log_subrequest) {
-            ngx_http_log_request(r);
-        }
-
-        r->logged = 1;
-    }
-
-    if (r != r->main || rc == NGX_AGAIN) {
-        if (ngx_http_set_write_handler(r) != NGX_OK) {
+    if (r != r->main) {
+
+        if (r->buffered || r->postponed) {
+
+            if (ngx_http_set_write_handler(r) != NGX_OK) {
+                ngx_http_close_request(r->main, 0);
+            }
+
             return;
         }
-    }
-
-    r->done = 1;
-
-    if (r != c->data) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http finalize non-active request: \"%V?%V\"",
-                       &r->uri, &r->args);
-        return;
-    }
-
-    if (r != r->main) {
+
+#if (NGX_DEBUG)
+        if (r != c->data) {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http finalize non-active request: \"%V?%V\"",
+                           &r->uri, &r->args);
+        }
+#endif
 
         pr = r->parent;
 
+        if (r == c->data) {
+
+            if (!r->logged) {
+
+                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+                if (clcf->log_subrequest) {
+                    ngx_http_log_request(r);
+                }
+
+                r->logged = 1;
+
+            } else {
+                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                              "subrequest: \"%V?%V\" logged again",
+                              &r->uri, &r->args);
+            }
+
+            r->done = 1;
+
+            if (pr->postponed && pr->postponed->request == r) {
+                pr->postponed = pr->postponed->next;
+            }
+
+            c->data = pr;
+
+        } else {
+
+            r->write_event_handler = ngx_http_request_finalizer;
+
+            if (r->waited) {
+                r->done = 1;
+            }
+        }
+
+        if (ngx_http_post_request(pr) != NGX_OK) {
+            ngx_http_close_request(r->main, 0);
+            return;
+        }
+
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http parent request: \"%V?%V\"", &pr->uri, &pr->args);
-
-        if (rc != NGX_AGAIN) {
-            c->data = pr;
-        }
-
-        ctx = c->log->data;
-        ctx->current_request = pr;
-
-        if (pr->postponed) {
-
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                           "http request: \"%V?%V\" has postponed",
-                           &pr->uri, &pr->args);
-
-            if (rc != NGX_AGAIN && pr->postponed->request == r) {
-                pr->postponed = pr->postponed->next;
-            }
-
-            if (r->fast_subrequest) {
-
-                if (rc == NGX_AGAIN) {
-                    r->fast_subrequest = 0;
-                }
-
-                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                               "http fast subrequest: \"%V?%V\" done",
-                               &r->uri, &r->args);
-                return;
-            }
-
-            if (rc != NGX_AGAIN) {
-                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                               "http wake parent request: \"%V?%V\"",
-                               &pr->uri, &pr->args);
-
-                pr->write_event_handler(pr);
-            }
+                       "http wake parent request: \"%V?%V\"",
+                       &pr->uri, &pr->args);
+
+        ngx_http_run_posted_requests(c);
+
+        return;
+    }
+
+    if (r->buffered || c->buffered || r->postponed) {
+
+        if (ngx_http_set_write_handler(r) != NGX_OK) {
+            ngx_http_close_request(r, 0);
         }
 
         return;
     }
 
-    if (rc == NGX_AGAIN) {
+    if (r != c->data) {
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                      "http finalize non-active request: \"%V?%V\"",
+                      &r->uri, &r->args);
         return;
     }
 
-    if (c->buffered) {
-        (void) ngx_http_set_write_handler(r);
-        return;
-    }
+    r->done = 1;
 
     if (!r->post_action) {
         r->request_complete = 1;
@@ -1868,6 +1932,8 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
     if (!ngx_terminate
          && !ngx_exiting
          && r->keepalive
@@ -1930,6 +1996,8 @@ ngx_http_writer(ngx_http_request_t *r)
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,
                    "http writer handler: \"%V?%V\"", &r->uri, &r->args);
 
+    clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
+
     if (wev->timedout) {
         if (!wev->delayed) {
             ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
@@ -1944,7 +2012,6 @@ ngx_http_writer(ngx_http_request_t *r)
         wev->delayed = 0;
 
         if (!wev->ready) {
-            clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
             ngx_add_timer(wev, clcf->send_timeout);
 
             if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
@@ -1959,8 +2026,6 @@ ngx_http_writer(ngx_http_request_t *r)
             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,
                            "http writer delayed");
 
-            clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
-
             if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
                 ngx_http_close_request(r, 0);
             }
@@ -1979,8 +2044,8 @@ ngx_http_writer(ngx_http_request_t *r)
                    "http writer output filter: %d, \"%V?%V\"",
                    rc, &r->uri, &r->args);
 
-    if (rc == NGX_AGAIN) {
-        clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
+    if (r->buffered || r->postponed || (r == r->main && c->buffered)) {
+
         if (!wev->ready && !wev->delayed) {
             ngx_add_timer(wev, clcf->send_timeout);
         }
@@ -1989,11 +2054,7 @@ ngx_http_writer(ngx_http_request_t *r)
             ngx_http_close_request(r, 0);
         }
 
-        if (r == r->main || r->buffered) {
-            return;
-        }
-
-        rc = NGX_OK;
+        return;
     }
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,
@@ -2003,6 +2064,16 @@ ngx_http_writer(ngx_http_request_t *r)
 }
 
 
+static void
+ngx_http_request_finalizer(ngx_http_request_t *r)
+{
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http finalizer done: \"%V?%V\"", &r->uri, &r->args);
+
+    ngx_http_finalize_request(r, 0);
+}
+
+
 void
 ngx_http_block_reading(ngx_http_request_t *r)
 {
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -58,6 +58,7 @@
 
 #define NGX_HTTP_ZERO_IN_URI               1
 #define NGX_HTTP_SUBREQUEST_IN_MEMORY      2
+#define NGX_HTTP_SUBREQUEST_WAITED         4
 
 
 #define NGX_HTTP_OK                        200
@@ -321,6 +322,14 @@ struct ngx_http_postponed_request_s {
 };
 
 
+typedef struct ngx_http_posted_request_s  ngx_http_posted_request_t;
+
+struct ngx_http_posted_request_s {
+    ngx_http_request_t               *request;
+    ngx_http_posted_request_t        *next;
+};
+
+
 typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
 typedef void (*ngx_http_event_handler_pt)(ngx_http_request_t *r);
 
@@ -373,6 +382,7 @@ struct ngx_http_request_s {
     ngx_http_request_t               *parent;
     ngx_http_postponed_request_t     *postponed;
     ngx_http_post_subrequest_t       *post_subrequest;
+    ngx_http_posted_request_t        *posted_requests;
 
     uint32_t                          in_addr;
     ngx_uint_t                        port;
@@ -428,8 +438,8 @@ struct ngx_http_request_s {
     unsigned                          request_body_file_group_access:1;
     unsigned                          request_body_file_log_level:3;
 
-    unsigned                          fast_subrequest:1;
     unsigned                          subrequest_in_memory:1;
+    unsigned                          waited:1;
 
     unsigned                          gzip:2;
 
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -274,13 +274,16 @@ ngx_module_t  ngx_http_upstream_module =
 static ngx_http_variable_t  ngx_http_upstream_vars[] = {
 
     { ngx_string("upstream_addr"), NULL,
-      ngx_http_upstream_addr_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
+      ngx_http_upstream_addr_variable, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("upstream_status"), NULL,
-      ngx_http_upstream_status_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
+      ngx_http_upstream_status_variable, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("upstream_response_time"), NULL,
-      ngx_http_upstream_response_time_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
+      ngx_http_upstream_response_time_variable, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
@@ -511,6 +514,37 @@ ngx_http_upstream_resolve_handler(ngx_re
 
 
 static void
+ngx_http_upstream_handler(ngx_event_t *ev)
+{
+    ngx_connection_t     *c;
+    ngx_http_request_t   *r;
+    ngx_http_log_ctx_t   *ctx;
+    ngx_http_upstream_t  *u;
+
+    c = ev->data;
+    r = c->data;
+
+    u = r->upstream;
+    c = r->connection;
+
+    ctx = c->log->data;
+    ctx->current_request = r;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream request: \"%V?%V\"", &r->uri, &r->args);
+
+    if (ev->write) {
+        u->write_event_handler(ev);
+
+    } else {
+        u->read_event_handler(ev);
+    }
+
+    ngx_http_run_posted_requests(c);
+}
+
+
+static void
 ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r)
 {
     ngx_http_upstream_check_broken_connection(r, r->connection->read);
@@ -712,8 +746,11 @@ ngx_http_upstream_connect(ngx_http_reque
 
     c->data = r;
 
-    c->write->handler = ngx_http_upstream_send_request_handler;
-    c->read->handler = ngx_http_upstream_process_header;
+    c->write->handler = ngx_http_upstream_handler;
+    c->read->handler = ngx_http_upstream_handler;
+
+    u->write_event_handler = ngx_http_upstream_send_request_handler;
+    u->read_event_handler = ngx_http_upstream_process_header;
 
     c->sendfile &= r->connection->sendfile;
     u->output.sendfile = c->sendfile;
@@ -838,8 +875,8 @@ ngx_http_upstream_ssl_handshake(ngx_conn
             u->peer.save_session(&u->peer, u->peer.data);
         }
 
-        c->write->handler = ngx_http_upstream_send_request_handler;
-        c->read->handler = ngx_http_upstream_process_header;
+        c->write->handler = ngx_http_upstream_handler;
+        c->read->handler = ngx_http_upstream_handler;
 
         ngx_http_upstream_send_request(r, u);
 
@@ -998,7 +1035,7 @@ ngx_http_upstream_send_request(ngx_http_
     }
 #endif
 
-    c->write->handler = ngx_http_upstream_dummy_handler;
+    u->write_event_handler = ngx_http_upstream_dummy_handler;
 
     if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
         ngx_http_upstream_finalize_request(r, u,
@@ -1037,7 +1074,7 @@ ngx_http_upstream_send_request_handler(n
 #endif
 
     if (u->header_sent) {
-        wev->handler = ngx_http_upstream_dummy_handler;
+        u->write_event_handler = ngx_http_upstream_dummy_handler;
 
         (void) ngx_handle_write_event(wev, 0);
 
@@ -1350,7 +1387,7 @@ ngx_http_upstream_process_header(ngx_eve
         }
     }
 
-    rev->handler = ngx_http_upstream_process_body_in_memory;
+    u->read_event_handler = ngx_http_upstream_process_body_in_memory;
 
     ngx_http_upstream_process_body_in_memory(rev);
 }
@@ -1614,8 +1651,7 @@ ngx_http_upstream_send_response(ngx_http
             u->input_filter_ctx = r;
         }
 
-        u->peer.connection->read->handler =
-                                   ngx_http_upstream_process_non_buffered_body;
+        u->read_event_handler = ngx_http_upstream_process_non_buffered_body;
         r->write_event_handler =
                              ngx_http_upstream_process_non_buffered_downstream;
 
@@ -1796,7 +1832,7 @@ ngx_http_upstream_send_response(ngx_http
     p->send_timeout = clcf->send_timeout;
     p->send_lowat = clcf->send_lowat;
 
-    u->peer.connection->read->handler = ngx_http_upstream_process_body;
+    u->read_event_handler = ngx_http_upstream_process_body;
     r->write_event_handler = ngx_http_upstream_process_downstream;
 
     ngx_http_upstream_process_body(u->peer.connection->read);
@@ -1926,7 +1962,7 @@ ngx_http_upstream_process_non_buffered_b
 
     if (downstream->data == r) {
         if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
-            == NGX_ERROR)
+            != NGX_OK)
         {
             ngx_http_upstream_finalize_request(r, u, 0);
             return;
@@ -1940,7 +1976,7 @@ ngx_http_upstream_process_non_buffered_b
         ngx_del_timer(downstream->write);
     }
 
-    if (ngx_handle_read_event(upstream->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
         ngx_http_upstream_finalize_request(r, u, 0);
         return;
     }
@@ -2016,7 +2052,6 @@ ngx_http_upstream_process_body(ngx_event
     ngx_temp_file_t      *tf;
     ngx_event_pipe_t     *p;
     ngx_connection_t     *c, *downstream;
-    ngx_http_log_ctx_t   *ctx;
     ngx_http_request_t   *r;
     ngx_http_upstream_t  *u;
 
@@ -2034,9 +2069,6 @@ ngx_http_upstream_process_body(ngx_event
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                        "http upstream process upstream");
         c->log->action = "reading upstream";
-
-        ctx = c->log->data;
-        ctx->current_request = r;
     }
 
     p = u->pipe;
@@ -2051,8 +2083,7 @@ ngx_http_upstream_process_body(ngx_event
                 if (!ev->ready) {
                     ngx_add_timer(ev, p->send_timeout);
 
-                    if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR)
-                    {
+                    if (ngx_handle_write_event(ev, p->send_lowat) != NGX_OK) {
                         ngx_http_upstream_finalize_request(r, u, 0);
                         return;
                     }
@@ -2086,7 +2117,8 @@ ngx_http_upstream_process_body(ngx_event
             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                            "http downstream delayed");
 
-            if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) {
+            if (ngx_handle_write_event(ev, p->send_lowat) != NGX_OK) {
+                ngx_http_upstream_finalize_request(r, u, 0);
                 return;
             }
 
@@ -2464,17 +2496,8 @@ ngx_http_upstream_finalize_request(ngx_h
 
     r->connection->log->action = "sending to client";
 
-    if (rc == 0) {
-        if (r == r->main) {
-            if (!r->post_action) {
-                rc = ngx_http_send_special(r, NGX_HTTP_LAST);
-            }
-
-        } else {
-            if (r->out) {
-                rc = NGX_AGAIN;
-            }
-        }
+    if (rc == 0 && r == r->main && !r->post_action) {
+        rc = ngx_http_send_special(r, NGX_HTTP_LAST);
     }
 
     ngx_http_finalize_request(r, rc);
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -217,6 +217,9 @@ typedef struct {
 
 
 struct ngx_http_upstream_s {
+    ngx_event_handler_pt            read_event_handler;
+    ngx_event_handler_pt            write_event_handler;
+
     ngx_peer_connection_t           peer;
 
     ngx_event_pipe_t               *pipe;