changeset 153:c71aeb75c071

nginx-0.0.1-2003-10-21-20:49:56 import
author Igor Sysoev <igor@sysoev.ru>
date Tue, 21 Oct 2003 16:49:56 +0000
parents fb48bf4fea1c
children eac26585476e
files src/core/ngx_hunk.c src/event/ngx_event_pipe.c src/event/ngx_event_pipe.h src/event/ngx_event_proxy.c src/event/ngx_event_proxy.h src/http/modules/ngx_http_chunked_filter.c src/http/modules/ngx_http_gzip_filter.c src/http/modules/ngx_http_not_modified_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_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_filter.h src/http/ngx_http_header_filter.c src/http/ngx_http_output_filter.c src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_special_response.c src/http/ngx_http_write_filter.c src/os/unix/ngx_freebsd_sendfile_chain.c
diffstat 20 files changed, 353 insertions(+), 301 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_hunk.c
+++ b/src/core/ngx_hunk.c
@@ -147,18 +147,6 @@ void ngx_chain_update_chains(ngx_chain_t
         if (ngx_hunk_size((*busy)->hunk) > 0) {
             break;
         }
-#if 0
-        if ((*busy)->hunk->type & NGX_HUNK_IN_MEMORY) {
-            if ((*busy)->hunk->pos != (*busy)->hunk->last) {
-                break;
-            }
-
-        } else {
-            if ((*busy)->hunk->file_pos != (*busy)->hunk->file_last) {
-                break;
-            }
-        }
-#endif
 
 #if (HAVE_WRITE_ZEROCOPY)
         if ((*busy)->hunk->type & NGX_HUNK_ZEROCOPY_BUSY) {
rename from src/event/ngx_event_proxy.c
rename to src/event/ngx_event_pipe.c
--- a/src/event/ngx_event_proxy.c
+++ b/src/event/ngx_event_pipe.c
@@ -2,29 +2,33 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_event.h>
-#include <ngx_event_proxy.h>
+#include <ngx_event_pipe.h>
+
 
-static int ngx_event_proxy_write_chain_to_temp_file(ngx_event_proxy_t *p);
+static int ngx_event_pipe_read_upstream(ngx_event_pipe_t *p);
+static int ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p);
+
+static int ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p);
 ngx_inline static void ngx_remove_shadow_links(ngx_hunk_t *hunk);
 ngx_inline static void ngx_remove_shadow_free_raw_hunk(ngx_chain_t **free,
                                                        ngx_hunk_t *h);
 ngx_inline static void ngx_add_after_partially_filled_hunk(ngx_chain_t **chain,
                                                            ngx_chain_t *ce);
-static int ngx_drain_chains(ngx_event_proxy_t *p);
+static int ngx_drain_chains(ngx_event_pipe_t *p);
 
 
-int ngx_event_proxy(ngx_event_proxy_t *p, int do_write)
+int ngx_event_pipe(ngx_event_pipe_t *p, int do_write)
 {
     for ( ;; ) {
         if (do_write) {
-            if (ngx_event_proxy_write_to_downstream(p) == NGX_ABORT) {
+            if (ngx_event_pipe_write_to_downstream(p) == NGX_ABORT) {
                 return NGX_ABORT;
             }
         }
 
         p->read = 0;
 
-        if (ngx_event_proxy_read_upstream(p) == NGX_ABORT) {
+        if (ngx_event_pipe_read_upstream(p) == NGX_ABORT) {
             return NGX_ABORT;
         }
 
@@ -48,7 +52,7 @@ int ngx_event_proxy(ngx_event_proxy_t *p
 }
 
 
-int ngx_event_proxy_read_upstream(ngx_event_proxy_t *p)
+int ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
 {
     int           n, rc, size;
     ngx_hunk_t   *h;
@@ -116,7 +120,6 @@ int ngx_event_proxy_read_upstream(ngx_ev
 
                 chain = p->free_raw_hunks;
                 p->free_raw_hunks = NULL;
-ngx_log_debug(p->log, "FREE: %08X:%d" _ chain->hunk->pos _ chain->hunk->end - chain->hunk->last);
 
             } else if (p->hunks < p->bufs.num) {
 
@@ -133,22 +136,22 @@ ngx_log_debug(p->log, "FREE: %08X:%d" _ 
             } else if (!p->cachable && p->downstream->write->ready) {
 
                 /*
-                 * If the hunks are not needed to be saved in a cache and
-                 * a downstream is ready then write the hunks to a downstream.
+                 * if the hunks are not needed to be saved in a cache and
+                 * a downstream is ready then write the hunks to a downstream
                  */
 
                 ngx_log_debug(p->log, "downstream ready");
 
                 break;
 
-            } else if (p->temp_offset < p->max_temp_file_size) {
+            } else if (p->cachable || p->temp_offset < p->max_temp_file_size) {
 
                 /*
-                 * If it's allowed then save some hunks from r->in
-                 * to a temporary file, and add them to a r->out chain.
+                 * if it's allowed then save some hunks from r->in
+                 * to a temporary file, and add them to a r->out chain
                  */
 
-                rc = ngx_event_proxy_write_chain_to_temp_file(p);
+                rc = ngx_event_pipe_write_chain_to_temp_file(p);
 
                 ngx_log_debug(p->log, "temp offset: %d" _ p->temp_offset);
 
@@ -223,9 +226,7 @@ ngx_log_debug(p->log, "FREE: %08X:%d" _ 
                 ce = ce->next;
 
             } else {
-ngx_log_debug(p->log, "PART: %08X:%d:%d" _ ce->hunk->pos _ ce->hunk->last - ce->hunk->pos _ n);
                 ce->hunk->last += n;
-ngx_log_debug(p->log, "PART: %08X:%d" _ ce->hunk->pos _ ce->hunk->end - ce->hunk->last);
                 n = 0;
             }
         }
@@ -243,7 +244,7 @@ ngx_log_debug(p->log, "PART: %08X:%d" _ 
     }
 
     if (p->cachable && p->in) {
-        if (ngx_event_proxy_write_chain_to_temp_file(p) == NGX_ABORT) {
+        if (ngx_event_pipe_write_chain_to_temp_file(p) == NGX_ABORT) {
             return NGX_ABORT;
         }
     }
@@ -252,11 +253,11 @@ ngx_log_debug(p->log, "PART: %08X:%d" _ 
 }
 
 
-int ngx_event_proxy_write_to_downstream(ngx_event_proxy_t *p)
+int ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
 {
     size_t        busy_len;
     ngx_hunk_t   *h;
-    ngx_chain_t  *out, *ce, *te;
+    ngx_chain_t  *out, **le, *ce, *te;
 
     ngx_log_debug(p->log, "write downstream: %d" _ p->downstream->write->ready);
 
@@ -285,38 +286,47 @@ int ngx_event_proxy_write_to_downstream(
             }
         }
 
+        out = NULL;
+        le = NULL;
 
-        if (p->out) {
-            out = p->out;
+        for ( ;; ) {
+            if (p->out) {
+                ce = p->out;
+
+                if (!(p->upstream_eof || p->upstream_error || p->upstream_done)
+                    && (busy_len + ngx_hunk_size(ce->hunk) > p->max_busy_len))
+                {
+                    break;
+                }
 
-            if (!(p->upstream_eof || p->upstream_error || p->upstream_done)
-                && (busy_len + ngx_hunk_size(out->hunk) > p->max_busy_len))
-            {
+                p->out = p->out->next;
+                ngx_remove_shadow_free_raw_hunk(&p->free_raw_hunks, ce->hunk);
+
+            } else if (!p->cachable && p->in) {
+                ce = p->in;
+
+                if (!(p->upstream_eof || p->upstream_error || p->upstream_done)
+                    && (busy_len + ngx_hunk_size(ce->hunk) > p->max_busy_len))
+                {
+                    break;
+                }
+
+                p->in = p->in->next;
+
+            } else {
                 break;
             }
 
-            p->out = p->out->next;
-            ngx_remove_shadow_free_raw_hunk(&p->free_raw_hunks, out->hunk);
-
-        } else if (!p->cachable && p->in) {
-            out = p->in;
+            busy_len += ngx_hunk_size(ce->hunk);
+            ce->next = NULL;
+            ngx_chain_add_ce(out, le, ce);
+        }
 
-            if (!(p->upstream_eof || p->upstream_error || p->upstream_done)
-                && (busy_len + ngx_hunk_size(out->hunk) > p->max_busy_len))
-            {
-                break;
-            }
-
-            p->in = p->in->next;
-
-        } else {
+        if (out == NULL) {
             break;
         }
 
-        out->next = NULL;
-
-
-        if (p->output_filter(p->output_ctx, out->hunk) == NGX_ERROR) {
+        if (p->output_filter(p->output_ctx, out) == NGX_ERROR) {
             p->downstream_error = 1;
             continue;
         }
@@ -326,7 +336,6 @@ int ngx_event_proxy_write_to_downstream(
         /* add the free shadow raw hunks to p->free_raw_hunks */
 
         for (ce = p->free; ce; ce = ce->next) {
-ngx_log_debug(p->log, "SHADOW %08X" _ ce->hunk->shadow);
             if (ce->hunk->type & NGX_HUNK_LAST_SHADOW) {
                 h = ce->hunk->shadow;
                 /* THINK NEEDED ??? */ h->pos = h->last = h->start;
@@ -334,31 +343,21 @@ ngx_log_debug(p->log, "SHADOW %08X" _ ce
                 ngx_alloc_ce_and_set_hunk(te, h, p->pool, NGX_ABORT);
                 ngx_add_after_partially_filled_hunk(&p->free_raw_hunks, te);
 
-ngx_log_debug(p->log, "RAW %08X" _ h->pos);
-
                 ce->hunk->type &= ~NGX_HUNK_LAST_SHADOW;
             }
             ce->hunk->shadow = NULL;
         }
     }
 
-    ngx_log_debug(p->log, "STATE %d:%d:%d:%X:%X" _
-                  p->upstream_eof _
-                  p->upstream_error _
-                  p->upstream_done _
-                  p->in _
-                  p->out
-                 );
-
     return NGX_OK;
 }
 
 
-static int ngx_event_proxy_write_chain_to_temp_file(ngx_event_proxy_t *p)
+static int ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p)
 {
     int           rc, size, hunk_size;
     ngx_hunk_t   *h;
-    ngx_chain_t  *ce, *te, *next, *in, **le, **last_free;
+    ngx_chain_t  *ce, *te, *next, *out, **le, **last_free;
 
     ngx_log_debug(p->log, "write to file");
 
@@ -379,6 +378,8 @@ static int ngx_event_proxy_write_chain_t
         }
     }
 
+    out = p->in;
+
     if (!p->cachable) {
 
         size = 0;
@@ -407,21 +408,21 @@ ngx_log_debug(p->log, "hunk size: %d" _ 
 ngx_log_debug(p->log, "size: %d" _ size);
 
         if (ce) {
-           in = ce;
+           p->in = ce;
            *le = NULL;
 
         } else {
-           in = NULL;
+           p->in = NULL;
            p->last_in = &p->in;
         }
 
     } else {
-        in = NULL;
+        p->in = NULL;
         p->last_in = &p->in;
     }
 
-    if (ngx_write_chain_to_file(p->temp_file, p->in, p->temp_offset,
-                                p->pool) == NGX_ERROR) {
+    if (ngx_write_chain_to_file(p->temp_file, out, p->temp_offset,
+                                                       p->pool) == NGX_ERROR) {
         return NGX_ABORT;
     }
 
@@ -432,7 +433,7 @@ ngx_log_debug(p->log, "size: %d" _ size)
         /* void */
     }
 
-    for (ce = p->in; ce; ce = next) {
+    for (ce = out; ce; ce = next) {
         next = ce->next;
         ce->next = NULL;
 
@@ -453,15 +454,13 @@ ngx_log_debug(p->log, "size: %d" _ size)
         }
     }
 
-    p->in = in;
-
     return NGX_OK;
 }
 
 
 /* the copy input filter */
 
-int ngx_event_proxy_copy_input_filter(ngx_event_proxy_t *p, ngx_hunk_t *hunk)
+int ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_hunk_t *hunk)
 {
     ngx_hunk_t   *h;
     ngx_chain_t  *ce;
@@ -567,7 +566,7 @@ ngx_inline static void ngx_add_after_par
 }
 
 
-static int ngx_drain_chains(ngx_event_proxy_t *p)
+static int ngx_drain_chains(ngx_event_pipe_t *p)
 {
     ngx_hunk_t   *h;
     ngx_chain_t  *ce, *te;
rename from src/event/ngx_event_proxy.h
rename to src/event/ngx_event_pipe.h
--- a/src/event/ngx_event_proxy.h
+++ b/src/event/ngx_event_pipe.h
@@ -1,5 +1,5 @@
-#ifndef _NGX_EVENT_PROXY_H_INCLUDED_
-#define _NGX_EVENT_PROXY_H_INCLUDED_
+#ifndef _NGX_EVENT_PIPE_H_INCLUDED_
+#define _NGX_EVENT_PIPE_H_INCLUDED_
 
 
 #include <ngx_config.h>
@@ -7,14 +7,14 @@
 #include <ngx_event.h>
 
 
-typedef struct ngx_event_proxy_s  ngx_event_proxy_t;
+typedef struct ngx_event_pipe_s  ngx_event_pipe_t;
 
-typedef int (*ngx_event_proxy_input_filter_pt)(ngx_event_proxy_t *p,
-                                               ngx_hunk_t *hunk);
-typedef int (*ngx_event_proxy_output_filter_pt)(void *data, ngx_hunk_t *hunk);
+typedef int (*ngx_event_pipe_input_filter_pt)(ngx_event_pipe_t *p,
+                                              ngx_hunk_t *hunk);
+typedef int (*ngx_event_pipe_output_filter_pt)(void *data, ngx_chain_t *chain);
 
 
-struct ngx_event_proxy_s {
+struct ngx_event_pipe_s {
     ngx_chain_t       *free_raw_hunks;
     ngx_chain_t       *in;
     ngx_chain_t      **last_in;
@@ -30,10 +30,10 @@ struct ngx_event_proxy_s {
      * from the raw hunks to an incoming chain
      */
 
-    ngx_event_proxy_input_filter_pt    input_filter;
+    ngx_event_pipe_input_filter_pt    input_filter;
     void                              *input_ctx;
 
-    ngx_event_proxy_output_filter_pt   output_filter;
+    ngx_event_pipe_output_filter_pt   output_filter;
     void                              *output_ctx;
 
     unsigned           read:1;
@@ -68,12 +68,8 @@ struct ngx_event_proxy_s {
 };
 
 
-int ngx_event_proxy(ngx_event_proxy_t *p, int do_write);
-int ngx_event_proxy_copy_input_filter(ngx_event_proxy_t *p, ngx_hunk_t *hunk);
-
-/* STUB */
-int ngx_event_proxy_read_upstream(ngx_event_proxy_t *p);
-int ngx_event_proxy_write_to_downstream(ngx_event_proxy_t *p);
+int ngx_event_pipe(ngx_event_pipe_t *p, int do_write);
+int ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_hunk_t *hunk);
 
 
-#endif /* _NGX_EVENT_PROXY_H_INCLUDED_ */
+#endif /* _NGX_EVENT_PIPE_H_INCLUDED_ */
--- a/src/http/modules/ngx_http_chunked_filter.c
+++ b/src/http/modules/ngx_http_chunked_filter.c
@@ -39,7 +39,7 @@ static int ngx_http_chunked_header_filte
         return next_header_filter(r);
     }
 
-    if (r->headers_out.content_length == -1) {
+    if (r->headers_out.content_length_n == -1) {
         if (r->http_version < NGX_HTTP_VERSION_11) {
             r->keepalive = 0;
 
--- a/src/http/modules/ngx_http_gzip_filter.c
+++ b/src/http/modules/ngx_http_gzip_filter.c
@@ -157,8 +157,9 @@ static int ngx_http_gzip_header_filter(n
     r->headers_out.content_encoding->value.len = 4;
     r->headers_out.content_encoding->value.data = "gzip";
 
-    ctx->length = r->headers_out.content_length;
-    r->headers_out.content_length = -1;
+    ctx->length = r->headers_out.content_length_n;
+    r->headers_out.content_length_n = -1;
+    r->headers_out.content_length = NULL;
     r->filter |= NGX_HTTP_FILTER_NEED_IN_MEMORY;
 
     return next_header_filter(r);
--- a/src/http/modules/ngx_http_not_modified_filter.c
+++ b/src/http/modules/ngx_http_not_modified_filter.c
@@ -54,7 +54,8 @@ static int ngx_http_not_modified_header_
 
     if (ims != NGX_ERROR && ims == r->headers_out.last_modified_time) {
         r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
-        r->headers_out.content_length = -1;
+        r->headers_out.content_length_n = -1;
+        r->headers_out.content_length = NULL;
         r->headers_out.content_type->key.len = 0;
         r->headers_out.content_type = NULL;
         r->headers_out.accept_ranges->key.len = 0;
--- a/src/http/modules/ngx_http_range_filter.c
+++ b/src/http/modules/ngx_http_range_filter.c
@@ -49,7 +49,7 @@ static int ngx_http_range_header_filter(
     if (r->main
         || r->http_version < NGX_HTTP_VERSION_10
         || r->headers_out.status != NGX_HTTP_OK
-        || r->headers_out.content_length == -1
+        || r->headers_out.content_length_n == -1
         /* STUB: we currently support ranges for file hunks only */
         || r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY)
     {
@@ -103,7 +103,7 @@ static int ngx_http_range_header_filter(
             break;
         }
 
-        if (start >= r->headers_out.content_length) {
+        if (start >= r->headers_out.content_length_n) {
             rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
             break;
         }
@@ -114,7 +114,7 @@ static int ngx_http_range_header_filter(
             ngx_test_null(range, ngx_push_array(&r->headers_out.ranges),
                           NGX_ERROR);
             range->start = start;
-            range->end = r->headers_out.content_length;
+            range->end = r->headers_out.content_length_n;
 
             if (*p++ == ',') {
                 continue;
@@ -139,7 +139,7 @@ static int ngx_http_range_header_filter(
             break;
         }
 
-        if (end >= r->headers_out.content_length || start >= end) {
+        if (end >= r->headers_out.content_length_n || start >= end) {
             rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
             break;
         }
@@ -170,9 +170,10 @@ static int ngx_http_range_header_filter(
         r->headers_out.content_range->value.len =
                         ngx_snprintf(r->headers_out.content_range->value.data,
                                      8 + 20 + 1, "bytes */" OFF_FMT,
-                                     r->headers_out.content_length);
+                                     r->headers_out.content_length_n);
 
-        r->headers_out.content_length = -1;
+        r->headers_out.content_length_n = -1;
+        r->headers_out.content_length = NULL;
 
         return rc;
 
@@ -193,9 +194,9 @@ static int ngx_http_range_header_filter(
                                       6 + 20 + 1 + 20 + 1 + 20 + 1,
                                       "bytes " OFF_FMT "-" OFF_FMT "/" OFF_FMT,
                                       range->start, range->end - 1,
-                                      r->headers_out.content_length);
+                                      r->headers_out.content_length_n);
 
-            r->headers_out.content_length = range->end - range->start;
+            r->headers_out.content_length_n = range->end - range->start;
 
         } else {
 
@@ -267,13 +268,14 @@ static int ngx_http_range_header_filter(
                                      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);
+                                     r->headers_out.content_length_n);
 
                  len += ctx->boundary_header.len + range[i].content_range.len
                         + (size_t) (range[i].end - range[i].start);
             }
 
-            r->headers_out.content_length = len;
+            r->headers_out.content_length_n = len;
+            r->headers_out.content_length = NULL;
         }
     }
 
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -191,6 +191,7 @@ static int ngx_http_static_handler(ngx_h
     ngx_log_e                  level;
     ngx_err_t                  err;
     ngx_hunk_t                *h;
+    ngx_chain_t                out;
     ngx_http_type_t           *type;
     ngx_http_log_ctx_t        *ctx;
     ngx_http_core_loc_conf_t  *clcf;
@@ -257,7 +258,7 @@ static int ngx_http_static_handler(ngx_h
 #endif
 
     r->headers_out.status = NGX_HTTP_OK;
-    r->headers_out.content_length = ngx_file_size(r->file.info);
+    r->headers_out.content_length_n = ngx_file_size(r->file.info);
     r->headers_out.last_modified_time = ngx_file_mtime(r->file.info);
 
     ngx_test_null(r->headers_out.content_type,
@@ -317,7 +318,10 @@ static int ngx_http_static_handler(ngx_h
     h->file->fd = r->file.fd;
     h->file->log = r->connection->log;
 
-    return ngx_http_output_filter(r, h);
+    out.hunk = h;
+    out.next = NULL;
+
+    return ngx_http_output_filter(r, &out);
 }
 
 
--- a/src/http/modules/proxy/ngx_http_proxy_handler.c
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.c
@@ -2,8 +2,8 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_event.h>
-/* STUB */ #include <ngx_event_connect.h>
-/* STUB */ #include <ngx_event_proxy.h>
+#include <ngx_event_connect.h>
+#include <ngx_event_pipe.h>
 #include <ngx_http.h>
 #include <ngx_http_proxy_handler.h>
 
@@ -632,12 +632,13 @@ static void ngx_http_proxy_send_response
 {
     int                  rc, i;
     ngx_table_elt_t     *ch, *ph;
-    ngx_event_proxy_t   *ep;
+    ngx_event_pipe_t    *ep;
     ngx_http_request_t  *r;
 
     r = p->request;
 
-    r->headers_out.content_length = -1;
+    r->headers_out.content_length_n = -1;
+    r->headers_out.content_length = NULL;
 
     /* copy an upstream header to r->headers_out */
 
@@ -655,13 +656,6 @@ static void ngx_http_proxy_send_response
             }
         }
 
-        if (&ph[i] == p->headers_in.content_length) {
-            r->headers_out.content_length =
-                             ngx_atoi(p->headers_in.content_length->value.data,
-                                      p->headers_in.content_length->value.len);
-            continue;
-        }
-
         ch = ngx_push_table(r->headers_out.headers);
         if (ch == NULL) {
             ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
@@ -675,6 +669,14 @@ static void ngx_http_proxy_send_response
             r->headers_out.content_type->key.len = 0;
             continue;
         }
+
+        if (&ph[i] == p->headers_in.content_length) {
+            r->headers_out.content_length_n =
+                             ngx_atoi(p->headers_in.content_length->value.data,
+                                      p->headers_in.content_length->value.len);
+            r->headers_out.content_length = ch;
+            continue;
+        }
     }
 
     /* STUB */
@@ -700,14 +702,14 @@ static void ngx_http_proxy_send_response
 
     p->header_sent = 1;
 
-    ep = ngx_pcalloc(r->pool, sizeof(ngx_event_proxy_t));
+    ep = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
     if (ep == NULL) {
         ngx_http_proxy_finalize_request(p, 0);
         return;
     }
 
-    ep->input_filter = ngx_event_proxy_copy_input_filter;
-    ep->output_filter = (ngx_event_proxy_output_filter_pt)
+    ep->input_filter = ngx_event_pipe_copy_input_filter;
+    ep->output_filter = (ngx_event_pipe_output_filter_pt)
                                                         ngx_http_output_filter;
     ep->output_ctx = r;
     ep->bufs = p->lcf->bufs;
@@ -743,14 +745,17 @@ static void ngx_http_proxy_send_response
     ep->preread_size = p->header_in->last - p->header_in->pos;
 
     /*
-     * event_proxy would do p->header_in->last += ep->preread_size
-     * as these bytes were read.
+     * event_pipe would do p->header_in->last += ep->preread_size
+     * as though these bytes were read.
      */
     p->header_in->last = p->header_in->pos;
 
-    /* STUB */ ep->cachable = 0;
+    /* STUB */ ep->cachable = 1;
+#if 0
+    ep->max_temp_file_size = 1000000000;
+#endif
 
-    p->event_proxy = ep;
+    p->event_pipe = ep;
 
 #if 0
     lcx = p->log->data;
@@ -771,7 +776,7 @@ static void ngx_http_proxy_process_body(
     ngx_connection_t      *c;
     ngx_http_request_t    *r;
     ngx_http_proxy_ctx_t  *p;
-    ngx_event_proxy_t     *ep;
+    ngx_event_pipe_t      *ep;
 
     c = ev->data;
 
@@ -786,7 +791,7 @@ static void ngx_http_proxy_process_body(
         r = p->request;
     }
 
-    ep = p->event_proxy;
+    ep = p->event_pipe;
 
     if (ev->timedout) {
         if (ev->write) {
@@ -797,7 +802,7 @@ static void ngx_http_proxy_process_body(
         }
 
     } else {
-        if (ngx_event_proxy(ep, ev->write) == NGX_ABORT) {
+        if (ngx_event_pipe(ep, ev->write) == NGX_ABORT) {
             ngx_http_proxy_finalize_request(p, 0);
             return;
         }
@@ -1159,14 +1164,14 @@ static void *ngx_http_proxy_create_loc_c
 
     conf->bufs.num = 10;
     conf->bufs.size = 4096;
-    conf->max_busy_len = 8192 + 4096;
+    conf->max_busy_len = 8192;
 
 
     /* CHECK in _init conf->max_temp_size >= conf->bufs.size !!! */
     conf->max_temp_file_size = 4096 * 6;
 
 
-    conf->temp_file_write_size = 4096 * 2;
+    conf->temp_file_write_size = 4096 * 1;
 
     ngx_test_null(conf->temp_path, ngx_pcalloc(cf->pool, sizeof(ngx_path_t)),
                   NULL);
--- a/src/http/modules/proxy/ngx_http_proxy_handler.h
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.h
@@ -48,6 +48,8 @@ typedef struct {
     ngx_table_elt_t  *last_modified;
     ngx_table_elt_t  *accept_ranges;
 
+    off_t             content_length_n;
+
     ngx_table_t      *headers;
 } ngx_http_proxy_headers_in_t;
 
@@ -74,7 +76,7 @@ struct ngx_http_proxy_ctx_s {
     int                         location_len;
     ngx_str_t                   host_header;
 
-    ngx_event_proxy_t          *event_proxy;
+    ngx_event_pipe_t           *event_pipe;
 
     unsigned                    accel:1;
     unsigned                    cachable:1;
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -130,13 +130,6 @@ static ngx_command_t  ngx_http_core_comm
      offsetof(ngx_http_core_loc_conf_t, doc_root),
      NULL},
 
-    {ngx_string("sendfile"),
-     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-     ngx_conf_set_flag_slot,
-     NGX_HTTP_LOC_CONF_OFFSET,
-     offsetof(ngx_http_core_loc_conf_t, sendfile),
-     NULL},
-
     {ngx_string("send_timeout"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_msec_slot,
@@ -207,15 +200,22 @@ void ngx_http_handler(ngx_http_request_t
     lcx = r->connection->log->data;
     lcx->action = NULL;
 
-    /* STUB */
-    r->keepalive = 1;
-    if (r->headers_in.connection) {
-        if (r->headers_in.connection->value.len == 5
-            && ngx_strcasecmp(r->headers_in.connection->value.data, "close")
-                                                                          == 0)
-        {
+    switch (r->headers_in.connection_type) {
+    case 0:
+        if (r->http_version > NGX_HTTP_VERSION_10) {
+            r->keepalive = 1;
+        } else {
             r->keepalive = 0;
         }
+        break;
+
+    case NGX_HTTP_CONNECTION_CLOSE:
+        r->keepalive = 0;
+        break;
+
+    case NGX_HTTP_CONNECTION_KEEP_ALIVE:
+        r->keepalive = 1;
+        break;
     }
 
 #if 0
@@ -331,9 +331,10 @@ static void ngx_http_run_phases(ngx_http
 
 int ngx_http_find_location_config(ngx_http_request_t *r)
 {
-    int                        i, rc;
-    ngx_http_core_loc_conf_t  *clcf, **clcfp;
-    ngx_http_core_srv_conf_t  *cscf;
+    int                            i, rc;
+    ngx_http_core_loc_conf_t      *clcf, **clcfp;
+    ngx_http_core_srv_conf_t      *cscf;
+    ngx_http_write_filter_conf_t  *wcf;
 
     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
 
@@ -363,12 +364,14 @@ ngx_log_debug(r->connection->log, "rc: %
         }
     }
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+    wcf = ngx_http_get_module_loc_conf(r, ngx_http_write_filter_module);
 
-    if (!(ngx_io.flags & NGX_IO_SENDFILE) || !clcf->sendfile) {
+    if (!(ngx_io.flags & NGX_IO_SENDFILE) || !wcf->sendfile) {
         r->filter = NGX_HTTP_FILTER_NEED_IN_MEMORY;
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
     if (clcf->handler) {
         /*
          * if the location already has content handler then skip
@@ -825,8 +828,6 @@ static void *ngx_http_core_create_loc_co
 
     */
 
-    lcf->sendfile = NGX_CONF_UNSET;
-
     lcf->send_timeout = NGX_CONF_UNSET;
     lcf->discarded_buffer_size = NGX_CONF_UNSET;
     lcf->keepalive_timeout = NGX_CONF_UNSET;
@@ -895,7 +896,6 @@ static char *ngx_http_core_merge_loc_con
     ngx_conf_merge_str_value(conf->default_type,
                              prev->default_type, "text/plain");
 
-    ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
     ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 10000);
     ngx_conf_merge_size_value(conf->discarded_buffer_size,
                               prev->discarded_buffer_size, 1500);
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -110,7 +110,6 @@ typedef struct {
     ngx_array_t  *types;
     ngx_str_t     default_type;
 
-    int           sendfile;                /* sendfile */
     ngx_msec_t    send_timeout;            /* send_timeout */
     ssize_t       send_lowat;              /* send_lowat */
     ssize_t       discarded_buffer_size;   /* discarded_buffer_size */
--- a/src/http/ngx_http_filter.h
+++ b/src/http/ngx_http_filter.h
@@ -7,12 +7,20 @@
 #define NGX_HTTP_FILTER_NEED_TEMP           4
 
 
-int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk);
+typedef struct {
+    ssize_t  buffer_output;
+    int      sendfile;
+} ngx_http_write_filter_conf_t;
+
+
+int ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in);
 int ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in);
 
 
 extern int (*ngx_http_top_header_filter) (ngx_http_request_t *r);
 extern int (*ngx_http_top_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch);
 
+extern ngx_module_t  ngx_http_write_filter_module;
+
 
 #endif /* _NGX_HTTP_FILTER_H_INCLUDED_ */
--- a/src/http/ngx_http_header_filter.c
+++ b/src/http/ngx_http_header_filter.c
@@ -159,9 +159,11 @@ static int ngx_http_header_filter(ngx_ht
         len += 15 + r->headers_out.content_range->value.len + 2;
     }
 
-    if (r->headers_out.content_length >= 0) {
-        /* "Content-Length: ... \r\n", 2^64 is 20 characters */
-        len += 48;
+    if (r->headers_out.content_length == NULL) {
+        if (r->headers_out.content_length_n >= 0) {
+            /* "Content-Length: ... \r\n", 2^64 is 20 characters */
+            len += 48;
+        }
     }
 
     if (r->headers_out.content_type && r->headers_out.content_type->value.len) {
@@ -260,11 +262,13 @@ static int ngx_http_header_filter(ngx_ht
         *(h->last++) = CR; *(h->last++) = LF;
     }
 
-    /* 2^64 is 20 characters  */
-    if (r->headers_out.content_length >= 0) {
-        h->last += ngx_snprintf(h->last, 49,
-                                "Content-Length: " OFF_FMT CRLF,
-                                r->headers_out.content_length);
+    if (r->headers_out.content_length == NULL) {
+        /* 2^64 is 20 characters  */
+        if (r->headers_out.content_length_n >= 0) {
+            h->last += ngx_snprintf(h->last, 49,
+                                    "Content-Length: " OFF_FMT CRLF,
+                                    r->headers_out.content_length_n);
+        }
     }
 
     if (r->headers_out.content_type && r->headers_out.content_type->value.len) {
--- a/src/http/ngx_http_output_filter.c
+++ b/src/http/ngx_http_output_filter.c
@@ -84,11 +84,11 @@ ngx_module_t  ngx_http_output_filter_mod
 
 
 
-int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk)
+int ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
     int                             rc, last;
     ssize_t                         size;
-    ngx_chain_t                     out, *ce, **le;
+    ngx_chain_t                    *ce;
     ngx_http_output_filter_ctx_t   *ctx;
     ngx_http_output_filter_conf_t  *conf;
 
@@ -103,33 +103,27 @@ int ngx_http_output_filter(ngx_http_requ
 
     /*
      * the short path for the case when the chain ctx->in is empty
-     * and there's no hunk or the hunk does not require the copy
+     * and the incoming chain is empty too or it has the single hunk
+     * that does not require the copy
      */
 
     if (ctx->in == NULL) {
 
-        if (hunk == NULL) {
-            return ngx_next_filter(r, NULL);
+        if (in == NULL) {
+            return ngx_next_filter(r, in);
         }
 
-        if (!need_to_copy(r, hunk)) {
-            out.hunk = hunk;
-            out.next = NULL;
-            return ngx_next_filter(r, &out);
+        if (in->next == NULL && (!need_to_copy(r, in->hunk))) {
+            return ngx_next_filter(r, in);
         }
     }
 
     /* add the incoming hunk to the chain ctx->in */
 
-    if (hunk) {
-        le = &ctx->in;
-
-        for (ce = ctx->in; ce; ce = ce->next) {
-            le = &ce->next;
+    if (in) {
+        if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
+            return NGX_ERROR;
         }
-
-        ngx_add_hunk_to_chain(ce, hunk, r->pool, NGX_ERROR);
-        *le = ce;
     }
 
     conf = ngx_http_get_module_loc_conf(r->main ? r->main : r,
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -9,6 +9,7 @@ static void ngx_http_init_request(ngx_ev
 static void ngx_http_process_request_line(ngx_event_t *rev);
 static void ngx_http_process_request_headers(ngx_event_t *rev);
 static ssize_t ngx_http_read_request_header(ngx_http_request_t *r);
+static int ngx_http_process_request_header(ngx_http_request_t *r);
 
 static void ngx_http_set_write_handler(ngx_http_request_t *r);
 
@@ -256,7 +257,8 @@ ngx_log_debug(rev->log, "IN: %08x" _ in_
     r->file.fd = NGX_INVALID_FILE;
 
     r->headers_in.content_length_n = -1;
-    r->headers_out.content_length = -1;
+    r->headers_in.keep_alive_n = -1;
+    r->headers_out.content_length_n = -1;
     r->headers_out.last_modified_time = -1;
 
     rev->event_handler = ngx_http_process_request_line;
@@ -503,14 +505,11 @@ static void ngx_http_process_request_lin
 static void ngx_http_process_request_headers(ngx_event_t *rev)
 {
     int                        rc, i, offset;
-    size_t                     len;
     ssize_t                    n;
     ngx_table_elt_t           *h;
     ngx_connection_t          *c;
     ngx_http_request_t        *r;
-    ngx_http_server_name_t    *name;
     ngx_http_core_srv_conf_t  *cscf;
-    ngx_http_core_loc_conf_t  *clcf;
 
     c = rev->data;
     r = c->data;
@@ -604,59 +603,11 @@ static void ngx_http_process_request_hea
 
             ngx_log_debug(r->connection->log, "HTTP header done");
 
-            if (r->headers_in.host) {
-                for (len = 0; len < r->headers_in.host->value.len; len++) {
-                    if (r->headers_in.host->value.data[len] == ':') {
-                        break;
-                    }
-                }
-                r->headers_in.host_name_len = len;
-
-                /* find the name based server configuration */
-
-                name = r->virtual_names->elts;
-                for (i = 0; i < r->virtual_names->nelts; i++) {
-                    if (r->headers_in.host_name_len != name[i].name.len) {
-                        continue;
-                    }
-
-                    if (ngx_strncasecmp(r->headers_in.host->value.data,
-                                        name[i].name.data,
-                                        r->headers_in.host_name_len) == 0)
-                    {
-                        r->srv_conf = name[i].core_srv_conf->ctx->srv_conf;
-                        r->loc_conf = name[i].core_srv_conf->ctx->loc_conf;
+            rc = ngx_http_process_request_header(r);
 
-                        clcf = ngx_http_get_module_loc_conf(r,
-                                                         ngx_http_core_module);
-                        c->log->file = clcf->err_log->file;
-                        c->log->log_level = clcf->err_log->log_level;
-
-                        break;
-                    }
-                }
-
-            } else {
-                if (r->http_version > NGX_HTTP_VERSION_10) {
-                    ngx_http_header_parse_error(r,
-                                                NGX_HTTP_PARSE_NO_HOST_HEADER,
-                                                NGX_HTTP_BAD_REQUEST);
-                    return;
-                }
-                r->headers_in.host_name_len = 0;
-            }
-
-            if (r->headers_in.content_length) {
-                r->headers_in.content_length_n =
-                             ngx_atoi(r->headers_in.content_length->value.data,
-                                      r->headers_in.content_length->value.len);
-
-                if (r->headers_in.content_length_n == NGX_ERROR) {
-                    ngx_http_header_parse_error(r,
-                                             NGX_HTTP_PARSE_INVALID_CL_HEADER,
-                                             NGX_HTTP_BAD_REQUEST);
-                    return;
-                }
+            if (rc != NGX_OK) {
+                ngx_http_header_parse_error(r, rc, NGX_HTTP_BAD_REQUEST);
+                return;
             }
 
             if (r->header_timeout_set) {
@@ -766,6 +717,86 @@ static ssize_t ngx_http_read_request_hea
 }
 
 
+static int ngx_http_process_request_header(ngx_http_request_t *r)
+{
+    int                        i;
+    size_t                     len;
+    ngx_http_server_name_t    *name;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    if (r->headers_in.host) {
+        for (len = 0; len < r->headers_in.host->value.len; len++) {
+            if (r->headers_in.host->value.data[len] == ':') {
+                break;
+            }
+        }
+        r->headers_in.host_name_len = len;
+
+        /* find the name based server configuration */
+
+        name = r->virtual_names->elts;
+        for (i = 0; i < r->virtual_names->nelts; i++) {
+            if (r->headers_in.host_name_len != name[i].name.len) {
+                continue;
+            }
+
+            if (ngx_strncasecmp(r->headers_in.host->value.data,
+                                name[i].name.data,
+                                r->headers_in.host_name_len) == 0)
+            {
+                r->srv_conf = name[i].core_srv_conf->ctx->srv_conf;
+                r->loc_conf = name[i].core_srv_conf->ctx->loc_conf;
+
+                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+                r->connection->log->file = clcf->err_log->file;
+                r->connection->log->log_level = clcf->err_log->log_level;
+
+                break;
+            }
+        }
+
+    } else {
+        if (r->http_version > NGX_HTTP_VERSION_10) {
+            return NGX_HTTP_PARSE_NO_HOST_HEADER;
+        }
+        r->headers_in.host_name_len = 0;
+    }
+
+    if (r->headers_in.content_length) {
+        r->headers_in.content_length_n =
+                             ngx_atoi(r->headers_in.content_length->value.data,
+                                      r->headers_in.content_length->value.len);
+
+        if (r->headers_in.content_length_n == NGX_ERROR) {
+            return NGX_HTTP_PARSE_INVALID_CL_HEADER;
+        }
+    }
+
+    if (r->headers_in.connection) {
+        if (r->headers_in.connection->value.len == 5
+            && ngx_strcasecmp(r->headers_in.connection->value.data, "close")
+                                                                          == 0)
+        {
+            r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
+
+        } else if (r->headers_in.connection->value.len == 10
+                   && ngx_strcasecmp(r->headers_in.connection->value.data,
+                                                            "keep-alive") == 0)
+        {
+            r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;
+
+            if (r->headers_in.keep_alive) {
+                r->headers_in.keep_alive_n =
+                                 ngx_atoi(r->headers_in.keep_alive->value.data,
+                                          r->headers_in.keep_alive->value.len);
+            }
+        }
+    }
+
+    return NGX_OK;
+}
+
+
 void ngx_http_finalize_request(ngx_http_request_t *r, int rc)
 {
     ngx_log_debug(r->connection->log, "finalize http request");
@@ -1283,12 +1314,15 @@ static void ngx_http_empty_handler(ngx_e
 
 int ngx_http_send_last(ngx_http_request_t *r)
 {
-    ngx_hunk_t  *h;
+    ngx_hunk_t   *h;
+    ngx_chain_t   out;
 
     ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
     h->type = NGX_HUNK_LAST;
+    out.hunk = h;
+    out.next = NULL;
 
-    return ngx_http_output_filter(r, h);
+    return ngx_http_output_filter(r, &out);
 }
 
 
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -10,11 +10,11 @@
 #define NGX_HTTP_HEAD  2
 #define NGX_HTTP_POST  3
 
-#define NGX_HTTP_CONN_CLOSE       0
-#define NGX_HTTP_CONN_KEEP_ALIVE  1
+#define NGX_HTTP_CONNECTION_CLOSE         1
+#define NGX_HTTP_CONNECTION_KEEP_ALIVE    2
 
 
-#define NGX_NONE                        1
+#define NGX_NONE                          1
 
 
 #define NGX_HTTP_PARSE_HEADER_DONE        1
@@ -63,9 +63,6 @@ typedef struct {
 
 
 typedef struct {
-    size_t            host_name_len;
-    ssize_t           content_length_n;
-
     ngx_table_elt_t  *host;
     ngx_table_elt_t  *connection;
     ngx_table_elt_t  *if_modified_since;
@@ -77,6 +74,11 @@ typedef struct {
     ngx_table_elt_t  *user_agent;
     ngx_table_elt_t  *keep_alive;
 
+    size_t            host_name_len;
+    ssize_t           content_length_n;
+    size_t            connection_type;
+    ssize_t           keep_alive_n;
+
     ngx_table_t      *headers;
 } ngx_http_headers_in_t;
 
@@ -107,6 +109,7 @@ typedef struct {
     ngx_table_elt_t  *server;
     ngx_table_elt_t  *date;
     ngx_table_elt_t  *content_type;
+    ngx_table_elt_t  *content_length;
     ngx_table_elt_t  *content_encoding;
     ngx_table_elt_t  *location;
     ngx_table_elt_t  *last_modified;
@@ -118,7 +121,7 @@ typedef struct {
 
     ngx_table_t      *headers;
 
-    off_t             content_length;
+    off_t             content_length_n;
     char             *etag;
     time_t            date_time;
     time_t            last_modified_time;
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -152,8 +152,9 @@ static ngx_str_t error_pages[] = {
 
 int ngx_http_special_response_handler(ngx_http_request_t *r, int error)
 {
-    int          err, rc;
-    ngx_hunk_t  *h;
+    int           err, rc;
+    ngx_hunk_t   *h;
+    ngx_chain_t  *out, **le, *ce;
 
     r->headers_out.status = error;
 
@@ -189,9 +190,9 @@ int ngx_http_special_response_handler(ng
     }
 
     if (error_pages[err].len) {
-        r->headers_out.content_length = error_pages[err].len
-                                        + sizeof(error_tail) - 1
-                                        + sizeof(msie_stub) - 1;
+        r->headers_out.content_length_n = error_pages[err].len
+                                          + sizeof(error_tail) - 1
+                                          + sizeof(msie_stub) - 1;
 
         ngx_test_null(r->headers_out.content_type,
                       ngx_push_table(r->headers_out.headers),
@@ -203,7 +204,8 @@ int ngx_http_special_response_handler(ng
         r->headers_out.content_type->value.data = "text/html";
 
     } else {
-        r->headers_out.content_length = -1;
+        r->headers_out.content_length_n = -1;
+        r->headers_out.content_length = NULL;
     }
 
     rc = ngx_http_send_header(r);
@@ -216,41 +218,42 @@ int ngx_http_special_response_handler(ng
         return NGX_OK;
     }
 
+    out = NULL;
+    le = NULL;
+
     ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
-
     h->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
     h->pos = error_pages[err].data;
     h->last = error_pages[err].data + error_pages[err].len;
 
-    if (ngx_http_output_filter(r, h) == NGX_ERROR) {
-        return NGX_ERROR;
-    }
+    ngx_alloc_ce_and_set_hunk(ce, h, r->pool, NGX_ERROR);
+    ngx_chain_add_ce(out, le, ce);
+
 
     ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
-
     h->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
     h->pos = error_tail;
     h->last = error_tail + sizeof(error_tail) - 1;
 
+    ngx_alloc_ce_and_set_hunk(ce, h, r->pool, NGX_ERROR);
+    ngx_chain_add_ce(out, le, ce);
+
     if (/* STUB: "msie_padding on/off" */ 1
         && r->http_version >= NGX_HTTP_VERSION_10
         && error >= NGX_HTTP_BAD_REQUEST
         && error != NGX_HTTP_REQUEST_URI_TOO_LARGE
        )
     {
-
-        if (ngx_http_output_filter(r, h) == NGX_ERROR) {
-            return NGX_ERROR;
-        }
-
         ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
-
         h->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
         h->pos = msie_stub;
         h->last = msie_stub + sizeof(msie_stub) - 1;
+
+        ngx_alloc_ce_and_set_hunk(ce, h, r->pool, NGX_ERROR);
+        ngx_chain_add_ce(out, le, ce);
     }
 
     h->type |= NGX_HUNK_LAST;
 
-    return ngx_http_output_filter(r, h);
+    return ngx_http_output_filter(r, out);
 }
--- a/src/http/ngx_http_write_filter.c
+++ b/src/http/ngx_http_write_filter.c
@@ -6,11 +6,6 @@
 
 
 typedef struct {
-    ssize_t  buffer_output;
-} ngx_http_write_filter_conf_t;
-
-
-typedef struct {
     ngx_chain_t  *out;
 } ngx_http_write_filter_ctx_t;
 
@@ -23,6 +18,13 @@ static int ngx_http_write_filter_init(ng
 
 static ngx_command_t ngx_http_write_filter_commands[] = {
 
+    {ngx_string("sendfile"),
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+     ngx_conf_set_flag_slot,
+     NGX_HTTP_LOC_CONF_OFFSET,
+     offsetof(ngx_http_write_filter_conf_t, sendfile),
+     NULL},
+
     {ngx_string("buffer_output"),
      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_size_slot,
@@ -81,36 +83,7 @@ int ngx_http_write_filter(ngx_http_reque
     for (ce = ctx->out; ce; ce = ce->next) {
         le = &ce->next;
 
-        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
-            size += ce->hunk->last - ce->hunk->pos;
-        } else {
-            size += ce->hunk->file_last - ce->hunk->file_pos;
-        }
-
-        if (ce->hunk->type & (NGX_HUNK_FLUSH|NGX_HUNK_RECYCLED)) {
-            flush = size;
-        }
-
-        if (ce->hunk->type & NGX_HUNK_LAST) {
-            last = 1;
-        }
-    }
-
-    /* add the new chain to the existent one */
-
-    for (/* void */; in; in = in->next) {
-        ngx_test_null(ce, ngx_alloc_chain_entry(r->pool), NGX_ERROR);
-
-        ce->hunk = in->hunk;
-        ce->next = NULL;
-        *le = ce;
-        le = &ce->next;
-
-        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
-            size += ce->hunk->last - ce->hunk->pos;
-        } else {
-            size += ce->hunk->file_last - ce->hunk->file_pos;
-        }
+        size += ngx_hunk_size(ce->hunk);
 
         if (ce->hunk->type & (NGX_HUNK_FLUSH|NGX_HUNK_RECYCLED)) {
             flush = size;
@@ -124,6 +97,31 @@ int ngx_http_write_filter(ngx_http_reque
     conf = ngx_http_get_module_loc_conf(r->main ? r->main : r,
                                         ngx_http_write_filter_module);
 
+    /* add the new chain to the existent one */
+
+    for (/* void */; in; in = in->next) {
+        ngx_test_null(ce, ngx_alloc_chain_entry(r->pool), NGX_ERROR);
+
+        ce->hunk = in->hunk;
+        ce->next = NULL;
+        *le = ce;
+        le = &ce->next;
+
+        if (!(ngx_io.flags & NGX_IO_SENDFILE) || !conf->sendfile) {
+            ce->hunk->type &= ~NGX_HUNK_FILE;
+        }
+
+        size += ngx_hunk_size(ce->hunk);
+
+        if (ce->hunk->type & (NGX_HUNK_FLUSH|NGX_HUNK_RECYCLED)) {
+            flush = size;
+        }
+
+        if (ce->hunk->type & NGX_HUNK_LAST) {
+            last = 1;
+        }
+    }
+
 #if (NGX_DEBUG_WRITE_FILTER)
     ngx_log_debug(r->connection->log,
                   "write filter: last:%d flush:%qd size:%qd" _
@@ -176,6 +174,7 @@ static void *ngx_http_write_filter_creat
                   NULL);
 
     conf->buffer_output = NGX_CONF_UNSET;
+    conf->sendfile = NGX_CONF_UNSET;
 
     return conf;
 }
@@ -188,6 +187,7 @@ static char *ngx_http_write_filter_merge
     ngx_http_write_filter_conf_t *conf = child;
 
     ngx_conf_merge_size_value(conf->buffer_output, prev->buffer_output, 1460);
+    ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
 
     return NULL;
 }
--- a/src/os/unix/ngx_freebsd_sendfile_chain.c
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c
@@ -154,6 +154,11 @@ ngx_log_debug(c->log, "NOPUSH");
             hdtr.trailers = (struct iovec *) trailer.elts;
             hdtr.trl_cnt = trailer.nelts;
 
+            /*
+             * the old sendfile() "nbytes bug":
+             * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
+             */
+
             if (ngx_freebsd_sendfile_nbytes_bug == 0) {
                 hsize = 0;
             }
@@ -194,7 +199,6 @@ ngx_log_debug(c->log, "NOPUSH");
             if (rc == -1) {
                 err = ngx_errno;
                 if (err == NGX_EAGAIN) {
-                    eagain = 1;
                     ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EAGAIN");
 
                 } else if (err == NGX_EINTR) {
@@ -256,6 +260,11 @@ ngx_log_debug(c->log, "NOPUSH");
         in = ce;
 
         if (eagain) {
+            /*
+             * sendfile() can return EAGAIN even if it has sent
+             * a whole file part and successive sendfile() would
+             * return EAGAIN right away and would not send anything.
+             */
             c->write->ready = 0;
             break;
         }