nginx-0.0.1-2003-11-09-23:03:38 import; separate building
diff --git a/src/http/modules/ngx_http_log_handler.c b/src/http/modules/ngx_http_log_handler.c
deleted file mode 100644
index 759ac76..0000000
--- a/src/http/modules/ngx_http_log_handler.c
+++ /dev/null
@@ -1,202 +0,0 @@
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_http.h>
-
-
-typedef struct {
-    ngx_open_file_t  *file;
-} ngx_http_log_conf_t;
-
-
-static void *ngx_http_log_create_conf(ngx_conf_t *cf);
-static char *ngx_http_log_merge_conf(ngx_conf_t *cf, void *parent, void *child);
-static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,
-                                  void *conf);
-
-static ngx_command_t ngx_http_log_commands[] = {
-
-    {ngx_string("access_log"),
-     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-     ngx_http_log_set_log,
-     NGX_HTTP_LOC_CONF_OFFSET,
-     0,
-     NULL},
-
-    ngx_null_command
-};
-
-
-ngx_http_module_t  ngx_http_log_module_ctx = {
-    NULL,                                  /* create main configuration */
-    NULL,                                  /* init main configuration */
-
-    NULL,                                  /* create server configuration */
-    NULL,                                  /* merge server configuration */
-
-    ngx_http_log_create_conf,              /* create location configration */
-    ngx_http_log_merge_conf                /* merge location configration */
-};
-
-
-ngx_module_t  ngx_http_log_module = {
-    NGX_MODULE,
-    &ngx_http_log_module_ctx,              /* module context */
-    ngx_http_log_commands,                 /* module directives */
-    NGX_HTTP_MODULE,                       /* module type */
-    NULL,                                  /* init module */
-    NULL                                   /* init child */
-};
-
-
-static ngx_str_t http_access_log = ngx_string("access.log");
-
-
-static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-                          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
-
-
-int ngx_http_log_handler(ngx_http_request_t *r)
-{
-    char                 *line, *p;
-    size_t                len;
-    ngx_tm_t              tm;
-    ngx_http_log_conf_t  *lcf;
-#if (WIN32)
-    int                   written;
-#endif
-
-    ngx_log_debug(r->connection->log, "log handler");
-
-    lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);
-
-    /* 10:%con, 1:%pipe, 22:%date, 2:"%r", 3:%status, 20:%bytes, 2:%user-agent,
-       7*" ", 2/1: "\r\n" */
-#if (WIN32)
-    len = 10 + 1 + 22 + 2 + 3 + 20 + 2 + 7 + 2;
-#else
-    len = 10 + 1 + 22 + 2 + 3 + 20 + 2 + 7 + 1;
-#endif
-
-    len += r->connection->addr_text.len;
-    len += r->request_line.len;
-    if (r->headers_in.user_agent) {
-        len += r->headers_in.user_agent->value.len;
-    }
-
-    ngx_test_null(line, ngx_palloc(r->pool, len), NGX_ERROR);
-    p = line;
-
-    ngx_memcpy(p, r->connection->addr_text.data, r->connection->addr_text.len);
-    p += r->connection->addr_text.len;
-
-    *p++ = ' ';
-
-    p += ngx_snprintf(p, 21, "%u", r->connection->number);
-
-    *p++ = ' ';
-
-    if (r->pipeline) {
-        *p++ = 'p';
-    } else {
-        *p++ = '.';
-    }
-
-    *p++ = ' ';
-
-    ngx_localtime(&tm);
-
-    *p++ = '[';
-    p += ngx_snprintf(p, 21, "%02d/%s/%d:%02d:%02d:%02d",
-                      tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1],
-                      tm.ngx_tm_year,
-                      tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);
-
-    *p++ = ']';
-
-    *p++ = ' ';
-
-    *p++ = '"';
-    ngx_memcpy(p, r->request_line.data, r->request_line.len);
-    p += r->request_line.len;
-    *p++ = '"';
-
-    *p++ = ' ';
-
-    p += ngx_snprintf(p, 4, "%d", r->headers_out.status);
-
-    *p++ = ' ';
-
-    p += ngx_snprintf(p, 21, OFF_FMT, r->connection->sent);
-
-    *p++ = ' ';
-
-    *p++ = '"';
-    if (r->headers_in.user_agent) {
-        ngx_memcpy(p, r->headers_in.user_agent->value.data,
-                   r->headers_in.user_agent->value.len);
-        p += r->headers_in.user_agent->value.len;
-    }
-    *p++ = '"';
-
-#if (WIN32)
-
-    *p++ = CR; *p++ = LF;
-    WriteFile(lcf->file->fd, line, p - line, &written, NULL);
-
-#else
-
-    *p++ = LF;
-    write(lcf->file->fd, line, p - line);
-
-#endif
-
-
-    return NGX_OK;
-}
-
-
-static void *ngx_http_log_create_conf(ngx_conf_t *cf)
-{
-    ngx_http_log_conf_t  *conf;
-
-    ngx_test_null(conf, ngx_pcalloc(cf->pool, sizeof(ngx_http_log_conf_t)),
-                  NGX_CONF_ERROR);
-
-    return conf;
-}
-
-
-static char *ngx_http_log_merge_conf(ngx_conf_t *cf, void *parent, void *child)
-{
-    ngx_http_log_conf_t *prev = parent;
-    ngx_http_log_conf_t *conf = child;
-
-    if (conf->file == NULL) {
-        if (prev->file) {
-            conf->file = prev->file;
-        } else {
-            ngx_test_null(conf->file,
-                          ngx_conf_open_file(cf->cycle, &http_access_log),
-                          NGX_CONF_ERROR);
-        }
-    }
-
-    return NGX_CONF_OK;
-}
-
-
-static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,
-                                  void *conf)
-{
-    ngx_http_log_conf_t *lcf = conf;
-
-    ngx_str_t  *value;
-
-    value = cf->args->elts;
-
-    ngx_test_null(lcf->file, ngx_conf_open_file(cf->cycle, &value[1]),
-                  NGX_CONF_ERROR);
-
-    return NGX_CONF_OK;
-}
diff --git a/src/http/modules/ngx_http_log_handler.h b/src/http/modules/ngx_http_log_handler.h
deleted file mode 100644
index 2bda1f0..0000000
--- a/src/http/modules/ngx_http_log_handler.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef _NGX_HTTP_LOG_HANDLER_H_INCLUDED_
-#define _NGX_HTTP_LOG_HANDLER_H_INCLUDED_
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_http.h>
-
-
-typedef struct {
-    int   dummy;
-} ngx_http_log_conf_t;
-
-
-typedef enum {
-
-    NGX_HTTP_LOG_HANDLER = 0,
-
-#if 0
-    /* the ngx_str_t field of the request */
-    NGX_HTTP_LOG_REQUEST_STR_FIELD,
-
-    /* the ngx_str_t field of the r->headers_in */
-    NGX_HTTP_LOG_REQUEST_HEADER_IN_FIELD,
-
-    /* the ngx_str_t field of the r->headers_out */
-    NGX_HTTP_LOG_REQUEST_HEADER_OUT_FIELD,
-#endif
-
-} ngx_http_log_code_e;
-
-
-typedef struct {
-    int      type;
-    int      size;
-    char  *(*handler) (ngx_http_request_t *r, char *p);
-    int      offset;
-} ngx_http_log_code_t;
-
-
-#endif /* _NGX_HTTP_LOG_HANDLER_H_INCLUDED_ */
diff --git a/src/http/modules/ngx_http_static_handler.c b/src/http/modules/ngx_http_static_handler.c
index ad2669b..9849dbf 100644
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -158,22 +158,6 @@
         r->file.info_valid = 1;
     }
 
-#if !(WIN32) /* the not regular files are probably Unix specific */
-
-    if (!ngx_is_file(r->file.info)) {
-        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
-                      "%s is not a regular file", r->file.name.data);
-
-        if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR)
-            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
-                          ngx_close_file_n " %s failed", r->file.name.data);
-
-        return NGX_HTTP_NOT_FOUND;
-    }
-
-#endif
-#endif
-
     if (ngx_is_dir(r->file.info)) {
 ngx_log_debug(r->connection->log, "HTTP DIR: '%s'" _ r->file.name.data);
 
@@ -203,6 +187,22 @@
         return NGX_HTTP_MOVED_PERMANENTLY;
     }
 
+#if !(WIN32) /* the not regular files are probably Unix specific */
+
+    if (!ngx_is_file(r->file.info)) {
+        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+                      "%s is not a regular file", r->file.name.data);
+
+        if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR)
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+                          ngx_close_file_n " %s failed", r->file.name.data);
+
+        return NGX_HTTP_NOT_FOUND;
+    }
+
+#endif
+#endif
+
     r->content_handler = ngx_http_static_handler;
 
     return NGX_OK;
diff --git a/src/http/modules/proxy/ngx_http_proxy_cache.c b/src/http/modules/proxy/ngx_http_proxy_cache.c
index 0bc7c61..0f56686 100644
--- a/src/http/modules/proxy/ngx_http_proxy_cache.c
+++ b/src/http/modules/proxy/ngx_http_proxy_cache.c
@@ -5,12 +5,14 @@
 #include <ngx_http_proxy_handler.h>
 
 
+static int ngx_http_proxy_process_cached_response(ngx_http_proxy_ctx_t *p,
+                                                  int rc);
 static int ngx_http_proxy_process_cached_header(ngx_http_proxy_ctx_t *p);
+static void ngx_http_proxy_cache_look_complete_request(ngx_http_proxy_ctx_t *p);
 
 
 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;
@@ -54,44 +56,69 @@
 
     c->ctx.buf = p->header_in; 
 
-    rc = ngx_http_cache_get_file(r, &c->ctx);
+    return ngx_http_proxy_process_cached_response(p,
+                                          ngx_http_cache_get_file(r, &c->ctx));
+}
 
-    switch (rc) {
-    case NGX_HTTP_CACHE_STALE:
-        p->stale = 1;
-        p->state->cache = NGX_HTTP_PROXY_CACHE_EXPR;
-        break;
 
-    case NGX_HTTP_CACHE_AGED:
-        p->stale = 1;
-        p->state->cache = NGX_HTTP_PROXY_CACHE_AGED;
-        break;
+static int ngx_http_proxy_process_cached_response(ngx_http_proxy_ctx_t *p,
+                                                  int rc)
+{
+    if (rc == NGX_OK) {
+        p->state->cache_state = NGX_HTTP_PROXY_CACHE_HIT;
+        p->header_in->pos = p->header_in->start + p->cache->ctx.header_size;
 
-    case NGX_OK:
-        p->state->cache = NGX_HTTP_PROXY_CACHE_HIT;
-        break;
-
-    default:
-        p->state->cache = NGX_HTTP_PROXY_CACHE_MISS;
-    }
-
-    if (rc == NGX_OK
-        || rc == NGX_HTTP_CACHE_STALE
-        || rc == NGX_HTTP_CACHE_AGED)
-    {
-        p->header_in->pos = p->header_in->start + c->ctx.header_size;
         if (ngx_http_proxy_process_cached_header(p) == NGX_ERROR) {
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
-        p->header_in->pos = p->header_in->start + c->ctx.header_size;
+
+        p->valid_header_in = 1;
+
+        return ngx_http_proxy_send_cached_response(p);
+    }
+
+    if (rc == NGX_HTTP_CACHE_STALE) {
+        p->state->cache_state = NGX_HTTP_PROXY_CACHE_EXPR;
+
+    } else if (rc == NGX_HTTP_CACHE_AGED) {
+        p->state->cache_state = NGX_HTTP_PROXY_CACHE_AGED;
+    }
+
+    if (rc == NGX_HTTP_CACHE_STALE || rc == NGX_HTTP_CACHE_AGED) {
+        p->header_in->pos = p->header_in->start + p->cache->ctx.header_size;
+
+        if (ngx_http_proxy_process_cached_header(p) == NGX_ERROR) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        p->header_in->pos = p->header_in->start + p->cache->ctx.header_size;
         p->header_in->last = p->header_in->pos;
 
+        p->stale = 1;
+        p->valid_header_in = 1;
+
     } else if (rc == NGX_DECLINED) {
-        p->header_in->pos = p->header_in->start + c->ctx.header_size;
+        p->state->cache_state = NGX_HTTP_PROXY_CACHE_MISS;
+        p->header_in->pos = p->header_in->start + p->cache->ctx.header_size;
         p->header_in->last = p->header_in->pos;
     }
 
-    return rc;
+    if (p->lcf->busy_lock) {
+        p->try_busy_lock = 1;
+
+        p->header_in->pos = p->header_in->start;
+        p->header_in->last = p->header_in->start;
+
+        p->busy_lock.time = 0;
+        p->busy_lock.event = p->request->connection->read;
+        p->busy_lock.event_handler = ngx_http_proxy_busy_lock_handler;
+        p->busy_lock.md5 = p->cache->ctx.md5;
+
+        ngx_http_proxy_cache_busy_lock(p);
+        return NGX_DONE;
+    }
+
+    return ngx_http_proxy_request_upstream(p);
 }
 
 
@@ -141,6 +168,7 @@
     ngx_log_debug(r->connection->log, "http cache status %d '%s'" _ 
                   c->status _ c->status_line.data);
 
+    /* TODO: ngx_init_table */
     c->headers_in.headers = ngx_create_table(r->pool, 20);
 
     for ( ;; ) {
@@ -216,28 +244,109 @@
 }
 
 
-#if 0
-
-static void ngx_http_proxy_cache_busy_lock(ngx_http_proxy_ctx_t *p)
+void ngx_http_proxy_cache_busy_lock(ngx_http_proxy_ctx_t *p)
 {
-    rc = ngx_http_busy_lock(p->lcf->busy_lock, p->cache->ctx.md5);
+    int  rc, ft_type;
+
+    rc = ngx_http_busy_lock_cachable(p->lcf->busy_lock, &p->busy_lock,
+                                     p->try_busy_lock);
 
     if (rc == NGX_OK) {
-        ngx_http_proxy_request_upstream(p);
+        if (p->try_busy_lock) {
+            p->busy_locked = 1;
+            p->header_in->pos = p->header_in->start + p->cache->ctx.header_size;
+            p->header_in->last = p->header_in->pos;
+
+            ngx_http_proxy_request_upstream(p);
+            return;
+        }
+
+        ngx_http_proxy_cache_look_complete_request(p);
+        return;
     }
 
-    if (rc == NGX_AGAIN) {
-        if (p->busy_lock_time) {
-            ngx_add_timer(p->request->connection->read, 1000);
+    p->try_busy_lock = 0;
+
+    if (p->cache->ctx.file.fd != NGX_INVALID_FILE
+        && !p->cache->ctx.file.info_valid)
+    {
+        if (ngx_stat_fd(p->cache->ctx.file.fd, &p->cache->ctx.file.info)
+                                                             == NGX_FILE_ERROR)
+        {
+            ngx_log_error(NGX_LOG_CRIT, p->request->connection->log, ngx_errno,
+                          ngx_stat_fd_n " \"%s\" failed",
+                          p->cache->ctx.file.name.data);
+            ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
             return;
         }
+
+        p->cache->ctx.file.info_valid = 1;
+    }
+
+
+    if (rc == NGX_AGAIN) {
+        return;
+    }
+
+    if (rc == NGX_DONE) {
+        ft_type = NGX_HTTP_PROXY_FT_BUSY_LOCK;
+
+    } else {
+        /* rc == NGX_ERROR */
+        ft_type = NGX_HTTP_PROXY_FT_MAX_WAITING;
+    }
+    
+    if (p->stale && (p->lcf->use_stale & ft_type)) {
+        ngx_http_proxy_finalize_request(p,
+                                        ngx_http_proxy_send_cached_response(p));
+        return;
+    }
+    
+    ngx_http_proxy_finalize_request(p, NGX_HTTP_SERVICE_UNAVAILABLE);
+}
+
+
+static void ngx_http_proxy_cache_look_complete_request(ngx_http_proxy_ctx_t *p)
+{
+    int                    rc;
+    ngx_http_cache_ctx_t  *ctx;
+
+    if (!(ctx = ngx_pcalloc(p->request->pool, sizeof(ngx_http_cache_ctx_t)))) {
+        ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
+    *ctx = p->cache->ctx;
+
+    rc = ngx_http_cache_open_file(p->request, ctx,
+                                  ngx_file_uniq(p->cache->ctx.file.info));
+
+    if (rc == NGX_HTTP_CACHE_THE_SAME) {
+        p->try_busy_lock = 1;
+        p->busy_lock.time = 0;
+        ngx_http_proxy_cache_busy_lock(p);
+        return;
+    }
+
+ngx_log_debug(p->request->connection->log, "OLD: %d, NEW: %d" _
+              p->cache->ctx.file.fd _ ctx->file.fd);
+
+    if (p->cache->ctx.file.fd != NGX_INVALID_FILE) {
+        if (ngx_close_file(p->cache->ctx.file.fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, p->request->connection->log, ngx_errno,
+                          ngx_close_file_n " \"%s\" failed",
+                          p->cache->ctx.file.name.data);
+        }
     }
 
-    rc == NGX_ERROR
-    check waitn
-}
+    p->cache->ctx = *ctx;
 
-#endif
+    p->status = 0;
+    p->status_count = 0;
+
+    ngx_http_proxy_finalize_request(p,
+                                ngx_http_proxy_process_cached_response(p, rc));
+}
 
 
 int ngx_http_proxy_send_cached_response(ngx_http_proxy_ctx_t *p)
@@ -435,8 +544,8 @@
         /* FIXME: time_t == int_64_t, we can use fpu */ 
 
         p->state->reason = NGX_HTTP_PROXY_CACHE_LMF;
-        p->cache->ctx.expires = ngx_time()
-              + (((int64_t) (date - last_modified)) * p->lcf->lm_factor) / 100;
+        p->cache->ctx.expires = (time_t) (ngx_time()
+             + (((int64_t) (date - last_modified)) * p->lcf->lm_factor) / 100);
         return 1;
     }
 
@@ -460,6 +569,9 @@
 
     ep = p->upstream->event_pipe;
 
+ngx_log_debug(p->request->connection->log, "LEN: " OFF_FMT ", " OFF_FMT _
+              p->cache->ctx.length _ ep->read_length);
+
     if (p->cache->ctx.length == -1) {
         /* TODO: test rc */
         ngx_write_file(&ep->temp_file->file,
diff --git a/src/http/modules/proxy/ngx_http_proxy_handler.c b/src/http/modules/proxy/ngx_http_proxy_handler.c
index a284956..4a48250 100644
--- a/src/http/modules/proxy/ngx_http_proxy_handler.c
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.c
@@ -132,7 +132,7 @@
 
 
     { ngx_string("proxy_busy_lock"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE3,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13,
       ngx_http_set_busy_lock_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_proxy_loc_conf_t, busy_lock),
@@ -240,9 +240,31 @@
 };
 
 
+static ngx_str_t cache_states[] = {
+    ngx_string("PASS"),
+    ngx_string("BYPASS"),
+    ngx_string("AUTH"),
+    ngx_string("PGNC"),
+    ngx_string("MISS"),
+    ngx_string("EXPR"),
+    ngx_string("AGED"),
+    ngx_string("HIT")
+};
+
+
+static ngx_str_t cache_reason[] = {
+    ngx_string("BPS"),
+    ngx_string("XAE"),
+    ngx_string("CTL"),
+    ngx_string("EXP"),
+    ngx_string("MVD"),
+    ngx_string("LMF"),
+    ngx_string("PDE")
+};
+
+
 static int ngx_http_proxy_handler(ngx_http_request_t *r)
 {
-    int                    rc;
     ngx_http_proxy_ctx_t  *p;
 
     ngx_http_create_ctx(r, p, ngx_http_proxy_module,
@@ -269,16 +291,16 @@
     if (!p->lcf->cache
         || (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD))
     {
-        p->state->cache = NGX_HTTP_PROXY_CACHE_PASS;
+        p->state->cache_state = NGX_HTTP_PROXY_CACHE_PASS;
 
     } else if (r->bypass_cache) {
-        p->state->cache = NGX_HTTP_PROXY_CACHE_BYPASS;
+        p->state->cache_state = NGX_HTTP_PROXY_CACHE_BYPASS;
 
     } else if (r->headers_in.authorization) {
-        p->state->cache = NGX_HTTP_PROXY_CACHE_AUTH;
+        p->state->cache_state = NGX_HTTP_PROXY_CACHE_AUTH;
 
     } else if (r->no_cache) {
-        p->state->cache = NGX_HTTP_PROXY_CACHE_PGNC;
+        p->state->cache_state = NGX_HTTP_PROXY_CACHE_PGNC;
         p->cachable = 1;
 
     } else {
@@ -286,25 +308,72 @@
     }
 
 
-    if (p->state->cache) {
+    if (p->state->cache_state != 0) {
         return ngx_http_proxy_request_upstream(p);
     }
 
-    rc = ngx_http_proxy_get_cached_response(p);
+    return ngx_http_proxy_get_cached_response(p);
+}
 
-    if (rc == NGX_DONE || rc == NGX_HTTP_INTERNAL_SERVER_ERROR) {
-        return rc;
+
+void ngx_http_proxy_busy_lock_handler(ngx_event_t *rev)
+{
+    ngx_connection_t      *c;
+    ngx_http_request_t    *r;
+    ngx_http_proxy_ctx_t  *p;
+    
+    ngx_log_debug(rev->log, "busy lock");
+
+    c = rev->data;
+    r = c->data;
+    p = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+    p->action = "waiting upstream in busy lock";
+
+    if (rev->timedout) {
+        rev->timedout = 0;
+        p->busy_lock.time++;
+        p->state->bl_time = p->busy_lock.time;
+        if (p->state->cache_state < NGX_HTTP_PROXY_CACHE_MISS) {
+            ngx_http_proxy_upstream_busy_lock(p);
+
+        } else {
+            ngx_http_proxy_cache_busy_lock(p);
+        }
+
+        return;
     }
 
-    p->valid_header_in = 1;
+    ngx_log_debug(rev->log, "client sent while busy lock");
 
-    if (rc == NGX_OK) {
-        return ngx_http_proxy_send_cached_response(p);
+    /*
+     * TODO: kevent() notify about error, otherwise we need to
+     * call ngx_peek(): recv(MGS_PEEK) to get errno. THINK about aio
+     * if there's no error we need to disable event.
+     */
+
+#if (HAVE_KQUEUE)
+
+    if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && rev->kq_eof) {
+        p->lcf->busy_lock->waiting--;
+
+        ngx_del_timer(rev);
+
+        ngx_log_error(NGX_LOG_ERR, c->log, rev->kq_errno,
+                      "client() closed connection");
+
+        if (ngx_del_event(rev, NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR) {
+            ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return;
+        }
+
+        /* we have not HTTP code for the case when a client cancels a request */
+
+        ngx_http_proxy_finalize_request(p, 0);
+        return;
     }
 
-    /* rc == NGX_DECLINED || NGX_HTTP_CACHE_STALE || NGX_HTTP_CACHE_AGED */
+#endif
 
-    return ngx_http_proxy_request_upstream(p);
 }
 
 
@@ -313,7 +382,7 @@
     ngx_log_debug(p->request->connection->log,
                   "finalize http proxy request");
 
-    if (p->upstream->peer.connection) {
+    if (p->upstream && p->upstream->peer.connection) {
         ngx_http_proxy_close_connection(p);
     }
 
@@ -323,8 +392,10 @@
         rc = 0;
     }
 
-    p->request->connection->log->data = p->saved_ctx;
-    p->request->connection->log->handler = p->saved_handler;
+    if (p->saved_ctx) {
+        p->request->connection->log->data = p->saved_ctx;
+        p->request->connection->log->handler = p->saved_handler;
+    }
 
     ngx_http_finalize_request(p->request, rc);
 }
@@ -338,7 +409,7 @@
     p->upstream->peer.connection = NULL;
 
     if (p->lcf->busy_lock) {
-        p->lcf->busy_lock->conn_n--;
+        p->lcf->busy_lock->busy--;
     }
 
     ngx_log_debug(c->log, "proxy close connection: %d" _ c->fd);
@@ -382,6 +453,12 @@
 }
 
 
+size_t ngx_http_proxy_log_state(void *data, char *buf, size_t len)
+{
+    return 0;
+}
+
+
 size_t ngx_http_proxy_log_error(void *data, char *buf, size_t len)
 {
     ngx_http_proxy_ctx_t *p = data;
@@ -513,21 +590,21 @@
         conf->busy_lock = prev->busy_lock;
     }
 
-    if (conf->busy_lock && conf->cache && conf->busy_lock->busy == NULL) {
+    if (conf->busy_lock && conf->cache && conf->busy_lock->md5 == NULL) {
 
-        /* ngx_alloc_shared() */
-        conf->busy_lock->busy_mask =
-                     ngx_palloc(cf->pool, (conf->busy_lock->max_conn + 7) / 8);
-        if (conf->busy_lock->busy_mask == NULL) {
+        /* ngx_calloc_shared() */
+        conf->busy_lock->md5_mask =
+                     ngx_pcalloc(cf->pool, (conf->busy_lock->max_busy + 7) / 8);
+        if (conf->busy_lock->md5_mask == NULL) {
             return NGX_CONF_ERROR;
         }
 
         /* 16 bytes are 128 bits of the md5 */
 
         /* ngx_alloc_shared() */
-        conf->busy_lock->busy = ngx_palloc(cf->pool,
-                                           16 * conf->busy_lock->max_conn);
-        if (conf->busy_lock->busy == NULL) {
+        conf->busy_lock->md5 = ngx_palloc(cf->pool,
+                                          16 * conf->busy_lock->max_busy);
+        if (conf->busy_lock->md5 == NULL) {
             return NGX_CONF_ERROR;
         }
     }
diff --git a/src/http/modules/proxy/ngx_http_proxy_handler.h b/src/http/modules/proxy/ngx_http_proxy_handler.h
index 919a0af..138a778 100644
--- a/src/http/modules/proxy/ngx_http_proxy_handler.h
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.h
@@ -80,10 +80,24 @@
 } ngx_http_proxy_loc_conf_t;
 
 
+/*
+ * "EXPR/10/5/- 200/EXP/60 4"
+ * "MISS/-/-/B 503/-/- -"
+ * "EXPR/10/20/SB HIT/-/- -"
+ * "EXPR/10/15/NB HIT/-/- -"
+ */
+
 typedef struct {
-    ngx_http_proxy_state_e           cache;
-    ngx_http_proxy_reason_e          reason;
+    ngx_http_proxy_state_e           cache_state;
+    time_t                           expired;
+    time_t                           bl_time;
+    int                              bl_state;
+
     int                              status;
+    ngx_http_proxy_reason_e          reason;
+    time_t                           time;
+    time_t                           expires;
+
     ngx_str_t                       *peer;
 } ngx_http_proxy_state_t;
 
@@ -140,12 +154,14 @@
 
     ngx_hunk_t                   *header_in;
 
-    time_t                        busy_lock_time;
+    ngx_http_busy_lock_ctx_t      busy_lock;
 
     unsigned                      accel:1;
 
     unsigned                      cachable:1;
     unsigned                      stale:1;
+    unsigned                      try_busy_lock:1;
+    unsigned                      busy_locked:1;
     unsigned                      valid_header_in:1;
 
     unsigned                      request_sent:1;
@@ -187,6 +203,10 @@
 int ngx_http_proxy_is_cachable(ngx_http_proxy_ctx_t *p);
 int ngx_http_proxy_update_cache(ngx_http_proxy_ctx_t *p);
 
+void ngx_http_proxy_busy_lock_handler(ngx_event_t *rev);
+void ngx_http_proxy_cache_busy_lock(ngx_http_proxy_ctx_t *p);
+void ngx_http_proxy_upstream_busy_lock(ngx_http_proxy_ctx_t *p);
+
 size_t ngx_http_proxy_log_error(void *data, char *buf, size_t len);
 void ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int rc);
 void ngx_http_proxy_close_connection(ngx_http_proxy_ctx_t *p);
diff --git a/src/http/modules/proxy/ngx_http_proxy_upstream.c b/src/http/modules/proxy/ngx_http_proxy_upstream.c
index c66c164..0ec2fee 100644
--- a/src/http/modules/proxy/ngx_http_proxy_upstream.c
+++ b/src/http/modules/proxy/ngx_http_proxy_upstream.c
@@ -11,8 +11,6 @@
 static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p);
 static void ngx_http_proxy_init_upstream(void *data);
 static void ngx_http_proxy_reinit_upstream(ngx_http_proxy_ctx_t *p);
-static void ngx_http_proxy_upstream_busy_lock(ngx_http_proxy_ctx_t *p);
-static void ngx_http_proxy_upstream_busy_lock_handler(ngx_event_t *rev);
 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_send_request_handler(ngx_event_t *wev);
@@ -266,7 +264,7 @@
 
     wctx->pool = r->pool;
 
-    if (p->lcf->busy_lock) {
+    if (p->lcf->busy_lock && !p->busy_locked) {
         ngx_http_proxy_upstream_busy_lock(p);
     } else {
         ngx_http_proxy_connect(p);
@@ -312,41 +310,31 @@
 }
 
 
-static void ngx_http_proxy_upstream_busy_lock(ngx_http_proxy_ctx_t *p)
+void ngx_http_proxy_upstream_busy_lock(ngx_http_proxy_ctx_t *p)
 {
-    int  ft_type;
+    int  rc, ft_type;
 
-    if (p->lcf->busy_lock->conn_n < p->lcf->busy_lock->max_conn) {
-        p->lcf->busy_lock->conn_n++;
+    if (p->busy_lock.time == 0) {
+        p->busy_lock.event = p->request->connection->read;
+        p->busy_lock.event_handler = ngx_http_proxy_busy_lock_handler;
+    }
 
-        if (p->busy_lock_time) {
-            p->busy_lock_time = 0;
-            p->lcf->busy_lock->waiting_n--;
-        }
+    rc = ngx_http_busy_lock(p->lcf->busy_lock, &p->busy_lock);
 
+    if (rc == NGX_AGAIN) {
+        return;
+    }
+
+    if (rc == NGX_OK) {
         ngx_http_proxy_connect(p);
         return;
     }
 
-    if (p->busy_lock_time) {
-        if (p->busy_lock_time < p->lcf->busy_lock->timeout) {
-            ngx_add_timer(p->request->connection->read, 1000);
-            return;
-        }
-
-        p->lcf->busy_lock->waiting_n--;
+    if (rc == NGX_DONE) {
         ft_type = NGX_HTTP_PROXY_FT_BUSY_LOCK;
 
     } else {
-        if (p->lcf->busy_lock->waiting_n < p->lcf->busy_lock->max_waiting) {
-            p->lcf->busy_lock->waiting_n++;
-            ngx_add_timer(p->request->connection->read, 1000);
-            p->request->connection->read->event_handler =
-                                     ngx_http_proxy_upstream_busy_lock_handler;
-            /* TODO: ngx_handle_level_read_event() */
-            return;
-        }
-
+        /* rc == NGX_ERROR */
         ft_type = NGX_HTTP_PROXY_FT_MAX_WAITING;
     }
 
@@ -357,61 +345,6 @@
     }
 
     ngx_http_proxy_finalize_request(p, NGX_HTTP_SERVICE_UNAVAILABLE);
-    return;
-}
-
-
-static void ngx_http_proxy_upstream_busy_lock_handler(ngx_event_t *rev)
-{
-    ngx_connection_t      *c;
-    ngx_http_request_t    *r;
-    ngx_http_proxy_ctx_t  *p;
-
-    ngx_log_debug(rev->log, "busy lock");
-
-    c = rev->data;
-    r = c->data;
-    p = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
-    p->action = "waiting upstream in busy lock";
-
-    if (rev->timedout) {
-        rev->timedout = 0;
-        p->busy_lock_time++;
-        ngx_http_proxy_upstream_busy_lock(p);
-        return;
-    }
-
-    ngx_log_debug(rev->log, "client sent while busy lock");
-
-    /*
-     * TODO: kevent() notify about error, otherwise we need to
-     * call ngx_peek(): recv(MGS_PEEK) to get errno. THINK about aio
-     * if there's no error we need to disable event.
-     */
-
-#if (HAVE_KQUEUE)
-
-    if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && rev->kq_eof) {
-        p->lcf->busy_lock->waiting_n--;
-
-        ngx_del_timer(rev);
-
-        ngx_log_error(NGX_LOG_ERR, c->log, rev->kq_errno,
-                      "client() closed connection");
-
-        if (ngx_del_event(rev, NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR) {
-            ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
-            return;
-        }
-
-        /* we have not HTTP code for the case when a client cancels a request */
-
-        ngx_http_proxy_finalize_request(p, 0);
-        return;
-    }
-
-#endif
-
 }
 
 
@@ -724,6 +657,16 @@
         }
     }
 
+    if (p->status == NGX_HTTP_NOT_FOUND
+        && p->upstream->peer.tries > 1
+        && p->lcf->next_upstream & NGX_HTTP_PROXY_FT_HTTP_404)
+    {
+        ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_404);
+        return;
+    }
+
+    /* TODO: "proxy_error_page" */
+
     p->upstream->status_line.len = p->status_end - p->status_start;
     p->upstream->status_line.data = ngx_palloc(p->request->pool,
                                               p->upstream->status_line.len + 1);
@@ -740,6 +683,7 @@
     if (p->upstream->headers_in.headers) {
         p->upstream->headers_in.headers->nelts = 0;
     } else {
+        /* TODO: ngx_init_table */
         p->upstream->headers_in.headers = ngx_create_table(p->request->pool,
                                                            20);
     }
@@ -956,8 +900,8 @@
         header->expires = p->cache->ctx.expires;
         header->last_modified = p->cache->ctx.last_modified;
         header->date = p->cache->ctx.date;
-        /* TODO: r->headers_out.content_length_n == -1 */
         header->length = r->headers_out.content_length_n;
+        p->cache->ctx.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);
@@ -1105,18 +1049,24 @@
     if (p->upstream->peer.connection) {
         if (ep->upstream_done && p->cachable) {
             if (ngx_http_proxy_update_cache(p) == NGX_ERROR) {
+                ngx_http_busy_unlock_cachable(p->lcf->busy_lock, &p->busy_lock);
                 ngx_http_proxy_finalize_request(p, 0);
                 return;
             }
 
+            ngx_http_busy_unlock_cachable(p->lcf->busy_lock, &p->busy_lock);
+
         } else if (ep->upstream_eof && p->cachable) {
 
             /* TODO: check length & update cache */
 
             if (ngx_http_proxy_update_cache(p) == NGX_ERROR) {
+                ngx_http_busy_unlock_cachable(p->lcf->busy_lock, &p->busy_lock);
                 ngx_http_proxy_finalize_request(p, 0);
                 return;
             }
+
+            ngx_http_busy_unlock_cachable(p->lcf->busy_lock, &p->busy_lock);
         }
 
         if (ep->upstream_done || ep->upstream_eof || ep->upstream_error) {
@@ -1206,7 +1156,7 @@
         }
     }
 
-    if (p->lcf->busy_lock) {
+    if (p->lcf->busy_lock && !p->busy_locked) {
         ngx_http_proxy_upstream_busy_lock(p);
     } else {
         ngx_http_proxy_connect(p);