diff src/http/modules/proxy/ngx_http_proxy_handler.c @ 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
line wrap: on
line diff
--- 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;