changeset 160:e7e094d34162

nginx-0.0.1-2003-10-27-11:53:49 import
author Igor Sysoev <igor@sysoev.ru>
date Mon, 27 Oct 2003 08:53:49 +0000
parents 981e4af2a425
children 88abd07d9f62
files src/core/ngx_file.c src/core/ngx_file.h src/core/ngx_hunk.c src/core/ngx_hunk.h src/core/ngx_log.h src/core/ngx_output_chain.c src/http/modules/ngx_http_gzip_filter.c src/http/modules/ngx_http_range_filter.c src/http/modules/ngx_http_static_handler.c src/http/modules/proxy/ngx_http_proxy_handler.c src/http/modules/proxy/ngx_http_proxy_handler.h src/http/ngx_http.h src/http/ngx_http_core_module.c src/http/ngx_http_output_filter.c src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_request_body.c
diffstat 17 files changed, 863 insertions(+), 561 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -7,6 +7,27 @@ static int ngx_temp_number;
 static int ngx_random;
 
 
+int ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain)
+{
+    int  rc;
+
+    if (tf->file.fd == NGX_INVALID_FILE) {
+        rc = ngx_create_temp_file(&tf->file, &tf->path, tf->pool,
+                                  tf->persistent);
+    
+        if (rc == NGX_ERROR || rc == NGX_AGAIN) {
+            return rc;
+        }
+
+        if (!tf->persistent && tf->warn) {
+            ngx_log_error(NGX_LOG_WARN, tf->file.log, 0, tf->warn);
+        }
+    }
+
+    return ngx_write_chain_to_file(&tf->file, chain, tf->file.offset, tf->pool);
+}
+
+
 int ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path,
                          ngx_pool_t *pool, int persistent)
 {
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -26,6 +26,17 @@ typedef struct {
 } ngx_path_t;
 
 
+typedef struct {
+    ngx_file_t   file;
+    ngx_path_t   path;
+    ngx_pool_t  *pool;
+    char        *warn;
+
+    unsigned     persistent:1;
+} ngx_temp_file_t;
+
+
+int ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain);
 int ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path,
                          ngx_pool_t *pool, int persistent);
 void ngx_create_hashed_filename(ngx_file_t *file, ngx_path_t *path);
@@ -46,7 +57,7 @@ char *ngx_conf_set_path_slot(ngx_conf_t 
             conf->level[0] = l1;                                             \
             conf->level[1] = l2;                                             \
             conf->level[2] = l3;                                             \
-            conf->len = l1 + l2 + l3 + l1 ? 1:0 + l2 ? 1:0 + l3 ? 1:0;       \
+            conf->len = l1 + l2 + l3 + (l1 ? 1:0) + (l2 ? 1:0) + (l3 ? 1:0); \
         } else {                                                             \
             conf = prev;                                                     \
         }                                                                    \
--- a/src/core/ngx_hunk.c
+++ b/src/core/ngx_hunk.c
@@ -26,6 +26,48 @@ ngx_hunk_t *ngx_create_temp_hunk(ngx_poo
     return h;
 }
 
+
+ngx_chain_t *ngx_create_chain_of_hunks(ngx_pool_t *pool, ngx_bufs_t *bufs)
+{
+    int           i;
+    char         *p;
+    ngx_hunk_t   *h;
+    ngx_chain_t  *chain, *cl, **ll;
+
+    ngx_test_null(p, ngx_palloc(pool, bufs->num * bufs->size), NULL);
+
+    ll = &chain;
+
+    for (i = 0; i < bufs->num; i++) {
+        ngx_test_null(h, ngx_alloc_hunk(pool), NULL);
+
+        h->pos = p;
+        h->last = p;
+        h->file_pos = 0;
+        h->file_last = 0;
+
+        h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP;
+
+        h->start = p;
+        p += bufs->size;
+        h->end = p;
+
+        h->file = NULL;
+        h->shadow = NULL;
+        h->tag = 0;
+
+        ngx_test_null(cl, ngx_alloc_chain_link(pool), NULL);
+        cl->hunk = h;
+        *ll = cl;
+        ll = &cl->next;
+    }
+
+    *ll = NULL;
+
+    return chain;
+}
+
+
 ngx_hunk_t *ngx_create_hunk_before(ngx_pool_t *pool, ngx_hunk_t *hunk, int size)
 {
     ngx_hunk_t *h;
@@ -61,6 +103,7 @@ ngx_hunk_t *ngx_create_hunk_before(ngx_p
     return h;
 }
 
+
 ngx_hunk_t *ngx_create_hunk_after(ngx_pool_t *pool, ngx_hunk_t *hunk, int size)
 {
     ngx_hunk_t *h;
--- a/src/core/ngx_hunk.h
+++ b/src/core/ngx_hunk.h
@@ -73,6 +73,29 @@ typedef struct {
 } ngx_bufs_t;
 
 
+typedef int  (*ngx_output_chain_filter_pt)(void *ctx, ngx_chain_t *out);
+
+typedef struct {
+    ngx_hunk_t                  *hunk;
+    ngx_chain_t                 *in;
+    ngx_chain_t                 *free;
+    ngx_chain_t                 *busy;
+
+    unsigned                     sendfile;
+    unsigned                     need_in_memory;
+    unsigned                     need_in_temp;
+    unsigned                     copy_chain;
+
+    ngx_pool_t                  *pool;
+    int                          hunks;
+    ngx_bufs_t                   bufs;
+    ngx_hunk_tag_t               tag;
+
+    ngx_output_chain_filter_pt   output_filter;
+    void                        *output_ctx;
+} ngx_output_chain_ctx_t;
+
+
 #define NGX_CHAIN_ERROR     (ngx_chain_t *) NGX_ERROR
 
 
@@ -120,6 +143,7 @@ ngx_hunk_t *ngx_create_temp_hunk(ngx_poo
             last = &cl->next
 
 
+int ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in);
 int ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in);
 void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy,
                              ngx_chain_t **out, ngx_hunk_tag_t tag);
--- a/src/core/ngx_log.h
+++ b/src/core/ngx_log.h
@@ -63,11 +63,14 @@ typedef enum {
 */
 
 
+typedef size_t  (*ngx_log_handler_pt) (void *ctx, char *buf, size_t len);
+
+
 typedef struct {
-    int               log_level;
-    ngx_open_file_t  *file;
-    void             *data;
-    size_t           (*handler)(void *ctx, char *buf, size_t len);
+    int                  log_level;
+    ngx_open_file_t     *file;
+    void                *data;
+    ngx_log_handler_pt   handler;
 
 #if 0
 /* STUB */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_output_chain.c
@@ -0,0 +1,252 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#define NGX_NONE      1
+
+
+ngx_inline static int ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t *ctx,
+                                                    ngx_hunk_t *hunk);
+static int ngx_output_chain_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src,
+                                      int sendfile);
+
+
+int ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
+{
+    int           rc, last;
+    ssize_t       size, hsize;
+    ngx_chain_t  *cl, *out, **last_out;
+
+    /*
+     * the short path for the case when the chain ctx->in is empty
+     * and the incoming chain is empty too or it has the single hunk
+     * that does not require the copy
+     */
+
+    if (ctx->in == NULL) {
+
+        if (in == NULL) {
+            return ctx->output_filter(ctx->output_ctx, in);
+        }
+
+        if (!ctx->copy_chain
+            && in->next == NULL
+            && (!ngx_output_chain_need_to_copy(ctx, in->hunk)))
+        {
+            return ctx->output_filter(ctx->output_ctx, in);
+        }
+    }
+
+    /* add the incoming hunk to the chain ctx->in */
+
+    if (in) {
+        if (ngx_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+    }
+
+    last = NGX_NONE;
+    last_out = &out;
+
+    for ( ;; ) {
+
+        while (ctx->in) {
+
+            if (!ngx_output_chain_need_to_copy(ctx, ctx->in->hunk)) {
+
+                /* move the chain link to the chain out */
+
+                cl = ctx->in;
+                ctx->in = cl->next;
+
+                *last_out = cl;
+                last_out = &cl->next;
+                cl->next = NULL;
+
+                continue;
+            }
+
+            if (ctx->hunk == NULL) {
+
+                /* get the free hunk */
+
+                if (ctx->free) {
+                    ctx->hunk = ctx->free->hunk;
+                    ctx->free = ctx->free->next;
+
+                } else if (ctx->hunks < ctx->bufs.num) {
+
+                    size = ctx->bufs.size;
+
+                    if (ctx->in->hunk->type & NGX_HUNK_LAST) {
+
+                        hsize = ngx_hunk_size(ctx->in->hunk);
+
+                        if (hsize < ctx->bufs.size) {
+
+                           /*
+                            * allocate small temp hunk for the small last hunk
+                            * or its small last part
+                            */
+
+                            size = hsize;
+
+                        } else if (ctx->bufs.num == 1
+                                   && (hsize < ctx->bufs.size
+                                                     + (ctx->bufs.size >> 2)))
+                        {
+                            /*
+                             * allocate a temp hunk that equals
+                             * to the last hunk if the last hunk size is lesser
+                             * than 1.25 of bufs.size and a temp hunk is single
+                             */
+
+                            size = hsize;
+                        }
+                    }
+
+                    ngx_test_null(ctx->hunk,
+                                  ngx_create_temp_hunk(ctx->pool, size, 0, 0),
+                                  NGX_ERROR);
+                    ctx->hunk->tag = ctx->tag;
+                    ctx->hunk->type |= NGX_HUNK_RECYCLED;
+                    ctx->hunks++;
+
+                } else {
+                    break;
+                }
+            }
+
+            rc = ngx_output_chain_copy_hunk(ctx->hunk, ctx->in->hunk,
+                                            ctx->sendfile);
+
+            if (rc == NGX_ERROR) {
+                return rc;
+            }
+
+            if (rc == NGX_AGAIN) {
+                if (out) {
+                    break;
+                }
+                return rc;
+            }
+
+            /* delete the completed hunk from the chain ctx->in */
+
+            if (ngx_hunk_size(ctx->in->hunk) == 0) {
+                ctx->in = ctx->in->next;
+            }
+
+            ngx_alloc_link_and_set_hunk(cl, ctx->hunk, ctx->pool, NGX_ERROR);
+            *last_out = cl;
+            last_out = &cl->next;
+            ctx->hunk = NULL;
+
+            if (ctx->free == NULL) {
+                break;
+            }
+        }
+
+        if (out == NULL && last != NGX_NONE) {
+            return last;
+        }
+
+        last = ctx->output_filter(ctx->output_ctx, out);
+
+        ngx_chain_update_chains(&ctx->free, &ctx->busy, &out, ctx->tag);
+        last_out = &out;
+    }
+}
+
+
+ngx_inline static int ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t *ctx,
+                                                    ngx_hunk_t *hunk)
+{
+    if (ngx_hunk_special(hunk)) {
+        return 0;
+    }
+
+    if (!ctx->sendfile) {
+        return 1;
+    }
+
+    if (ctx->need_in_memory && (!(hunk->type & NGX_HUNK_IN_MEMORY))) {
+        return 1;
+    }
+
+
+    if (ctx->need_in_temp && (hunk->type & (NGX_HUNK_MEMORY|NGX_HUNK_MMAP))) {
+        return 1;
+    }
+
+    return 0;
+}
+
+
+static int ngx_output_chain_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src,
+                                      int sendfile)
+{
+    ssize_t  n, size;
+
+    size = ngx_hunk_size(src);
+
+    if (size > (dst->end - dst->pos)) {
+        size = dst->end - dst->pos;
+    }
+
+    if (src->type & NGX_HUNK_IN_MEMORY) {
+        ngx_memcpy(dst->pos, src->pos, size);
+        src->pos += size;
+        dst->last += size;
+
+        if (src->type & NGX_HUNK_FILE) {
+            src->file_pos += size;
+        }
+
+        if ((src->type & NGX_HUNK_LAST) && src->pos == src->last) {
+            dst->type |= NGX_HUNK_LAST;
+        }
+
+    } else {
+        n = ngx_read_file(src->file, dst->pos, size, src->file_pos);
+
+if (n == 0) {
+ngx_log_debug(src->file->log, "READ: %qd:%qd %X:%X %X:%X" _
+              src->file_pos _ src->file_last _
+              dst->pos _ dst->last _ dst->start _ dst->end);
+}
+
+        if (n == NGX_ERROR) {
+            return n;
+        }
+
+#if (NGX_FILE_AIO_READ)
+        if (n == NGX_AGAIN) {
+            return n;
+        }
+#endif
+
+        if (n != size) {
+            ngx_log_error(NGX_LOG_ALERT, src->file->log, 0,
+                          ngx_read_file_n " reads only %d of %d from file",
+                          n, size);
+            if (n == 0) {
+                return NGX_ERROR;
+            }
+        }
+
+        src->file_pos += n;
+        dst->last += n;
+
+        if (!sendfile) {
+            dst->type &= ~NGX_HUNK_FILE;
+        }
+
+        if ((src->type & NGX_HUNK_LAST) && src->file_pos == src->file_last) {
+            dst->type |= NGX_HUNK_LAST;
+        }
+    }
+
+    return NGX_OK;
+}
--- a/src/http/modules/ngx_http_gzip_filter.c
+++ b/src/http/modules/ngx_http_gzip_filter.c
@@ -168,7 +168,6 @@ static int ngx_http_gzip_header_filter(n
     if (!conf->enable
         || r->headers_out.status != NGX_HTTP_OK
         || r->header_only
-        || r->main
         /* TODO: conf->http_version */
         || (r->headers_out.content_encoding
             && r->headers_out.content_encoding->value.len)
--- a/src/http/modules/ngx_http_range_filter.c
+++ b/src/http/modules/ngx_http_range_filter.c
@@ -4,6 +4,41 @@
 #include <ngx_http.h>
 
 
+/*
+ * the single part format:
+ *
+ * "HTTP/1.0 206 Partial Content" CRLF
+ * ... header ...
+ * "Content-Type: image/jpeg" CRLF
+ * "Content-Length: SIZE" CRLF
+ * "Content-Range: bytes START-END/SIZE" CRLF
+ * CRLF
+ * ... data ...
+ *
+ *
+ * the mutlipart format:
+ *
+ * "HTTP/1.0 206 Partial Content" CRLF
+ * ... header ...
+ * "Content-Type: multipart/byteranges; boundary=0123456789" CRLF
+ * CRLF
+ * CRLF
+ * "--0123456789" CRLF
+ * "Content-Type: image/jpeg" CRLF
+ * "Content-Range: bytes START0-END0/SIZE" CRLF
+ * CRLF
+ * ... data ...
+ * CRLF
+ * "--0123456789" CRLF
+ * "Content-Type: image/jpeg" CRLF
+ * "Content-Range: bytes START1-END1/SIZE" CRLF
+ * CRLF
+ * ... data ...
+ * CRLF
+ * "--0123456789--" CRLF
+ */
+
+
 typedef struct {
     ngx_str_t  boundary_header;
 } ngx_http_range_filter_ctx_t;
@@ -46,11 +81,12 @@ static int ngx_http_range_header_filter(
     ngx_http_range_t             *range;
     ngx_http_range_filter_ctx_t  *ctx;
 
-    if (r->main
-        || r->http_version < NGX_HTTP_VERSION_10
+    if (r->http_version < NGX_HTTP_VERSION_10
         || r->headers_out.status != NGX_HTTP_OK
         || r->headers_out.content_length_n == -1
+
         /* STUB: we currently support ranges for file hunks only */
+        || !r->sendfile
         || r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY)
     {
         return ngx_http_next_header_filter(r);
@@ -75,11 +111,8 @@ static int ngx_http_range_header_filter(
     ngx_init_array(r->headers_out.ranges, r->pool, 5, sizeof(ngx_http_range_t),
                    NGX_ERROR);
 
-#if (NGX_SUPPRESS_WARN)
+    rc = 0;
     range = NULL;
-#endif
-
-    rc = 0;
     p = r->headers_in.range->value.data + 6;
 
     for ( ;; ) {
@@ -156,6 +189,9 @@ static int ngx_http_range_header_filter(
     }
 
     if (rc) {
+
+        /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */
+
         r->headers_out.status = rc;
         r->headers_out.ranges.nelts = 0;
 
@@ -189,6 +225,8 @@ static int ngx_http_range_header_filter(
                           ngx_palloc(r->pool, 6 + 20 + 1 + 20 + 1 + 20 + 1),
                           NGX_ERROR);
 
+            /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
+
             r->headers_out.content_range->value.len =
                          ngx_snprintf(r->headers_out.content_range->value.data,
                                       6 + 20 + 1 + 20 + 1 + 20 + 1,
@@ -222,6 +260,14 @@ static int ngx_http_range_header_filter(
 
             boundary = ngx_next_temp_number(0);
 
+            /*
+             * The boundary header of the range:
+             * CRLF
+             * "--0123456789" CRLF
+             * "Content-Type: image/jpeg" CRLF
+             * "Content-Range: bytes "
+             */
+
             if (r->headers_out.charset.len) {
                 ctx->boundary_header.len =
                          ngx_snprintf(ctx->boundary_header.data, len,
@@ -248,30 +294,34 @@ static int ngx_http_range_header_filter(
                           ngx_palloc(r->pool, 31 + 10 + 1),
                           NGX_ERROR);
 
+            /* "Content-Type: multipart/byteranges; boundary=0123456789" */
+
             r->headers_out.content_type->value.len =
                       ngx_snprintf(r->headers_out.content_type->value.data,
                                    31 + 10 + 1,
                                    "multipart/byteranges; boundary=%010u",
                                    boundary);
 
-            /* the last "CRLF--BOUNDARY--CRLF" */
+            /* the size of the last boundary CRLF "--0123456789--" CRLF */
             len = 4 + 10 + 4;
 
             range = r->headers_out.ranges.elts;
             for (i = 0; i < r->headers_out.ranges.nelts; i++) {
-                 ngx_test_null(range[i].content_range.data,
-                               ngx_palloc(r->pool, 20 + 1 + 20 + 1 + 20 + 5),
-                               NGX_ERROR);
+                ngx_test_null(range[i].content_range.data,
+                              ngx_palloc(r->pool, 20 + 1 + 20 + 1 + 20 + 5),
+                              NGX_ERROR);
 
-                 range[i].content_range.len =
+                /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */
+
+                range[i].content_range.len =
                         ngx_snprintf(range[i].content_range.data,
                                      20 + 1 + 20 + 1 + 20 + 5,
                                      OFF_FMT "-" OFF_FMT "/" OFF_FMT CRLF CRLF,
                                      range[i].start, range[i].end - 1,
                                      r->headers_out.content_length_n);
 
-                 len += ctx->boundary_header.len + range[i].content_range.len
-                        + (size_t) (range[i].end - range[i].start);
+                len += ctx->boundary_header.len + range[i].content_range.len
+                                    + (size_t) (range[i].end - range[i].start);
             }
 
             r->headers_out.content_length_n = len;
@@ -304,8 +354,9 @@ static int ngx_http_range_body_filter(ng
         && in->hunk->type & NGX_HUNK_FILE
         && in->hunk->type & NGX_HUNK_LAST)
     {
+        range = r->headers_out.ranges.elts;
+
         if (r->headers_out.ranges.nelts == 1) {
-            range = r->headers_out.ranges.elts;
             in->hunk->file_pos = range->start;
             in->hunk->file_last = range->end;
 
@@ -315,9 +366,16 @@ static int ngx_http_range_body_filter(ng
         ctx = ngx_http_get_module_ctx(r, ngx_http_range_filter_module);
         ll = &out;
 
-        range = r->headers_out.ranges.elts;
         for (i = 0; i < r->headers_out.ranges.nelts; i++) {
 
+            /*
+             * The boundary header of the range:
+             * CRLF
+             * "--0123456789" CRLF
+             * "Content-Type: image/jpeg" CRLF
+             * "Content-Range: bytes "
+             */
+
             ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
             h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_MEMORY;
             h->pos = ctx->boundary_header.data;
@@ -326,6 +384,8 @@ static int ngx_http_range_body_filter(ng
             ngx_test_null(hcl, ngx_alloc_chain_link(r->pool), NGX_ERROR);
             hcl->hunk = h;
 
+            /* "SSSS-EEEE/TTTT" CRLF CRLF */
+
             ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
             h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP;
             h->pos = range[i].content_range.data;
@@ -334,6 +394,8 @@ static int ngx_http_range_body_filter(ng
             ngx_test_null(rcl, ngx_alloc_chain_link(r->pool), NGX_ERROR);
             rcl->hunk = h;
 
+            /* the range data */
+
             ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
             h->type = NGX_HUNK_FILE;
             h->file_pos = range[i].start;
@@ -348,6 +410,8 @@ static int ngx_http_range_body_filter(ng
             ll = &dcl->next;
         }
 
+        /* the last boundary CRLF "--0123456789--" CRLF  */
+
         ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
         h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP|NGX_HUNK_LAST;
         ngx_test_null(h->pos, ngx_palloc(r->pool, 4 + 10 + 4), NGX_ERROR);
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -88,7 +88,7 @@ ngx_log_debug(r->connection->log, "HTTP 
     /*
      * There is no way to open a file or a directory in Win9X with
      * one syscall: Win9X has no FILE_FLAG_BACKUP_SEMANTICS flag.
-     * so we need to check its type before the opening
+     * So we need to check its type before the opening
      */
 
     r->file.info.dwFileAttributes = GetFileAttributes(r->file.name.data);
@@ -151,6 +151,21 @@ ngx_log_debug(r->connection->log, "FILE:
 
         r->file.info_valid = 1;
     }
+
+#if !(WIN32) /* the not regular files are probably Unix specific */
+
+    if (!ngx_is_file(r->file.info)) {
+        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+                      "%s is not a regular file", r->file.name.data);
+
+        if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR)
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+                          ngx_close_file_n " %s failed", r->file.name.data);
+
+        return NGX_HTTP_NOT_FOUND;
+    }
+
+#endif
 #endif
 
     if (ngx_is_dir(r->file.info)) {
@@ -163,6 +178,7 @@ ngx_log_debug(r->connection->log, "HTTP 
         }
 
         r->file.fd = NGX_INVALID_FILE;
+        r->file.info_valid = 0;
 #endif
 
         ngx_test_null(h, ngx_push_table(r->headers_out.headers),
@@ -203,7 +219,7 @@ static int ngx_http_static_handler(ngx_h
     }
 
     ctx = r->connection->log->data;
-    ctx->action = "sending response";
+    ctx->action = "sending response to client";
 
     if (r->file.fd == NGX_INVALID_FILE) {
         r->file.fd = ngx_open_file(r->file.name.data,
@@ -242,21 +258,6 @@ static int ngx_http_static_handler(ngx_h
         r->file.info_valid = 1;
     }
 
-#if !(WIN32) /* the not regular files are probably Unix specific */
-
-    if (!ngx_is_file(r->file.info)) {
-        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
-                      "%s is not regular file", r->file.name.data);
-
-        if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR)
-            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
-                          ngx_close_file_n " %s failed", r->file.name.data);
-
-        return NGX_HTTP_NOT_FOUND;
-    }
-
-#endif
-
     r->headers_out.status = NGX_HTTP_OK;
     r->headers_out.content_length_n = ngx_file_size(r->file.info);
     r->headers_out.last_modified_time = ngx_file_mtime(r->file.info);
--- a/src/http/modules/proxy/ngx_http_proxy_handler.c
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.c
@@ -10,6 +10,7 @@
 
 
 static int ngx_http_proxy_handler(ngx_http_request_t *r);
+static void ngx_http_proxy_init_request(void *data);
 static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p);
 static void ngx_http_proxy_send_request_handler(ngx_event_t *wev);
 static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p);
@@ -20,7 +21,7 @@ static void ngx_http_proxy_send_response
 static void ngx_http_proxy_process_body(ngx_event_t *ev);
 
 static int ngx_http_proxy_parse_status_line(ngx_http_proxy_ctx_t *p);
-static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p);
+static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p, int ft_type);
 static void ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int rc);
 static void ngx_http_proxy_close_connection(ngx_connection_t *c);
 
@@ -46,6 +47,13 @@ static ngx_command_t ngx_http_proxy_comm
      0,
      NULL},
 
+    {ngx_string("proxy_request_buffer_size"),
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+     ngx_conf_set_size_slot,
+     NGX_HTTP_LOC_CONF_OFFSET,
+     offsetof(ngx_http_proxy_loc_conf_t, request_buffer_size),
+     NULL},
+
     {ngx_string("proxy_connect_timeout"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_msec_slot,
@@ -135,13 +143,10 @@ static ngx_str_t http_methods[] = {
 };
 
 
-#if 0
-static char *header_errors[] = {
-    "upstream sent too long status line",
+static char *upstream_header_errors[] = {
     "upstream sent invalid header",
     "upstream sent too long header line"
 };
-#endif
 
 
 static ngx_http_header_t headers_in[] = {
@@ -170,6 +175,7 @@ static char connection_close_header[] = 
 
 static int ngx_http_proxy_handler(ngx_http_request_t *r)
 {
+    int                    rc;
     ngx_http_proxy_ctx_t  *p;
 
     ngx_http_create_ctx(r, p, ngx_http_proxy_module,
@@ -186,20 +192,89 @@ static int ngx_http_proxy_handler(ngx_ht
     /* TODO: we currently support reverse proxy only */
     p->accel = 1;
 
-    ngx_test_null(p->request_hunks, ngx_http_proxy_create_request(p),
-                  NGX_HTTP_INTERNAL_SERVER_ERROR);
+    if (r->headers_in.content_length_n > 0) {
+        ngx_test_null(r->temp_file,
+                      ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)),
+                      NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+        r->temp_file->file.fd = NGX_INVALID_FILE;
+        r->temp_file->file.log = r->connection->log;
+        r->temp_file->path = *p->lcf->temp_path;
+        r->temp_file->pool = r->pool;
+        r->temp_file->warn = "a client request body is buffered "
+                             "to a temporary file";
+        /* STUB */ r->temp_file->persistent = 1;
+
+        r->request_body_handler = ngx_http_proxy_init_request;
+        r->data = p;
+
+        rc = ngx_http_read_client_request_body(r, p->lcf->request_buffer_size);
+
+        if (rc != NGX_OK) {
+            return rc;
+        }
+    }
+
+    ngx_http_proxy_init_request(p);
 
-    /* TODO: read request body */
+    return NGX_DONE;
+}
+
+
+static void ngx_http_proxy_init_request(void *data)
+{
+    ngx_http_proxy_ctx_t *p = data;
+
+    ngx_chain_t             *cl;
+    ngx_http_request_t      *r;
+    ngx_output_chain_ctx_t  *ctx;
+
+
+    r = p->request;
+
+    cl = ngx_http_proxy_create_request(p);
+    if (cl == NULL) {
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
+    if (r->request_hunks) {
+        cl->next = r->request_hunks;
+    }
+
+    r->request_hunks = cl;
 
     p->upstream.log = r->connection->log;
     p->saved_ctx = r->connection->log->data;
-    r->connection->log->data = p;;
+    p->saved_handler = r->connection->log->handler;
+    r->connection->log->data = p;
     r->connection->log->handler = ngx_http_proxy_log_error;
     p->action = "connecting to upstream";
 
-    ngx_http_proxy_send_request(p);
+    ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t));
+    if (ctx == NULL) {
+        ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
+    }
+
+    p->output_chain_ctx = ctx;
 
-    return NGX_DONE;
+    if (r->request_body_hunk) {
+        ctx->free = ngx_alloc_chain_link(r->pool);
+        if (ctx->free == NULL) {
+            ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        }
+        ctx->free->hunk = r->request_body_hunk;
+        ctx->free->next = NULL;
+    }
+
+    ctx->sendfile = r->sendfile;
+    ctx->copy_chain = 1;
+    ctx->pool = r->pool;
+    ctx->bufs.num = 1;
+    ctx->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module;
+    ctx->output_filter = (ngx_output_chain_filter_pt) ngx_write_chain;
+
+    ngx_http_proxy_send_request(p);
 }
 
 
@@ -324,9 +399,11 @@ static void ngx_http_proxy_send_request_
     c = wev->data;
     p = c->data;
 
+    p->action = "sending request to upstream";
+
     if (wev->timedout) {
         p->timedout = 1;
-        ngx_http_proxy_next_upstream(p);
+        ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT);
         return;
     }
 
@@ -338,30 +415,43 @@ static void ngx_http_proxy_send_request_
 
 static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p)
 {
-    int                rc;
-    ngx_chain_t       *chain, *cl, *tl, **ll;
-    ngx_connection_t  *c;
+    int                      rc;
+    ngx_chain_t             *cl;
+    ngx_connection_t        *c;
 
     c = p->upstream.connection;
 
     for ( ;; ) {
 
         if (c) {
-            chain = ngx_write_chain(c, p->work_request_hunks);
+            p->output_chain_ctx->output_ctx = c;
+            rc = ngx_output_chain(p->output_chain_ctx,
+                                  p->request->request_hunks);
 
-            if (chain != NGX_CHAIN_ERROR) {
-                p->work_request_hunks = chain;
+            if (rc != NGX_ERROR) {
                 p->request_sent = 1;
 
                 if (c->write->timer_set) {
                     ngx_del_timer(c->write);
                 }
 
-                if (chain) {
+                if (rc == NGX_AGAIN) {
                     ngx_add_timer(c->write, p->lcf->send_timeout);
 
                 } else {
                     /* TODO: del event */
+
+                    if (c->tcp_nopush) {
+                        if (ngx_tcp_push(c->fd) == NGX_ERROR) {
+                            ngx_log_error(NGX_LOG_CRIT, c->log,
+                                          ngx_socket_errno,
+                                          ngx_tcp_push_n " failed");
+                            ngx_http_proxy_finalize_request(p,
+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
+                            return; 
+                        }
+                        c->tcp_nopush = 0;
+                    }
                 }
 
                 return;
@@ -404,34 +494,9 @@ static void ngx_http_proxy_send_request(
 
                 /* reinit the request chain */
 
-                p->work_request_hunks = ngx_alloc_chain_link(p->request->pool);
-                if (p->work_request_hunks == NULL) {
-                    ngx_http_proxy_finalize_request(p,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-                    return;
+                for (cl = p->request->request_hunks; cl; cl = cl->next) {
+                    cl->hunk->pos = cl->hunk->start;
                 }
-
-                tl = p->work_request_hunks;
-                ll = &p->work_request_hunks;
-
-                for (cl = p->request_hunks; cl; cl = cl->next) {
-                    tl->hunk = cl->hunk;
-                    *ll = tl;
-                    ll = &tl->next;
-                    cl->hunk->pos = cl->hunk->start;
-
-                    tl = ngx_alloc_chain_link(p->request->pool);
-                    if (tl == NULL) {
-                        ngx_http_proxy_finalize_request(p,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-                        return;
-                    }
-                }
-
-                *ll = NULL;
-
-            } else {
-                p->work_request_hunks = p->request_hunks;
             }
 
             p->request_sent = 0;
@@ -461,11 +526,13 @@ static void ngx_http_proxy_process_upstr
     c = rev->data;
     p = c->data;
 
+    p->action = "reading upstream status line";
+
     ngx_log_debug(rev->log, "http proxy process status line");
 
     if (rev->timedout) {
         p->timedout = 1;
-        ngx_http_proxy_next_upstream(p);
+        ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT);
         return;
     }
 
@@ -483,7 +550,7 @@ static void ngx_http_proxy_process_upstr
     n = ngx_http_proxy_read_upstream_header(p);
 
     if (n == NGX_ERROR) {
-        ngx_http_proxy_next_upstream(p);
+        ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR);
         return;
     }
 
@@ -496,21 +563,39 @@ static void ngx_http_proxy_process_upstr
     if (rc == NGX_AGAIN) {
         if (p->header_in->pos == p->header_in->last) {
             ngx_log_error(NGX_LOG_ERR, rev->log, 0,
-                          "upstream sent too big header");
-            ngx_http_proxy_finalize_request(p, NGX_HTTP_BAD_GATEWAY);
-            return;
+                          "upstream sent too long status line");
+            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER);
         }
 
         return;
     }
 
     if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) {
-        /* TODO: HTTP/0.9 */
+        ngx_log_error(NGX_LOG_ERR, rev->log, 0,
+                      "upstream sent no valid HTTP/1.0 header");
+
+        if (p->accel) {
+            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER);
+
+        } else {
+            p->request->http_version = NGX_HTTP_VERSION_9;
+            p->status = NGX_HTTP_OK;
+            ngx_http_proxy_send_response(p);
+        }
+
         return;
     }
 
     /* rc == NGX_OK */
 
+    if (p->status == NGX_HTTP_INTERNAL_SERVER_ERROR
+        && p->upstream.tries > 1
+        && (p->lcf->next_upstream & NGX_HTTP_PROXY_FT_HTTP_500))
+    {
+        ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_500);
+        return;
+    }
+
     p->status_line.len = p->status_end - p->status_start;
     p->status_line.data = ngx_palloc(p->request->pool, p->status_line.len + 1);
     if (p->status_line.data == NULL) {
@@ -522,7 +607,6 @@ static void ngx_http_proxy_process_upstr
     ngx_log_debug(rev->log, "http proxy status %d '%s'" _
                   p->status _ p->status_line.data);
 
-
     if (p->headers_in.headers) {
         p->headers_in.headers->nelts = 0;
     } else {
@@ -549,11 +633,13 @@ static void ngx_http_proxy_process_upstr
     p = c->data;
     r = p->request;
 
+    p->action = "reading upstream headers";
+
     ngx_log_debug(rev->log, "http proxy process header line");
 
     if (rev->timedout) {
         p->timedout = 1;
-        ngx_http_proxy_next_upstream(p);
+        ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT);
         return;
     }
 
@@ -564,7 +650,7 @@ static void ngx_http_proxy_process_upstr
             n = ngx_http_proxy_read_upstream_header(p);
 
             if (n == NGX_ERROR) {
-                ngx_http_proxy_next_upstream(p);
+                ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR);
                 return;
             }
 
@@ -609,6 +695,7 @@ static void ngx_http_proxy_process_upstr
                 if (ngx_strcasecmp(headers_in[i].name.data, h->key.data) == 0) {
                     *((ngx_table_elt_t **)
                         ((char *) &p->headers_in + headers_in[i].offset)) = h;
+                    break;
                 }
             }
 
@@ -631,7 +718,10 @@ static void ngx_http_proxy_process_upstr
 
             /* there was error while a header line parsing */
 
-            ngx_http_proxy_finalize_request(p, NGX_HTTP_BAD_GATEWAY);
+            ngx_log_error(NGX_LOG_ERR, rev->log, 0,
+                      upstream_header_errors[rc - NGX_HTTP_PARSE_HEADER_ERROR]);
+
+            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER);
             return;
         }
 
@@ -640,7 +730,8 @@ static void ngx_http_proxy_process_upstr
         if (p->header_in->last == p->header_in->end) {
             ngx_log_error(NGX_LOG_ERR, rev->log, 0,
                           "upstream sent too big header");
-            ngx_http_proxy_finalize_request(p, NGX_HTTP_BAD_GATEWAY);
+
+            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER);
             return;
         }
     }
@@ -679,7 +770,7 @@ static ssize_t ngx_http_proxy_read_upstr
     }
 
     if (n == 0) {
-        ngx_log_error(NGX_LOG_INFO, rev->log, 0,
+        ngx_log_error(NGX_LOG_ERR, rev->log, 0,
                       "upstream closed prematurely connection");
     }
 
@@ -834,11 +925,6 @@ static void ngx_http_proxy_send_response
 
     p->event_pipe = ep;
 
-#if 0
-    lcx = p->log->data;
-    lcx->action = "reading an upstream";
-#endif
-
     p->upstream.connection->read->event_handler = ngx_http_proxy_process_body;
     r->connection->write->event_handler = ngx_http_proxy_process_body;
 
@@ -861,11 +947,13 @@ static void ngx_http_proxy_process_body(
         ngx_log_debug(ev->log, "http proxy process downstream");
         r = c->data;
         p = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+        p->action = "sending to client";
 
     } else {
         ngx_log_debug(ev->log, "http proxy process upstream");
         p = c->data;
         r = p->request;
+        p->action = "reading upstream body";
     }
 
     ep = p->event_pipe;
@@ -1123,7 +1211,7 @@ static int ngx_http_proxy_parse_status_l
 }
 
 
-static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p)
+static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p, int ft_type)
 {
     ngx_event_connect_peer_failed(&p->upstream);
 
@@ -1137,7 +1225,7 @@ static void ngx_http_proxy_next_upstream
         p->upstream.connection = NULL;
     }
 
-    if (p->upstream.tries == 0) {
+    if (p->upstream.tries == 0 || !(p->lcf->next_upstream & ft_type)) {
         ngx_http_proxy_finalize_request(p,
                                         p->timedout ? NGX_HTTP_GATEWAY_TIME_OUT:
                                                       NGX_HTTP_BAD_GATEWAY);
@@ -1146,8 +1234,11 @@ static void ngx_http_proxy_next_upstream
 
     if (!p->fatal_error) {
         ngx_http_proxy_send_request(p);
+        return;
     }
 
+ngx_log_debug(p->request->connection->log, "FATAL ERROR IN NEXT UPSTREAM");
+
     return;
 }
 
@@ -1164,6 +1255,9 @@ static void ngx_http_proxy_finalize_requ
         rc = 0;
     }
 
+    p->request->connection->log->data = p->saved_ctx;
+    p->request->connection->log->handler = p->saved_handler;
+
     ngx_http_finalize_request(p->request, rc);
 
     p->fatal_error = 1;
@@ -1223,11 +1317,15 @@ static size_t ngx_http_proxy_log_error(v
     ngx_http_proxy_ctx_t *p = data;
 
     return ngx_snprintf(buf, len,
-            " while %s, upstream: %s, client: %s, URL: %s",
+            " while %s, client: %s, URL: %s, upstream: %s%s%s%s%s",
             p->action,
+            p->request->connection->addr_text.data,
+            p->request->unparsed_uri.data,
             p->upstream.peers->peers[p->upstream.cur_peer].addr_port_text.data,
-            p->request->connection->addr_text.data,
-            p->request->unparsed_uri.data);
+            p->lcf->upstream->uri.data,
+            p->request->uri.data + p->lcf->upstream->location->len,
+            p->request->args.len ? "?" : "",
+            p->request->args.len ? p->request->args.data : "");
 }
 
 
@@ -1250,6 +1348,7 @@ static void *ngx_http_proxy_create_loc_c
 
     */
 
+    conf->request_buffer_size = NGX_CONF_UNSET;
     conf->connect_timeout = NGX_CONF_UNSET;
     conf->send_timeout = NGX_CONF_UNSET;
     conf->header_buffer_size = NGX_CONF_UNSET;
@@ -1267,6 +1366,8 @@ static void *ngx_http_proxy_create_loc_c
     /* "proxy_cyclic_temp_file" is disabled */
     conf->cyclic_temp_file = 0;
 
+    conf->next_upstream = NGX_CONF_UNSET;
+
     return conf;
 }
 
@@ -1277,6 +1378,8 @@ static char *ngx_http_proxy_merge_loc_co
     ngx_http_proxy_loc_conf_t *prev = parent;
     ngx_http_proxy_loc_conf_t *conf = child;
 
+    ngx_conf_merge_size_value(conf->request_buffer_size,
+                              prev->request_buffer_size, 8192);
     ngx_conf_merge_msec_value(conf->connect_timeout,
                               prev->connect_timeout, 60000);
     ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 30000);
@@ -1297,6 +1400,9 @@ static char *ngx_http_proxy_merge_loc_co
     ngx_conf_merge_size_value(conf->temp_file_write_size,
                               prev->temp_file_write_size, 16384);
 
+    ngx_conf_merge_value(conf->next_upstream, prev->next_upstream,
+                         (NGX_HTTP_PROXY_FT_ERROR|NGX_HTTP_PROXY_FT_TIMEOUT));
+
     ngx_conf_merge_path_value(conf->temp_path, prev->temp_path,
                               "temp", 1, 2, 0, cf->pool);
 
--- a/src/http/modules/proxy/ngx_http_proxy_handler.h
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.h
@@ -19,6 +19,7 @@ typedef struct {
 
 
 typedef struct {
+    ssize_t                     request_buffer_size;
     ngx_msec_t                  connect_timeout;
     ngx_msec_t                  send_timeout;
     ssize_t                     header_buffer_size;
@@ -31,6 +32,8 @@ typedef struct {
     ssize_t                     temp_file_write_size;
     int                         cyclic_temp_file;
 
+    int                         next_upstream;
+
     ngx_path_t                 *temp_path;
 
     ngx_http_proxy_upstream_t  *upstream;
@@ -67,8 +70,7 @@ struct ngx_http_proxy_ctx_s {
     int                         status;
     ngx_str_t                   status_line;
 
-    ngx_chain_t                *work_request_hunks;
-    ngx_chain_t                *request_hunks;
+    ngx_output_chain_ctx_t     *output_chain_ctx;
 
     int                         method;
 
@@ -89,10 +91,16 @@ struct ngx_http_proxy_ctx_s {
 
     char                       *action;
     ngx_http_log_ctx_t         *saved_ctx;
+    ngx_log_handler_pt          saved_handler;
 };
 
 
 #define NGX_HTTP_PROXY_PARSE_NO_HEADER  20
 
+#define NGX_HTTP_PROXY_FT_ERROR         1
+#define NGX_HTTP_PROXY_FT_TIMEOUT       2
+#define NGX_HTTP_PROXY_FT_HTTP_HEADER   4
+#define NGX_HTTP_PROXY_FT_HTTP_500      8
+
 
 #endif /* _NGX_HTTP_PROXY_HANDLER_H_INCLUDED_ */
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -51,12 +51,8 @@ void ngx_http_close_request(ngx_http_req
 void ngx_http_close_connection(ngx_connection_t *c);
 
 
-
-int ngx_http_init_client_request_body(ngx_http_request_t *r, int size);
-int ngx_http_read_client_request_body(ngx_http_request_t *r);
-int ngx_http_init_client_request_body_chain(ngx_http_request_t *r);
-void ngx_http_reinit_client_request_body_hunks(ngx_http_request_t *r);
-
+int ngx_http_read_client_request_body(ngx_http_request_t *r,
+                                      int request_buffer_size);
 
 int ngx_http_send_header(ngx_http_request_t *r);
 int ngx_http_special_response_handler(ngx_http_request_t *r, int error);
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -232,7 +232,7 @@ void ngx_http_handler(ngx_http_request_t
         break;
     }
 
-#if 1
+#if 0
     /* TEST STUB */ r->http_version = NGX_HTTP_VERSION_10;
     /* TEST STUB */ r->keepalive = 0;
 #endif
@@ -295,11 +295,6 @@ static void ngx_http_run_phases(ngx_http
                 return;
             }
 
-            /* TODO THINK: is it dupliate NGX_DONE ??? */
-            if (r->closed) {
-                return;
-            }
-
             if (rc == NGX_DECLINED) {
                 continue;
             }
@@ -403,6 +398,10 @@ ngx_log_debug(r->connection->log, "rc: %
 
 int ngx_http_send_header(ngx_http_request_t *r)
 {
+    if (r->main) {
+        return NGX_OK;
+    }
+
     return (*ngx_http_top_header_filter)(r);
 }
 
--- a/src/http/ngx_http_output_filter.c
+++ b/src/http/ngx_http_output_filter.c
@@ -9,33 +9,6 @@ typedef struct {
 } ngx_http_output_filter_conf_t;
 
 
-typedef struct {
-
-    /*
-     * NOTE: we do not need now to store hunk in ctx,
-     * it's needed for the future NGX_FILE_AIO_READ support only
-     */
-
-    ngx_hunk_t    *hunk;
-
-    ngx_chain_t   *in;
-
-    /* TODO: out and last_out should be local variables */
-    ngx_chain_t   *out;
-    ngx_chain_t  **last_out;
-    /* */
-
-    ngx_chain_t   *free;
-    ngx_chain_t   *busy;
-
-    int            hunks;
-} ngx_http_output_filter_ctx_t;
-
-
-ngx_inline static int ngx_http_output_filter_need_to_copy(ngx_http_request_t *r,
-                                                          ngx_hunk_t *hunk);
-static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src,
-                                            int sendfile);
 static void *ngx_http_output_filter_create_conf(ngx_conf_t *cf);
 static char *ngx_http_output_filter_merge_conf(ngx_conf_t *cf,
                                                void *parent, void *child);
@@ -79,244 +52,34 @@ ngx_module_t  ngx_http_output_filter_mod
 
 int ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    int                             rc, last;
-    ssize_t                         size;
-    ngx_chain_t                    *cl;
-    ngx_http_output_filter_ctx_t   *ctx;
+    ngx_output_chain_ctx_t         *ctx;
     ngx_http_output_filter_conf_t  *conf;
 
     ctx = ngx_http_get_module_ctx(r->main ? r->main : r,
                                             ngx_http_output_filter_module);
 
     if (ctx == NULL) {
-        ngx_http_create_ctx(r, ctx, ngx_http_output_filter_module,
-                            sizeof(ngx_http_output_filter_ctx_t), NGX_ERROR);
-        ctx->last_out = &ctx->out;
-    }
+        conf = ngx_http_get_module_loc_conf(r->main ? r->main : r,
+                                            ngx_http_output_filter_module);
 
-    /*
-     * the short path for the case when the chain ctx->in is empty
-     * and the incoming chain is empty too or it has the single hunk
-     * that does not require the copy
-     */
-
-    if (ctx->in == NULL) {
+        ngx_http_create_ctx(r, ctx, ngx_http_output_filter_module,
+                            sizeof(ngx_output_chain_ctx_t), NGX_ERROR);
 
-        if (in == NULL) {
-            return ngx_http_top_body_filter(r, in);
-        }
+        ctx->sendfile = r->sendfile;
+        ctx->need_in_memory = r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY;
+        ctx->need_in_temp = r->filter & NGX_HTTP_FILTER_NEED_TEMP;
 
-        if (in->next == NULL
-            && (!ngx_http_output_filter_need_to_copy(r, in->hunk)))
-        {
-            return ngx_http_top_body_filter(r, in);
-        }
-    }
+        ctx->pool = r->pool;
+        ctx->bufs = conf->bufs;
+        ctx->tag = (ngx_hunk_tag_t) &ngx_http_output_filter_module;
 
-    /* add the incoming hunk to the chain ctx->in */
+        ctx->output_filter = (ngx_output_chain_filter_pt)
+                                                      ngx_http_top_body_filter;
+        ctx->output_ctx = r;
 
-    if (in) {
-        if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
-            return NGX_ERROR;
-        }
     }
 
-    conf = ngx_http_get_module_loc_conf(r->main ? r->main : r,
-                                        ngx_http_output_filter_module);
-
-    last = NGX_NONE;
-
-    for ( ;; ) {
-
-        while (ctx->in) {
-
-            if (!ngx_http_output_filter_need_to_copy(r, ctx->in->hunk)) {
-
-                /* move the chain link to the chain ctx->out */
-
-                cl = ctx->in;
-                ctx->in = cl->next;
-
-                *ctx->last_out = cl;
-                ctx->last_out = &cl->next;
-                cl->next = NULL;
-
-                continue;
-            }
-
-            if (ctx->hunk == NULL) {
-
-                /* get the free hunk */
-
-                if (ctx->free) {
-                    ctx->hunk = ctx->free->hunk;
-                    ctx->free = ctx->free->next;
-
-                } else if (ctx->hunks < conf->bufs.num) {
-                    ngx_test_null(ctx->hunk,
-                                  ngx_create_temp_hunk(r->pool, conf->bufs.size,
-                                                       0, 0),
-                                  NGX_ERROR);
-                    ctx->hunk->tag = (ngx_hunk_tag_t)
-                                                &ngx_http_output_filter_module;
-                    ctx->hunk->type |= NGX_HUNK_RECYCLED;
-                    ctx->hunks++;
-
-                } else {
-                    break;
-                }
-            }
-
-            rc = ngx_http_output_filter_copy_hunk(ctx->hunk, ctx->in->hunk,
-                                                  r->sendfile);
-
-            if (rc == NGX_ERROR) {
-                return rc;
-            }
-
-#if (NGX_FILE_AIO_READ)
-            if (rc == NGX_AGAIN) {
-                if (ctx->out) {
-                    break;
-                }
-                return rc;
-            }
-#endif
-
-            if (ctx->in->hunk->type & NGX_HUNK_IN_MEMORY) {
-                size = ctx->in->hunk->last - ctx->in->hunk->pos;
-
-            } else {
-                size = (size_t) (ctx->in->hunk->file_last
-                                                    - ctx->in->hunk->file_pos);
-            }
-
-            /* delete the completed hunk from the chain ctx->in */
-
-            if (size == 0) {
-                ctx->in = ctx->in->next;
-            }
-
-            ngx_alloc_link_and_set_hunk(cl, ctx->hunk, r->pool, NGX_ERROR);
-            *ctx->last_out = cl;
-            ctx->last_out = &cl->next;
-            ctx->hunk = NULL;
-
-            if (ctx->free == NULL) {
-                break;
-            }
-        }
-
-        if (ctx->out == NULL && last != NGX_NONE) {
-            return last;
-        }
-
-        last = ngx_http_top_body_filter(r, ctx->out);
-
-        ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out,
-                               (ngx_hunk_tag_t) &ngx_http_output_filter_module);
-        ctx->last_out = &ctx->out;
-    }
-}
-
-
-ngx_inline static int ngx_http_output_filter_need_to_copy(ngx_http_request_t *r,
-                                                          ngx_hunk_t *hunk)
-{
-    if (ngx_hunk_special(hunk)) {
-        return 0;
-    }
-
-    if (!r->sendfile) {
-        return 1;
-    }
-
-    if ((r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY)
-        && (!(hunk->type & NGX_HUNK_IN_MEMORY)))
-    {
-        return 1;
-    }
-
-
-    if ((r->filter & NGX_HTTP_FILTER_NEED_TEMP)
-        && (hunk->type & (NGX_HUNK_MEMORY|NGX_HUNK_MMAP)))
-    {
-        return 1;
-    }
-
-    return 0;
-}
-
-
-static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src,
-                                            int sendfile)
-{
-    ssize_t  n, size;
-
-    if (src->type & NGX_HUNK_IN_MEMORY) {
-        size = src->last - src->pos;
-    } else {
-        size = (size_t) (src->file_last - src->file_pos);
-    }
-
-    if (size > (dst->end - dst->pos)) {
-        size = dst->end - dst->pos;
-    }
-
-    if (src->type & NGX_HUNK_IN_MEMORY) {
-        ngx_memcpy(dst->pos, src->pos, size);
-        src->pos += size;
-        dst->last += size;
-
-        if (src->type & NGX_HUNK_FILE) {
-            src->file_pos += size;
-        }
-
-        if ((src->type & NGX_HUNK_LAST) && src->pos == src->last) {
-            dst->type |= NGX_HUNK_LAST;
-        }
-
-    } else {
-        n = ngx_read_file(src->file, dst->pos, size, src->file_pos);
-
-if (n == 0) {
-ngx_log_debug(src->file->log, "READ: %qd:%qd %X:%X %X:%X" _
-              src->file_pos _ src->file_last _
-              dst->pos _ dst->last _ dst->start _ dst->end);
-}
-
-        if (n == NGX_ERROR) {
-            return n;
-        }
-
-#if (NGX_FILE_AIO_READ)
-        if (n == NGX_AGAIN) {
-            return n;
-        }
-#endif
-
-        if (n != size) {
-            ngx_log_error(NGX_LOG_ALERT, src->file->log, 0,
-                          ngx_read_file_n " reads only %d of %d from file",
-                          n, size);
-            if (n == 0) {
-                return NGX_ERROR;
-            }
-        }
-
-        src->file_pos += n;
-        dst->last += n;
-
-        if (!sendfile) {
-            dst->type &= ~NGX_HUNK_FILE;
-        }
-
-        if ((src->type & NGX_HUNK_LAST) && src->file_pos == src->file_last) {
-            dst->type |= NGX_HUNK_LAST;
-        }
-    }
-
-    return NGX_OK;
+    return ngx_output_chain(ctx, in);
 }
 
 
@@ -340,7 +103,7 @@ static char *ngx_http_output_filter_merg
     ngx_http_output_filter_conf_t *prev = parent;
     ngx_http_output_filter_conf_t *conf = child;
 
-    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 2, 32768);
+    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 1, 32768);
 
     return NULL;
 }
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -23,14 +23,14 @@ static void ngx_http_set_lingering_close
 static void ngx_http_lingering_close_handler(ngx_event_t *ev);
 static void ngx_http_empty_handler(ngx_event_t *wev);
 
-static void ngx_http_header_parse_error(ngx_http_request_t *r,
-                                        int parse_err, int error);
+static void ngx_http_client_error(ngx_http_request_t *r,
+                                  int client_error, int error);
 static size_t ngx_http_log_error(void *data, char *buf, size_t len);
 
 
-/* NGX_HTTP_PARSE_ ... errors */
+/* NGX_HTTP_PARSE_... errors */
 
-static char *header_errors[] = {
+static char *client_header_errors[] = {
     "client %s sent invalid method",
     "client %s sent invalid request",
     "client %s sent too long URI",
@@ -66,6 +66,12 @@ static ngx_http_header_t headers_in[] = 
 };
 
 
+static void ngx_http_dummy(ngx_event_t *wev)
+{
+    return;
+}
+
+
 void ngx_http_init_connection(ngx_connection_t *c)
 {
     ngx_event_t         *rev;
@@ -117,6 +123,16 @@ void ngx_http_init_connection(ngx_connec
         ngx_http_close_connection(c);
         return;
     }
+
+#if 0
+    c->write->ready = 0;
+    c->write->event_handler = ngx_http_dummy;
+
+    if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        ngx_http_close_connection(c);
+        return;
+    }
+#endif
 }
 
 
@@ -135,6 +151,12 @@ static void ngx_http_init_request(ngx_ev
 
     c = rev->data;
 
+    if (rev->timedout) {
+        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+        ngx_http_close_connection(c);
+        return;
+    }
+
     if (c->data) {
         r = c->data;
         ngx_memzero(r, sizeof(ngx_http_request_t));
@@ -281,8 +303,7 @@ static void ngx_http_process_request_lin
     ngx_log_debug(rev->log, "http process request line");
 
     if (rev->timedout) {
-        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
-        ngx_http_close_connection(c);
+        ngx_http_client_error(r, 0, NGX_HTTP_REQUEST_TIME_OUT);
         return;
     }
 
@@ -304,8 +325,8 @@ static void ngx_http_process_request_lin
             r->request_line.data = r->request_start;
             r->request_line.data[r->request_line.len] = '\0';
 
-            ngx_http_header_parse_error(r, NGX_HTTP_PARSE_INVALID_REQUEST,
-                                        NGX_HTTP_BAD_REQUEST);
+            ngx_http_client_error(r, NGX_HTTP_PARSE_INVALID_REQUEST,
+                                  NGX_HTTP_BAD_REQUEST);
             return;
         }
 
@@ -317,8 +338,8 @@ static void ngx_http_process_request_lin
         {
             /* no space for "\r\n" at the end of the header */
 
-            ngx_http_header_parse_error(r, NGX_HTTP_PARSE_TOO_LONG_URI,
-                                        NGX_HTTP_REQUEST_URI_TOO_LARGE);
+            ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_URI,
+                                  NGX_HTTP_REQUEST_URI_TOO_LARGE);
             return;
         }
 
@@ -447,7 +468,7 @@ static void ngx_http_process_request_lin
 
         /* there was error while a request line parsing */
 
-        ngx_http_header_parse_error(r, rc, NGX_HTTP_BAD_REQUEST);
+        ngx_http_client_error(r, rc, NGX_HTTP_BAD_REQUEST);
 
         return;
     }
@@ -470,8 +491,8 @@ static void ngx_http_process_request_lin
             offset = r->request_start - r->header_in->start;
 
             if (offset == 0) {
-                ngx_http_header_parse_error(r, NGX_HTTP_PARSE_TOO_LONG_URI,
-                                            NGX_HTTP_REQUEST_URI_TOO_LARGE);
+                ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_URI,
+                                      NGX_HTTP_REQUEST_URI_TOO_LARGE);
                 return;
             }
 
@@ -492,8 +513,8 @@ static void ngx_http_process_request_lin
             }
 
         } else {
-            ngx_http_header_parse_error(r, NGX_HTTP_PARSE_TOO_LONG_URI,
-                                        NGX_HTTP_REQUEST_URI_TOO_LARGE);
+            ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_URI,
+                                  NGX_HTTP_REQUEST_URI_TOO_LARGE);
         }
     }
 
@@ -516,8 +537,7 @@ static void ngx_http_process_request_hea
     ngx_log_debug(rev->log, "http process request header line");
 
     if (rev->timedout) {
-        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
-        ngx_http_close_connection(c);
+        ngx_http_client_error(r, 0, NGX_HTTP_REQUEST_TIME_OUT);
         return;
     }
 
@@ -605,7 +625,7 @@ static void ngx_http_process_request_hea
             rc = ngx_http_process_request_header(r);
 
             if (rc != NGX_OK) {
-                ngx_http_header_parse_error(r, rc, NGX_HTTP_BAD_REQUEST);
+                ngx_http_client_error(r, rc, NGX_HTTP_BAD_REQUEST);
                 return;
             }
 
@@ -621,7 +641,7 @@ static void ngx_http_process_request_hea
 
             /* there was error while a header line parsing */
 
-            ngx_http_header_parse_error(r, rc, NGX_HTTP_BAD_REQUEST);
+            ngx_http_client_error(r, rc, NGX_HTTP_BAD_REQUEST);
             return;
         }
 
@@ -636,9 +656,8 @@ static void ngx_http_process_request_hea
                 offset = r->header_name_start - r->header_in->start;
 
                 if (offset == 0) {
-                    ngx_http_header_parse_error(r,
-                                                NGX_HTTP_PARSE_TOO_LONG_HEADER,
-                                                NGX_HTTP_BAD_REQUEST);
+                    ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_HEADER,
+                                          NGX_HTTP_BAD_REQUEST);
                     return;
                 }
 
@@ -653,8 +672,8 @@ static void ngx_http_process_request_hea
                 r->header_end -= offset;
 
             } else {
-                ngx_http_header_parse_error(r, NGX_HTTP_PARSE_TOO_LONG_HEADER,
-                                            NGX_HTTP_BAD_REQUEST);
+                ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_HEADER,
+                                      NGX_HTTP_BAD_REQUEST);
                 return;
             }
         }
@@ -798,12 +817,14 @@ static int ngx_http_process_request_head
 
 void ngx_http_finalize_request(ngx_http_request_t *r, int rc)
 {
-    ngx_log_debug(r->connection->log, "finalize http request");
+    /* r can be already destroyed when rc == NGX_DONE */
 
-    if (rc == NGX_DONE || r->main || r->closed) {
+    if (rc == NGX_DONE || r->main) {
         return;
     }
 
+    ngx_log_debug(r->connection->log, "finalize http request");
+
     if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
 
         if (r->connection->read->timer_set) {
@@ -886,6 +907,17 @@ void ngx_http_writer(ngx_event_t *wev)
     c = wev->data;
     r = c->data;
 
+#if 0 /* TODO: THINK */
+    if (wev->delayed) {
+        return;
+    }
+#endif
+
+    if (wev->timedout) {
+        ngx_http_client_error(r, 0, NGX_HTTP_REQUEST_TIME_OUT);
+        return;
+    }
+
     rc = ngx_http_output_filter(r, NULL);
 
     ngx_log_debug(c->log, "writer output filter: %d" _ rc);
@@ -1356,8 +1388,6 @@ void ngx_http_close_request(ngx_http_req
 
     ngx_destroy_pool(r->pool);
 
-    r->closed = 1;
-
     return;
 }
 
@@ -1405,23 +1435,32 @@ void ngx_http_close_connection(ngx_conne
 }
 
 
-static void ngx_http_header_parse_error(ngx_http_request_t *r,
-                                        int parse_err, int error)
+static void ngx_http_client_error(ngx_http_request_t *r,
+                                  int client_error, int error)
 {
     ngx_http_log_ctx_t  *ctx;
 
     ctx = r->connection->log->data;
+
+    if (error == NGX_HTTP_REQUEST_TIME_OUT) {
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT,
+                      "client timed out");
+        ngx_http_close_request(r, error);
+        ngx_http_close_connection(r->connection);
+        return;
+    }
+
     r->connection->log->handler = NULL;
 
     if (ctx->url) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      header_errors[parse_err - NGX_HTTP_PARSE_INVALID_METHOD],
-                      ctx->client, ctx->url);
+                     client_header_errors[client_error - NGX_HTTP_CLIENT_ERROR],
+                     ctx->client, ctx->url);
 
     } else {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      header_errors[parse_err - NGX_HTTP_PARSE_INVALID_METHOD],
-                      ctx->client);
+                     client_header_errors[client_error - NGX_HTTP_CLIENT_ERROR],
+                     ctx->client);
     }
 
     r->connection->log->handler = ngx_http_log_error;
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -18,10 +18,14 @@
 
 
 #define NGX_HTTP_PARSE_HEADER_DONE        1
+
+#define NGX_HTTP_CLIENT_ERROR             10
 #define NGX_HTTP_PARSE_INVALID_METHOD     10
 #define NGX_HTTP_PARSE_INVALID_REQUEST    11
 #define NGX_HTTP_PARSE_TOO_LONG_URI       12
 #define NGX_HTTP_PARSE_INVALID_09_METHOD  13
+
+#define NGX_HTTP_PARSE_HEADER_ERROR       14
 #define NGX_HTTP_PARSE_INVALID_HEADER     14
 #define NGX_HTTP_PARSE_TOO_LONG_HEADER    15
 #define NGX_HTTP_PARSE_NO_HOST_HEADER     16
@@ -176,6 +180,13 @@ struct ngx_http_request_s {
     int                  phase_handler;
     ngx_http_handler_pt  content_handler;
 
+    ngx_temp_file_t     *temp_file;
+    ngx_chain_t         *request_hunks;
+    ngx_hunk_t          *request_body_hunk;
+    int                  request_body_len;
+    void               (*request_body_handler) (void *data); 
+    void                *data;
+
     char                *discarded_buffer;
 
     /* URI is not started with '/' - "GET http://" */
@@ -197,7 +208,9 @@ struct ngx_http_request_s {
     unsigned             header_only:1;
     unsigned             keepalive:1;
     unsigned             lingering_close:1;
+#if 0
     unsigned             closed:1;
+#endif
 
     /* TODO: use filter or bits ???? */
     int                  filter;
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -1,196 +1,156 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
+#include <ngx_event.h>
 #include <ngx_http.h>
 
 
-int ngx_http_init_client_request_body(ngx_http_request_t *r, int size)
-{
-    int                       header_in_part, len;
-    ngx_hunk_t               *h;
-    ngx_http_request_body_t  *rb;
+static void ngx_http_read_client_request_body_handler(ngx_event_t *rev);
+
 
-    ngx_test_null(rb, ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)),
-                  NGX_HTTP_INTERNAL_SERVER_ERROR);
+int ngx_http_read_client_request_body(ngx_http_request_t *r,
+                                      int request_buffer_size)
+{
+    ssize_t       size;
+    ngx_hunk_t   *h;
+    ngx_chain_t  *cl;
 
-    header_in_part = r->header_in->end - r->header_in->pos;
-
-    if (header_in_part) {
-        rb->header_in_pos = r->header_in->pos;
-    }
+    size = r->header_in->last - r->header_in->pos;
 
-    if (header_in_part > r->headers_in.content_length_n) {
-        header_in_part = r->headers_in.content_length_n;
-
-    } else {
-        len = r->headers_in.content_length_n - header_in_part;
-        if (len > size) {
-            len = size;
+    if (size) {
+        ngx_test_null(h, ngx_calloc_hunk(r->pool),
+                      NGX_HTTP_INTERNAL_SERVER_ERROR);
 
-        } else if (len > NGX_PAGE_SIZE) {
-            len = ((len + NGX_PAGE_SIZE - 1) / NGX_PAGE_SIZE) * NGX_PAGE_SIZE;
-        }
+        h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP;
+        h->start = h->pos = r->header_in->pos;
+        h->end = h->last = r->header_in->last;
 
-        if (len) {
-            ngx_test_null(rb->hunk, ngx_create_temp_hunk(r->pool, len, 0, 0),
-                          NGX_HTTP_INTERNAL_SERVER_ERROR);
+        ngx_alloc_link_and_set_hunk(r->request_hunks, h, r->pool,
+                                    NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+        if (size >= r->headers_in.content_length_n) {
+            r->header_in->pos += r->headers_in.content_length_n;
+            return NGX_OK;
         }
     }
 
-    r->request_body = rb;
+    r->request_body_len = r->headers_in.content_length_n - size;
+
+    if (r->request_body_len < request_buffer_size + (request_buffer_size >> 2))
+    {
+        size = r->request_body_len;
+
+    } else {
+        size = request_buffer_size;
+    }
+
+    ngx_test_null(r->request_body_hunk,
+                  ngx_create_temp_hunk(r->pool, size, 0, 0),
+                  NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+    r->connection->read->event_handler =
+                                     ngx_http_read_client_request_body_handler;
+
+    ngx_http_read_client_request_body_handler(r->connection->read);
+
+    ngx_alloc_link_and_set_hunk(cl, r->request_body_hunk, r->pool,
+                                NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+    if (r->request_hunks) {
+        r->request_hunks->next = cl;
+
+    } else {
+        r->request_hunks = cl;
+    }
+
+    if (r->request_body_len) {
+        return NGX_AGAIN;
+    }
 
     return NGX_OK;
 }
 
 
-int ngx_http_read_client_request_body(ngx_http_request_t *r)
+static void ngx_http_read_client_request_body_handler(ngx_event_t *rev)
 {
-    int                       size, n, rc;
-    ngx_chain_t              *entry;
-    ngx_http_request_body_t  *rb;
-
-    rb = r->request_body;
-
-    do {
-        if (r->header_in->last < r->header_in->end) {
-            rb->chain[0].hunk = r->header_in;
-
-            if (rb->hunk) {
-                rb->chain[0].next = &rb->chain[1];
-                rb->chain[1].hunk = rb->hunk;
-                rb->chain[1].next = NULL;
+    ssize_t              n, size;
+    ngx_hunk_t          *h;
+    ngx_connection_t    *c;
+    ngx_http_request_t  *r;
 
-            } else {
-                rb->chain[0].next = NULL;
-            }
-
-        } else {
-            rb->chain[0].hunk = rb->hunk;
-            rb->chain[0].next = NULL;
-        }
+    c = rev->data;
+    r = c->data;
 
-        n = ngx_recv_chain(r->connection, rb->chain);
-
-        if (n == NGX_ERROR) {
-            return NGX_ERROR;
-        }
-
-        if (n == NGX_AGAIN) {
-            return NGX_AGAIN;
-        }
+    if (r->request_body_hunk->end - r->request_body_hunk->last == 0) {
+        n = ngx_write_chain_to_temp_file(r->temp_file,
+                               r->request_hunks->next ? r->request_hunks->next:
+                                                        r->request_hunks);
+        /* TODO: n == 0 or not complete and level event */
 
-        for (entry = rb->chain; entry; entry = entry->next) {
-            size = entry->hunk->end - entry->hunk->last;
-
-            if (n >= size) {
-                n -= size;
-                entry->hunk->last = entry->hunk->end;
+        r->request_body_hunk->pos = r->request_body_hunk->start;
+        r->request_body_hunk->last = r->request_body_hunk->start;
+    }
 
-                continue;
-            }
-
-            entry->hunk->last += n;
-
-            break;
-        }
+    size = r->request_body_hunk->end - r->request_body_hunk->last;
 
-        if (rb->hunk && rb->hunk->last == rb->hunk->end) {
-            if (rb->temp_file.fd == NGX_INVALID_FILE) {
-                rc = ngx_create_temp_file(&rb->temp_file, rb->temp_path,
-                                          r->pool, 0);
-
-                if (rc == NGX_ERROR) {
-                    return NGX_ERROR;
-                }
+    if (size > r->request_body_len) {
+        size = r->request_body_len;
+    }
 
-                if (rc == NGX_AGAIN) {
-                    return NGX_AGAIN;
-                }
-            }
+    n = ngx_recv(c, r->request_body_hunk->last, size);
 
-            n = ngx_write_file(&rb->temp_file, rb->hunk->pos,
-                               rb->hunk->last - rb->hunk->pos, rb->offset);
-
-            if (rc == NGX_ERROR) {
-                return NGX_ERROR;
-            }
-
-            rb->offset += n;
-            rb->hunk->last = rb->hunk->pos;
+    if (n == NGX_AGAIN) {
+        if (ngx_handle_read_event(rev) == NGX_ERROR) {
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         }
 
-    } while (r->connection->read->ready);
+        return;
+    }
 
-    return NGX_OK;
-}
-
+    if (n == 0) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "client closed prematurely connection");
+    }
 
-int ngx_http_init_client_request_body_chain(ngx_http_request_t *r)
-{
-    int                       i;
-    ngx_hunk_t               *h;
-    ngx_http_request_body_t  *rb;
-
-    rb = r->request_body;
+    if (n == 0 || n == NGX_ERROR) {
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return;
+    }
 
-    rb->chain[0].hunk = rb->header_out;
-    i = 0;
+    r->request_body_hunk->last += n;
+    r->request_body_len -= n;
 
-    if (r->header_in->pos < r->header_in->last) {
-        rb->chain[i].next = &rb->chain[i + 1];
-        i++;
-        rb->chain[i].hunk = r->header_in;
+    if (r->request_body_len) {
+        return;
     }
 
-    if (rb->temp_file.fd != NGX_INVALID_FILE) {
-
-        if (rb->file_hunk == NULL) {
-            ngx_test_null(h, ngx_alloc_hunk(r->pool), NGX_ERROR);
+    if (r->temp_file->file.fd != NGX_INVALID_FILE) {
 
-            h->type = NGX_HUNK_FILE;
-            h->pos = h->start = h->pre_start = 0;
-            h->last = h->end = h->post_end = 0;
-            h->file_pos = 0;
-            h->file_last = rb->offset;
-            h->file = &rb->temp_file;
-            h->shadow = NULL;
-            h->tag = 0;
+        /* save the last part */
+        n = ngx_write_chain_to_temp_file(r->temp_file,
+                               r->request_hunks->next ? r->request_hunks->next:
+                                                        r->request_hunks);
+        /* TODO: n == 0 or not complete and level event */
 
-            rb->file_hunk = h;
+
+        h = ngx_calloc_hunk(r->pool);
+        if (h == NULL) {
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return;
         }
 
-        rb->chain[i].next = &rb->chain[i + 1];
-        i++;
-        rb->chain[i].hunk = rb->file_hunk;
+        h->type = NGX_HUNK_FILE;
+        h->file_pos = 0;
+        h->file_last = r->temp_file->file.offset;
+        h->file = &r->temp_file->file;
+
+        if (r->request_hunks->next) {
+            r->request_hunks->next->hunk = h;
+
+        } else {
+            r->request_hunks->hunk = h;
+        }
     }
 
-    if (rb->hunk && rb->hunk->pos < rb->hunk->last) {
-        rb->chain[i].next = &rb->chain[i + 1];
-        i++;
-        rb->chain[i].hunk = h;
-    }
-
-    rb->chain[i].next = NULL;
-
-    return NGX_OK;
+    r->request_body_handler(r->data);
 }
-
-
-void ngx_http_reinit_client_request_body_hunks(ngx_http_request_t *r)
-{
-    ngx_http_request_body_t  *rb;
-
-    rb = r->request_body;
-
-    if (rb->header_in_pos) {
-        r->header_in->pos = rb->header_in_pos;
-    }
-
-    if (rb->file_hunk) {
-        rb->file_hunk->file_pos = 0;
-    }
-
-    if (rb->hunk) {
-        rb->hunk->pos = rb->hunk->start;
-    }
-}