changeset 170:c42be4185301

nginx-0.0.1-2003-11-03-01:56:18 import
author Igor Sysoev <igor@sysoev.ru>
date Sun, 02 Nov 2003 22:56:18 +0000
parents edf29bb717da
children aff0e5d32af8
files src/core/ngx_conf_file.h src/core/ngx_file.c src/core/ngx_file.h src/core/ngx_hunk.h src/core/ngx_output_chain.c src/event/modules/ngx_kqueue_module.c src/event/ngx_event.h src/event/ngx_event_pipe.c src/event/ngx_event_pipe.h src/http/modules/ngx_http_static_handler.c src/http/modules/proxy/ngx_http_proxy_cache.c src/http/modules/proxy/ngx_http_proxy_handler.c src/http/modules/proxy/ngx_http_proxy_handler.h src/http/modules/proxy/ngx_http_proxy_header.c src/http/modules/proxy/ngx_http_proxy_parse.c src/http/modules/proxy/ngx_http_proxy_upstream.c src/http/ngx_http.h src/http/ngx_http_cache.c src/http/ngx_http_cache.h src/http/ngx_http_core_module.c src/http/ngx_http_request.h src/http/ngx_http_request_body.c src/os/unix/ngx_files.c src/os/unix/ngx_files.h src/os/unix/ngx_freebsd_sendfile_chain.c src/os/unix/ngx_writev_chain.c
diffstat 26 files changed, 1416 insertions(+), 745 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -150,6 +150,8 @@ typedef struct {
 } ngx_conf_num_bounds_t;
 
 
+#define NGX_CONF_BITMASK_SET  1
+
 typedef struct {
     ngx_str_t  name;
     int        mask;
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -12,7 +12,7 @@ int ngx_write_chain_to_temp_file(ngx_tem
     int  rc;
 
     if (tf->file.fd == NGX_INVALID_FILE) {
-        rc = ngx_create_temp_file(&tf->file, &tf->path, tf->pool,
+        rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                                   tf->persistent);
     
         if (rc == NGX_ERROR || rc == NGX_AGAIN) {
@@ -24,7 +24,7 @@ int ngx_write_chain_to_temp_file(ngx_tem
         }
     }
 
-    return ngx_write_chain_to_file(&tf->file, chain, tf->file.offset, tf->pool);
+    return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool);
 }
 
 
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -28,7 +28,8 @@ typedef struct {
 
 typedef struct {
     ngx_file_t   file;
-    ngx_path_t   path;
+    off_t        offset;
+    ngx_path_t  *path;
     ngx_pool_t  *pool;
     char        *warn;
 
--- a/src/core/ngx_hunk.h
+++ b/src/core/ngx_hunk.h
@@ -20,7 +20,7 @@
 #define NGX_HUNK_RECYCLED     0x0010
 
 /* the hunk is in file */
-#define NGX_HUNK_FILE         0x0100
+#define NGX_HUNK_FILE         0x0020
 
 #define NGX_HUNK_STORAGE      (NGX_HUNK_IN_MEMORY                            \
                                |NGX_HUNK_TEMP|NGX_HUNK_MEMORY|NGX_HUNK_MMAP  \
@@ -30,9 +30,12 @@
 
 /* in thread state flush means to write the hunk completely before return */
 /* in event state flush means to start to write the hunk */
-#define NGX_HUNK_FLUSH        0x1000
+#define NGX_HUNK_FLUSH        0x0100
 /* last hunk */
-#define NGX_HUNK_LAST         0x2000
+#define NGX_HUNK_LAST         0x0200
+
+
+#define NGX_HUNK_PREREAD      0x2000
 #define NGX_HUNK_LAST_SHADOW  0x4000
 #define NGX_HUNK_TEMP_FILE    0x8000
 
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -169,8 +169,12 @@ ngx_inline static int ngx_output_chain_n
         return 0;
     }
 
-    if (!ctx->sendfile && (!(hunk->type & NGX_HUNK_IN_MEMORY))) {
-        return 1;
+    if (!ctx->sendfile) {
+        if (!(hunk->type & NGX_HUNK_IN_MEMORY)) {
+            return 1;
+        }
+
+        hunk->type &= ~NGX_HUNK_FILE;
     }
 
     if (ctx->need_in_memory && (!(hunk->type & NGX_HUNK_IN_MEMORY))) {
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -380,7 +380,9 @@ static int ngx_kqueue_process_events(ngx
     for (i = 0; i < events; i++) {
 
 #if (NGX_DEBUG_EVENT)
-        if (event_list[i].ident > 0x8000000) {
+        if (event_list[i].ident > 0x8000000
+            && event_list[i].ident != (unsigned) -1)
+        {
             ngx_log_debug(log,
                           "kevent: %08x: ft:%d fl:%08x ff:%08x d:%d ud:%08x" _
                           event_list[i].ident _ event_list[i].filter _
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -22,6 +22,7 @@ typedef struct {
 
 struct ngx_event_s {
     void            *data;
+    /* TODO rename to handler, move flags to struct start */
     void           (*event_handler)(ngx_event_t *ev);
 
     void            *context;
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -42,24 +42,28 @@ int ngx_event_pipe(ngx_event_pipe_t *p, 
         do_write = 1;
     }
 
-    rev = p->upstream->read;
+    if (p->upstream->fd != -1) {
+        rev = p->upstream->read;
 
-    if (ngx_handle_read_event(rev, (rev->eof || rev->error)) == NGX_ERROR) {
-        return NGX_ABORT;
+        if (ngx_handle_read_event(rev, (rev->eof || rev->error)) == NGX_ERROR) {
+            return NGX_ABORT;
+        }
+
+        if (rev->active) {
+            ngx_add_timer(rev, p->read_timeout);
+        }
     }
 
-    if (rev->active) {
-        ngx_add_timer(rev, p->read_timeout);
-    }
-
-    wev = p->downstream->write;
+    if (p->downstream->fd != -1) {
+        wev = p->downstream->write;
 
-    if (ngx_handle_write_event(wev, p->send_lowat) == NGX_ERROR) {
-        return NGX_ABORT;
-    }
+        if (ngx_handle_write_event(wev, p->send_lowat) == NGX_ERROR) {
+            return NGX_ABORT;
+        }
 
-    if (wev->active) {
-        ngx_add_timer(wev, p->send_timeout);
+        if (wev->active) {
+            ngx_add_timer(wev, p->send_timeout);
+        }
     }
 
     return NGX_OK;
@@ -166,7 +170,9 @@ int ngx_event_pipe_read_upstream(ngx_eve
 
                 break;
 
-            } else if (p->cachable || p->temp_offset < p->max_temp_file_size) {
+            } else if (p->cachable
+                       || p->temp_file->offset < p->max_temp_file_size)
+            {
 
                 /*
                  * if it's allowed then save some hunks from r->in
@@ -175,7 +181,7 @@ int ngx_event_pipe_read_upstream(ngx_eve
 
                 rc = ngx_event_pipe_write_chain_to_temp_file(p);
 
-                ngx_log_debug(p->log, "temp offset: %d" _ p->temp_offset);
+                ngx_log_debug(p->log, "temp offset: %d" _ p->temp_file->offset);
 
                 if (rc == NGX_AGAIN) {
                     if (ngx_event_flags & NGX_USE_LEVEL_EVENT
@@ -414,8 +420,8 @@ int ngx_event_pipe_write_to_downstream(n
 
                 /* reset p->temp_offset if all hunks had been sent */
 
-                if (cl->hunk->file_last == p->temp_offset) {
-                    p->temp_offset = 0;
+                if (cl->hunk->file_last == p->temp_file->offset) {
+                    p->temp_file->offset = 0;
                 }
             }
         }
@@ -428,37 +434,29 @@ int ngx_event_pipe_write_to_downstream(n
 static int ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p)
 {
     int           rc, size, hsize;
+    char         *save_pos;
     ngx_hunk_t   *h;
     ngx_chain_t  *cl, *tl, *next, *out, **ll, **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;
-        }
+    out = p->in;
 
-        if (rc == NGX_AGAIN) {
-            return NGX_AGAIN;
-        }
+    if (out->hunk->type & NGX_HUNK_PREREAD) {
+        save_pos = out->hunk->pos;
+        out->hunk->pos = out->hunk->start;
 
-        if (!p->cachable && p->temp_file_warn) {
-            ngx_log_error(NGX_LOG_WARN, p->log, 0, p->temp_file_warn);
-        }
+    } else {
+        save_pos = NULL;
     }
 
-    out = p->in;
-
     if (!p->cachable) {
 
         size = 0;
         cl = p->in;
         ll = NULL;
 
-ngx_log_debug(p->log, "offset: %d" _ p->temp_offset);
+ngx_log_debug(p->log, "offset: %d" _ p->temp_file->offset);
 
         do {
             hsize = cl->hunk->last - cl->hunk->pos;
@@ -466,7 +464,7 @@ ngx_log_debug(p->log, "offset: %d" _ p->
 ngx_log_debug(p->log, "hunk size: %d" _ hsize);
 
             if ((size + hsize > p->temp_file_write_size)
-                || (p->temp_offset + size + hsize > p->max_temp_file_size))
+               || (p->temp_file->offset + size + hsize > p->max_temp_file_size))
             {
                 break;
             }
@@ -493,8 +491,7 @@ ngx_log_debug(p->log, "size: %d" _ size)
         p->last_in = &p->in;
     }
 
-    if (ngx_write_chain_to_file(p->temp_file, out, p->temp_offset,
-                                                       p->pool) == NGX_ERROR) {
+    if (ngx_write_chain_to_temp_file(p->temp_file, out) == NGX_ERROR) {
         return NGX_ABORT;
     }
 
@@ -505,15 +502,21 @@ ngx_log_debug(p->log, "size: %d" _ size)
         /* void */
     }
 
+    if (out->hunk->type & NGX_HUNK_PREREAD) {
+        p->temp_file->offset += save_pos - out->hunk->pos;
+        out->hunk->pos = save_pos;
+        out->hunk->type &= ~NGX_HUNK_PREREAD;
+    }
+
     for (cl = out; cl; cl = next) {
         next = cl->next;
         cl->next = NULL;
 
         h = cl->hunk;
-        h->file = p->temp_file;
-        h->file_pos = p->temp_offset;
-        p->temp_offset += h->last - h->pos;
-        h->file_last = p->temp_offset;
+        h->file = &p->temp_file->file;
+        h->file_pos = p->temp_file->offset;
+        p->temp_file->offset += h->last - h->pos;
+        h->file_last = p->temp_file->offset;
 
         if (p->cachable) {
             h->type |= NGX_HUNK_FILE;
--- a/src/event/ngx_event_pipe.h
+++ b/src/event/ngx_event_pipe.h
@@ -54,7 +54,6 @@ struct ngx_event_pipe_s {
 
     size_t             busy_size;
 
-    off_t              temp_offset;
     off_t              max_temp_file_size;
     int                temp_file_write_size;
 
@@ -71,9 +70,8 @@ struct ngx_event_pipe_s {
     ngx_chain_t       *preread_hunks;
     int                preread_size;
 
-    ngx_file_t        *temp_file;
-    ngx_path_t        *temp_path;
-    char              *temp_file_warn;
+    ngx_temp_file_t   *temp_file;
+
     /* STUB */ int     num;
 };
 
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -39,6 +39,7 @@ ngx_module_t  ngx_http_static_module = {
 
 int ngx_http_static_translate_handler(ngx_http_request_t *r)
 {
+    int                        rc, level;
     char                      *location, *last;
     ngx_err_t                  err;
     ngx_http_core_loc_conf_t  *clcf;
@@ -116,18 +117,24 @@ ngx_log_debug(r->connection->log, "HTTP 
 
     if (r->file.fd == NGX_INVALID_FILE) {
         err = ngx_errno;
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
-                      ngx_open_file_n " \"%s\" failed", r->file.name.data);
 
         if (err == NGX_ENOENT || err == NGX_ENOTDIR) {
-            return NGX_HTTP_NOT_FOUND;
+            level = NGX_LOG_ERR;
+            rc = NGX_HTTP_NOT_FOUND;
 
         } else if (err == NGX_EACCES) {
-            return NGX_HTTP_FORBIDDEN;
+            level = NGX_LOG_ERR;
+            rc = NGX_HTTP_FORBIDDEN;
 
         } else {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            level = NGX_LOG_CRIT;
+            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
+
+        ngx_log_error(level, r->connection->log, ngx_errno,
+                      ngx_open_file_n " \"%s\" failed", r->file.name.data);
+
+        return rc;
     }
 
 ngx_log_debug(r->connection->log, "FILE: %d" _ r->file.fd);
new file mode 100644
--- /dev/null
+++ b/src/http/modules/proxy/ngx_http_proxy_cache.c
@@ -0,0 +1,235 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_proxy_handler.h>
+
+
+int ngx_http_proxy_get_cached_response(ngx_http_proxy_ctx_t *p)
+{
+    int                         rc;
+    char                       *last;
+    ngx_http_request_t         *r;
+    ngx_http_proxy_cache_t     *c;
+    ngx_http_proxy_upstream_t  *u;
+
+    r = p->request;
+
+    if (!(c = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_cache_t)))) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    c->ctx.file.fd = NGX_INVALID_FILE;
+    c->ctx.file.log = r->connection->log;
+    c->ctx.path = p->lcf->cache_path;
+
+    u = p->lcf->upstream;
+
+    c->ctx.key.len = u->url.len + r->uri.len - u->location->len + r->args.len;
+    if (!(c->ctx.key.data = ngx_palloc(r->pool, c->ctx.key.len + 1))) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    last = ngx_cpymem(c->ctx.key.data, u->url.data, u->url.len);
+
+    last = ngx_cpymem(last, r->uri.data + u->location->len,
+                      r->uri.len - u->location->len);
+
+    if (r->args.len > 0) {
+        *(last++) = '?';
+        last = ngx_cpymem(last, r->args.data, r->args.len);
+    }
+    *last = '\0';
+
+    p->header_in = ngx_create_temp_hunk(r->pool, p->lcf->header_buffer_size);
+    if (p->header_in == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+    p->header_in->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module;
+
+    c->ctx.buf = p->header_in; 
+    p->cache = c;
+
+    rc = ngx_http_cache_get_file(r, &c->ctx);
+
+    if (rc == NGX_OK || rc == NGX_STALE) {
+        p->header_in->pos += c->ctx.header.size;
+
+    } else if (rc == NGX_DECLINED) {
+        p->header_in->pos += c->ctx.header.size;
+        p->header_in->last = p->header_in->pos;
+    }
+
+    return rc;
+}
+
+
+int ngx_http_proxy_process_cached_response(ngx_http_proxy_ctx_t *p)
+{
+    int                      rc, i;
+    ngx_table_elt_t         *h;
+    ngx_http_request_t      *r;
+    ngx_http_proxy_cache_t  *c;
+
+    rc = ngx_http_proxy_parse_status_line(p);
+
+    c = p->cache;
+    r = p->request;
+
+    if (rc == NGX_AGAIN) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "\"proxy_header_buffer_size\" "
+                      "is too small to read header from \"%s\"",
+                      c->ctx.file.name.data);
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "no valid HTTP/1.0 header in \"%s\"",
+                      c->ctx.file.name.data);
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* rc == NGX_OK */
+
+    c->status = p->status;
+    c->status_line.len = p->status_end - p->status_start;
+    c->status_line.data = ngx_palloc(r->pool, c->status_line.len + 1);
+    if (c->status_line.data == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    ngx_cpystrn(c->status_line.data, p->status_start, c->status_line.len + 1);
+
+    ngx_log_debug(r->connection->log, "http cache status %d '%s'" _ 
+                  c->status _ c->status_line.data);
+
+    c->headers_in.headers = ngx_create_table(r->pool, 20);
+
+    for ( ;; ) {
+        rc = ngx_http_parse_header_line(r, p->header_in);
+
+        if (rc == NGX_OK) {
+
+            /* a header line has been parsed successfully */
+
+            h = ngx_http_add_header(&c->headers_in, ngx_http_proxy_headers_in);
+            if (h == NULL) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+
+            h->key.len = r->header_name_end - r->header_name_start;
+            h->value.len = r->header_end - r->header_start;
+
+            h->key.data = ngx_palloc(r->pool,
+                                     h->key.len + 1 + h->value.len + 1);
+            if (h->key.data == NULL) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+
+            h->value.data = h->key.data + h->key.len + 1;
+            ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1);
+            ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1);
+
+            for (i = 0; ngx_http_proxy_headers_in[i].name.len != 0; i++) {
+                if (ngx_http_proxy_headers_in[i].name.len != h->key.len) {
+                    continue;
+                }
+
+                if (ngx_strcasecmp(ngx_http_proxy_headers_in[i].name.data,
+                                                             h->key.data) == 0)
+                {
+                    *((ngx_table_elt_t **) ((char *) &c->headers_in
+                                   + ngx_http_proxy_headers_in[i].offset)) = h;
+                    break;
+                }
+            }
+
+            ngx_log_debug(r->connection->log, "HTTP cache header: '%s: %s'" _
+                          h->key.data _ h->value.data);
+
+            continue;
+
+        } else if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
+
+            /* a whole header has been parsed successfully */
+
+            ngx_log_debug(r->connection->log, "HTTP header done");
+
+            return ngx_http_proxy_send_cached_response(p);
+
+        } else if (rc == NGX_HTTP_PARSE_INVALID_HEADER) {
+
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "invalid header in \"%s\"",
+                          c->ctx.file.name.data);
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        /* rc == NGX_AGAIN || rc == NGX_HTTP_PARSE_TOO_LONG_HEADER */
+
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "\"proxy_header_buffer_size\" "
+                      "is too small to read header from \"%s\"",
+                      c->ctx.file.name.data);
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+}
+
+
+int ngx_http_proxy_send_cached_response(ngx_http_proxy_ctx_t *p)
+{
+    int                  rc;
+    ngx_hunk_t          *h;
+    ngx_chain_t          out;
+    ngx_http_request_t  *r;
+
+    r = p->request;
+
+    r->headers_out.status = p->status;
+
+#if 0
+    r->headers_out.content_length_n = -1;
+    r->headers_out.content_length = NULL;
+#endif
+
+    /* copy an cached header to r->headers_out */
+    
+    if (ngx_http_proxy_copy_header(p, &p->cache->headers_in) == NGX_ERROR) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* we need to allocate all before the header would be sent */
+
+    if (!((h = ngx_calloc_hunk(r->pool)))) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if (!((h->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t))))) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    rc = ngx_http_send_header(r);
+
+    /* NEEDED ??? */ p->header_sent = 1;
+
+    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+        return rc;
+    }
+
+    /* TODO: part in p->header_in */
+
+    h->type = r->main ? NGX_HUNK_FILE : NGX_HUNK_FILE|NGX_HUNK_LAST;
+
+    h->file_pos = p->header_in->pos - p->header_in->start;
+    h->file_last = h->file_pos + p->cache->ctx.header.length;
+
+    h->file->fd = p->cache->ctx.file.fd;
+    h->file->log = r->connection->log;
+    
+    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
@@ -1,26 +1,24 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
-#include <ngx_event_connect.h>
-#include <ngx_event_pipe.h>
 #include <ngx_http.h>
 #include <ngx_http_proxy_handler.h>
 
 
-
 static int ngx_http_proxy_handler(ngx_http_request_t *r);
+static int ngx_http_proxy_request_upstream(ngx_http_proxy_ctx_t *p);
 static void ngx_http_proxy_init_request(void *data);
 static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p);
 static void ngx_http_proxy_send_request_handler(ngx_event_t *wev);
+static void ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p);
 static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p);
 static void ngx_http_proxy_process_upstream_status_line(ngx_event_t *rev);
 static void ngx_http_proxy_process_upstream_headers(ngx_event_t *rev);
 static ssize_t ngx_http_proxy_read_upstream_header(ngx_http_proxy_ctx_t *);
 static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p);
 static void ngx_http_proxy_process_body(ngx_event_t *ev);
+static int ngx_http_proxy_update_cache(ngx_http_proxy_ctx_t *p);
 
-static int ngx_http_proxy_parse_status_line(ngx_http_proxy_ctx_t *p);
 static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p, int ft_type);
 static int ngx_http_proxy_log_state(ngx_http_proxy_ctx_t *p, int status);
 static void ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int rc);
@@ -42,8 +40,20 @@ static char *ngx_http_proxy_parse_upstre
 static ngx_conf_bitmask_t  next_upstream_masks[] = {
     { ngx_string("error"), NGX_HTTP_PROXY_FT_ERROR },
     { ngx_string("timeout"), NGX_HTTP_PROXY_FT_TIMEOUT },
-    { ngx_string("http_header"), NGX_HTTP_PROXY_FT_HTTP_HEADER },
+    { ngx_string("invalid_header"), NGX_HTTP_PROXY_FT_INVALID_HEADER },
     { ngx_string("http_500"), NGX_HTTP_PROXY_FT_HTTP_500 },
+    { ngx_string("http_404"), NGX_HTTP_PROXY_FT_HTTP_404 },
+    { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_bitmask_t  use_stale_masks[] = {
+    { ngx_string("error"), NGX_HTTP_PROXY_FT_ERROR },
+    { ngx_string("timeout"), NGX_HTTP_PROXY_FT_TIMEOUT },
+    { ngx_string("invalid_header"), NGX_HTTP_PROXY_FT_INVALID_HEADER },
+    { ngx_string("http_500"), NGX_HTTP_PROXY_FT_HTTP_500 },
+    { ngx_string("busy_lock"), NGX_HTTP_PROXY_FT_BUSY_LOCK },
+    { ngx_string("max_waiting"), NGX_HTTP_PROXY_FT_MAX_WAITING },
     { ngx_null_string, 0 }
 };
 
@@ -105,6 +115,13 @@ static ngx_command_t  ngx_http_proxy_com
       offsetof(ngx_http_proxy_loc_conf_t, busy_buffers_size),
       NULL },
 
+    { ngx_string("proxy_cache_path"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
+      ngx_conf_set_path_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_proxy_loc_conf_t, cache_path),
+      NULL },
+
     { ngx_string("proxy_temp_path"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
       ngx_conf_set_path_slot,
@@ -119,6 +136,13 @@ static ngx_command_t  ngx_http_proxy_com
       offsetof(ngx_http_proxy_loc_conf_t, temp_file_write_size),
       NULL },
 
+    { ngx_string("proxy_cache"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_proxy_loc_conf_t, cache),
+      NULL },
+
     { ngx_string("proxy_pass_server"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -133,6 +157,13 @@ static ngx_command_t  ngx_http_proxy_com
       offsetof(ngx_http_proxy_loc_conf_t, next_upstream),
       &next_upstream_masks },
 
+    { ngx_string("proxy_use_stale"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY,
+      ngx_conf_set_bitmask_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_proxy_loc_conf_t, use_stale),
+      &use_stale_masks },
+
       ngx_null_command
 };
 
@@ -172,7 +203,7 @@ static char *upstream_header_errors[] = 
 };
 
 
-static ngx_http_header_t headers_in[] = {
+ngx_http_header_t ngx_http_proxy_headers_in[] = {
     { ngx_string("Date"), offsetof(ngx_http_proxy_headers_in_t, date) },
     { ngx_string("Server"), offsetof(ngx_http_proxy_headers_in_t, server) },
     { ngx_string("Connection"),
@@ -198,13 +229,50 @@ static char  connection_close_header[] =
 
 static int ngx_http_proxy_handler(ngx_http_request_t *r)
 {
-    ngx_http_proxy_ctx_t  *p;
+    int                         rc;
+    char                       *last;
+    ngx_http_cache_ctx_t       *cctx;
+    ngx_http_proxy_ctx_t       *p;
+    ngx_http_proxy_upstream_t  *u;
 
     ngx_http_create_ctx(r, p, ngx_http_proxy_module,
                         sizeof(ngx_http_proxy_ctx_t),
                         NGX_HTTP_INTERNAL_SERVER_ERROR);
 
     p->lcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
+    p->request = r;
+
+    /* TODO: we currently support reverse proxy only */
+    p->accel = 1;
+
+    if (!p->lcf->cache || r->bypass_cache) {
+        return ngx_http_proxy_request_upstream(p);
+    }
+
+    rc = ngx_http_proxy_get_cached_response(p);
+
+    if (rc == NGX_OK) {
+        return ngx_http_proxy_process_cached_response(p);
+    }
+
+    if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) {
+        return rc;
+    }
+
+    if (rc == NGX_DECLINED || rc == NGX_STALE) {
+        return ngx_http_proxy_request_upstream(p);
+    }
+
+    return NGX_DONE;
+}
+
+
+static int ngx_http_proxy_request_upstream(ngx_http_proxy_ctx_t *p)
+{
+    ngx_http_request_t  *r;
+
+    r = p->request;
+
     p->upstream.peers = p->lcf->peers;
     p->upstream.tries = p->lcf->peers->number;
 
@@ -212,29 +280,28 @@ static int ngx_http_proxy_handler(ngx_ht
                    sizeof(ngx_http_proxy_state_t),
                    NGX_HTTP_INTERNAL_SERVER_ERROR);
 
-    p->request = r;
     p->method = r->method;
 
-    /* TODO: we currently support reverse proxy only */
-    p->accel = 1;
+    /* STUB */ p->cachable = p->lcf->cache;
 
     if (r->headers_in.content_length_n > 0) {
-        ngx_test_null(r->temp_file,
-                      ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)),
-                      NGX_HTTP_INTERNAL_SERVER_ERROR);
+        if (!(r->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)))) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
 
         r->temp_file->file.fd = NGX_INVALID_FILE;
         r->temp_file->file.log = r->connection->log;
-        r->temp_file->path = *p->lcf->temp_path;
+        r->temp_file->path = p->lcf->temp_path;
         r->temp_file->pool = r->pool;
         r->temp_file->warn = "a client request body is buffered "
                              "to a temporary file";
-        /* STUB */ r->temp_file->persistent = 1;
+        /* r->temp_file->persistent = 0; */
 
         r->request_body_handler = ngx_http_proxy_init_request;
         r->data = p;
 
-        /* TODO: we ignore return value of ngx_http_read_client_request_body */
+        /* TODO: we ignore return value of ngx_http_read_client_request_body,
+           probably we should not return anything  */
         ngx_http_read_client_request_body(r, p->lcf->request_buffer_size);
 
         return NGX_DONE;
@@ -255,7 +322,6 @@ static void ngx_http_proxy_init_request(
     ngx_output_chain_ctx_t  *octx;
     ngx_chain_writer_ctx_t  *wctx;
 
-
     r = p->request;
 
 ngx_log_debug(r->connection->log, "timer_set: %d" _
@@ -283,40 +349,27 @@ ngx_log_debug(r->connection->log, "timer
     r->connection->log->handler = ngx_http_proxy_log_error;
     p->action = "connecting to upstream";
 
-    octx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t));
-    if (octx == NULL) {
+    if (!(octx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t)))) {
         ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
         return;
     }
 
     p->output_chain_ctx = octx;
-
-    if (r->request_body_hunk) {
-        octx->free = ngx_alloc_chain_link(r->pool);
-        if (octx->free == NULL) {
-            ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
-        }
-        octx->free->hunk = r->request_body_hunk;
-        octx->free->next = NULL;
-    }
-
     octx->sendfile = r->sendfile;
     octx->pool = r->pool;
     octx->bufs.num = 1;
     octx->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module;
     octx->output_filter = (ngx_output_chain_filter_pt) ngx_chain_writer;
 
-    wctx = ngx_pcalloc(r->pool, sizeof(ngx_chain_writer_ctx_t));
-    if (wctx == NULL) {
+    if (!(wctx = ngx_palloc(r->pool, sizeof(ngx_chain_writer_ctx_t)))) {
         ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
         return;
     }
 
     octx->output_ctx = wctx;
     wctx->pool = r->pool;
-    wctx->last = &wctx->out;
 
-    ngx_http_proxy_send_request(p);
+    ngx_http_proxy_connect(p);
 }
 
 
@@ -441,106 +494,135 @@ static void ngx_http_proxy_send_request_
     c = wev->data;
     p = c->data;
 
-    p->action = "sending request to upstream";
-
     if (wev->timedout) {
-        p->timedout = 1;
+        p->action = "sending request to upstream";
         ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT);
         return;
     }
 
     ngx_http_proxy_send_request(p);
+}
 
-    return;
+
+static void ngx_http_proxy_dummy_handler(ngx_event_t *wev)
+{
+    ngx_log_debug(wev->log, "dummy handler");
 }
 
 
-#if 0
-
-static int ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p)
+static void ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p)
 {
-    int                rc;
-    ngx_chain_t       *cl;
-    ngx_connection_t  *c;
+    int                      rc;
+    ngx_chain_t             *cl;
+    ngx_connection_t        *c;
+    ngx_http_request_t      *r;
+    ngx_output_chain_ctx_t  *octx;
+
+    p->action = "connecting to upstream";
+
+    rc = ngx_event_connect_peer(&p->upstream);
+
+    if (rc == NGX_ERROR) {
+        ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
+    if (rc == NGX_CONNECT_ERROR) {
+        ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR);
+        return;
+    }
 
-    for ( ;; ) {
-        p->action = "connecting to upstream";
+    p->upstream.connection->data = p;
+    p->upstream.connection->write->event_handler =
+                                           ngx_http_proxy_send_request_handler;
+    p->upstream.connection->read->event_handler =
+                                   ngx_http_proxy_process_upstream_status_line;
+
+    r = p->request;
+    c = p->upstream.connection;
+    c->pool = r->pool;
+    c->read->log = c->write->log = c->log = r->connection->log;
 
-        rc = ngx_event_connect_peer(&p->upstream);
+    octx = p->output_chain_ctx;
+
+    if (p->upstream.tries > 1 && p->request_sent) {
+        ngx_http_proxy_reinit_upstream(p);
+    }
 
-        if (rc == NGX_ERROR) {
+    /* init or reinit ngx_output_chain() context */
+
+    if (r->request_body_hunk) {
+        if (!(octx->free = ngx_alloc_chain_link(r->pool))) {
             ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
-            return NGX_DONE;
+            return;
         }
 
-        if (rc == NGX_CONNECT_ERROR) {
-            ngx_event_connect_peer_failed(&p->upstream);
-
-            if (ngx_http_proxy_log_state(p, NGX_HTTP_BAD_GATEWAY) == NGX_ERROR)
-            {
-                ngx_http_proxy_finalize_request(p,
-                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
-                return NGX_DONE;
-            }
+        octx->free->hunk = r->request_body_hunk;
+        octx->free->next = NULL;
+        octx->hunks = 1;
 
-            if (p->upstream.tries == 0) {
-                ngx_http_proxy_finalize_request(p, NGX_HTTP_BAD_GATEWAY);
-                return NGX_DONE;
-            }
+        r->request_body_hunk->pos = r->request_body_hunk->start;
+        r->request_body_hunk->last = r->request_body_hunk->start;
+    }
 
-            continue;
-        }
-
-        p->upstream.connection->data = p;
-        p->upstream.connection->write->event_handler =
-                                           ngx_http_proxy_send_request_handler;
-        p->upstream.connection->read->event_handler =
-                                   ngx_http_proxy_process_upstream_status_line;
+    p->request_sent = 0;
 
-        c = p->upstream.connection;
-        c->pool = p->request->pool;
-        c->read->log = c->write->log = c->log = p->request->connection->log;
-
-        if (p->upstream.tries > 1 && p->request_sent) {
+    if (rc == NGX_AGAIN) {
+        ngx_add_timer(c->write, p->lcf->connect_timeout);
+        return;
+    }
 
-            /* reinit the request chain */
+    /* rc == NGX_OK */
 
-            for (cl = p->request->request_hunks; cl; cl = cl->next) {
-                cl->hunk->pos = cl->hunk->start;
-            }
-        }
+#if 1 /* test only */
 
-        p->request_sent = 0;
-        p->timedout = 0;
-
-        if (rc == NGX_OK) {
-            return ngx_http_proxy_send_request0(p);
-        }
+    if (c->read->ready) {
+        /* post aio operation */
+        ngx_http_proxy_process_upstream_status_line(c->read);
+        return;
+    }
+#endif
 
-        /* rc == NGX_AGAIN */
-
-        ngx_add_timer(c->write, p->lcf->connect_timeout);
-
-        return NGX_AGAIN;
-    }
+    ngx_http_proxy_send_request(p);
 }
 
 
-static int ngx_http_proxy_send_request0(ngx_http_proxy_ctx_t *p)
+static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p)
 {
+    int                      rc;
     ngx_connection_t        *c;
     ngx_chain_writer_ctx_t  *wctx;
 
     c = p->upstream.connection;
 
+#if (HAVE_KQUEUE)
+
+    if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT)
+        && !p->request_sent
+        && c->write->kq_eof)
+    {
+        ngx_log_error(NGX_LOG_ERR, c->log, c->write->kq_errno,
+                      "connect() failed");
+
+        ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR);
+        return;
+    }
+
+#endif
+
     p->action = "sending request to upstream";
+
     wctx = p->output_chain_ctx->output_ctx;
+    wctx->out = NULL;
+    wctx->last = &wctx->out;
     wctx->connection = c;
+
     rc = ngx_output_chain(p->output_chain_ctx,
-                          !p->request_sent ? p->request->request_hunks:
-                                             NULL);
+                          p->request_sent ? NULL : p->request->request_hunks);
+
     if (rc == NGX_ERROR) {
-        return ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR);
+        ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR);
+        return;
     }
 
     p->request_sent = 1;
@@ -558,188 +640,45 @@ static int ngx_http_proxy_send_request0(
             return;
         }
 
-        return NGX_AGAIN;
+        return;
     }
 
     /* rc == NGX_OK */
 
-    if (c->read->ready) {
-        /* post aio operation */
-        ngx_http_proxy_process_upstream_status_line(c->read);
-    }
-
-            if (ngx_handle_level_write_event(c->write) == NGX_ERROR) {
-                ngx_http_proxy_finalize_request(p,
-                                       NGX_HTTP_INTERNAL_SERVER_ERROR);
-                return;
-            }
-
-            if (c->tcp_nopush) {
-                if (ngx_tcp_push(c->fd) == NGX_ERROR) {
-                    ngx_log_error(NGX_LOG_CRIT, c->log,
-                                  ngx_socket_errno,
-                                  ngx_tcp_push_n " failed");
-                    ngx_http_proxy_finalize_request(p,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-                    return; 
-                }
-
-                c->tcp_nopush = 0;
-            }
+    if (c->tcp_nopush) {
+        if (ngx_tcp_push(c->fd) == NGX_ERROR) {
+            ngx_log_error(NGX_LOG_CRIT, c->log,
+                          ngx_socket_errno,
+                          ngx_tcp_push_n " failed");
+            ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return; 
         }
 
+        c->tcp_nopush = 0;
         return;
     }
 
-    ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR);
+#if 0
+    if (c->read->ready) {
+
+        /* post aio operation */
 
-    return NGX_OK;
-}
+        /*
+         * although we can post aio operation just in the end
+         * of ngx_http_proxy_connect() CHECK IT !!!
+         * it's better to do here because we postpone header buffer allocation
+         */
 
-
+        ngx_http_proxy_process_upstream_status_line(c->read);
+        return;
+    }
 #endif
 
-
-static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p)
-{
-    int                      rc;
-    ngx_chain_t             *cl;
-    ngx_connection_t        *c;
-    ngx_chain_writer_ctx_t  *wctx;
-
-    c = p->upstream.connection;
-
-    for ( ;; ) {
-
-        if (c) {
-            p->action = "sending request to upstream";
-            wctx = p->output_chain_ctx->output_ctx;
-            wctx->connection = c;
-            rc = ngx_output_chain(p->output_chain_ctx,
-                                  !p->request_sent ? p->request->request_hunks:
-                                                     NULL);
-
-            if (rc != NGX_ERROR) {
-                p->request_sent = 1;
-
-                if (c->write->timer_set) {
-                    ngx_del_timer(c->write);
-                }
-
-                if (rc == NGX_AGAIN) {
-                    ngx_add_timer(c->write, p->lcf->send_timeout);
-
-                    if (ngx_handle_write_event(c->write, /* STUB: lowat */ 0)
-                                                                == NGX_ERROR)
-                    {
-                        ngx_http_proxy_finalize_request(p,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-                        return;
-                    }
-
-                } else {
-
-                    /* rc == NGX_OK */
-
-                    if (c->read->ready) {
-                        /* post aio operation */
-                        ngx_http_proxy_process_upstream_status_line(c->read);
-                    }
-
-                    if (ngx_handle_level_write_event(c->write) == NGX_ERROR) {
-                        ngx_http_proxy_finalize_request(p,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-                        return;
-                    }
-
-                    if (c->tcp_nopush) {
-                        if (ngx_tcp_push(c->fd) == NGX_ERROR) {
-                            ngx_log_error(NGX_LOG_CRIT, c->log,
-                                          ngx_socket_errno,
-                                          ngx_tcp_push_n " failed");
-                            ngx_http_proxy_finalize_request(p,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-                            return; 
-                        }
-                        c->tcp_nopush = 0;
-                    }
-                }
-
-                return;
-            }
+    p->upstream.connection->write->event_handler = ngx_http_proxy_dummy_handler;
 
-            ngx_event_connect_peer_failed(&p->upstream);
-            ngx_http_proxy_close_connection(c);
-
-            if (p->upstream.tries == 0
-                || !(p->lcf->next_upstream & NGX_HTTP_PROXY_FT_ERROR))
-            {
-                ngx_http_proxy_finalize_request(p,
-                                        p->timedout ? NGX_HTTP_GATEWAY_TIME_OUT:
-                                                      NGX_HTTP_BAD_GATEWAY);
-                return;
-            }
-
-            if (!p->fatal_error) {
-                ngx_http_proxy_send_request(p);
-                return;
-            }
-        }
-
-        for ( ;; ) {
-            p->action = "connecting to upstream";
-
-            rc = ngx_event_connect_peer(&p->upstream);
-
-            if (rc == NGX_ERROR) {
-                ngx_http_proxy_finalize_request(p,
-                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
-                return;
-            }
-
-            if (rc == NGX_CONNECT_ERROR) {
-                ngx_event_connect_peer_failed(&p->upstream);
-
-                if (p->upstream.tries == 0) {
-                    ngx_http_proxy_finalize_request(p, NGX_HTTP_BAD_GATEWAY);
-                    return;
-                }
-
-                continue;
-            }
-
-            p->upstream.connection->data = p;
-            p->upstream.connection->write->event_handler =
-                                           ngx_http_proxy_send_request_handler;
-            p->upstream.connection->read->event_handler =
-                                   ngx_http_proxy_process_upstream_status_line;
-
-            c = p->upstream.connection;
-            c->pool = p->request->pool;
-            c->read->log = c->write->log = c->log = p->request->connection->log;
-
-            if (p->upstream.tries > 1 && p->request_sent) {
-
-                /* reinit the request chain */
-
-                for (cl = p->request->request_hunks; cl; cl = cl->next) {
-                    cl->hunk->pos = cl->hunk->start;
-                }
-            }
-
-            p->request_sent = 0;
-            p->timedout = 0;
-
-            if (rc == NGX_OK) {
-                break;
-            }
-
-            /* rc == NGX_AGAIN */
-
-            ngx_add_timer(c->write, p->lcf->connect_timeout);
-
-            return;
-        }
+    if (ngx_handle_level_write_event(c->write) == NGX_ERROR) {
+        ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
     }
 }
 
@@ -753,13 +692,11 @@ static void ngx_http_proxy_process_upstr
 
     c = rev->data;
     p = c->data;
-
     p->action = "reading upstream status line";
 
     ngx_log_debug(rev->log, "http proxy process status line");
 
     if (rev->timedout) {
-        p->timedout = 1;
         ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT);
         return;
     }
@@ -772,18 +709,30 @@ static void ngx_http_proxy_process_upstr
             return;
         }
         p->header_in->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module;
+
+        if (p->cache) {
+            p->header_in->pos += p->cache->ctx.header.size;
+            p->header_in->last = p->header_in->pos;
+        }
     }
 
     n = ngx_http_proxy_read_upstream_header(p);
 
-    if (n == NGX_ERROR) {
+    if (n == NGX_AGAIN) {
+        return;
+    }
+
+    if (n == 0) {
+        ngx_log_error(NGX_LOG_ERR, rev->log, 0,
+                      "upstream prematurely closed connection");
+    }
+
+    if (n == NGX_ERROR || n == 0) {
         ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR);
         return;
     }
 
-    if (n == NGX_AGAIN) {
-        return;
-    }
+    p->upstream.cached = 0;
 
     rc = ngx_http_proxy_parse_status_line(p);
 
@@ -791,9 +740,8 @@ static void ngx_http_proxy_process_upstr
         if (p->header_in->pos == p->header_in->last) {
             ngx_log_error(NGX_LOG_ERR, rev->log, 0,
                           "upstream sent too long status line");
-            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER);
+            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER);
         }
-
         return;
     }
 
@@ -802,7 +750,7 @@ static void ngx_http_proxy_process_upstr
                       "upstream sent no valid HTTP/1.0 header");
 
         if (p->accel) {
-            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER);
+            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER);
 
         } else {
             p->request->http_version = NGX_HTTP_VERSION_9;
@@ -815,12 +763,25 @@ static void ngx_http_proxy_process_upstr
 
     /* rc == NGX_OK */
 
-    if (p->status == NGX_HTTP_INTERNAL_SERVER_ERROR
-        && p->upstream.tries > 1
-        && (p->lcf->next_upstream & NGX_HTTP_PROXY_FT_HTTP_500))
-    {
-        ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_500);
-        return;
+    if (p->status == NGX_HTTP_INTERNAL_SERVER_ERROR) {
+
+        if (p->upstream.tries > 1
+            && (p->lcf->next_upstream & NGX_HTTP_PROXY_FT_HTTP_500))
+        {
+            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_500);
+            return;
+        }
+
+        if (p->upstream.tries == 0
+            && p->stale
+            && (p->lcf->use_stale & NGX_HTTP_PROXY_FT_HTTP_500))
+        {
+            /*
+             * TODO: use stale cached response if it exists and enabled
+             */
+
+            return;
+        }
     }
 
     p->status_line.len = p->status_end - p->status_start;
@@ -842,8 +803,6 @@ static void ngx_http_proxy_process_upstr
 
     c->read->event_handler = ngx_http_proxy_process_upstream_headers;
     ngx_http_proxy_process_upstream_headers(rev);
-
-    return;
 }
 
 
@@ -859,13 +818,11 @@ static void ngx_http_proxy_process_upstr
     c = rev->data;
     p = c->data;
     r = p->request;
-
     p->action = "reading upstream headers";
 
     ngx_log_debug(rev->log, "http proxy process header line");
 
     if (rev->timedout) {
-        p->timedout = 1;
         ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT);
         return;
     }
@@ -876,7 +833,12 @@ static void ngx_http_proxy_process_upstr
         if (rc == NGX_AGAIN) {
             n = ngx_http_proxy_read_upstream_header(p);
 
-            if (n == NGX_ERROR) {
+            if (n == 0) {
+                ngx_log_error(NGX_LOG_ERR, rev->log, 0,
+                              "upstream prematurely closed connection");
+            }
+
+            if (n == NGX_ERROR || n == 0) {
                 ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR);
                 return;
             }
@@ -892,7 +854,8 @@ static void ngx_http_proxy_process_upstr
 
             /* a header line has been parsed successfully */
 
-            if (!(h = ngx_http_add_header(&p->headers_in, headers_in))) {
+            h = ngx_http_add_header(&p->headers_in, ngx_http_proxy_headers_in);
+            if (h == NULL) {
                 ngx_http_proxy_finalize_request(p,
                                                 NGX_HTTP_INTERNAL_SERVER_ERROR);
                 return;
@@ -913,14 +876,16 @@ static void ngx_http_proxy_process_upstr
             ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1);
             ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1);
 
-            for (i = 0; headers_in[i].name.len != 0; i++) {
-                if (headers_in[i].name.len != h->key.len) {
+            for (i = 0; ngx_http_proxy_headers_in[i].name.len != 0; i++) {
+                if (ngx_http_proxy_headers_in[i].name.len != h->key.len) {
                     continue;
                 }
 
-                if (ngx_strcasecmp(headers_in[i].name.data, h->key.data) == 0) {
-                    *((ngx_table_elt_t **)
-                        ((char *) &p->headers_in + headers_in[i].offset)) = h;
+                if (ngx_strcasecmp(ngx_http_proxy_headers_in[i].name.data,
+                                                           h->key.data) == 0)
+                {
+                    *((ngx_table_elt_t **) ((char *) &p->headers_in
+                                   + ngx_http_proxy_headers_in[i].offset)) = h;
                     break;
                 }
             }
@@ -937,7 +902,6 @@ static void ngx_http_proxy_process_upstr
             ngx_log_debug(c->log, "HTTP header done");
 
             ngx_http_proxy_send_response(p);
-
             return;
 
         } else if (rc != NGX_AGAIN) {
@@ -947,7 +911,7 @@ static void ngx_http_proxy_process_upstr
             ngx_log_error(NGX_LOG_ERR, rev->log, 0,
                       upstream_header_errors[rc - NGX_HTTP_PARSE_HEADER_ERROR]);
 
-            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER);
+            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER);
             return;
         }
 
@@ -957,7 +921,7 @@ static void ngx_http_proxy_process_upstr
             ngx_log_error(NGX_LOG_ERR, rev->log, 0,
                           "upstream sent too big header");
 
-            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_HEADER);
+            ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER);
             return;
         }
     }
@@ -1008,86 +972,44 @@ static ssize_t ngx_http_proxy_read_upstr
 
 static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p)
 {
-    int                        rc, i;
-    ngx_table_elt_t           *ho, *h;
-    ngx_event_pipe_t          *ep;
-    ngx_http_request_t        *r;
-    ngx_http_core_loc_conf_t  *clcf;
+    int                           rc, i;
+    ngx_table_elt_t              *ho, *h;
+    ngx_event_pipe_t             *ep;
+    ngx_http_request_t           *r;
+    ngx_http_bin_cache_t         *header;
+    ngx_http_core_loc_conf_t     *clcf;
 
     r = p->request;
 
     r->headers_out.status = p->status;
 
+#if 0
     r->headers_out.content_length_n = -1;
     r->headers_out.content_length = NULL;
+#endif
 
     /* copy an upstream header to r->headers_out */
 
-    h = p->headers_in.headers->elts;
-    for (i = 0; i < p->headers_in.headers->nelts; i++) {
-
-        if (&h[i] == p->headers_in.connection) {
-            continue;
-        }
-
-        if (p->accel) {
-            if (&h[i] == p->headers_in.date
-                || &h[i] == p->headers_in.accept_ranges) {
-                continue;
-            }
-
-            if (&h[i] == p->headers_in.server && !p->lcf->pass_server) {
-                continue;
-            }
-        }
-
-        if (&h[i] == p->headers_in.content_type) {
-            r->headers_out.content_type = &h[i];
-            r->headers_out.content_type->key.len = 0;
-            continue;
-        }
-
-        if (!(ho = ngx_http_add_header(&r->headers_out, ngx_http_headers_out)))
-        {
-            ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
-            return;
-        }
-
-        *ho = h[i];
-
-        /*
-         * ngx_http_header_filter() output the following headers
-         * from r->headers_out.headers if they are set:
-         *     r->headers_out.server,
-         *     r->headers_out.date,
-         *     r->headers_out.content_length
-         */
-
-        if (&h[i] == p->headers_in.server) {
-            r->headers_out.server = ho;
-            continue;
-        }
-
-        if (&h[i] == p->headers_in.date) {
-            r->headers_out.date = ho;
-            continue;
-        }
-
-        if (&h[i] == p->headers_in.content_length) {
-            r->headers_out.content_length = ho;
-            r->headers_out.content_length_n = ngx_atoi(ho->value.data,
-                                                       ho->value.len);
-            continue;
-        }
+    if (ngx_http_proxy_copy_header(p, &p->headers_in) == NGX_ERROR) {
+        ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
     }
 
-
     /* TODO: preallocate event_pipe hunks, look "Content-Length" */
 
     rc = ngx_http_send_header(r);
 
     p->header_sent = 1;
 
+    if (p->cache) {
+        header = (ngx_http_bin_cache_t *) p->header_in->start;
+        header->type = 0x42424242;  /* "BBBB" */
+        header->header.length = r->headers_out.content_length_n;
+        header->key_len = p->cache->ctx.key.len;
+        ngx_memcpy(&header->key, p->cache->ctx.key.data, header->key_len);
+        header->key[header->key_len] = LF;
+    }
+
     ep = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
     if (ep == NULL) {
         ngx_http_proxy_finalize_request(p, 0);
@@ -1105,21 +1027,21 @@ static void ngx_http_proxy_send_response
     ep->downstream = r->connection;
     ep->pool = r->pool;
     ep->log = r->connection->log;
-    ep->temp_path = p->lcf->temp_path;
 
-    ep->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
-    if (ep->temp_file == NULL) {
+    if (!(ep->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)))) {
         ngx_http_proxy_finalize_request(p, 0);
         return;
     }
 
-    ep->temp_file->fd = NGX_INVALID_FILE;
-    ep->temp_file->log = r->connection->log;
+    ep->temp_file->file.fd = NGX_INVALID_FILE;
+    ep->temp_file->file.log = r->connection->log;
+    ep->temp_file->path = p->lcf->temp_path;
+    ep->temp_file->pool = r->pool;
+    ep->temp_file->warn = "an upstream response is buffered "
+                         "to a temporary file";
 
     ep->max_temp_file_size = p->lcf->max_temp_file_size;
     ep->temp_file_write_size = p->lcf->temp_file_write_size;
-    ep->temp_file_warn = "an upstream response is buffered "
-                         "to a temporary file";
 
     ep->preread_hunks = ngx_alloc_chain_link(r->pool);
     if (ep->preread_hunks == NULL) {
@@ -1128,6 +1050,7 @@ static void ngx_http_proxy_send_response
     }
     ep->preread_hunks->hunk = p->header_in;
     ep->preread_hunks->next = NULL;
+    p->header_in->type |= NGX_HUNK_PREREAD;
 
     ep->preread_size = p->header_in->last - p->header_in->pos;
 
@@ -1146,7 +1069,7 @@ static void ngx_http_proxy_send_response
      */
     p->header_in->last = p->header_in->pos;
 
-    /* STUB */ ep->cachable = 0;
+    ep->cachable = p->cachable;
 
     if (p->lcf->cyclic_temp_file) {
 
@@ -1226,10 +1149,19 @@ static void ngx_http_proxy_process_body(
 
     if (p->upstream.connection) {
         if (ep->upstream_done) {
-            /* TODO: update cache */
+            if (ngx_http_proxy_update_cache(p) == NGX_ERROR) {
+                ngx_http_proxy_finalize_request(p, 0);
+                return;
+            }
 
         } else if (ep->upstream_eof) {
+
             /* TODO: check length & update cache */
+
+            if (ngx_http_proxy_update_cache(p) == NGX_ERROR) {
+                ngx_http_proxy_finalize_request(p, 0);
+                return;
+            }
         }
 
         if (ep->upstream_done || ep->upstream_eof || ep->upstream_error) {
@@ -1259,202 +1191,14 @@ static void ngx_http_proxy_process_body(
 }
 
 
-static int ngx_http_proxy_parse_status_line(ngx_http_proxy_ctx_t *p)
+static int ngx_http_proxy_update_cache(ngx_http_proxy_ctx_t *p)
 {
-    char   ch;
-    char  *pos;
-    enum  {
-        sw_start = 0,
-        sw_H,
-        sw_HT,
-        sw_HTT,
-        sw_HTTP,
-        sw_first_major_digit,
-        sw_major_digit,
-        sw_first_minor_digit,
-        sw_minor_digit,
-        sw_status,
-        sw_space_after_status,
-        sw_status_text,
-        sw_almost_done,
-        sw_done
-    } state;
-
-    state = p->state;
-    pos = p->header_in->pos;
-
-    while (pos < p->header_in->last && state < sw_done) {
-        ch = *pos++;
-
-        switch (state) {
-
-        /* "HTTP/" */
-        case sw_start:
-            switch (ch) {
-            case 'H':
-                state = sw_H;
-                break;
-            default:
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-            break;
-
-        case sw_H:
-            switch (ch) {
-            case 'T':
-                state = sw_HT;
-                break;
-            default:
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-            break;
-
-        case sw_HT:
-            switch (ch) {
-            case 'T':
-                state = sw_HTT;
-                break;
-            default:
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-            break;
-
-        case sw_HTT:
-            switch (ch) {
-            case 'P':
-                state = sw_HTTP;
-                break;
-            default:
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-            break;
-
-        case sw_HTTP:
-            switch (ch) {
-            case '/':
-                state = sw_first_major_digit;
-                break;
-            default:
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-            break;
-
-        /* the first digit of major HTTP version */
-        case sw_first_major_digit:
-            if (ch < '1' || ch > '9') {
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-
-            state = sw_major_digit;
-            break;
-
-        /* the major HTTP version or dot */
-        case sw_major_digit:
-            if (ch == '.') {
-                state = sw_first_minor_digit;
-                break;
-            }
-
-            if (ch < '0' || ch > '9') {
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-
-            break;
-
-        /* the first digit of minor HTTP version */
-        case sw_first_minor_digit:
-            if (ch < '0' || ch > '9') {
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-
-            state = sw_minor_digit;
-            break;
-
-        /* the minor HTTP version or the end of the request line */
-        case sw_minor_digit:
-            if (ch == ' ') {
-                state = sw_status;
-                break;
-            }
-
-            if (ch < '0' || ch > '9') {
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-
-            break;
-
-        /* HTTP status code */
-        case sw_status:
-            if (ch < '0' || ch > '9') {
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-
-            p->status = p->status * 10 + ch - '0';
-
-            if (++p->status_count == 3) {
-                state = sw_space_after_status;
-                p->status_start = pos - 3;
-            }
-
-            break;
-
-         /* space or end of line */
-         case sw_space_after_status:
-            switch (ch) {
-            case ' ':
-                state = sw_status_text;
-                break;
-            case CR:
-                state = sw_almost_done;
-                break;
-            case LF:
-                state = sw_done;
-                break;
-            default:
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-            break;
-
-        /* any text until end of line */
-        case sw_status_text:
-            switch (ch) {
-            case CR:
-                state = sw_almost_done;
-
-                break;
-            case LF:
-                state = sw_done;
-                break;
-            }
-            break;
-
-        /* end of request line */
-        case sw_almost_done:
-            p->status_end = pos - 2;
-            switch (ch) {
-            case LF:
-                state = sw_done;
-                break;
-            default:
-                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
-            }
-            break;
-        }
-    }
-
-    p->header_in->pos = pos;
-
-    if (state == sw_done) {
-        if (p->status_end == NULL) {
-            p->status_end = pos - 1;
-        }
-
-        p->state = sw_start;
+    if (p->cache == NULL) {
         return NGX_OK;
     }
 
-    p->state = state;
-    return NGX_AGAIN;
+    return ngx_http_cache_update_file(p->request, &p->cache->ctx,
+                                      &p->event_pipe->temp_file->file.name);
 }
 
 
@@ -1462,43 +1206,69 @@ static void ngx_http_proxy_next_upstream
 {
     int  status;
 
-    ngx_event_connect_peer_failed(&p->upstream);
+ngx_log_debug(p->request->connection->log, "next upstream: %d" _ ft_type);
 
-    if (p->timedout) {
+    if (ft_type != NGX_HTTP_PROXY_FT_HTTP_404) {
+        ngx_event_connect_peer_failed(&p->upstream);
+    }
+
+    if (ft_type == NGX_HTTP_PROXY_FT_TIMEOUT) {
         ngx_log_error(NGX_LOG_ERR, p->request->connection->log, NGX_ETIMEDOUT,
                       "upstream timed out");
     }
 
+    if (p->upstream.cached && ft_type == NGX_HTTP_PROXY_FT_ERROR) {
+        status = 0;
+
+    } else {
+        switch(ft_type) {
+        case NGX_HTTP_PROXY_FT_TIMEOUT:
+            status = NGX_HTTP_GATEWAY_TIME_OUT;
+            break;
+
+        case NGX_HTTP_PROXY_FT_HTTP_500:
+            status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+            break;
+
+        case NGX_HTTP_PROXY_FT_HTTP_404:
+            status = NGX_HTTP_NOT_FOUND;
+            break;
+
+        /*
+         * NGX_HTTP_PROXY_FT_BUSY_LOCK and NGX_HTTP_PROXY_FT_MAX_WAITING
+         * never reach here
+         */
+
+        default:
+            status = NGX_HTTP_BAD_GATEWAY;
+        }
+    }
+
     if (p->upstream.connection) {
         ngx_http_proxy_close_connection(p->upstream.connection);
         p->upstream.connection = NULL;
     }
 
-    if (ft_type == NGX_HTTP_PROXY_FT_TIMEOUT) {
-        status = NGX_HTTP_GATEWAY_TIME_OUT;
+    if (status) {
+        if (ngx_http_proxy_log_state(p, status) == NGX_ERROR) {
+            ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return;
+        }
+
+        if (p->upstream.tries == 0 || !(p->lcf->next_upstream & ft_type)) {
 
-    } else {
-        status = NGX_HTTP_BAD_GATEWAY;
-    }
+            if (p->stale && (p->lcf->use_stale & ft_type)) {
+                /*
+                 * TODO: use stale cached response if it exists and enabled
+                 */
+            }
 
-    if (ngx_http_proxy_log_state(p, status) == NGX_ERROR) {
-        ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
-        return;
+            ngx_http_proxy_finalize_request(p, status);
+            return;
+        }
     }
 
-    if (p->upstream.tries == 0 || !(p->lcf->next_upstream & ft_type)) {
-        ngx_http_proxy_finalize_request(p, status);
-        return;
-    }
-
-    if (!p->fatal_error) {
-        ngx_http_proxy_send_request(p);
-        return;
-    }
-
-ngx_log_debug(p->request->connection->log, "FATAL ERROR IN NEXT UPSTREAM");
-
-    return;
+    ngx_http_proxy_connect(p);
 }
 
 
@@ -1538,16 +1308,12 @@ static void ngx_http_proxy_finalize_requ
     p->request->connection->log->handler = p->saved_handler;
 
     ngx_http_finalize_request(p->request, rc);
-
-    p->fatal_error = 1;
-
-    return;
 }
 
 
 static void ngx_http_proxy_close_connection(ngx_connection_t *c)
 {
-    ngx_log_debug(c->log, "close connection: %d" _ c->fd);
+    ngx_log_debug(c->log, "proxy close connection: %d" _ c->fd);
 
     if (c->fd == -1) {
 #if 0
@@ -1585,8 +1351,6 @@ static void ngx_http_proxy_close_connect
     }
 
     c->fd = -1;
-
-    return;
 }
 
 
@@ -1622,10 +1386,14 @@ static void *ngx_http_proxy_create_loc_c
     conf->path = NULL;
 
     conf->next_upstream = 0;
+    conf->use_stale = 0;
 
     conf->upstreams = NULL;
     conf->peers = NULL;
 
+    conf->cache_path = NULL;
+    conf->temp_path = NULL;
+
     */
 
     conf->request_buffer_size = NGX_CONF_UNSET;
@@ -1646,6 +1414,8 @@ static void *ngx_http_proxy_create_loc_c
     /* "proxy_cyclic_temp_file" is disabled */
     conf->cyclic_temp_file = 0;
 
+    conf->cache = NGX_CONF_UNSET;
+
     conf->pass_server = NGX_CONF_UNSET;
 
     return conf;
@@ -1681,11 +1451,21 @@ static char *ngx_http_proxy_merge_loc_co
                               prev->temp_file_write_size, 16384);
 
     ngx_conf_merge_bitmask_value(conf->next_upstream, prev->next_upstream,
-                           (NGX_HTTP_PROXY_FT_ERROR|NGX_HTTP_PROXY_FT_TIMEOUT));
+                                 (NGX_CONF_BITMASK_SET
+                                  |NGX_HTTP_PROXY_FT_ERROR
+                                  |NGX_HTTP_PROXY_FT_TIMEOUT));
+
+    ngx_conf_merge_bitmask_value(conf->use_stale, prev->use_stale,
+                                 NGX_CONF_BITMASK_SET);
+
+    ngx_conf_merge_path_value(conf->cache_path, prev->cache_path,
+                              "cache", 1, 2, 0, cf->pool);
 
     ngx_conf_merge_path_value(conf->temp_path, prev->temp_path,
                               "temp", 1, 2, 0, cf->pool);
 
+    ngx_conf_merge_value(conf->cache, prev->cache, 0);
+
     ngx_conf_merge_value(conf->pass_server, prev->pass_server, 0);
 
     return NULL;
@@ -1717,6 +1497,12 @@ static char *ngx_http_proxy_set_pass(ngx
                   ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_upstream_t)),
                   NGX_CONF_ERROR);
 
+    lcf->upstream->url.len = value[1].len;
+    if (!(lcf->upstream->url.data = ngx_palloc(cf->pool, value[1].len + 1))) {
+        return NGX_CONF_ERROR;
+    }
+    ngx_cpystrn(lcf->upstream->url.data, value[1].data, value[1].len + 1);
+
     value[1].data += 7;
     value[1].len -= 7;
 
--- a/src/http/modules/proxy/ngx_http_proxy_handler.h
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.h
@@ -5,10 +5,13 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <ngx_event_pipe.h>
 #include <ngx_http.h>
 
 
 typedef struct {
+    ngx_str_t   url;
     ngx_str_t   host;
     ngx_str_t   uri;
     ngx_str_t   host_header;
@@ -19,49 +22,60 @@ typedef struct {
 
 
 typedef struct {
-    ssize_t                     request_buffer_size;
-    ngx_msec_t                  connect_timeout;
-    ngx_msec_t                  send_timeout;
-    ssize_t                     header_buffer_size;
-    ngx_msec_t                  read_timeout;
+    ssize_t                       request_buffer_size;
+    ngx_msec_t                    connect_timeout;
+    ngx_msec_t                    send_timeout;
+    ssize_t                       header_buffer_size;
+    ngx_msec_t                    read_timeout;
 
-    ngx_bufs_t                  bufs;
-    ssize_t                     busy_buffers_size;
+    ngx_bufs_t                    bufs;
+    ssize_t                       busy_buffers_size;
 
-    ssize_t                     max_temp_file_size;
-    ssize_t                     temp_file_write_size;
-    int                         cyclic_temp_file;
+    ssize_t                       max_temp_file_size;
+    ssize_t                       temp_file_write_size;
+    int                           cyclic_temp_file;
 
-    int                         pass_server;
+    int                           cache;
+    int                           pass_server;
 
-    int                         next_upstream;
+    int                           next_upstream;
+    int                           use_stale;
 
-    ngx_path_t                 *temp_path;
+    ngx_path_t                   *cache_path;
+    ngx_path_t                   *temp_path;
 
-    ngx_http_proxy_upstream_t  *upstream;
-    ngx_peers_t                *peers;
+    ngx_http_proxy_upstream_t    *upstream;
+    ngx_peers_t                  *peers;
 } ngx_http_proxy_loc_conf_t;
 
 
 typedef struct {
-    int         status;
-    ngx_str_t  *peer;
+    int                           status;
+    ngx_str_t                    *peer;
 } ngx_http_proxy_state_t;
 
 
 typedef struct {
-    ngx_table_t      *headers;   /* it must be first field */
+    ngx_table_t                  *headers;   /* it must be first field */
+
+    ngx_table_elt_t              *date;
+    ngx_table_elt_t              *server;
+    ngx_table_elt_t              *connection;
+    ngx_table_elt_t              *content_type;
+    ngx_table_elt_t              *content_length;
+    ngx_table_elt_t              *last_modified;
+    ngx_table_elt_t              *accept_ranges;
 
-    ngx_table_elt_t  *date;
-    ngx_table_elt_t  *server;
-    ngx_table_elt_t  *connection;
-    ngx_table_elt_t  *content_type;
-    ngx_table_elt_t  *content_length;
-    ngx_table_elt_t  *last_modified;
-    ngx_table_elt_t  *accept_ranges;
+    off_t                         content_length_n;
+} ngx_http_proxy_headers_in_t;
+
 
-    off_t             content_length_n;
-} ngx_http_proxy_headers_in_t;
+typedef struct {
+    ngx_http_cache_ctx_t          ctx;
+    int                           status;
+    ngx_str_t                     status_line;
+    ngx_http_proxy_headers_in_t   headers_in;
+} ngx_http_proxy_cache_t;
 
 
 typedef struct ngx_http_proxy_ctx_s  ngx_http_proxy_ctx_t;
@@ -72,45 +86,69 @@ struct ngx_http_proxy_ctx_s {
 
     ngx_http_request_t           *request;
     ngx_http_proxy_loc_conf_t    *lcf;
+    ngx_http_proxy_cache_t       *cache;
     ngx_http_proxy_headers_in_t   headers_in;
 
-    ngx_hunk_t                 *header_in;
-    int                         status;
-    ngx_str_t                   status_line;
+    ngx_hunk_t                   *header_in;
+    int                           status;
+    ngx_str_t                     status_line;
 
-    ngx_output_chain_ctx_t     *output_chain_ctx;
+    ngx_output_chain_ctx_t       *output_chain_ctx;
 
-    int                         method;
+    int                           method;
 
-    ngx_event_pipe_t           *event_pipe;
+    ngx_event_pipe_t             *event_pipe;
+
+    unsigned                      accel:1;
 
-    unsigned                    accel:1;
-    unsigned                    cachable:1;
-    unsigned                    fatal_error:1;
-    unsigned                    request_sent:1;
-    unsigned                    timedout:1;
-    unsigned                    header_sent:1;
+    unsigned                      cachable:1;
+    unsigned                      stale:1;
+
+    unsigned                      request_sent:1;
+    unsigned                      header_sent:1;
 
     /* used to parse an upstream HTTP header */
-    char                       *status_start;
-    char                       *status_end;
-    int                         status_count;
-    int                         state;
+    char                         *status_start;
+    char                         *status_end;
+    int                           status_count;
+    int                           state;
 
-    ngx_array_t                 states;    /* of ngx_http_proxy_state_t */
+    ngx_array_t                   states;    /* of ngx_http_proxy_state_t */
 
-    char                       *action;
-    ngx_http_log_ctx_t         *saved_ctx;
-    ngx_log_handler_pt          saved_handler;
+    char                         *action;
+    ngx_http_log_ctx_t           *saved_ctx;
+    ngx_log_handler_pt            saved_handler;
 };
 
 
-#define NGX_HTTP_PROXY_PARSE_NO_HEADER  20
+#define NGX_STALE                            1
+
+#define NGX_HTTP_PROXY_PARSE_NO_HEADER       20
+
+#define NGX_HTTP_PROXY_FT_ERROR              2
+#define NGX_HTTP_PROXY_FT_TIMEOUT            4
+#define NGX_HTTP_PROXY_FT_INVALID_HEADER     8
+#define NGX_HTTP_PROXY_FT_HTTP_500           16
+#define NGX_HTTP_PROXY_FT_HTTP_404           32
+#define NGX_HTTP_PROXY_FT_BUSY_LOCK          64
+#define NGX_HTTP_PROXY_FT_MAX_WAITING        128
+
 
-#define NGX_HTTP_PROXY_FT_ERROR         1
-#define NGX_HTTP_PROXY_FT_TIMEOUT       2
-#define NGX_HTTP_PROXY_FT_HTTP_HEADER   4
-#define NGX_HTTP_PROXY_FT_HTTP_500      8
+void ngx_http_proxy_reinit_upstream(ngx_http_proxy_ctx_t *p);
+
+int ngx_http_proxy_get_cached_response(ngx_http_proxy_ctx_t *p);
+int ngx_http_proxy_process_cached_response(ngx_http_proxy_ctx_t *p);
+int ngx_http_proxy_send_cached_response(ngx_http_proxy_ctx_t *p);
+
+int ngx_http_proxy_parse_status_line(ngx_http_proxy_ctx_t *p);
+int ngx_http_proxy_copy_header(ngx_http_proxy_ctx_t *p,
+                               ngx_http_proxy_headers_in_t *headers_in);
+
+
+
+extern ngx_module_t  ngx_http_proxy_module;
+extern ngx_http_header_t ngx_http_proxy_headers_in[];
+
 
 
 #endif /* _NGX_HTTP_PROXY_HANDLER_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/http/modules/proxy/ngx_http_proxy_header.c
@@ -0,0 +1,75 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_proxy_handler.h>
+
+
+int ngx_http_proxy_copy_header(ngx_http_proxy_ctx_t *p,
+                               ngx_http_proxy_headers_in_t *headers_in)
+{
+    int                  i;
+    ngx_table_elt_t     *ho, *h;
+    ngx_http_request_t  *r;
+
+    r = p->request;
+
+    h = headers_in->headers->elts;
+    for (i = 0; i < headers_in->headers->nelts; i++) {
+
+        if (&h[i] == headers_in->connection) {
+            continue;
+        }
+    
+        if (p->accel) {
+            if (&h[i] == headers_in->date
+                || &h[i] == headers_in->accept_ranges) {
+                continue;
+            }
+    
+            if (&h[i] == headers_in->server && !p->lcf->pass_server) {
+                continue;
+            } 
+        }
+    
+        if (&h[i] == headers_in->content_type) {
+            r->headers_out.content_type = &h[i];
+            r->headers_out.content_type->key.len = 0;
+            continue;
+        }
+
+        if (!(ho = ngx_http_add_header(&r->headers_out, ngx_http_headers_out)))
+        {
+            return NGX_ERROR;
+        }
+    
+        *ho = h[i];
+    
+        /*
+         * ngx_http_header_filter() does not handle specially
+         * the following headers if they are set:
+         *     r->headers_out.server,
+         *     r->headers_out.date,
+         *     r->headers_out.content_length
+         */
+
+        if (&h[i] == headers_in->server) {
+            r->headers_out.server = ho;
+            continue;
+        }
+
+        if (&h[i] == headers_in->date) {
+            r->headers_out.date = ho;
+            continue;
+        }
+
+        if (&h[i] == headers_in->content_length) {
+            r->headers_out.content_length = ho;
+            r->headers_out.content_length_n = ngx_atoi(ho->value.data,
+                                                       ho->value.len);
+            continue;
+        }
+    }
+
+    return NGX_OK;
+}
new file mode 100644
--- /dev/null
+++ b/src/http/modules/proxy/ngx_http_proxy_parse.c
@@ -0,0 +1,204 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_proxy_handler.h>
+
+
+int ngx_http_proxy_parse_status_line(ngx_http_proxy_ctx_t *p)
+{
+    char   ch;
+    char  *pos;
+    enum  {
+        sw_start = 0,
+        sw_H,
+        sw_HT,
+        sw_HTT,
+        sw_HTTP,
+        sw_first_major_digit,
+        sw_major_digit,
+        sw_first_minor_digit,
+        sw_minor_digit,
+        sw_status,
+        sw_space_after_status,
+        sw_status_text,
+        sw_almost_done,
+        sw_done
+    } state;
+
+    state = p->state;
+    pos = p->header_in->pos;
+
+    while (pos < p->header_in->last && state < sw_done) {
+        ch = *pos++;
+
+        switch (state) {
+
+        /* "HTTP/" */
+        case sw_start:
+            switch (ch) {
+            case 'H':
+                state = sw_H;
+                break;
+            default:
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+            break;
+
+        case sw_H:
+            switch (ch) {
+            case 'T':
+                state = sw_HT;
+                break;
+            default:
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+            break;
+
+        case sw_HT:
+            switch (ch) {
+            case 'T':
+                state = sw_HTT;
+                break;
+            default:
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+            break;
+
+        case sw_HTT:
+            switch (ch) {
+            case 'P':
+                state = sw_HTTP;
+                break;
+            default:
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+            break;
+
+        case sw_HTTP:
+            switch (ch) {
+            case '/':
+                state = sw_first_major_digit;
+                break;
+            default:
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+            break;
+
+        /* the first digit of major HTTP version */
+        case sw_first_major_digit:
+            if (ch < '1' || ch > '9') {
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+
+            state = sw_major_digit;
+            break;
+
+        /* the major HTTP version or dot */
+        case sw_major_digit:
+            if (ch == '.') {
+                state = sw_first_minor_digit;
+                break;
+            }
+
+            if (ch < '0' || ch > '9') {
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+
+            break;
+
+        /* the first digit of minor HTTP version */
+        case sw_first_minor_digit:
+            if (ch < '0' || ch > '9') {
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+
+            state = sw_minor_digit;
+            break;
+
+        /* the minor HTTP version or the end of the request line */
+        case sw_minor_digit:
+            if (ch == ' ') {
+                state = sw_status;
+                break;
+            }
+
+            if (ch < '0' || ch > '9') {
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+
+            break;
+
+        /* HTTP status code */
+        case sw_status:
+            if (ch < '0' || ch > '9') {
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+
+            p->status = p->status * 10 + ch - '0';
+
+            if (++p->status_count == 3) {
+                state = sw_space_after_status;
+                p->status_start = pos - 3;
+            }
+
+            break;
+
+         /* space or end of line */
+         case sw_space_after_status:
+            switch (ch) {
+            case ' ':
+                state = sw_status_text;
+                break;
+            case CR:
+                state = sw_almost_done;
+                break;
+            case LF:
+                state = sw_done;
+                break;
+            default:
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+            break;
+
+        /* any text until end of line */
+        case sw_status_text:
+            switch (ch) {
+            case CR:
+                state = sw_almost_done;
+
+                break;
+            case LF:
+                state = sw_done;
+                break;
+            }
+            break;
+
+        /* end of request line */
+        case sw_almost_done:
+            p->status_end = pos - 2;
+            switch (ch) {
+            case LF:
+                state = sw_done;
+                break;
+            default:
+                return NGX_HTTP_PROXY_PARSE_NO_HEADER;
+            }
+            break;
+        }
+    }
+
+    p->header_in->pos = pos;
+
+    if (state == sw_done) {
+        if (p->status_end == NULL) {
+            p->status_end = pos - 1;
+        }
+
+        p->state = sw_start;
+        return NGX_OK;
+    }
+
+    p->state = state;
+    return NGX_AGAIN;
+}
new file mode 100644
--- /dev/null
+++ b/src/http/modules/proxy/ngx_http_proxy_upstream.c
@@ -0,0 +1,43 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <ngx_event_pipe.h>
+#include <ngx_http.h>
+#include <ngx_http_proxy_handler.h>
+
+
+void ngx_http_proxy_reinit_upstream(ngx_http_proxy_ctx_t *p)
+{
+    ngx_chain_t             *cl;
+    ngx_output_chain_ctx_t  *octx;
+
+    octx = p->output_chain_ctx;
+
+    /* reinit the request chain */
+
+    for (cl = p->request->request_hunks; cl; cl = cl->next) {
+        cl->hunk->pos = cl->hunk->start;
+    }
+
+    /* reinit ngx_output_chain() context */
+
+    octx->hunk = NULL;
+    octx->in = NULL;
+    octx->free = NULL;
+    octx->busy = NULL;
+
+    /* reinit r->header_in buffer */
+
+    if (p->header_in) {
+        if (p->cache) {
+            p->header_in->pos = p->header_in->start + p->cache->ctx.header.size;
+            p->header_in->last = p->header_in->pos;
+
+        } else {
+            p->header_in->pos = p->header_in->start;
+            p->header_in->last = p->header_in->start;
+        }
+    }
+}
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -6,6 +6,7 @@
 #include <ngx_core.h>
 #include <ngx_http_request.h>
 #include <ngx_http_config.h>
+#include <ngx_http_cache.h>
 #include <ngx_http_filter.h>
 #include <ngx_http_core_module.h>
 
--- a/src/http/ngx_http_cache.c
+++ b/src/http/ngx_http_cache.c
@@ -1,4 +1,161 @@
 
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <md5.h>
+
+
+int ngx_http_cache_get_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx)
+{
+    int                    small;
+    ssize_t                n, len;
+    MD5_CTX                md5;
+    ngx_err_t              err;
+    ngx_str_t              key;
+    ngx_http_bin_cache_t  *h;
+
+    ctx->file.name.len = ctx->path->name.len + 1 + ctx->path->len + 32;
+    if (!(ctx->file.name.data = ngx_palloc(r->pool, ctx->file.name.len + 1))) {
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(ctx->file.name.data, ctx->path->name.data, ctx->path->name.len);
+
+    MD5Init(&md5);
+    MD5Update(&md5, (u_char *) ctx->key.data, ctx->key.len);
+    MD5End(&md5,
+           ctx->file.name.data + ctx->path->name.len + 1 + ctx->path->len);
+
+ngx_log_debug(r->connection->log, "URL: %s, md5: %s" _ ctx->key.data _
+              ctx->file.name.data + ctx->path->name.len + 1 + ctx->path->len);
+
+    ngx_create_hashed_filename(&ctx->file, ctx->path);
+
+ngx_log_debug(r->connection->log, "FILE: %s" _ ctx->file.name.data);
+
+    /* TODO: look open files cache */
+
+    ctx->file.fd = ngx_open_file(ctx->file.name.data,
+                                 NGX_FILE_RDONLY, NGX_FILE_OPEN);
+
+    if (ctx->file.fd == NGX_INVALID_FILE) {
+        err = ngx_errno;
+
+        if (err == NGX_ENOENT || err == NGX_ENOTDIR) {
+
+            /* TODO: text size */
+
+            ctx->header.size = 2 * sizeof(ssize_t)
+                               + sizeof(ngx_http_cache_header_t)
+                               + ctx->key.len + 1;
+
+            return NGX_DECLINED;
+        }
+
+        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+                      ngx_open_file_n " \"%s\" failed", ctx->file.name.data);
+        return NGX_ERROR;
+    }
+
+    n = ngx_read_file(&ctx->file, ctx->buf->pos,
+                      ctx->buf->end - ctx->buf->last, 0);
+
+    if (n == NGX_ERROR || n == NGX_AGAIN) {
+        return n;
+    }
+
+    len = 0;
+    small = 1;
+
+    if (n > 1) {
+        if (ctx->buf->pos[0] == 'T') {
+            /* STUB */
+            return NGX_ERROR;
+
+        } else if (ctx->buf->pos[0] == 'B') {
+
+            len = sizeof(ngx_http_bin_cache_t);
+
+            if (n > len) {
+                h = (ngx_http_bin_cache_t *) ctx->buf->pos;
+                key.len =  h->key_len;
+
+                if (n >= len + (ssize_t) key.len + 1) {
+                    ctx->header = h->header;
+                    key.data = h->key;
+
+                    small = 0;
+                }
+            }
+
+        } else {
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+                          "unknown type of cache file \"%s\"",
+                          ctx->file.name.data);
+            return NGX_ERROR;
+        }
+
+    }
+
+    if (small) {
+        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+                      "cache file \"%s\" is to small", ctx->file.name.data);
+        return NGX_ERROR;
+    }
+
+    if (key.len != ctx->key.len
+        || ngx_strncmp(key.data, ctx->key.data, key.len) != 0)
+    {
+        key.data[key.len] = '\0';
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                          "md5 collision: \"%s\" and \"%s\"",
+                          key.data, ctx->key.data);
+        return NGX_DECLINED;
+    }
+
+    ctx->header.size = len + key.len + 1;
+    ctx->buf->last += n;
+
+    return NGX_OK;
+}
+
+
+int ngx_http_cache_update_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx,
+                               ngx_str_t *temp_file)
+{
+    int        retry;
+    ngx_err_t  err;
+
+    retry = 0;
+
+    for ( ;; ) {
+        if (ngx_rename_file(temp_file->data, ctx->file.name.data)
+                                                             != NGX_FILE_ERROR)
+        {
+            return NGX_OK;
+        }
+
+        err = ngx_errno;
+
+        if (retry || (err != NGX_ENOENT && err != NGX_ENOTDIR)) {
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+                          ngx_rename_file_n "(\"%s\", \"%s\") failed",
+                          temp_file->data, ctx->file.name.data);
+
+            return NGX_ERROR;
+        }
+
+        if (ngx_create_path(&ctx->file, ctx->path) == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        retry = 1;
+    }
+}
+
+
+#if 0
 
 /*
  * small file in malloc()ed memory, mmap()ed file, file descriptor only,
@@ -94,3 +251,5 @@ int ngx_crc(char *data, size_t len)
 
     return sum;
 }
+
+#endif
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_cache.h
@@ -0,0 +1,71 @@
+#ifndef _NGX_HTTP_CACHE_H_INCLUDED_
+#define _NGX_HTTP_CACHE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef struct {
+    time_t       expires;
+    time_t       last_modified;
+    off_t        length;
+
+    size_t       size;
+} ngx_http_cache_header_t;
+
+
+typedef struct {
+    ssize_t                  type;
+    ngx_http_cache_header_t  header;
+    ssize_t                  key_len;
+    char                     key[0];
+} ngx_http_bin_cache_t;
+
+
+typedef struct {
+    char                     type;
+    char                     space0;
+    char                     expires[8];
+    char                     space1;
+    char                     last_modified[8];
+    char                     space2;
+    char                     length[16];
+    char                     space3;
+    char                     lf;
+    char                     key_len[0];
+} ngx_http_text_cache_t;
+
+
+typedef struct {
+    u_int32_t    crc;
+    ngx_str_t    key;
+    ngx_fd_t     fd;
+    off_t        size;
+    void        *data;          /* mmap, memory */
+    time_t       accessed;
+    time_t       last_modified;
+    time_t       updated;      /* no needed with kqueue */
+    int          refs;
+    int          flags;
+} ngx_http_cache_entry_t;
+
+
+typedef struct {
+    ngx_file_t                file;
+    ngx_str_t                 key;
+    ngx_path_t               *path;
+    ngx_hunk_t               *buf;
+    ngx_http_cache_header_t   header;
+} ngx_http_cache_ctx_t;
+
+
+int ngx_http_cache_get_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx);
+int ngx_http_cache_update_file(ngx_http_request_t *r,ngx_http_cache_ctx_t *ctx,
+                               ngx_str_t *temp_file);
+
+
+
+
+
+#endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -376,10 +376,7 @@ ngx_log_debug(r->connection->log, "trans
             continue;
         }
 
-        rc = ngx_strncmp(r->uri.data, clcfp[i]->name.data,
-                         clcfp[i]->name.len);
-
-ngx_log_debug(r->connection->log, "rc: %d" _ rc);
+        rc = ngx_strncmp(r->uri.data, clcfp[i]->name.data, clcfp[i]->name.len);
 
         if (rc < 0) {
             break;
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -197,6 +197,8 @@ struct ngx_http_request_s {
     unsigned             header_timeout_set:1;
 
     unsigned             proxy:1;
+    unsigned             bypass_cache:1;
+
 #if 0
     unsigned             cachable:1;
 #endif
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -95,6 +95,8 @@ static void ngx_http_read_client_request
             return;
         }
 
+        r->temp_file->offset += n;
+
         r->request_body_hunk->pos = r->request_body_hunk->start;
         r->request_body_hunk->last = r->request_body_hunk->start;
     }
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -50,9 +50,10 @@ ssize_t ngx_write_file(ngx_file_t *file,
 }
 
 
-ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce,
+ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl,
                                 off_t offset, ngx_pool_t *pool)
 {
+    char          *prev;
     size_t         size;
     ssize_t        n;
     struct iovec  *iov;
@@ -61,20 +62,39 @@ ssize_t ngx_write_chain_to_file(ngx_file
 
     /* use pwrite() if there's the only hunk in a chain */
 
-    if (ce->next == NULL) {
-        return ngx_write_file(file, ce->hunk->pos,
-                              ce->hunk->last - ce->hunk->pos, offset);
+    if (cl->next == NULL) {
+        return ngx_write_file(file, cl->hunk->pos,
+                              cl->hunk->last - cl->hunk->pos, offset);
     }
 
+    prev = NULL;
+    iov = NULL;
+    size = 0;
+
     ngx_init_array(io, pool, 10, sizeof(struct iovec), NGX_ERROR);
-    size = 0;
+
+    /* create the iovec and coalesce the neighbouring hunks */
+
+    while (cl) {
+        if (prev == cl->hunk->pos) {
+            iov->iov_len += cl->hunk->last - cl->hunk->pos;
 
-    while (ce) {
-        ngx_test_null(iov, ngx_push_array(&io), NGX_ERROR);
-        iov->iov_base = ce->hunk->pos;
-        iov->iov_len = ce->hunk->last - ce->hunk->pos;
-        size += ce->hunk->last - ce->hunk->pos;
-        ce = ce->next;
+        } else {
+            ngx_test_null(iov, ngx_push_array(&io), NGX_ERROR);
+            iov->iov_base = cl->hunk->pos;
+            iov->iov_len = cl->hunk->last - cl->hunk->pos;
+        }
+
+        size += cl->hunk->last - cl->hunk->pos;
+        prev = cl->hunk->last;
+        cl = cl->next;
+    }
+
+    /* use pwrite() if there's the only iovec buffer */
+
+    if (io.nelts == 1) {
+        iov = io.elts;
+        return ngx_write_file(file, iov[0].iov_base, iov[0].iov_len, offset);
     }
 
     if (file->offset != offset) {
@@ -84,7 +104,7 @@ ssize_t ngx_write_chain_to_file(ngx_file
         }
     }
 
-    n = writev(file->fd, (struct iovec *) io.elts, io.nelts);
+    n = writev(file->fd, io.elts, io.nelts);
 
     if (n == -1) {
         ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "writev() failed");
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -45,6 +45,10 @@ ssize_t ngx_write_chain_to_file(ngx_file
                                 off_t offset, ngx_pool_t *pool);
 
 
+#define ngx_rename_file          rename
+#define ngx_rename_file_n        "rename"
+
+
 #define ngx_mkdir(name)          mkdir(name, 0700)
 #define ngx_mkdir_n              "mkdir()"
 
--- a/src/os/unix/ngx_freebsd_sendfile_chain.c
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c
@@ -31,13 +31,28 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(
     struct sf_hdtr   hdtr;
     ngx_err_t        err;
     ngx_array_t      header, trailer;
+    ngx_event_t     *wev;
     ngx_hunk_t      *file;
     ngx_chain_t     *cl, *tail;
 
-    if (!c->write->ready) {
+    wev = c->write;
+
+    if (!wev->ready) {
         return in;
     }
 
+#if (HAVE_KQUEUE)
+
+    if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && wev->kq_eof) {
+        ngx_log_error(NGX_LOG_ERR, c->log, wev->kq_errno,
+                      "kevent() reported about closed connection");
+
+        wev->error = 1;
+        return NGX_CHAIN_ERROR;
+    }
+
+#endif
+
     do {
         cl = in;
         file = NULL;
@@ -181,7 +196,7 @@ ngx_log_debug(c->log, "NOPUSH");
                                   "sendfile() sent only %qd bytes", sent);
 
                 } else {
-                    c->write->error = 1;
+                    wev->error = 1;
                     ngx_log_error(NGX_LOG_CRIT, c->log, err,
                                   "sendfile() failed");
                     return NGX_CHAIN_ERROR;
@@ -194,7 +209,7 @@ ngx_log_debug(c->log, "NOPUSH");
 #endif
 
         } else {
-            rc = writev(c->fd, (struct iovec *) header.elts, header.nelts);
+            rc = writev(c->fd, header.elts, header.nelts);
 
             if (rc == -1) {
                 err = ngx_errno;
@@ -206,7 +221,7 @@ ngx_log_debug(c->log, "NOPUSH");
                     ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EINTR");
 
                 } else {
-                    c->write->error = 1;
+                    wev->error = 1;
                     ngx_log_error(NGX_LOG_CRIT, c->log, err, "writev() failed");
                     return NGX_CHAIN_ERROR;
                 }
@@ -268,7 +283,7 @@ ngx_log_debug(c->log, "NOPUSH");
              * return EAGAIN right away and would not send anything
              */
 
-            c->write->ready = 0;
+            wev->ready = 0;
             break;
         }
 
@@ -277,7 +292,7 @@ ngx_log_debug(c->log, "NOPUSH");
     } while ((tail && tail == in) || eintr);
 
     if (in) {
-        c->write->ready = 0;
+        wev->ready = 0;
     }
 
     return in;
--- a/src/os/unix/ngx_writev_chain.c
+++ b/src/os/unix/ngx_writev_chain.c
@@ -11,34 +11,34 @@ ngx_chain_t *ngx_writev_chain(ngx_connec
     off_t            sent;
     struct iovec    *iov;
     ngx_err_t        err;
-    ngx_array_t      iovecs;
-    ngx_chain_t     *ce;
+    ngx_array_t      io;
+    ngx_chain_t     *cl;
 
     if (!c->write->ready) {
         return in;
     }
 
-    ngx_init_array(iovecs, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR);
+    ngx_init_array(io, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR);
 
     prev = NULL;
     iov = NULL;
 
-    /* create the iovec and coalesce the neighbouring chain entries */
-    for (ce = in; ce; ce = ce->next) {
+    /* create the iovec and coalesce the neighbouring hunks */
+    for (cl = in; cl; cl = cl->next) {
 
-        if (prev == ce->hunk->pos) {
-            iov->iov_len += ce->hunk->last - ce->hunk->pos;
-            prev = ce->hunk->last;
+        if (prev == cl->hunk->pos) {
+            iov->iov_len += cl->hunk->last - cl->hunk->pos;
+            prev = cl->hunk->last;
 
         } else {
-            ngx_test_null(iov, ngx_push_array(&iovecs), NGX_CHAIN_ERROR);
-            iov->iov_base = ce->hunk->pos;
-            iov->iov_len = ce->hunk->last - ce->hunk->pos;
-            prev = ce->hunk->last;
+            ngx_test_null(iov, ngx_push_array(&io), NGX_CHAIN_ERROR);
+            iov->iov_base = cl->hunk->pos;
+            iov->iov_len = cl->hunk->last - cl->hunk->pos;
+            prev = cl->hunk->last;
         }
     }
 
-    n = writev(c->fd, iovecs.elts, iovecs.nelts);
+    n = writev(c->fd, io.elts, io.nelts);
 
     if (n == -1) {
         err = ngx_errno;
@@ -62,42 +62,40 @@ ngx_chain_t *ngx_writev_chain(ngx_connec
 
     c->sent += sent;
 
-    for (ce = in; ce && sent > 0; ce = ce->next) {
+    for (cl = in; cl && sent > 0; cl = cl->next) {
 
-        size = ce->hunk->last - ce->hunk->pos;
+        size = cl->hunk->last - cl->hunk->pos;
 
 ngx_log_debug(c->log, "SIZE: %d" _ size);
 
         if (sent >= size) {
             sent -= size;
 
-            if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
-                ce->hunk->pos = ce->hunk->last;
+            if (cl->hunk->type & NGX_HUNK_IN_MEMORY) {
+                cl->hunk->pos = cl->hunk->last;
             }
 
 #if 0
-            if (ce->hunk->type & NGX_HUNK_FILE) {
-                ce->hunk->file_pos = ce->hunk->file_last;
+            if (cl->hunk->type & NGX_HUNK_FILE) {
+                cl->hunk->file_pos = cl->hunk->file_last;
             }
 #endif
 
             continue;
         }
 
-        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
-            ce->hunk->pos += sent;
+        if (cl->hunk->type & NGX_HUNK_IN_MEMORY) {
+            cl->hunk->pos += sent;
         }
 
 #if 0
-        if (ce->hunk->type & NGX_HUNK_FILE) {
-            ce->hunk->file_pos += sent;
+        if (cl->hunk->type & NGX_HUNK_FILE) {
+            cl->hunk->file_pos += sent;
         }
 #endif
 
         break;
     }
 
-    ngx_destroy_array(&iovecs);
-
-    return ce;
+    return cl;
 }