diff src/http/ngx_http_upstream.c @ 464:c8cfb6c462ef NGINX_0_7_44

nginx 0.7.44 *) Feature: the ngx_http_proxy_module preliminary cache support. *) Feature: the --with-pcre option in the configure. *) Feature: the "try_files" directive is now allowed on the server block level. *) Bugfix: the "try_files" directive handled incorrectly a query string in a fallback parameter. *) Bugfix: the "try_files" directive might test incorrectly directories. *) Bugfix: if there is the single server for given address:port pair, then captures in regular expressions in a "server_name" directive did not work.
author Igor Sysoev <http://sysoev.ru>
date Mon, 23 Mar 2009 00:00:00 +0300
parents a8424ffa495c
children 09f0ef15d544
line wrap: on
line diff
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -9,6 +9,13 @@
 #include <ngx_http.h>
 
 
+#if (NGX_HTTP_CACHE)
+static ngx_int_t ngx_http_upstream_cache(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+#endif
+
 static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx);
 static void ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r);
 static void ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r);
@@ -29,6 +36,8 @@ static ngx_int_t ngx_http_upstream_test_
 static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
 static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c);
+static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
 static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
 static void ngx_http_upstream_send_response(ngx_http_request_t *r,
@@ -80,10 +89,15 @@ static ngx_int_t ngx_http_upstream_copy_
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_copy_content_length(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
+
 #if (NGX_HTTP_GZIP)
 static ngx_int_t ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
@@ -139,8 +153,7 @@ ngx_http_upstream_header_t  ngx_http_ups
     { ngx_string("Last-Modified"),
                  ngx_http_upstream_process_header_line,
                  offsetof(ngx_http_upstream_headers_in_t, last_modified),
-                 ngx_http_upstream_copy_header_line,
-                 offsetof(ngx_http_headers_out_t, last_modified), 0 },
+                 ngx_http_upstream_copy_last_modified, 0, 0 },
 
     { ngx_string("Server"),
                  ngx_http_upstream_process_header_line,
@@ -185,7 +198,7 @@ ngx_http_upstream_header_t  ngx_http_ups
     { ngx_string("Accept-Ranges"),
                  ngx_http_upstream_process_header_line,
                  offsetof(ngx_http_upstream_headers_in_t, accept_ranges),
-                 ngx_http_upstream_copy_header_line,
+                 ngx_http_upstream_copy_allow_ranges,
                  offsetof(ngx_http_headers_out_t, accept_ranges), 1 },
 
     { ngx_string("Connection"),
@@ -360,6 +373,25 @@ ngx_http_upstream_init(ngx_http_request_
         u->request_bufs = r->request_body->bufs;
     }
 
+#if (NGX_HTTP_CACHE)
+
+    if (u->conf->cache) {
+        ngx_int_t  rc;
+
+        rc = ngx_http_upstream_cache(r, u);
+
+        if (rc == NGX_DONE) {
+            return;
+        }
+
+        if (rc != NGX_DECLINED) {
+            ngx_http_finalize_request(r, rc);
+            return;
+        }
+    }
+
+#endif
+
     if (u->create_request(r) != NGX_OK) {
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         return;
@@ -491,6 +523,136 @@ found:
 }
 
 
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+    ngx_int_t          rc;
+    ngx_http_cache_t  *c;
+
+    c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t));
+    if (c == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (ngx_array_init(&c->keys, r->pool, 4, sizeof(ngx_str_t)) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    r->cache = c;
+    c->file.log = r->connection->log;
+
+    if (u->create_key(r) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    /* TODO: add keys */
+
+    ngx_http_file_cache_create_key(r);
+
+    u->cacheable = 1;
+
+    c->min_uses = r->upstream->conf->cache_min_uses;
+    c->body_start = r->upstream->conf->buffer_size;
+    c->file_cache = u->conf->cache->data;
+
+    rc = ngx_http_file_cache_open(r);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http upstream cache: %i u:%ui", rc, c->uses);
+
+    if (rc == NGX_OK) {
+
+        rc = ngx_http_upstream_cache_send(r, u);
+
+        if (rc != NGX_HTTP_UPSTREAM_INVALID_HEADER) {
+            return rc;
+        }
+
+    } else if (rc == NGX_ERROR) {
+
+        return NGX_ERROR;
+
+    } else if (rc == NGX_HTTP_CACHE_STALE) {
+
+        u->stale_cache = 1;
+        u->buffer.start = NULL;
+
+    } else if (rc == NGX_DECLINED) {
+
+        if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) {
+            u->buffer.start = NULL;
+
+        } else {
+            u->buffer.pos = u->buffer.start + c->header_start;
+            u->buffer.last = u->buffer.pos;
+        }
+
+    } else if (rc == NGX_AGAIN) {
+
+        u->cacheable = 0;
+
+    } else {
+
+        /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */
+
+        return rc;
+    }
+
+    r->cached = 0;
+
+    return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+    ngx_int_t          rc;
+    ngx_http_cache_t  *c;
+
+    c = r->cache;
+
+    /* TODO: cache stack */
+
+    u->buffer = *c->buf;
+    u->buffer.pos += c->header_start;
+
+    ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
+
+    if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
+                      sizeof(ngx_table_elt_t))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    rc = u->process_header(r);
+
+    if (rc == NGX_OK) {
+
+        if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
+            return NGX_DONE;
+        }
+
+        return ngx_http_cache_send(r);
+    }
+
+    if (rc == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    /* rc == NGX_HTTP_UPSTREAM_INVALID_HEADER */
+
+    /* TODO: delete file */
+
+    return rc;
+}
+
+#endif
+
+
 static void
 ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx)
 {
@@ -969,22 +1131,18 @@ ngx_http_upstream_reinit(ngx_http_reques
 
     /* reinit u->buffer */
 
-#if 0
-    if (u->cache) {
-        u->buffer.pos = u->buffer.start + u->cache->ctx.header_size;
-        u->buffer.last = u->buffer.pos;
-
-    } else {
-        u->buffer.pos = u->buffer.start;
-        u->buffer.last = u->buffer.start;
+    u->buffer.pos = u->buffer.start;
+
+#if (NGX_HTTP_CACHE)
+
+    if (r->cache) {
+        u->buffer.pos += r->cache->header_start;
     }
-#else
-
-        u->buffer.pos = u->buffer.start;
-        u->buffer.last = u->buffer.start;
 
 #endif
 
+    u->buffer.last = u->buffer.pos;
+
     return NGX_OK;
 }
 
@@ -1115,15 +1273,9 @@ ngx_http_upstream_send_request_handler(n
 static void
 ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
-    ssize_t                         n;
-    ngx_int_t                       rc;
-    ngx_str_t                      *uri, args;
-    ngx_uint_t                      i, flags;
-    ngx_list_part_t                *part;
-    ngx_table_elt_t                *h;
-    ngx_connection_t               *c;
-    ngx_http_upstream_header_t     *hh;
-    ngx_http_upstream_main_conf_t  *umcf;
+    ssize_t            n;
+    ngx_int_t          rc;
+    ngx_connection_t  *c;
 
     c = u->peer.connection;
 
@@ -1166,9 +1318,10 @@ ngx_http_upstream_process_header(ngx_htt
             return;
         }
 
-#if 0
-        if (u->cache) {
-            u->buffer.pos += u->cache->ctx.header_size;
+#if (NGX_HTTP_CACHE)
+
+        if (r->cache) {
+            u->buffer.pos += r->cache->header_start;
             u->buffer.last = u->buffer.pos;
         }
 #endif
@@ -1257,124 +1410,10 @@ ngx_http_upstream_process_header(ngx_htt
         }
     }
 
-    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
-
-    if (u->headers_in.x_accel_redirect) {
-
-        ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);
-
-        part = &u->headers_in.headers.part;
-        h = part->elts;
-
-        for (i = 0; /* void */; i++) {
-
-            if (i >= part->nelts) {
-                if (part->next == NULL) {
-                    break;
-                }
-
-                part = part->next;
-                h = part->elts;
-                i = 0;
-            }
-
-            hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
-                               h[i].lowcase_key, h[i].key.len);
-
-            if (hh && hh->redirect) {
-                if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
-                    ngx_http_finalize_request(r,
-                                              NGX_HTTP_INTERNAL_SERVER_ERROR);
-                    return;
-                }
-            }
-        }
-
-        uri = &u->headers_in.x_accel_redirect->value;
-        args.len = 0;
-        args.data = NULL;
-        flags = 0;
-
-        if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
-            ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
-            return;
-        }
-
-        if (flags & NGX_HTTP_ZERO_IN_URI) {
-            r->zero_in_uri = 1;
-        }
-
-        if (r->method != NGX_HTTP_HEAD) {
-            r->method = NGX_HTTP_GET;
-        }
-
-        r->valid_unparsed_uri = 0;
-
-        ngx_http_internal_redirect(r, uri, &args);
+    if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
         return;
     }
 
-    part = &u->headers_in.headers.part;
-    h = part->elts;
-
-    for (i = 0; /* void */; i++) {
-
-        if (i >= part->nelts) {
-            if (part->next == NULL) {
-                break;
-            }
-
-            part = part->next;
-            h = part->elts;
-            i = 0;
-        }
-
-        if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
-                          h[i].lowcase_key, h[i].key.len))
-        {
-            continue;
-        }
-
-        hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
-                           h[i].lowcase_key, h[i].key.len);
-
-        if (hh) {
-            if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
-                ngx_http_upstream_finalize_request(r, u,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-                return;
-            }
-
-            continue;
-        }
-
-        if (ngx_http_upstream_copy_header_line(r, &h[i], 0) != NGX_OK) {
-            ngx_http_upstream_finalize_request(r, u,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-            return;
-        }
-    }
-
-    if (r->headers_out.server && r->headers_out.server->value.data == NULL) {
-        r->headers_out.server->hash = 0;
-    }
-
-    if (r->headers_out.date && r->headers_out.date->value.data == NULL) {
-        r->headers_out.date->hash = 0;
-    }
-
-    r->headers_out.status = u->headers_in.status_n;
-    r->headers_out.status_line = u->headers_in.status_line;
-
-    u->headers_in.content_length_n = r->headers_out.content_length_n;
-
-    if (r->headers_out.content_length_n != -1) {
-        u->length = (size_t) r->headers_out.content_length_n;
-
-    } else {
-        u->length = NGX_MAX_SIZE_T_VALUE;
-    }
-
     if (!r->subrequest_in_memory) {
         ngx_http_upstream_send_response(r, u);
         return;
@@ -1443,9 +1482,12 @@ ngx_http_upstream_test_next(ngx_http_req
 
 #if (NGX_HTTP_CACHE)
 
-        if (u->peer.tries == 0 && u->stale && (u->conf->use_stale & un->mask)) {
+        if (u->peer.tries == 0
+            && u->stale_cache
+            && (u->conf->cache_use_stale & un->mask))
+        {
             ngx_http_upstream_finalize_request(r, u,
-                                              ngx_http_send_cached_response(r));
+                                           ngx_http_upstream_cache_send(r, u));
             return NGX_OK;
         }
 
@@ -1557,6 +1599,138 @@ ngx_http_upstream_test_connect(ngx_conne
 }
 
 
+static ngx_int_t
+ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+    ngx_str_t                      *uri, args;
+    ngx_uint_t                      i, flags;
+    ngx_list_part_t                *part;
+    ngx_table_elt_t                *h;
+    ngx_http_upstream_header_t     *hh;
+    ngx_http_upstream_main_conf_t  *umcf;
+
+    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
+
+    if (u->headers_in.x_accel_redirect) {
+
+        ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);
+
+        part = &u->headers_in.headers.part;
+        h = part->elts;
+
+        for (i = 0; /* void */; i++) {
+
+            if (i >= part->nelts) {
+                if (part->next == NULL) {
+                    break;
+                }
+
+                part = part->next;
+                h = part->elts;
+                i = 0;
+            }
+
+            hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
+                               h[i].lowcase_key, h[i].key.len);
+
+            if (hh && hh->redirect) {
+                if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
+                    ngx_http_finalize_request(r,
+                                              NGX_HTTP_INTERNAL_SERVER_ERROR);
+                    return NGX_DONE;
+                }
+            }
+        }
+
+        uri = &u->headers_in.x_accel_redirect->value;
+        args.len = 0;
+        args.data = NULL;
+        flags = 0;
+
+        if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
+            ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
+            return NGX_DONE;
+        }
+
+        if (flags & NGX_HTTP_ZERO_IN_URI) {
+            r->zero_in_uri = 1;
+        }
+
+        if (r->method != NGX_HTTP_HEAD) {
+            r->method = NGX_HTTP_GET;
+        }
+
+        r->valid_unparsed_uri = 0;
+
+        ngx_http_internal_redirect(r, uri, &args);
+        return NGX_DONE;
+    }
+
+    part = &u->headers_in.headers.part;
+    h = part->elts;
+
+    for (i = 0; /* void */; i++) {
+
+        if (i >= part->nelts) {
+            if (part->next == NULL) {
+                break;
+            }
+
+            part = part->next;
+            h = part->elts;
+            i = 0;
+        }
+
+        if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
+                          h[i].lowcase_key, h[i].key.len))
+        {
+            continue;
+        }
+
+        hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
+                           h[i].lowcase_key, h[i].key.len);
+
+        if (hh) {
+            if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
+                ngx_http_upstream_finalize_request(r, u,
+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return NGX_DONE;
+            }
+
+            continue;
+        }
+
+        if (ngx_http_upstream_copy_header_line(r, &h[i], 0) != NGX_OK) {
+            ngx_http_upstream_finalize_request(r, u,
+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return NGX_DONE;
+        }
+    }
+
+    if (r->headers_out.server && r->headers_out.server->value.data == NULL) {
+        r->headers_out.server->hash = 0;
+    }
+
+    if (r->headers_out.date && r->headers_out.date->value.data == NULL) {
+        r->headers_out.date->hash = 0;
+    }
+
+    r->headers_out.status = u->headers_in.status_n;
+    r->headers_out.status_line = u->headers_in.status_line;
+
+    u->headers_in.content_length_n = r->headers_out.content_length_n;
+
+    if (r->headers_out.content_length_n != -1) {
+        u->length = (size_t) r->headers_out.content_length_n;
+
+    } else {
+        u->length = NGX_MAX_SIZE_T_VALUE;
+    }
+
+    return NGX_OK;
+}
+
+
 static void
 ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
     ngx_http_upstream_t *u)
@@ -1637,8 +1811,6 @@ ngx_http_upstream_send_response(ngx_http
     ngx_int_t                  rc;
     ngx_event_pipe_t          *p;
     ngx_connection_t          *c;
-    ngx_pool_cleanup_t        *cl;
-    ngx_pool_cleanup_file_t   *clf;
     ngx_http_core_loc_conf_t  *clcf;
 
     rc = ngx_http_send_header(r);
@@ -1651,18 +1823,8 @@ ngx_http_upstream_send_response(ngx_http
     u->header_sent = 1;
 
     if (r->request_body && r->request_body->temp_file) {
-        for (cl = r->pool->cleanup; cl; cl = cl->next) {
-            if (cl->handler == ngx_pool_cleanup_file) {
-                clf = cl->data;
-
-                if (clf->fd == r->request_body->temp_file->file.fd) {
-                    cl->handler(clf);
-                    cl->handler = NULL;
-                    r->request_body->temp_file->file.fd = NGX_INVALID_FILE;
-                    break;
-                }
-            }
-        }
+        ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd);
+        r->request_body->temp_file->file.fd = NGX_INVALID_FILE;
     }
 
     c = r->connection;
@@ -1738,28 +1900,38 @@ ngx_http_upstream_send_response(ngx_http
 
     /* TODO: preallocate event_pipe bufs, look "Content-Length" */
 
-#if 0
-
-    if (u->cache && u->cache->ctx.file.fd != NGX_INVALID_FILE) {
-        if (ngx_close_file(u->cache->ctx.file.fd) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
-                          ngx_close_file_n " \"%s\" failed",
-                          u->cache->ctx.file.name.data);
-        }
+#if (NGX_HTTP_CACHE)
+
+    if (r->cache && r->cache->file.fd != NGX_INVALID_FILE) {
+        ngx_pool_run_cleanup_file(r->pool, r->cache->file.fd);
+        r->cache->file.fd = NGX_INVALID_FILE;
     }
 
     if (u->cacheable) {
-        header = (ngx_http_cache_header_t *) u->buffer->start;
-
-        header->expires = u->cache->ctx.expires;
-        header->last_modified = u->cache->ctx.last_modified;
-        header->date = u->cache->ctx.date;
-        header->length = r->headers_out.content_length_n;
-        u->cache->ctx.length = r->headers_out.content_length_n;
-
-        header->key_len = u->cache->ctx.key0.len;
-        ngx_memcpy(&header->key, u->cache->ctx.key0.data, header->key_len);
-        header->key[header->key_len] = LF;
+        time_t  now, valid;
+
+        valid = ngx_http_file_cache_valid(u->conf->cache_valid,
+                                          u->headers_in.status_n);
+        if (valid) {
+
+            now = ngx_time();
+
+            r->cache->valid_sec = now + valid;
+
+            r->cache->last_modified = r->headers_out.last_modified_time;
+            r->cache->date = now;
+            r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start);
+
+            if (r->headers_out.content_length_n != -1) {
+                r->cache->length = r->cache->body_start
+                                   + r->headers_out.content_length_n;
+            }
+
+            ngx_http_file_cache_set_header(r, u->buffer.start);
+
+        } else {
+            u->cacheable = 0;
+        }
     }
 
 #endif
@@ -1789,7 +1961,7 @@ ngx_http_upstream_send_response(ngx_http
     p->temp_file->path = u->conf->temp_path;
     p->temp_file->pool = r->pool;
 
-    if (u->cacheable || u->store) {
+    if (p->cacheable) {
         p->temp_file->persistent = 1;
 
     } else {
@@ -2241,23 +2413,21 @@ ngx_http_upstream_process_request(ngx_ht
             }
         }
 
-#if (NGX_HTTP_FILE_CACHE)
-
-        if (p->upstream_done && u->cacheable) {
-            if (ngx_http_cache_update(r) == NGX_ERROR) {
-                ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock);
-                ngx_http_upstream_finalize_request(r, u, 0);
-                return;
-            }
-
-        } else if (p->upstream_eof && u->cacheable) {
-
-            /* TODO: check length & update cache */
-
-            if (ngx_http_cache_update(r) == NGX_ERROR) {
-                ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock);
-                ngx_http_upstream_finalize_request(r, u, 0);
-                return;
+#if (NGX_HTTP_CACHE)
+
+        if (u->cacheable) {
+
+            if (p->upstream_done) {
+                ngx_http_file_cache_update(r, u->pipe->temp_file);
+
+            } else if (p->upstream_eof) {
+
+                /* TODO: check length & update cache */
+
+                ngx_http_file_cache_update(r, u->pipe->temp_file);
+
+            } else if (p->upstream_error) {
+                ngx_http_file_cache_free(r, u->pipe->temp_file);
             }
         }
 
@@ -2438,12 +2608,12 @@ ngx_http_upstream_next(ngx_http_request_
 
 #if (NGX_HTTP_CACHE)
 
-            if (u->stale && (u->conf->use_stale & ft_type)) {
+            if (u->stale_cache && (u->conf->cache_use_stale & ft_type)) {
+
                 ngx_http_upstream_finalize_request(r, u,
-                                             ngx_http_send_cached_response(r));
+                                           ngx_http_upstream_cache_send(r, u));
                 return;
             }
-
 #endif
 
             ngx_http_upstream_finalize_request(r, u, status);
@@ -2557,25 +2727,41 @@ ngx_http_upstream_finalize_request(ngx_h
 
     u->peer.connection = NULL;
 
-    if (u->header_sent && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE))
-    {
-        rc = 0;
-    }
-
     if (u->pipe && u->pipe->temp_file) {
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http upstream temp fd: %d",
                        u->pipe->temp_file->file.fd);
     }
 
-#if 0
-    if (u->cache) {
+#if (NGX_HTTP_CACHE)
+
+    if (r->cache) {
+        time_t  valid;
+
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http upstream cache fd: %d",
-                       u->cache->ctx.file.fd);
+                       r->cache->file.fd);
+
+        if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT) {
+
+            valid = ngx_http_file_cache_valid(u->conf->cache_valid, rc);
+
+            if (valid) {
+                r->cache->valid_sec = ngx_time() + valid;
+                r->cache->error = rc;
+            }
+        }
+
+        ngx_http_file_cache_free(r, u->pipe->temp_file);
     }
+
 #endif
 
+    if (u->header_sent && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE))
+    {
+        rc = 0;
+    }
+
     if (rc == NGX_DECLINED) {
         return;
     }
@@ -2827,6 +3013,33 @@ ngx_http_upstream_copy_content_length(ng
 
 
 static ngx_int_t
+ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    ngx_table_elt_t  *ho;
+
+    ho = ngx_list_push(&r->headers_out.headers);
+    if (ho == NULL) {
+        return NGX_ERROR;
+    }
+
+    *ho = *h;
+
+#if (NGX_HTTP_CACHE)
+
+    if (r->cached) {
+        r->headers_out.last_modified = ho;
+        r->headers_out.last_modified_time = ngx_http_parse_time(h->value.data,
+                                                                h->value.len);
+    }
+
+#endif
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_upstream_rewrite_location(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
 {
@@ -2916,6 +3129,33 @@ ngx_http_upstream_rewrite_refresh(ngx_ht
 }
 
 
+static ngx_int_t
+ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset)
+{
+    ngx_table_elt_t  *ho;
+
+#if (NGX_HTTP_CACHE)
+
+    if (r->cached) {
+        r->allow_ranges = 1;
+        return NGX_OK;
+
+    }
+
+#endif
+
+    ho = ngx_list_push(&r->headers_out.headers);
+    if (ho == NULL) {
+        return NGX_ERROR;
+    }
+
+    *ho = *h;
+
+    return NGX_OK;
+}
+
+
 #if (NGX_HTTP_GZIP)
 
 static ngx_int_t