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 22 files changed, 958 insertions(+), 906 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_hunk.c	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/core/ngx_hunk.c	Tue Oct 21 16:49:56 2003 +0000
@@ -147,18 +147,6 @@
         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) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/event/ngx_event_pipe.c	Tue Oct 21 16:49:56 2003 +0000
@@ -0,0 +1,606 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_pipe.h>
+
+
+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_pipe_t *p);
+
+
+int ngx_event_pipe(ngx_event_pipe_t *p, int do_write)
+{
+    for ( ;; ) {
+        if (do_write) {
+            if (ngx_event_pipe_write_to_downstream(p) == NGX_ABORT) {
+                return NGX_ABORT;
+            }
+        }
+
+        p->read = 0;
+
+        if (ngx_event_pipe_read_upstream(p) == NGX_ABORT) {
+            return NGX_ABORT;
+        }
+
+        if (!p->read) {
+            break;
+        }
+
+        do_write = 1;
+    }
+
+    if (ngx_handle_read_event(p->upstream->read) == NGX_ERROR) {
+        return NGX_ABORT;
+    }
+
+    if (ngx_handle_write_event(p->downstream->write,
+                                           /* TODO: lowat */ 0) == NGX_ERROR) {
+        return NGX_ABORT;
+    }
+
+    return NGX_OK;
+}
+
+
+int ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
+{
+    int           n, rc, size;
+    ngx_hunk_t   *h;
+    ngx_chain_t  *chain, *ce, *te;
+
+    if (p->upstream_eof || p->upstream_error || p->upstream_done) {
+        return NGX_OK;
+    }
+
+    ngx_log_debug(p->log, "read upstream: %d" _ p->upstream->read->ready);
+
+    for ( ;; ) {
+
+        if (p->upstream_eof || p->upstream_error || p->upstream_done) {
+            break;
+        }
+
+        if (p->preread_hunks == NULL && !p->upstream->read->ready) {
+            break;
+        }
+
+        if (p->preread_hunks) {
+
+            /* use the pre-read hunks if they exist */
+
+            p->read = 1;
+            chain = p->preread_hunks;
+            p->preread_hunks = NULL;
+            n = p->preread_size;
+
+            ngx_log_debug(p->log, "preread: %d" _ n);
+
+        } else {
+
+#if (HAVE_KQUEUE)
+
+            /*
+             * kqueue notifies about the end of file or a pending error.
+             * This test allows not to allocate a hunk on these conditions
+             * and not to call ngx_recv_chain().
+             */
+
+            if (ngx_event_flags == NGX_HAVE_KQUEUE_EVENT) {
+
+                if (p->upstream->read->error) {
+                    ngx_log_error(NGX_LOG_ERR, p->log, p->upstream->read->error,
+                                  "readv() failed");
+                    p->upstream_error = 1;
+
+                    break;
+
+                } else if (p->upstream->read->eof
+                           && p->upstream->read->available == 0) {
+                    p->upstream_eof = 1;
+                    p->read = 1;
+
+                    break;
+                }
+            }
+#endif
+
+            if (p->free_raw_hunks) {
+
+                /* use the free hunks if they exist */
+
+                chain = p->free_raw_hunks;
+                p->free_raw_hunks = NULL;
+
+            } else if (p->hunks < p->bufs.num) {
+
+                /* allocate a new hunk if it's still allowed */
+
+                ngx_test_null(h, ngx_create_temp_hunk(p->pool,
+                                                      p->bufs.size, 0, 0),
+                              NGX_ABORT);
+                p->hunks++;
+
+                ngx_alloc_ce_and_set_hunk(te, h, p->pool, NGX_ABORT);
+                chain = te;
+
+            } 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
+                 */
+
+                ngx_log_debug(p->log, "downstream ready");
+
+                break;
+
+            } 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
+                 */
+
+                rc = ngx_event_pipe_write_chain_to_temp_file(p);
+
+                ngx_log_debug(p->log, "temp offset: %d" _ p->temp_offset);
+
+                if (rc == NGX_AGAIN) {
+                    if (ngx_event_flags & NGX_USE_LEVEL_EVENT
+                        && p->upstream->read->active
+                        && p->upstream->read->ready)
+                    {
+                        if (ngx_del_event(p->upstream->read, NGX_READ_EVENT, 0)
+                                                                  == NGX_ERROR)
+                        {
+                            return NGX_ABORT;
+                        }
+                    }
+                }
+
+                if (rc != NGX_OK) {
+                    return rc;
+                }
+
+                chain = p->free_raw_hunks;
+                p->free_raw_hunks = NULL;
+
+            } else {
+
+                /* if there're no hunks to read in then disable a level event */
+
+                ngx_log_debug(p->log, "no hunks to read in");
+    
+                break;
+            }
+
+            n = ngx_recv_chain(p->upstream, chain);
+
+            ngx_log_debug(p->log, "recv_chain: %d" _ n);
+
+            p->free_raw_hunks = chain;
+
+            if (n == NGX_ERROR) {
+                p->upstream_error = 1;
+                return NGX_ERROR;
+            }
+
+            if (n == NGX_AGAIN) {
+                break;
+            }
+
+            p->read = 1;
+
+            if (n == 0) {
+                p->upstream_eof = 1;
+                break;
+            }
+        }
+
+        ce = chain;
+
+        while (ce && n > 0) {
+
+            ngx_remove_shadow_links(ce->hunk);
+
+            size = ce->hunk->end - ce->hunk->last;
+
+            if (n >= size) {
+                ce->hunk->last = ce->hunk->end;
+
+                if (p->input_filter(p, ce->hunk) == NGX_ERROR) {
+                    return NGX_ABORT;
+                }
+
+                n -= size;
+                ce = ce->next;
+
+            } else {
+                ce->hunk->last += n;
+                n = 0;
+            }
+        }
+
+        p->free_raw_hunks = ce;
+    }
+
+    if ((p->upstream_eof || p->upstream_error) && p->free_raw_hunks) {
+        if (p->input_filter(p, p->free_raw_hunks->hunk) == NGX_ERROR) {
+            return NGX_ABORT;
+        }
+
+        /* TODO: p->free_raw_hunk->next can be free()ed */
+        p->free_raw_hunks = p->free_raw_hunks->next;
+    }
+
+    if (p->cachable && p->in) {
+        if (ngx_event_pipe_write_chain_to_temp_file(p) == NGX_ABORT) {
+            return NGX_ABORT;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+int ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
+{
+    size_t        busy_len;
+    ngx_hunk_t   *h;
+    ngx_chain_t  *out, **le, *ce, *te;
+
+    ngx_log_debug(p->log, "write downstream: %d" _ p->downstream->write->ready);
+
+    for ( ;; ) {
+        if (p->downstream_error) {
+            return ngx_drain_chains(p);
+        }
+
+        if ((p->upstream_eof || p->upstream_error || p->upstream_done)
+            && p->out == NULL && p->in == NULL)
+        {
+            p->downstream_done = 1;
+            break;
+        }
+
+        if (!p->downstream->write->ready) {
+            break;
+        }
+
+        busy_len = 0;
+
+        if (!(p->upstream_eof || p->upstream_error || p->upstream_done)) {
+            /* calculate p->busy_len */
+            for (ce = p->busy; ce; ce = ce->next) {
+                busy_len += ngx_hunk_size(ce->hunk);
+            }
+        }
+
+        out = NULL;
+        le = NULL;
+
+        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;
+                }
+
+                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;
+            }
+
+            busy_len += ngx_hunk_size(ce->hunk);
+            ce->next = NULL;
+            ngx_chain_add_ce(out, le, ce);
+        }
+
+        if (out == NULL) {
+            break;
+        }
+
+        if (p->output_filter(p->output_ctx, out) == NGX_ERROR) {
+            p->downstream_error = 1;
+            continue;
+        }
+
+        ngx_chain_update_chains(&p->free, &p->busy, &out);
+
+        /* add the free shadow raw hunks to p->free_raw_hunks */
+
+        for (ce = p->free; ce; ce = ce->next) {
+            if (ce->hunk->type & NGX_HUNK_LAST_SHADOW) {
+                h = ce->hunk->shadow;
+                /* THINK NEEDED ??? */ h->pos = h->last = h->start;
+                h->shadow = NULL;
+                ngx_alloc_ce_and_set_hunk(te, h, p->pool, NGX_ABORT);
+                ngx_add_after_partially_filled_hunk(&p->free_raw_hunks, te);
+
+                ce->hunk->type &= ~NGX_HUNK_LAST_SHADOW;
+            }
+            ce->hunk->shadow = NULL;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+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, *out, **le, **last_free;
+
+    ngx_log_debug(p->log, "write to file");
+
+    if (p->temp_file->fd == NGX_INVALID_FILE) {
+        rc = ngx_create_temp_file(p->temp_file, p->temp_path, p->pool,
+                                  p->cachable);
+
+        if (rc == NGX_ERROR) {
+            return NGX_ABORT;
+        }
+
+        if (rc == NGX_AGAIN) {
+            return NGX_AGAIN;
+        }
+
+        if (!p->cachable && p->temp_file_warn) {
+            ngx_log_error(NGX_LOG_WARN, p->log, 0, p->temp_file_warn);
+        }
+    }
+
+    out = p->in;
+
+    if (!p->cachable) {
+
+        size = 0;
+        ce = p->in;
+        le = NULL;
+
+ngx_log_debug(p->log, "offset: %d" _ p->temp_offset);
+
+        do {
+            hunk_size = ce->hunk->last - ce->hunk->pos;
+
+ngx_log_debug(p->log, "hunk size: %d" _ hunk_size);
+
+            if ((size + hunk_size > p->temp_file_write_size)
+                || (p->temp_offset + hunk_size > p->max_temp_file_size))
+            {
+                break;
+            }
+
+            size += hunk_size;
+            le = &ce->next;
+            ce = ce->next;
+
+        } while (ce);
+
+ngx_log_debug(p->log, "size: %d" _ size);
+
+        if (ce) {
+           p->in = ce;
+           *le = NULL;
+
+        } else {
+           p->in = NULL;
+           p->last_in = &p->in;
+        }
+
+    } else {
+        p->in = NULL;
+        p->last_in = &p->in;
+    }
+
+    if (ngx_write_chain_to_file(p->temp_file, out, p->temp_offset,
+                                                       p->pool) == NGX_ERROR) {
+        return NGX_ABORT;
+    }
+
+    for (last_free = &p->free_raw_hunks;
+         *last_free != NULL;
+         last_free = &(*last_free)->next)
+    {
+        /* void */
+    }
+
+    for (ce = out; ce; ce = next) {
+        next = ce->next;
+        ce->next = NULL;
+
+        h = ce->hunk;
+        h->type |= NGX_HUNK_FILE;
+        h->file = p->temp_file;
+        h->file_pos = p->temp_offset;
+        p->temp_offset += h->last - h->pos;
+        h->file_last = p->temp_offset;
+
+        ngx_chain_add_ce(p->out, p->last_out, ce);
+
+        if (h->type & NGX_HUNK_LAST_SHADOW) {
+            h->shadow->last = h->shadow->pos = h->shadow->start;
+            ngx_alloc_ce_and_set_hunk(te, h->shadow, p->pool, NGX_ABORT);
+            *last_free = te;
+            last_free = &te->next;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+/* the copy input filter */
+
+int ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_hunk_t *hunk)
+{
+    ngx_hunk_t   *h;
+    ngx_chain_t  *ce;
+
+    if (hunk->pos == hunk->last) {
+        return NGX_OK;
+    }
+
+    if (p->free) {
+        h = p->free->hunk;
+        p->free = p->free->next;
+
+    } else {
+        ngx_test_null(h, ngx_alloc_hunk(p->pool), NGX_ERROR);
+    }
+
+    ngx_memcpy(h, hunk, sizeof(ngx_hunk_t));
+    h->shadow = hunk;
+    h->type |= NGX_HUNK_LAST_SHADOW|NGX_HUNK_RECYCLED;
+    hunk->shadow = h;
+
+    ngx_alloc_ce_and_set_hunk(ce, h, p->pool, NGX_ERROR);
+    ngx_chain_add_ce(p->in, p->last_in, ce);
+
+    return NGX_OK;
+}
+
+
+ngx_inline static void ngx_remove_shadow_links(ngx_hunk_t *hunk)
+{
+    ngx_hunk_t  *h, *next;
+
+    if (hunk->shadow == NULL) {
+        return;
+    }
+
+    h = hunk->shadow;
+
+    while (!(h->type & NGX_HUNK_LAST_SHADOW)) {
+        next = h->shadow;
+        h->type &= ~(NGX_HUNK_TEMP|NGX_HUNK_IN_MEMORY|NGX_HUNK_RECYCLED);
+        h->shadow = NULL;
+        h = next;
+    }
+
+    h->type &= ~(NGX_HUNK_TEMP
+                 |NGX_HUNK_IN_MEMORY
+                 |NGX_HUNK_RECYCLED
+                 |NGX_HUNK_LAST_SHADOW);
+    h->shadow = NULL;
+
+    hunk->shadow = NULL;
+}
+
+
+ngx_inline static void ngx_remove_shadow_free_raw_hunk(ngx_chain_t **free,
+                                                       ngx_hunk_t *h)
+{
+    ngx_hunk_t   *s;
+    ngx_chain_t  *ce, **le;
+
+    if (h->shadow == NULL) {
+        return;
+    }
+
+    for (s = h->shadow; !(s->type & NGX_HUNK_LAST_SHADOW); s = s->shadow) {
+        /* void */
+    }
+
+    le = free;
+
+    for (ce = *free ; ce; ce = ce->next) {
+        if (ce->hunk == s) {
+            *le = ce->next;
+            break;
+        }
+
+        if (ce->hunk->shadow) {
+            break;
+        }
+
+        le = &ce->next;
+    }
+}
+
+
+ngx_inline static void ngx_add_after_partially_filled_hunk(ngx_chain_t **chain,
+                                                           ngx_chain_t *ce)
+{
+    if (*chain == NULL) {
+        *chain = ce;
+        return;
+    }
+
+    if ((*chain)->hunk->pos != (*chain)->hunk->last) {
+        ce->next = (*chain)->next;
+        (*chain)->next = ce;
+
+    } else {
+        ce->next = (*chain);
+        (*chain) = ce;
+    }
+}
+
+
+static int ngx_drain_chains(ngx_event_pipe_t *p)
+{
+    ngx_hunk_t   *h;
+    ngx_chain_t  *ce, *te;
+
+    for ( ;; ) {
+        if (p->busy) {
+            ce = p->busy;
+
+        } else if (p->out) {
+            ce = p->out;
+
+        } else if (p->in) {
+            ce = p->in;
+
+        } else {
+            return NGX_OK;
+        }
+
+        while (ce) {
+            if (ce->hunk->type & NGX_HUNK_LAST_SHADOW) {
+                h = ce->hunk->shadow;
+                /* THINK NEEDED ??? */ h->pos = h->last = h->start;
+                h->shadow = NULL;
+                ngx_alloc_ce_and_set_hunk(te, h, p->pool, NGX_ABORT);
+                ngx_add_after_partially_filled_hunk(&p->free_raw_hunks, te);
+
+                ce->hunk->type &= ~NGX_HUNK_LAST_SHADOW;
+            }
+
+            ce->hunk->shadow = NULL;
+            te = ce->next;
+            ce->next = p->free;
+            p->free = ce;
+            ce = te;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/event/ngx_event_pipe.h	Tue Oct 21 16:49:56 2003 +0000
@@ -0,0 +1,75 @@
+#ifndef _NGX_EVENT_PIPE_H_INCLUDED_
+#define _NGX_EVENT_PIPE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+typedef struct ngx_event_pipe_s  ngx_event_pipe_t;
+
+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_pipe_s {
+    ngx_chain_t       *free_raw_hunks;
+    ngx_chain_t       *in;
+    ngx_chain_t      **last_in;
+
+    ngx_chain_t       *out;
+    ngx_chain_t      **last_out;
+
+    ngx_chain_t       *free;
+    ngx_chain_t       *busy;
+
+    /*
+     * the input filter i.e. that moves HTTP/1.1 chunks
+     * from the raw hunks to an incoming chain
+     */
+
+    ngx_event_pipe_input_filter_pt    input_filter;
+    void                              *input_ctx;
+
+    ngx_event_pipe_output_filter_pt   output_filter;
+    void                              *output_ctx;
+
+    unsigned           read:1;
+    unsigned           cachable:1;
+    unsigned           upstream_done:1;
+    unsigned           upstream_eof:1;
+    unsigned           upstream_error:1;
+    unsigned           downstream_done:1;
+    unsigned           downstream_error:1;
+
+    int                hunks;
+    ngx_bufs_t         bufs;
+
+    size_t             max_busy_len;
+
+    off_t              temp_offset;
+    off_t              max_temp_file_size;
+    int                temp_file_write_size;
+
+    ngx_connection_t  *upstream;
+    ngx_connection_t  *downstream;
+
+    ngx_pool_t        *pool;
+    ngx_log_t         *log;
+
+    ngx_chain_t       *preread_hunks;
+    int                preread_size;
+
+    ngx_file_t        *temp_file;
+    ngx_path_t        *temp_path;
+    char              *temp_file_warn;
+};
+
+
+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_PIPE_H_INCLUDED_ */
--- a/src/event/ngx_event_proxy.c	Tue Oct 21 07:47:21 2003 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,607 +0,0 @@
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_event.h>
-#include <ngx_event_proxy.h>
-
-static int ngx_event_proxy_write_chain_to_temp_file(ngx_event_proxy_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);
-
-
-int ngx_event_proxy(ngx_event_proxy_t *p, int do_write)
-{
-    for ( ;; ) {
-        if (do_write) {
-            if (ngx_event_proxy_write_to_downstream(p) == NGX_ABORT) {
-                return NGX_ABORT;
-            }
-        }
-
-        p->read = 0;
-
-        if (ngx_event_proxy_read_upstream(p) == NGX_ABORT) {
-            return NGX_ABORT;
-        }
-
-        if (!p->read) {
-            break;
-        }
-
-        do_write = 1;
-    }
-
-    if (ngx_handle_read_event(p->upstream->read) == NGX_ERROR) {
-        return NGX_ABORT;
-    }
-
-    if (ngx_handle_write_event(p->downstream->write,
-                                           /* TODO: lowat */ 0) == NGX_ERROR) {
-        return NGX_ABORT;
-    }
-
-    return NGX_OK;
-}
-
-
-int ngx_event_proxy_read_upstream(ngx_event_proxy_t *p)
-{
-    int           n, rc, size;
-    ngx_hunk_t   *h;
-    ngx_chain_t  *chain, *ce, *te;
-
-    if (p->upstream_eof || p->upstream_error || p->upstream_done) {
-        return NGX_OK;
-    }
-
-    ngx_log_debug(p->log, "read upstream: %d" _ p->upstream->read->ready);
-
-    for ( ;; ) {
-
-        if (p->upstream_eof || p->upstream_error || p->upstream_done) {
-            break;
-        }
-
-        if (p->preread_hunks == NULL && !p->upstream->read->ready) {
-            break;
-        }
-
-        if (p->preread_hunks) {
-
-            /* use the pre-read hunks if they exist */
-
-            p->read = 1;
-            chain = p->preread_hunks;
-            p->preread_hunks = NULL;
-            n = p->preread_size;
-
-            ngx_log_debug(p->log, "preread: %d" _ n);
-
-        } else {
-
-#if (HAVE_KQUEUE)
-
-            /*
-             * kqueue notifies about the end of file or a pending error.
-             * This test allows not to allocate a hunk on these conditions
-             * and not to call ngx_recv_chain().
-             */
-
-            if (ngx_event_flags == NGX_HAVE_KQUEUE_EVENT) {
-
-                if (p->upstream->read->error) {
-                    ngx_log_error(NGX_LOG_ERR, p->log, p->upstream->read->error,
-                                  "readv() failed");
-                    p->upstream_error = 1;
-
-                    break;
-
-                } else if (p->upstream->read->eof
-                           && p->upstream->read->available == 0) {
-                    p->upstream_eof = 1;
-                    p->read = 1;
-
-                    break;
-                }
-            }
-#endif
-
-            if (p->free_raw_hunks) {
-
-                /* use the free hunks if they exist */
-
-                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) {
-
-                /* allocate a new hunk if it's still allowed */
-
-                ngx_test_null(h, ngx_create_temp_hunk(p->pool,
-                                                      p->bufs.size, 0, 0),
-                              NGX_ABORT);
-                p->hunks++;
-
-                ngx_alloc_ce_and_set_hunk(te, h, p->pool, NGX_ABORT);
-                chain = te;
-
-            } 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.
-                 */
-
-                ngx_log_debug(p->log, "downstream ready");
-
-                break;
-
-            } else if (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.
-                 */
-
-                rc = ngx_event_proxy_write_chain_to_temp_file(p);
-
-                ngx_log_debug(p->log, "temp offset: %d" _ p->temp_offset);
-
-                if (rc == NGX_AGAIN) {
-                    if (ngx_event_flags & NGX_USE_LEVEL_EVENT
-                        && p->upstream->read->active
-                        && p->upstream->read->ready)
-                    {
-                        if (ngx_del_event(p->upstream->read, NGX_READ_EVENT, 0)
-                                                                  == NGX_ERROR)
-                        {
-                            return NGX_ABORT;
-                        }
-                    }
-                }
-
-                if (rc != NGX_OK) {
-                    return rc;
-                }
-
-                chain = p->free_raw_hunks;
-                p->free_raw_hunks = NULL;
-
-            } else {
-
-                /* if there're no hunks to read in then disable a level event */
-
-                ngx_log_debug(p->log, "no hunks to read in");
-    
-                break;
-            }
-
-            n = ngx_recv_chain(p->upstream, chain);
-
-            ngx_log_debug(p->log, "recv_chain: %d" _ n);
-
-            p->free_raw_hunks = chain;
-
-            if (n == NGX_ERROR) {
-                p->upstream_error = 1;
-                return NGX_ERROR;
-            }
-
-            if (n == NGX_AGAIN) {
-                break;
-            }
-
-            p->read = 1;
-
-            if (n == 0) {
-                p->upstream_eof = 1;
-                break;
-            }
-        }
-
-        ce = chain;
-
-        while (ce && n > 0) {
-
-            ngx_remove_shadow_links(ce->hunk);
-
-            size = ce->hunk->end - ce->hunk->last;
-
-            if (n >= size) {
-                ce->hunk->last = ce->hunk->end;
-
-                if (p->input_filter(p, ce->hunk) == NGX_ERROR) {
-                    return NGX_ABORT;
-                }
-
-                n -= size;
-                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;
-            }
-        }
-
-        p->free_raw_hunks = ce;
-    }
-
-    if ((p->upstream_eof || p->upstream_error) && p->free_raw_hunks) {
-        if (p->input_filter(p, p->free_raw_hunks->hunk) == NGX_ERROR) {
-            return NGX_ABORT;
-        }
-
-        /* TODO: p->free_raw_hunk->next can be free()ed */
-        p->free_raw_hunks = p->free_raw_hunks->next;
-    }
-
-    if (p->cachable && p->in) {
-        if (ngx_event_proxy_write_chain_to_temp_file(p) == NGX_ABORT) {
-            return NGX_ABORT;
-        }
-    }
-
-    return NGX_OK;
-}
-
-
-int ngx_event_proxy_write_to_downstream(ngx_event_proxy_t *p)
-{
-    size_t        busy_len;
-    ngx_hunk_t   *h;
-    ngx_chain_t  *out, *ce, *te;
-
-    ngx_log_debug(p->log, "write downstream: %d" _ p->downstream->write->ready);
-
-    for ( ;; ) {
-        if (p->downstream_error) {
-            return ngx_drain_chains(p);
-        }
-
-        if ((p->upstream_eof || p->upstream_error || p->upstream_done)
-            && p->out == NULL && p->in == NULL)
-        {
-            p->downstream_done = 1;
-            break;
-        }
-
-        if (!p->downstream->write->ready) {
-            break;
-        }
-
-        busy_len = 0;
-
-        if (!(p->upstream_eof || p->upstream_error || p->upstream_done)) {
-            /* calculate p->busy_len */
-            for (ce = p->busy; ce; ce = ce->next) {
-                busy_len += ngx_hunk_size(ce->hunk);
-            }
-        }
-
-
-        if (p->out) {
-            out = p->out;
-
-            if (!(p->upstream_eof || p->upstream_error || p->upstream_done)
-                && (busy_len + ngx_hunk_size(out->hunk) > p->max_busy_len))
-            {
-                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;
-
-            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 {
-            break;
-        }
-
-        out->next = NULL;
-
-
-        if (p->output_filter(p->output_ctx, out->hunk) == NGX_ERROR) {
-            p->downstream_error = 1;
-            continue;
-        }
-
-        ngx_chain_update_chains(&p->free, &p->busy, &out);
-
-        /* 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;
-                h->shadow = NULL;
-                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)
-{
-    int           rc, size, hunk_size;
-    ngx_hunk_t   *h;
-    ngx_chain_t  *ce, *te, *next, *in, **le, **last_free;
-
-    ngx_log_debug(p->log, "write to file");
-
-    if (p->temp_file->fd == NGX_INVALID_FILE) {
-        rc = ngx_create_temp_file(p->temp_file, p->temp_path, p->pool,
-                                  p->cachable);
-
-        if (rc == NGX_ERROR) {
-            return NGX_ABORT;
-        }
-
-        if (rc == NGX_AGAIN) {
-            return NGX_AGAIN;
-        }
-
-        if (!p->cachable && p->temp_file_warn) {
-            ngx_log_error(NGX_LOG_WARN, p->log, 0, p->temp_file_warn);
-        }
-    }
-
-    if (!p->cachable) {
-
-        size = 0;
-        ce = p->in;
-        le = NULL;
-
-ngx_log_debug(p->log, "offset: %d" _ p->temp_offset);
-
-        do {
-            hunk_size = ce->hunk->last - ce->hunk->pos;
-
-ngx_log_debug(p->log, "hunk size: %d" _ hunk_size);
-
-            if ((size + hunk_size > p->temp_file_write_size)
-                || (p->temp_offset + hunk_size > p->max_temp_file_size))
-            {
-                break;
-            }
-
-            size += hunk_size;
-            le = &ce->next;
-            ce = ce->next;
-
-        } while (ce);
-
-ngx_log_debug(p->log, "size: %d" _ size);
-
-        if (ce) {
-           in = ce;
-           *le = NULL;
-
-        } else {
-           in = NULL;
-           p->last_in = &p->in;
-        }
-
-    } else {
-        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) {
-        return NGX_ABORT;
-    }
-
-    for (last_free = &p->free_raw_hunks;
-         *last_free != NULL;
-         last_free = &(*last_free)->next)
-    {
-        /* void */
-    }
-
-    for (ce = p->in; ce; ce = next) {
-        next = ce->next;
-        ce->next = NULL;
-
-        h = ce->hunk;
-        h->type |= NGX_HUNK_FILE;
-        h->file = p->temp_file;
-        h->file_pos = p->temp_offset;
-        p->temp_offset += h->last - h->pos;
-        h->file_last = p->temp_offset;
-
-        ngx_chain_add_ce(p->out, p->last_out, ce);
-
-        if (h->type & NGX_HUNK_LAST_SHADOW) {
-            h->shadow->last = h->shadow->pos = h->shadow->start;
-            ngx_alloc_ce_and_set_hunk(te, h->shadow, p->pool, NGX_ABORT);
-            *last_free = te;
-            last_free = &te->next;
-        }
-    }
-
-    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)
-{
-    ngx_hunk_t   *h;
-    ngx_chain_t  *ce;
-
-    if (hunk->pos == hunk->last) {
-        return NGX_OK;
-    }
-
-    if (p->free) {
-        h = p->free->hunk;
-        p->free = p->free->next;
-
-    } else {
-        ngx_test_null(h, ngx_alloc_hunk(p->pool), NGX_ERROR);
-    }
-
-    ngx_memcpy(h, hunk, sizeof(ngx_hunk_t));
-    h->shadow = hunk;
-    h->type |= NGX_HUNK_LAST_SHADOW|NGX_HUNK_RECYCLED;
-    hunk->shadow = h;
-
-    ngx_alloc_ce_and_set_hunk(ce, h, p->pool, NGX_ERROR);
-    ngx_chain_add_ce(p->in, p->last_in, ce);
-
-    return NGX_OK;
-}
-
-
-ngx_inline static void ngx_remove_shadow_links(ngx_hunk_t *hunk)
-{
-    ngx_hunk_t  *h, *next;
-
-    if (hunk->shadow == NULL) {
-        return;
-    }
-
-    h = hunk->shadow;
-
-    while (!(h->type & NGX_HUNK_LAST_SHADOW)) {
-        next = h->shadow;
-        h->type &= ~(NGX_HUNK_TEMP|NGX_HUNK_IN_MEMORY|NGX_HUNK_RECYCLED);
-        h->shadow = NULL;
-        h = next;
-    }
-
-    h->type &= ~(NGX_HUNK_TEMP
-                 |NGX_HUNK_IN_MEMORY
-                 |NGX_HUNK_RECYCLED
-                 |NGX_HUNK_LAST_SHADOW);
-    h->shadow = NULL;
-
-    hunk->shadow = NULL;
-}
-
-
-ngx_inline static void ngx_remove_shadow_free_raw_hunk(ngx_chain_t **free,
-                                                       ngx_hunk_t *h)
-{
-    ngx_hunk_t   *s;
-    ngx_chain_t  *ce, **le;
-
-    if (h->shadow == NULL) {
-        return;
-    }
-
-    for (s = h->shadow; !(s->type & NGX_HUNK_LAST_SHADOW); s = s->shadow) {
-        /* void */
-    }
-
-    le = free;
-
-    for (ce = *free ; ce; ce = ce->next) {
-        if (ce->hunk == s) {
-            *le = ce->next;
-            break;
-        }
-
-        if (ce->hunk->shadow) {
-            break;
-        }
-
-        le = &ce->next;
-    }
-}
-
-
-ngx_inline static void ngx_add_after_partially_filled_hunk(ngx_chain_t **chain,
-                                                           ngx_chain_t *ce)
-{
-    if (*chain == NULL) {
-        *chain = ce;
-        return;
-    }
-
-    if ((*chain)->hunk->pos != (*chain)->hunk->last) {
-        ce->next = (*chain)->next;
-        (*chain)->next = ce;
-
-    } else {
-        ce->next = (*chain);
-        (*chain) = ce;
-    }
-}
-
-
-static int ngx_drain_chains(ngx_event_proxy_t *p)
-{
-    ngx_hunk_t   *h;
-    ngx_chain_t  *ce, *te;
-
-    for ( ;; ) {
-        if (p->busy) {
-            ce = p->busy;
-
-        } else if (p->out) {
-            ce = p->out;
-
-        } else if (p->in) {
-            ce = p->in;
-
-        } else {
-            return NGX_OK;
-        }
-
-        while (ce) {
-            if (ce->hunk->type & NGX_HUNK_LAST_SHADOW) {
-                h = ce->hunk->shadow;
-                /* THINK NEEDED ??? */ h->pos = h->last = h->start;
-                h->shadow = NULL;
-                ngx_alloc_ce_and_set_hunk(te, h, p->pool, NGX_ABORT);
-                ngx_add_after_partially_filled_hunk(&p->free_raw_hunks, te);
-
-                ce->hunk->type &= ~NGX_HUNK_LAST_SHADOW;
-            }
-
-            ce->hunk->shadow = NULL;
-            te = ce->next;
-            ce->next = p->free;
-            p->free = ce;
-            ce = te;
-        }
-    }
-}
--- a/src/event/ngx_event_proxy.h	Tue Oct 21 07:47:21 2003 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-#ifndef _NGX_EVENT_PROXY_H_INCLUDED_
-#define _NGX_EVENT_PROXY_H_INCLUDED_
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_event.h>
-
-
-typedef struct ngx_event_proxy_s  ngx_event_proxy_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);
-
-
-struct ngx_event_proxy_s {
-    ngx_chain_t       *free_raw_hunks;
-    ngx_chain_t       *in;
-    ngx_chain_t      **last_in;
-
-    ngx_chain_t       *out;
-    ngx_chain_t      **last_out;
-
-    ngx_chain_t       *free;
-    ngx_chain_t       *busy;
-
-    /*
-     * the input filter i.e. that moves HTTP/1.1 chunks
-     * from the raw hunks to an incoming chain
-     */
-
-    ngx_event_proxy_input_filter_pt    input_filter;
-    void                              *input_ctx;
-
-    ngx_event_proxy_output_filter_pt   output_filter;
-    void                              *output_ctx;
-
-    unsigned           read:1;
-    unsigned           cachable:1;
-    unsigned           upstream_done:1;
-    unsigned           upstream_eof:1;
-    unsigned           upstream_error:1;
-    unsigned           downstream_done:1;
-    unsigned           downstream_error:1;
-
-    int                hunks;
-    ngx_bufs_t         bufs;
-
-    size_t             max_busy_len;
-
-    off_t              temp_offset;
-    off_t              max_temp_file_size;
-    int                temp_file_write_size;
-
-    ngx_connection_t  *upstream;
-    ngx_connection_t  *downstream;
-
-    ngx_pool_t        *pool;
-    ngx_log_t         *log;
-
-    ngx_chain_t       *preread_hunks;
-    int                preread_size;
-
-    ngx_file_t        *temp_file;
-    ngx_path_t        *temp_path;
-    char              *temp_file_warn;
-};
-
-
-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);
-
-
-#endif /* _NGX_EVENT_PROXY_H_INCLUDED_ */
--- a/src/http/modules/ngx_http_chunked_filter.c	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/modules/ngx_http_chunked_filter.c	Tue Oct 21 16:49:56 2003 +0000
@@ -39,7 +39,7 @@
         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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/modules/ngx_http_gzip_filter.c	Tue Oct 21 16:49:56 2003 +0000
@@ -157,8 +157,9 @@
     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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/modules/ngx_http_not_modified_filter.c	Tue Oct 21 16:49:56 2003 +0000
@@ -54,7 +54,8 @@
 
     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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/modules/ngx_http_range_filter.c	Tue Oct 21 16:49:56 2003 +0000
@@ -49,7 +49,7 @@
     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 @@
             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 @@
             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 @@
             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 @@
         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 @@
                                       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 @@
                                      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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/modules/ngx_http_static_handler.c	Tue Oct 21 16:49:56 2003 +0000
@@ -191,6 +191,7 @@
     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 @@
 #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 @@
     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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.c	Tue Oct 21 16:49:56 2003 +0000
@@ -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 @@
 {
     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 @@
             }
         }
 
-        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 @@
             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 @@
 
     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 @@
     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 @@
     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 @@
         r = p->request;
     }
 
-    ep = p->event_proxy;
+    ep = p->event_pipe;
 
     if (ev->timedout) {
         if (ev->write) {
@@ -797,7 +802,7 @@
         }
 
     } 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 @@
 
     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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.h	Tue Oct 21 16:49:56 2003 +0000
@@ -48,6 +48,8 @@
     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 @@
     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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/ngx_http_core_module.c	Tue Oct 21 16:49:56 2003 +0000
@@ -130,13 +130,6 @@
      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 @@
     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 @@
 
 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 @@
         }
     }
 
-    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 @@
 
     */
 
-    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 @@
     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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/ngx_http_core_module.h	Tue Oct 21 16:49:56 2003 +0000
@@ -110,7 +110,6 @@
     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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/ngx_http_filter.h	Tue Oct 21 16:49:56 2003 +0000
@@ -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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/ngx_http_header_filter.c	Tue Oct 21 16:49:56 2003 +0000
@@ -159,9 +159,11 @@
         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 @@
         *(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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/ngx_http_output_filter.c	Tue Oct 21 16:49:56 2003 +0000
@@ -84,11 +84,11 @@
 
 
 
-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 @@
 
     /*
      * 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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/ngx_http_request.c	Tue Oct 21 16:49:56 2003 +0000
@@ -9,6 +9,7 @@
 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 @@
     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_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 @@
 
             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 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 @@
 
 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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/ngx_http_request.h	Tue Oct 21 16:49:56 2003 +0000
@@ -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 {
-    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 @@
     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 @@
     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 @@
 
     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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/ngx_http_special_response.c	Tue Oct 21 16:49:56 2003 +0000
@@ -152,8 +152,9 @@
 
 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 @@
     }
 
     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 @@
         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 @@
         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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/http/ngx_http_write_filter.c	Tue Oct 21 16:49:56 2003 +0000
@@ -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 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 @@
     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 @@
     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 @@
                   NULL);
 
     conf->buffer_output = NGX_CONF_UNSET;
+    conf->sendfile = NGX_CONF_UNSET;
 
     return conf;
 }
@@ -188,6 +187,7 @@
     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	Tue Oct 21 07:47:21 2003 +0000
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c	Tue Oct 21 16:49:56 2003 +0000
@@ -154,6 +154,11 @@
             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 @@
             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 @@
         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;
         }