changeset 176:c0552e5ab567

nginx-0.0.1-2003-11-09-23:03:38 import; separate building
author Igor Sysoev <igor@sysoev.ru>
date Sun, 09 Nov 2003 20:03:38 +0000
parents e92c2c647c57
children 4db54fdbcbe7
files src/core/ngx_conf_file.c src/core/ngx_conf_file.h src/core/ngx_modules.c src/core/ngx_string.c src/event/ngx_event_pipe.c src/http/modules/ngx_http_log_handler.c src/http/modules/ngx_http_log_handler.h src/http/modules/ngx_http_static_handler.c src/http/modules/proxy/ngx_http_proxy_cache.c src/http/modules/proxy/ngx_http_proxy_handler.c src/http/modules/proxy/ngx_http_proxy_handler.h src/http/modules/proxy/ngx_http_proxy_upstream.c src/http/ngx_http.c src/http/ngx_http.h src/http/ngx_http_busy_lock.c src/http/ngx_http_busy_lock.h src/http/ngx_http_cache.c src/http/ngx_http_cache.h src/http/ngx_http_config.h src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_header_filter.c src/http/ngx_http_headers.c src/http/ngx_http_log_handler.c src/http/ngx_http_log_handler.h src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_special_response.c src/os/unix/ngx_files.h src/os/unix/ngx_types.h src/os/win32/ngx_files.h src/os/win32/ngx_stat.c src/os/win32/ngx_stat.h src/os/win32/ngx_types.h src/os/win32/ngx_win32_config.h
diffstat 33 files changed, 1546 insertions(+), 456 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -168,7 +168,15 @@ ngx_log_debug(cf->log, "command '%s'" _ 
 
                     } else if (cmd->type & NGX_CONF_1MORE) {
 
-                        if (cf->args->nelts != 1) {
+                        if (cf->args->nelts > 1) {
+                            valid = 1;
+                        } else {
+                            valid = 0;
+                        }
+
+                    } else if (cmd->type & NGX_CONF_2MORE) {
+
+                        if (cf->args->nelts > 2) {
                             valid = 1;
                         } else {
                             valid = 0;
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -23,14 +23,19 @@
 #define NGX_CONF_TAKE8       0x00000100
 #define NGX_CONF_TAKE9       0x00000200
 
+#define NGX_CONF_TAKE12      (NGX_CONF_TAKE1|NGX_CONF_TAKE2)
+
+#define NGX_CONF_TAKE13      (NGX_CONF_TAKE1|NGX_CONF_TAKE3)
+
 #define NGX_CONF_TAKE1234    (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3   \
                               |NGX_CONF_TAKE4)
 
 #define NGX_CONF_ARGS_NUMBER 0x0000ffff
-#define NGX_CONF_ANY         0x00010000
-#define NGX_CONF_1MORE       0x00020000
-#define NGX_CONF_BLOCK       0x00040000
-#define NGX_CONF_FLAG        0x00080000
+#define NGX_CONF_BLOCK       0x00010000
+#define NGX_CONF_FLAG        0x00020000
+#define NGX_CONF_ANY         0x00040000
+#define NGX_CONF_1MORE       0x00080000
+#define NGX_CONF_2MORE       0x00100000
 
 #define NGX_MAIN_CONF        0x01000000
 
--- a/src/core/ngx_modules.c
+++ b/src/core/ngx_modules.c
@@ -27,6 +27,7 @@ extern ngx_module_t  ngx_aio_module;
 
 extern ngx_module_t  ngx_http_module;
 extern ngx_module_t  ngx_http_core_module;
+extern ngx_module_t  ngx_http_log_module;
 
 extern ngx_module_t  ngx_http_write_filter_module;
 extern ngx_module_t  ngx_http_output_filter_module;
@@ -42,8 +43,6 @@ extern ngx_module_t  ngx_http_static_mod
 extern ngx_module_t  ngx_http_index_module;
 extern ngx_module_t  ngx_http_proxy_module;
 
-extern ngx_module_t  ngx_http_log_module;
-
 
 ngx_module_t *ngx_modules[] = {
 
@@ -78,6 +77,7 @@ ngx_module_t *ngx_modules[] = {
     &ngx_http_module,
 
     &ngx_http_core_module,
+    &ngx_http_log_module,
     &ngx_http_write_filter_module,
     &ngx_http_output_filter_module,
     &ngx_http_header_filter_module,
@@ -93,7 +93,5 @@ ngx_module_t *ngx_modules[] = {
     &ngx_http_index_module,
     &ngx_http_proxy_module,
 
-    &ngx_http_log_module,
-
     NULL
 };
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -71,13 +71,15 @@ int ngx_atoi(char *line, size_t n)
 
 void ngx_md5_text(char *text, u_char *md5)
 {
-    /* STUB */
+    int          i;
+    static char  hex[] = "0123456789abcdef";
 
-    ngx_snprintf(text, 33,
-            "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
-            md5[0], md5[1], md5[2], md5[3], md5[4], md5[5],
-            md5[6], md5[7], md5[8], md5[9], md5[10], md5[11],
-            md5[12], md5[13], md5[14], md5[15]);
+    for (i = 0; i < 16; i++) {
+        *text++ = hex[md5[i] >> 4];
+        *text++ = hex[md5[i] & 0xf];
+    }
+
+    *text = '\0';
 }
 
 
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -417,6 +417,7 @@ int ngx_event_pipe_write_to_downstream(n
             }
 
             /* TODO: free hunk if p->free_bufs && upstream done */
+
             /* add the free shadow raw hunk to p->free_raw_hunks */
 
             if (cl->hunk->type & NGX_HUNK_LAST_SHADOW) {
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -158,22 +158,6 @@ ngx_log_debug(r->connection->log, "FILE:
         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 @@ ngx_log_debug(r->connection->log, "HTTP 
         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;
--- 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 @@ int ngx_http_proxy_get_cached_response(n
 
     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;
 
-    case NGX_OK:
-        p->state->cache = NGX_HTTP_PROXY_CACHE_HIT;
-        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;
 
-    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 @@ static int ngx_http_proxy_process_cached
     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,29 +244,110 @@ static int ngx_http_proxy_process_cached
 }
 
 
-#if 0
+void ngx_http_proxy_cache_busy_lock(ngx_http_proxy_ctx_t *p)
+{
+    int  rc, ft_type;
 
-static 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);
+    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;
     }
 
+    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) {
-        if (p->busy_lock_time) {
-            ngx_add_timer(p->request->connection->read, 1000);
-            return;
+        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;
+
+    p->status = 0;
+    p->status_count = 0;
+
+    ngx_http_proxy_finalize_request(p,
+                                ngx_http_proxy_process_cached_response(p, rc));
 }
 
-#endif
-
 
 int ngx_http_proxy_send_cached_response(ngx_http_proxy_ctx_t *p)
 {
@@ -435,8 +544,8 @@ int ngx_http_proxy_is_cachable(ngx_http_
         /* 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 @@ int ngx_http_proxy_update_cache(ngx_http
 
     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,
--- a/src/http/modules/proxy/ngx_http_proxy_handler.c
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.c
@@ -132,7 +132,7 @@ static ngx_command_t  ngx_http_proxy_com
 
 
     { 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 @@ ngx_http_header_t ngx_http_proxy_headers
 };
 
 
+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 @@ static int ngx_http_proxy_handler(ngx_ht
     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 @@ static int ngx_http_proxy_handler(ngx_ht
     }
 
 
-    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);
+}
+
+
+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");
 
-    if (rc == NGX_DONE || rc == NGX_HTTP_INTERNAL_SERVER_ERROR) {
-        return rc;
+    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");
+
+    /*
+     * 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--;
 
-    if (rc == NGX_OK) {
-        return ngx_http_proxy_send_cached_response(p);
+        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 @@ void ngx_http_proxy_finalize_request(ngx
     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 @@ void ngx_http_proxy_finalize_request(ngx
         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 @@ void ngx_http_proxy_close_connection(ngx
     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 @@ void ngx_http_proxy_close_connection(ngx
 }
 
 
+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 @@ static char *ngx_http_proxy_merge_loc_co
         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;
         }
     }
--- a/src/http/modules/proxy/ngx_http_proxy_handler.h
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.h
@@ -80,10 +80,24 @@ typedef struct {
 } 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_state_e           cache_state;
+    time_t                           expired;
+    time_t                           bl_time;
+    int                              bl_state;
+
+    int                              status;
     ngx_http_proxy_reason_e          reason;
-    int                              status;
+    time_t                           time;
+    time_t                           expires;
+
     ngx_str_t                       *peer;
 } ngx_http_proxy_state_t;
 
@@ -140,12 +154,14 @@ struct ngx_http_proxy_ctx_s {
 
     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_send_cached_response(
 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);
--- 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 @@ ngx_log_debug(r->connection->log, "timer
 
     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_reinit_upstre
 }
 
 
-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 @@ static void ngx_http_proxy_upstream_busy
     }
 
     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 @@ static void ngx_http_proxy_process_upstr
         }
     }
 
+    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 @@ static void ngx_http_proxy_process_upstr
     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 @@ static void ngx_http_proxy_send_response
         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 @@ static void ngx_http_proxy_process_body(
     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 @@ ngx_log_debug(p->request->connection->lo
         }
     }
 
-    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);
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -123,7 +123,6 @@ static char *ngx_http_block(ngx_conf_t *
         }
     }
 
-
     /* parse inside the http{} block */
 
     pcf = *cf;
@@ -131,14 +130,16 @@ static char *ngx_http_block(ngx_conf_t *
     cf->module_type = NGX_HTTP_MODULE;
     cf->cmd_type = NGX_HTTP_MAIN_CONF;
     rv = ngx_conf_parse(cf, NULL);
-    *cf = pcf;
 
-    if (rv != NGX_CONF_OK)
+    if (rv != NGX_CONF_OK) {
+        *cf = pcf;
         return rv;
-
+    }
 
-    /* init http{} main_conf's, merge the server{}s' srv_conf's
-       and its location{}s' loc_conf's */
+    /*
+     * init http{} main_conf's, merge the server{}s' srv_conf's
+     * and its location{}s' loc_conf's
+     */
 
     cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
     cscfp = cmcf->servers.elts;
@@ -556,5 +557,6 @@ ngx_log_debug(cf->log, "%s %08x" _ s_nam
     }
     /**/
 
+    *cf = pcf;
     return NGX_CONF_OK;
 }
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -9,6 +9,7 @@
 #include <ngx_http_cache.h>
 #include <ngx_http_busy_lock.h>
 #include <ngx_http_filter.h>
+#include <ngx_http_log_handler.h>
 #include <ngx_http_core_module.h>
 
 
--- a/src/http/ngx_http_busy_lock.c
+++ b/src/http/ngx_http_busy_lock.c
@@ -4,42 +4,151 @@
 #include <ngx_http.h>
 
 
-int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, u_char *md5)
+
+static int ngx_http_busy_lock_look_cachable(ngx_http_busy_lock_t *bl,
+                                            ngx_http_busy_lock_ctx_t *bc,
+                                            int lock);
+
+
+int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc)
+{
+    if (bl->busy < bl->max_busy) {
+        bl->busy++;
+
+        if (bc->time) {
+            bc->time = 0;
+            bl->waiting--;
+        }
+
+        return NGX_OK;
+    }
+
+    if (bc->time) {
+        if (bc->time < bl->timeout) {
+            ngx_add_timer(bc->event, 1000);
+            return NGX_AGAIN;
+        }
+
+        bl->waiting--;
+        return NGX_DONE;
+
+    }
+
+    if (bl->timeout == 0) {
+        return NGX_DONE;
+    }
+
+    if (bl->waiting < bl->max_waiting) {
+        bl->waiting++;
+        ngx_add_timer(bc->event, 1000);
+        bc->event->event_handler = bc->event_handler;
+
+        /* TODO: ngx_handle_level_read_event() */
+
+        return NGX_AGAIN;
+    }
+
+    return NGX_ERROR;
+}
+
+
+int ngx_http_busy_lock_cachable(ngx_http_busy_lock_t *bl,
+                                ngx_http_busy_lock_ctx_t *bc, int lock)
 {
-    int    i, b, busy, free;
+    int  rc;
+
+    rc = ngx_http_busy_lock_look_cachable(bl, bc, lock);
+
+ngx_log_debug(bc->event->log, "BUSYLOCK: %d" _ rc);
+
+    if (rc == NGX_OK) {  /* no the same request, there's free slot */
+        return NGX_OK;
+    }
+
+    if (rc == NGX_ERROR && !lock) { /* no the same request, no free slot */
+        return NGX_OK;
+    }
+
+    /* rc == NGX_AGAIN:  the same request */
+
+    if (bc->time) {
+        if (bc->time < bl->timeout) {
+            ngx_add_timer(bc->event, 1000);
+            return NGX_AGAIN;
+        }
+
+        bl->waiting--;
+        return NGX_DONE;
+
+    }
+
+    if (bl->timeout == 0) {
+        return NGX_DONE;
+    }
+
+    if (bl->waiting < bl->max_waiting) {
+        bl->waiting++;
+        ngx_add_timer(bc->event, 1000);
+        bc->event->event_handler = bc->event_handler;
+
+        /* TODO: ngx_handle_level_read_event() */
+
+        return NGX_AGAIN;
+    }
+
+    return NGX_ERROR;
+}
+
+
+void ngx_http_busy_unlock_cachable(ngx_http_busy_lock_t *bl,
+                                   ngx_http_busy_lock_ctx_t *bc)
+{
+    bl->md5_mask[bc->slot / 8] &= ~(1 << (bc->slot & 7));
+    bl->cachable--;
+    bl->busy--;
+}
+
+
+static int ngx_http_busy_lock_look_cachable(ngx_http_busy_lock_t *bl,
+                                            ngx_http_busy_lock_ctx_t *bc,
+                                            int lock)
+{
+    int    i, b, cachable, free;
     u_int  mask;
 
     b = 0;
-    busy = 0;
+    cachable = 0;
     free = -1;
 
 #if (NGX_SUPPRESS_WARN)
     mask = 0;
 #endif
 
-    for (i = 0; i < bl->max_conn; i++) {
+    for (i = 0; i < bl->max_busy; i++) {
 
         if ((b & 7) == 0) {
-            mask = bl->busy_mask[i / 8];
+            mask = bl->md5_mask[i / 8];
         }
 
         if (mask & 1) {
-            if (ngx_memcmp(&bl->busy[i * 16], md5, 16) == 0) {
+            if (ngx_memcmp(&bl->md5[i * 16], bc->md5, 16) == 0) {
                 return NGX_AGAIN;
             }
-            busy++;
+            cachable++;
 
         } else if (free == -1) {
             free = i;
         }
 
-        if (busy == bl->busy_n) {
-            if (busy < bl->max_conn) {
+#if 1
+        if (cachable == bl->cachable) {
+            if (free == -1 && cachable < bl->max_busy) {
                 free = i + 1;
             }
 
             break;
         }
+#endif
 
         mask >>= 1;
         b++;
@@ -49,11 +158,18 @@ int ngx_http_busy_lock(ngx_http_busy_loc
         return NGX_ERROR;
     }
 
-    ngx_memcpy(&bl->busy[free * 16], md5, 16);
-    bl->busy_mask[free / 8] |= free % 8;
+    if (lock) {
+        if (bl->busy == bl->max_busy) {
+            return NGX_ERROR;
+        }
 
-    bl->busy_n++;
-    bl->conn_n++;
+        ngx_memcpy(&bl->md5[free * 16], bc->md5, 16);
+        bl->md5_mask[free / 8] |= 1 << (free & 7);
+        bc->slot = free;
+
+        bl->cachable++;
+        bl->busy++;
+    }
 
     return NGX_OK;
 }
@@ -64,8 +180,8 @@ char *ngx_http_set_busy_lock_slot(ngx_co
 {
     char  *p = conf;
 
-    int                    i;
-    ngx_str_t             *value;
+    int                    i, dup, invalid;
+    ngx_str_t             *value, line;
     ngx_http_busy_lock_t  *bl, **blp;
 
     blp = (ngx_http_busy_lock_t **) (p + cmd->offset);
@@ -79,57 +195,86 @@ char *ngx_http_set_busy_lock_slot(ngx_co
     }
     *blp = bl;
 
+    dup = 0;
+    invalid = 0;
     value = (ngx_str_t *) cf->args->elts;
 
-    for (i = 1; i < 4; i++) {
+    for (i = 1; i < cf->args->nelts; i++) {
 
-        if (value[i].len > 2 && ngx_strncasecmp(value[i].data, "c:", 2) == 0) {
-            if (bl->max_conn) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "duplicate \"%s\"", value[i].data);
-                return NGX_CONF_ERROR;
+        if (value[i].data[1] != '=') {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid value \"%s\"", value[i].data);
+            return NGX_CONF_ERROR;
+        }
+
+        switch (value[i].data[0]) {
+
+        case 'b':
+            if (bl->max_busy) {
+                dup = 1;
+                break;
             }
 
-            bl->max_conn = ngx_atoi(value[i].data + 2, value[i].len - 2);
-            if (bl->max_conn == NGX_ERROR) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "invalid value \"%s\"", value[i].data);
-                return NGX_CONF_ERROR;
+            bl->max_busy = ngx_atoi(value[i].data + 2, value[i].len - 2);
+            if (bl->max_busy == NGX_ERROR) {
+                invalid = 1;
+                break;
             }
 
             continue;
-        }
 
-        if (value[i].len > 2 && ngx_strncasecmp(value[i].data, "w:", 2) == 0) {
+        case 'w':
             if (bl->max_waiting) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "duplicate \"%s\"", value[i].data);
-                return NGX_CONF_ERROR;
+                dup = 1;
+                break;
             }
 
             bl->max_waiting = ngx_atoi(value[i].data + 2, value[i].len - 2);
             if (bl->max_waiting == NGX_ERROR) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "invalid value \"%s\"", value[i].data);
-                return NGX_CONF_ERROR;
+                invalid = 1;
+                break;
             }
 
             continue;
+
+        case 't':
+            if (bl->timeout) {
+                dup = 1;
+                break;
+            }
+
+            line.len = value[i].len - 2;
+            line.data = value[i].data + 2;
+
+            bl->timeout = ngx_parse_time(&line, 1);
+            if (bl->timeout == NGX_ERROR) {
+                invalid = 1;
+                break;
+            }
+
+            continue;
+
+        default:
+            invalid = 1;
         }
 
-        if (bl->timeout) {
+        if (dup) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "duplicate timeout \"%s\"", value[i].data);
+                               "duplicate value \"%s\"", value[i].data);
             return NGX_CONF_ERROR;
         }
 
-        bl->timeout = ngx_parse_time(&value[1], 1);
-        if (bl->timeout == NGX_ERROR) {
+        if (invalid) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "invalid timeout \"%s\"", value[i].data);
+                               "invalid value \"%s\"", value[i].data);
             return NGX_CONF_ERROR;
         }
     }
 
+    if (bl->timeout == 0 && bl->max_waiting) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "busy lock waiting is useless with zero timeout");
+    }
+
     return NGX_CONF_OK;
 }
--- a/src/http/ngx_http_busy_lock.h
+++ b/src/http/ngx_http_busy_lock.h
@@ -4,19 +4,20 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
+#include <ngx_event.h>
 #include <ngx_http.h>
 
 
 typedef struct {
-    u_char  *busy_mask;
-    char    *busy;
-    int      busy_n;
+    u_char  *md5_mask;
+    char    *md5;
+    int      cachable;
 
-    int      waiting_n;
-    int      max_waiting;
+    int      busy;
+    int      max_busy;
 
-    int      conn_n;
-    int      max_conn;
+    int      waiting;
+    int      max_waiting;
 
     time_t   timeout;
 
@@ -25,7 +26,21 @@ typedef struct {
 } ngx_http_busy_lock_t;
 
 
-int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, u_char *md5);
+typedef struct {
+    time_t         time;
+    ngx_event_t   *event;
+    void         (*event_handler)(ngx_event_t *ev);
+    u_char        *md5;
+    int            slot;
+} ngx_http_busy_lock_ctx_t;
+
+
+int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc);
+int ngx_http_busy_lock_cachable(ngx_http_busy_lock_t *bl,
+                                ngx_http_busy_lock_ctx_t *bc, int lock);
+void ngx_http_busy_unlock_cachable(ngx_http_busy_lock_t *bl,
+                                   ngx_http_busy_lock_ctx_t *bc);
+
 char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd,
                                   void *conf);
 
--- a/src/http/ngx_http_cache.c
+++ b/src/http/ngx_http_cache.c
@@ -8,10 +8,7 @@
 
 int ngx_http_cache_get_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx)
 {
-    ssize_t                   n;
-    MD5_CTX                   md5;
-    ngx_err_t                 err;
-    ngx_http_cache_header_t  *h;
+    MD5_CTX  md5;
 
     ctx->header_size = sizeof(ngx_http_cache_header_t) + ctx->key.len + 1;
 
@@ -38,6 +35,19 @@ ngx_log_debug(r->connection->log, "FILE:
 
     /* TODO: look open files cache */
 
+    return ngx_http_cache_open_file(r, ctx, 0);
+}
+
+
+/* TODO: Win32 inode analogy */
+
+int ngx_http_cache_open_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx,
+                             ngx_file_uniq_t uniq)
+{
+    ssize_t                   n;
+    ngx_err_t                 err;
+    ngx_http_cache_header_t  *h;
+
     ctx->file.fd = ngx_open_file(ctx->file.name.data,
                                  NGX_FILE_RDONLY, NGX_FILE_OPEN);
 
@@ -53,6 +63,25 @@ ngx_log_debug(r->connection->log, "FILE:
         return NGX_ERROR;
     }
 
+    if (uniq) {
+        if (ngx_stat_fd(ctx->file.fd, &ctx->file.info) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+                          ngx_stat_fd_n " \"%s\" failed", ctx->file.name.data);
+
+            return NGX_ERROR;
+        }
+
+        if (ngx_file_uniq(ctx->file.info) == uniq) {
+            if (ngx_close_file(ctx->file.fd) == NGX_FILE_ERROR) {
+                ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+                              ngx_close_file_n " \"%s\" failed",
+                              ctx->file.name.data);
+            }
+
+            return NGX_HTTP_CACHE_THE_SAME;
+        }
+    }
+
     n = ngx_read_file(&ctx->file, ctx->buf->pos,
                       ctx->buf->end - ctx->buf->last, 0);
 
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -13,7 +13,7 @@ typedef struct {
     time_t       date;
     off_t        length;
     size_t       key_len;
-    char         key[0];
+    char         key[1];
 } ngx_http_cache_header_t;
 
 
@@ -46,11 +46,14 @@ typedef struct {
 } ngx_http_cache_ctx_t;
 
 
-#define NGX_HTTP_CACHE_STALE  1
-#define NGX_HTTP_CACHE_AGED   2
+#define NGX_HTTP_CACHE_STALE     1
+#define NGX_HTTP_CACHE_AGED      2
+#define NGX_HTTP_CACHE_THE_SAME  3
 
 
 int ngx_http_cache_get_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx);
+int ngx_http_cache_open_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx,
+                             ngx_file_uniq_t uniq);
 int ngx_http_cache_update_file(ngx_http_request_t *r,ngx_http_cache_ctx_t *ctx,
                                ngx_str_t *temp_file);
 
--- a/src/http/ngx_http_config.h
+++ b/src/http/ngx_http_config.h
@@ -47,6 +47,8 @@ typedef struct {
 #define ngx_http_get_module_srv_conf(r, module)  r->srv_conf[module.ctx_index]
 #define ngx_http_get_module_loc_conf(r, module)  r->loc_conf[module.ctx_index]
 
+#define ngx_http_conf_module_main_conf(cf, module)                            \
+            ((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
 
 
 extern int (*ngx_http_top_header_filter) (ngx_http_request_t *r);
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -31,6 +31,7 @@ static char *ngx_set_type(ngx_conf_t *cf
 static char *ngx_set_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_set_server_name(ngx_conf_t *cf, ngx_command_t *cmd,
                                  void *conf);
+static char *ngx_set_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 
 static char *ngx_http_lowat_check(ngx_conf_t *cf, void *post, void *data);
@@ -192,6 +193,13 @@ static ngx_command_t  ngx_http_core_comm
      offsetof(ngx_http_core_loc_conf_t, msie_padding),
      NULL},
 
+    {ngx_string("error_page"),
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE,
+     ngx_set_error_page,
+     NGX_HTTP_LOC_CONF_OFFSET,
+     0,
+     NULL},
+
     {ngx_string("error_log"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_set_error_log,
@@ -631,8 +639,8 @@ static char *ngx_location_block(ngx_conf
     char                      *rv;
     ngx_str_t                 *location;
     ngx_http_module_t         *module;
-    ngx_conf_t                 pcf;
-    ngx_http_conf_ctx_t       *ctx, *pctx;
+    ngx_conf_t                 pvcf;
+    ngx_http_conf_ctx_t       *ctx, *pvctx;
     ngx_http_core_srv_conf_t  *cscf;
     ngx_http_core_loc_conf_t  *clcf, **clcfp;
 
@@ -640,9 +648,9 @@ static char *ngx_location_block(ngx_conf
                   ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)),
                   NGX_CONF_ERROR);
 
-    pctx = (ngx_http_conf_ctx_t *) cf->ctx;
-    ctx->main_conf = pctx->main_conf;
-    ctx->srv_conf = pctx->srv_conf;
+    pvctx = (ngx_http_conf_ctx_t *) cf->ctx;
+    ctx->main_conf = pvctx->main_conf;
+    ctx->srv_conf = pvctx->srv_conf;
 
     ngx_test_null(ctx->loc_conf,
                   ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module),
@@ -672,11 +680,11 @@ static char *ngx_location_block(ngx_conf
     ngx_test_null(clcfp, ngx_push_array(&cscf->locations), NGX_CONF_ERROR);
     *clcfp = clcf;
 
-    pcf = *cf;
+    pvcf = *cf;
     cf->ctx = ctx;
     cf->cmd_type = NGX_HTTP_LOC_CONF;
     rv = ngx_conf_parse(cf, NULL);
-    *cf = pcf;
+    *cf = pvcf;
 
     return rv;
 }
@@ -856,6 +864,7 @@ static void *ngx_http_core_create_loc_co
     lcf->default_type.len = 0;
     lcf->default_type.data = NULL;
     lcf->err_log = NULL;
+    lcf->error_pages = NULL;
 
     */
 
@@ -929,6 +938,10 @@ static char *ngx_http_core_merge_loc_con
         }
     }
 
+    if (conf->error_pages == NULL && prev->error_pages) {
+        conf->error_pages = prev->error_pages;
+    }
+
     ngx_conf_merge_str_value(conf->default_type,
                              prev->default_type, "text/plain");
 
@@ -1061,6 +1074,47 @@ static char *ngx_set_server_name(ngx_con
 }
 
 
+static char *ngx_set_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_core_loc_conf_t *lcf = conf;
+
+    int                   i;
+    ngx_str_t            *value;
+    ngx_http_err_page_t  *err;
+
+    if (lcf->error_pages == NULL) {
+        lcf->error_pages = ngx_create_array(cf->pool, 5,
+                                            sizeof(ngx_http_err_page_t));
+        if (lcf->error_pages == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    value = cf->args->elts;
+
+    for (i = 1; i < cf->args->nelts - 1; i++) {
+        ngx_test_null(err, ngx_push_array(lcf->error_pages), NGX_CONF_ERROR);
+        err->code = ngx_atoi(value[i].data, value[i].len);
+        if (err->code == NGX_ERROR) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid value \"%s\"", value[i].data);
+            return NGX_CONF_ERROR;
+        }
+
+        if (err->code < 400 || err->code > 599) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "value \"%s\" must be between 400 and 599",
+                               value[i].data);
+            return NGX_CONF_ERROR;
+        }
+
+        err->uri = value[cf->args->nelts - 1];
+    }
+
+    return NGX_CONF_OK;
+}
+
+
 static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_core_loc_conf_t *lcf = conf;
@@ -1079,9 +1133,9 @@ static char *ngx_set_error_log(ngx_conf_
 
 static char *ngx_http_lowat_check(ngx_conf_t *cf, void *post, void *data)
 {
-    int *np = data;
+#if (HAVE_LOWAT_EVENT)
 
-#if (HAVE_LOWAT_EVENT)
+    int *np = data;
 
     if (*np >= ngx_freebsd_net_inet_tcp_sendspace) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -100,6 +100,12 @@ typedef struct {
 
 
 typedef struct {
+    int        code;
+    ngx_str_t  uri;
+} ngx_http_err_page_t;
+
+
+typedef struct {
     ngx_str_t     name;          /* location name */
     void        **loc_conf ;     /* pointer to the modules' loc_conf */
 
@@ -120,6 +126,7 @@ typedef struct {
     ngx_msec_t    lingering_timeout;       /* lingering_timeout */
 
     int           msie_padding;            /* msie_padding */
+    ngx_array_t  *error_pages;             /* error_page */
 
     ngx_log_t    *err_log;
 } ngx_http_core_loc_conf_t;
--- a/src/http/ngx_http_header_filter.c
+++ b/src/http/ngx_http_header_filter.c
@@ -90,8 +90,9 @@ static ngx_str_t http_codes[] = {
 static int ngx_http_header_filter(ngx_http_request_t *r)
 {
     int                len, status, i;
+    char              *p;
     ngx_hunk_t        *h;
-    ngx_chain_t       *ch;
+    ngx_chain_t       *ln;
     ngx_table_elt_t   *header;
 
     if (r->http_version < NGX_HTTP_VERSION_10) {
@@ -149,7 +150,7 @@ static int ngx_http_header_filter(ngx_ht
         len += r->headers_out.date->key.len
                + r->headers_out.date->value.len + 2;
     } else {
-        len += sizeof("Date: Mon, 28 Sep 1970 00:00:00 GMT" CRLF) - 1;
+        len += sizeof("Date: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
     }
 
     if (r->headers_out.content_length == NULL) {
@@ -174,7 +175,7 @@ static int ngx_http_header_filter(ngx_ht
         && r->headers_out.location->value.data[0] == '/')
     {
         r->headers_out.location->key.len = 0;
-        len += sizeof("Location: http://") - 1,
+        len += sizeof("Location: http://") - 1
                + r->server_name->len + r->headers_out.location->value.len + 2;
 
         if (r->port != 80) {
@@ -187,7 +188,7 @@ static int ngx_http_header_filter(ngx_ht
                + r->headers_out.last_modified->value.len + 2;
 
     } else if (r->headers_out.last_modified_time != -1) {
-        len += sizeof("Last-Modified: Mon, 28 Sep 1970 00:00:00 GMT" CRLF) - 1;
+        len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
     }
 
     if (r->chunked) {
@@ -210,7 +211,9 @@ static int ngx_http_header_filter(ngx_ht
         len += header[i].key.len + 2 + header[i].value.len + 2;
     }
 
-    ngx_test_null(h, ngx_create_temp_hunk(r->pool, len), NGX_ERROR);
+    if (!(h = ngx_create_temp_hunk(r->pool, len))) {
+        return NGX_ERROR;
+    }
 
     /* "HTTP/1.x " */
     h->last = ngx_cpymem(h->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1);
@@ -232,30 +235,70 @@ static int ngx_http_header_filter(ngx_ht
 
     if (!(r->headers_out.date && r->headers_out.date->key.len)) {
         h->last = ngx_cpymem(h->last, "Date: ", sizeof("Date: ") - 1);
-        h->last += ngx_http_get_time(h->last, time(NULL));
+#if (NGX_HTTP_LOG_ALL_HEADERS_OUT)
+        p = h->last;
+#endif
+        h->last += ngx_http_get_time(h->last, ngx_time());
+
+#if (NGX_HTTP_LOG_ALL_HEADERS_OUT)
+        r->headers_out.date = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
+        if (r->headers_out.date == NULL) {
+            return NGX_ERROR;
+        }
+
+        r->headers_out.date->key.len = 0;
+        r->headers_out.date->key.data = NULL;
+        r->headers_out.date->value.len = h->last - p;
+        r->headers_out.date->value.data = p;
+#endif
+
         *(h->last++) = CR; *(h->last++) = LF;
     }
 
     if (r->headers_out.content_length == NULL) {
         if (r->headers_out.content_length_n >= 0) {
+#if (NGX_HTTP_LOG_ALL_HEADERS_OUT)
+            p = h->last + sizeof("Content-Length: ") - 1;
+#endif
             h->last += ngx_snprintf(h->last,        /* 2^64 */
                             sizeof("Content-Length: 18446744073709551616" CRLF),
                             "Content-Length: " OFF_FMT CRLF,
                             r->headers_out.content_length_n);
+
+#if (NGX_HTTP_LOG_ALL_HEADERS_OUT)
+            r->headers_out.content_length = ngx_palloc(r->pool,
+                                                       sizeof(ngx_table_elt_t));
+            if (r->headers_out.content_length == NULL) {
+                return NGX_ERROR;
+            }
+
+            r->headers_out.content_length->key.len = 0;
+            r->headers_out.content_length->key.data = NULL;
+            r->headers_out.content_length->value.len = h->last - p - 2;
+            r->headers_out.content_length->value.data = p;
+#endif
         }
     }
 
     if (r->headers_out.content_type && r->headers_out.content_type->value.len) {
         h->last = ngx_cpymem(h->last, "Content-Type: ",
                              sizeof("Content-Type: ") - 1);
+#if (NGX_HTTP_LOG_ALL_HEADERS_OUT)
+        p = h->last;
+#endif
         h->last = ngx_cpymem(h->last, r->headers_out.content_type->value.data,
                              r->headers_out.content_type->value.len);
 
+#if (NGX_HTTP_LOG_ALL_HEADERS_OUT)
         if (r->headers_out.charset.len) {
             h->last = ngx_cpymem(h->last, "; charset=",
                                  sizeof("; charset=") - 1);
             h->last = ngx_cpymem(h->last, r->headers_out.charset.data,
                                  r->headers_out.charset.len);
+
+            r->headers_out.content_type->value.len = h->last - p;
+            r->headers_out.content_type->value.data = p;
+#endif
         }
 
         *(h->last++) = CR; *(h->last++) = LF;
@@ -265,6 +308,7 @@ static int ngx_http_header_filter(ngx_ht
         && r->headers_out.location->value.len
         && r->headers_out.location->value.data[0] == '/')
     {
+        p = h->last + sizeof("Location: ") - 1;
         h->last = ngx_cpymem(h->last, "Location: http://",
                              sizeof("Location: http://") - 1);
         h->last = ngx_cpymem(h->last, r->server_name->data,
@@ -277,6 +321,9 @@ static int ngx_http_header_filter(ngx_ht
         h->last = ngx_cpymem(h->last, r->headers_out.location->value.data,
                              r->headers_out.location->value.len);
 
+        r->headers_out.location->value.len = h->last - p;
+        r->headers_out.location->value.data = p;
+
         *(h->last++) = CR; *(h->last++) = LF;
     }
 
@@ -285,8 +332,25 @@ static int ngx_http_header_filter(ngx_ht
     {
         h->last = ngx_cpymem(h->last, "Last-Modified: ",
                              sizeof("Last-Modified: ") - 1);
+#if (NGX_HTTP_LOG_ALL_HEADERS_OUT)
+        p = h->last;
+#endif
         h->last += ngx_http_get_time(h->last,
-                                            r->headers_out.last_modified_time);
+                                     r->headers_out.last_modified_time);
+
+#if (NGX_HTTP_LOG_ALL_HEADERS_OUT)
+        r->headers_out.last_modified = ngx_palloc(r->pool,
+                                                  sizeof(ngx_table_elt_t));
+        if (r->headers_out.last_modified == NULL) {
+            return NGX_ERROR;
+        }
+
+        r->headers_out.last_modified->key.len = 0;
+        r->headers_out.last_modified->key.data = NULL;
+        r->headers_out.last_modified->value.len = h->last - p;
+        r->headers_out.last_modified->value.data = p;
+#endif
+
         *(h->last++) = CR; *(h->last++) = LF;
     }
 
@@ -329,12 +393,14 @@ static int ngx_http_header_filter(ngx_ht
         h->type |= NGX_HUNK_LAST;
     }
 
-    ngx_test_null(ch, ngx_palloc(r->pool, sizeof(ngx_chain_t)), NGX_ERROR);
+    if (!(ln = ngx_alloc_chain_link(r->pool))) {
+        return NGX_ERROR;
+    }
 
-    ch->hunk = h;
-    ch->next = NULL;
+    ln->hunk = h;
+    ln->next = NULL;
 
-    return ngx_http_write_filter(r, ch);
+    return ngx_http_write_filter(r, ln);
 }
 
 
--- a/src/http/ngx_http_headers.c
+++ b/src/http/ngx_http_headers.c
@@ -10,6 +10,7 @@ ngx_http_header_t  ngx_http_headers_in[]
     { ngx_string("If-Modified-Since"),
                          offsetof(ngx_http_headers_in_t, if_modified_since) },
     { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent) },
+    { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer) },
 
     { ngx_string("Content-Length"),
                             offsetof(ngx_http_headers_in_t, content_length) },
rename from src/http/modules/ngx_http_log_handler.c
rename to src/http/ngx_http_log_handler.c
--- a/src/http/modules/ngx_http_log_handler.c
+++ b/src/http/ngx_http_log_handler.c
@@ -2,22 +2,55 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_http.h>
-
-
-typedef struct {
-    ngx_open_file_t  *file;
-} ngx_http_log_conf_t;
+#include <nginx.h>
 
 
-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_addr(ngx_http_request_t *r, char *buf,
+                               uintptr_t data);
+static char *ngx_http_log_connection(ngx_http_request_t *r, char *buf,
+                                     uintptr_t data);
+static char *ngx_http_log_pipe(ngx_http_request_t *r, char *buf,
+                               uintptr_t data);
+static char *ngx_http_log_time(ngx_http_request_t *r, char *buf,
+                               uintptr_t data);
+static char *ngx_http_log_request(ngx_http_request_t *r, char *buf,
+                                  uintptr_t data);
+static char *ngx_http_log_status(ngx_http_request_t *r, char *buf,
+                                 uintptr_t data);
+static char *ngx_http_log_length(ngx_http_request_t *r, char *buf,
+                                 uintptr_t data);
+static char *ngx_http_log_header_in(ngx_http_request_t *r, char *buf,
+                                    uintptr_t data);
+static char *ngx_http_log_unknown_header_in(ngx_http_request_t *r, char *buf,
+                                            uintptr_t data);
+static char *ngx_http_log_header_out(ngx_http_request_t *r, char *buf,
+                                    uintptr_t data);
+static char *ngx_http_log_unknown_header_out(ngx_http_request_t *r, char *buf,
+                                             uintptr_t data);
+
+static void *ngx_http_log_create_main_conf(ngx_conf_t *cf);
+static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_log_merge_loc_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 char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd,
+                                     void *conf);
+static int ngx_http_log_parse_format(ngx_conf_t *cf, ngx_array_t *ops,
+                                     ngx_str_t *line);
 
-static ngx_command_t ngx_http_log_commands[] = {
+
+static ngx_command_t  ngx_http_log_commands[] = {
+
+    {ngx_string("log_format"),
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE,
+     ngx_http_log_set_format,
+     NGX_HTTP_MAIN_CONF_OFFSET,
+     0,
+     NULL},
 
     {ngx_string("access_log"),
-     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
      ngx_http_log_set_log,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
@@ -28,14 +61,14 @@ static ngx_command_t ngx_http_log_comman
 
 
 ngx_http_module_t  ngx_http_log_module_ctx = {
-    NULL,                                  /* create main configuration */
+    ngx_http_log_create_main_conf,         /* 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_http_log_create_loc_conf,          /* create location configration */
+    ngx_http_log_merge_loc_conf            /* merge location configration */
 };
 
 
@@ -55,130 +88,444 @@ static ngx_str_t http_access_log = ngx_s
 static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
 
+static ngx_str_t ngx_http_combined_fmt =
+    ngx_string("%addr - - [%time] \"%request\" %status %length "
+               "\"%{Referer}i\" %{User-Agent}i\"");
+
+
+static ngx_http_log_op_name_t ngx_http_log_fmt_ops[] = {
+    { ngx_string("addr"), INET_ADDRSTRLEN - 1, ngx_http_log_addr },
+    { ngx_string("conn"), NGX_INT32_LEN, ngx_http_log_connection },
+    { ngx_string("pipe"), 1, ngx_http_log_pipe },
+    { ngx_string("time"), sizeof("28/Sep/1970:12:00:00") - 1,
+                          ngx_http_log_time },
+    { ngx_string("request"), 0, ngx_http_log_request },
+    { ngx_string("status"), 3, ngx_http_log_status },
+    { ngx_string("length"), NGX_OFF_LEN, ngx_http_log_length },
+    { ngx_string("i"), NGX_HTTP_LOG_ARG, ngx_http_log_header_in },
+    { ngx_string("o"), NGX_HTTP_LOG_ARG, ngx_http_log_header_out },
+    { ngx_null_string, 0, NULL }
+};
+
 
 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;
+    int                       i, l;
+    u_int                     data;
+    char                     *line, *p;
+    size_t                    len;
+    ngx_http_log_t           *log;
+    ngx_http_log_op_t        *op;
+    ngx_http_log_loc_conf_t  *lcf;
 #if (WIN32)
-    int                   written;
+    u_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" */
+    log = lcf->logs->elts;
+    for (l = 0; l < lcf->logs->nelts; l++) {
+
+        len = 0;
+        op = log[l].ops->elts;
+        for (i = 0; i < log[l].ops->nelts; i++) {
+            if (op[i].len == 0) {
+                len += (size_t) op[i].op(r, NULL, op[i].data);
+
+            } else {
+                len += op[i].len;
+            }
+        }
+
 #if (WIN32)
-    len = 10 + 1 + 22 + 2 + 3 + 20 + 2 + 7 + 2;
+        len += 2;
 #else
-    len = 10 + 1 + 22 + 2 + 3 + 20 + 2 + 7 + 1;
+        len++;
 #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);
+        ngx_test_null(line, ngx_palloc(r->pool, len), NGX_ERROR);
+        p = line;
 
-    *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++ = '"';
+        for (i = 0; i < log[l].ops->nelts; i++) {
+            if (op[i].op == NGX_HTTP_LOG_COPY_SHORT) {
+                len = op[i].len;
+                data = op[i].data;
+                while (len--) {
+                    *p++ = data & 0xff;
+                    data >>= 8;
+                }
 
-    *p++ = ' ';
-
-    p += ngx_snprintf(p, 4, "%d", r->headers_out.status);
-
-    *p++ = ' ';
-
-    p += ngx_snprintf(p, 21, OFF_FMT, r->connection->sent);
+            } else if (op[i].op == NGX_HTTP_LOG_COPY_LONG) {
+                p = ngx_cpymem(p, (void *) op[i].data, op[i].len);
 
-    *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++ = '"';
+            } else {
+                p = op[i].op(r, p, op[i].data);
+            }
+        }
 
 #if (WIN32)
-
-    *p++ = CR; *p++ = LF;
-    WriteFile(lcf->file->fd, line, p - line, &written, NULL);
-
+        *p++ = CR; *p++ = LF;
+        WriteFile(log[l].file->fd, line, p - line, &written, NULL);
 #else
-
-    *p++ = LF;
-    write(lcf->file->fd, line, p - line);
-
+        *p++ = LF;
+        write(log[l].file->fd, line, p - line);
 #endif
-
+    }
 
     return NGX_OK;
 }
 
 
-static void *ngx_http_log_create_conf(ngx_conf_t *cf)
+static char *ngx_http_log_addr(ngx_http_request_t *r, char *buf, uintptr_t data)
+{
+    return ngx_cpymem(buf, r->connection->addr_text.data,
+                      r->connection->addr_text.len);
+}
+
+
+static char *ngx_http_log_connection(ngx_http_request_t *r, char *buf,
+                                     uintptr_t data)
+{
+    return buf + ngx_snprintf(buf, NGX_INT32_LEN + 1, "%u",
+                              r->connection->number);
+}
+
+
+static char *ngx_http_log_pipe(ngx_http_request_t *r, char *buf, uintptr_t data)
+{
+    if (r->pipeline) {
+        *buf = 'p';
+    } else {
+        *buf = '.';
+    }
+
+    return buf + 1;
+}
+
+
+static char *ngx_http_log_time(ngx_http_request_t *r, char *buf, uintptr_t data)
+{
+    ngx_tm_t  tm;
+
+    ngx_localtime(&tm);
+
+    return buf + ngx_snprintf(buf, sizeof("28/Sep/1970:12:00:00"),
+                              "%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);
+}
+
+
+static char *ngx_http_log_request(ngx_http_request_t *r, char *buf,
+                                  uintptr_t data)
+{
+    if (buf == NULL) {
+        /* find the request line length */
+        return (char *) r->request_line.len;
+    }
+
+    return ngx_cpymem(buf, r->request_line.data, r->request_line.len);
+}
+
+
+static char *ngx_http_log_status(ngx_http_request_t *r, char *buf,
+                                 uintptr_t data)
+{
+    return buf + ngx_snprintf(buf, 4, "%d", r->headers_out.status);
+}
+
+
+static char *ngx_http_log_length(ngx_http_request_t *r, char *buf,
+                                 uintptr_t data)
+{
+    return buf + ngx_snprintf(buf, NGX_OFF_LEN + 1, OFF_FMT,
+                              r->connection->sent);
+}
+
+
+static char *ngx_http_log_header_in(ngx_http_request_t *r, char *buf,
+                                    uintptr_t data)
+{
+    int                 i;
+    ngx_str_t          *s;
+    ngx_table_elt_t    *h;
+    ngx_http_log_op_t  *op;
+
+    if (r) {
+        h = *(ngx_table_elt_t **) ((char *) &r->headers_in + data);
+
+        if (h == NULL) {
+
+            /* no header */
+
+            if (buf) {
+                *buf = '-';
+            }
+
+            return buf + 1;
+        }
+
+        if (buf == NULL) {
+            /* find the header length */
+            return (char *) h->value.len;
+        }
+
+        return ngx_cpymem(buf, h->value.data, h->value.len);
+    }
+
+    /* find an offset while a format string compilation */
+
+    op = (ngx_http_log_op_t *) buf;
+    s = (ngx_str_t *) data;
+
+    op->len = 0;
+
+    for (i = 0; ngx_http_headers_in[i].name.len != 0; i++) {
+        if (ngx_http_headers_in[i].name.len != s->len) {
+            continue;
+        }
+
+        if (ngx_strncasecmp(ngx_http_headers_in[i].name.data, s->data, s->len)
+                                                                          == 0)
+        {
+            op->op = ngx_http_log_header_in;
+            op->data = ngx_http_headers_in[i].offset;
+            return NULL;
+        }
+    }
+
+    op->op = ngx_http_log_unknown_header_in;
+    op->data = (uintptr_t) s;
+
+    return NULL;
+}
+
+
+static char *ngx_http_log_unknown_header_in(ngx_http_request_t *r, char *buf,
+                                            uintptr_t data)
 {
-    ngx_http_log_conf_t  *conf;
+    int               i;
+    ngx_str_t        *s;
+    ngx_table_elt_t  *h;
+
+    s = (ngx_str_t *) data;
+
+    h = r->headers_in.headers->elts;
+    for (i = 0; i < r->headers_in.headers->nelts; i++) {
+        if (h[i].key.len != s->len) {
+            continue;
+        }
+
+        if (ngx_strncasecmp(h[i].key.data, s->data, s->len) == 0) {
+            if (buf == NULL) {
+                /* find the header length */
+                return (char *) h[i].value.len;
+            }
+
+            return ngx_cpymem(buf, h[i].value.data, h[i].value.len);
+        }
+    }
+
+    /* no header */
+
+    if (buf) {
+        *buf = '-';
+    }
+
+    return buf + 1;
+}
+
+
+static char *ngx_http_log_header_out(ngx_http_request_t *r, char *buf,
+                                     uintptr_t data)
+{
+    int                 i;
+    ngx_str_t          *s;
+    ngx_table_elt_t    *h;
+    ngx_http_log_op_t  *op;
+
+    if (r) {
+        h = *(ngx_table_elt_t **) ((char *) &r->headers_out + data);
+
+        if (h == NULL) {
+
+            /* no header */
+
+            if (data == offsetof(ngx_http_headers_out_t, server)) {
+                if (buf == NULL) {
+                    return (char *) (sizeof(NGINX_VER) - 1);
+                }
+                return ngx_cpymem(buf, NGINX_VER, sizeof(NGINX_VER) - 1);
+            }
+
+            if (buf) {
+                *buf = '-';
+            }
+
+            return buf + 1;
+        }
+
+        if (buf == NULL) {
+            /* find the header length */
+            return (char *) h->value.len;
+        }
+
+        return ngx_cpymem(buf, h->value.data, h->value.len);
+    }
+
+    /* find an offset while a format string compilation */
+
+    op = (ngx_http_log_op_t *) buf;
+    s = (ngx_str_t *) data;
+
+    op->len = 0;
+
+    for (i = 0; ngx_http_headers_out[i].name.len != 0; i++) {
+        if (ngx_http_headers_out[i].name.len != s->len) {
+            continue;
+        }
+
+        if (ngx_strncasecmp(ngx_http_headers_out[i].name.data, s->data, s->len)
+                                                                          == 0)
+        {
+            op->op = ngx_http_log_header_out;
+            op->data = ngx_http_headers_out[i].offset;
+            return NULL;
+        }
+    }
 
-    ngx_test_null(conf, ngx_pcalloc(cf->pool, sizeof(ngx_http_log_conf_t)),
+    op->op = ngx_http_log_unknown_header_out;
+    op->data = (uintptr_t) s;
+
+    return NULL;
+}
+
+
+static char *ngx_http_log_unknown_header_out(ngx_http_request_t *r, char *buf,
+                                             uintptr_t data)
+{
+    int               i;
+    ngx_str_t        *s;
+    ngx_table_elt_t  *h;
+
+    s = (ngx_str_t *) data;
+
+    h = r->headers_out.headers->elts;
+    for (i = 0; i < r->headers_out.headers->nelts; i++) {
+        if (h[i].key.len != s->len) {
+            continue;
+        }
+
+        if (ngx_strncasecmp(h[i].key.data, s->data, s->len) == 0) {
+            if (buf == NULL) {
+                /* find the header length */
+                return (char *) h[i].value.len;
+            }
+
+            return ngx_cpymem(buf, h[i].value.data, h[i].value.len);
+        }
+    }
+
+    /* no header */
+
+    if (buf) {
+        *buf = '-';
+    }
+
+    return buf + 1;
+}
+
+
+static void *ngx_http_log_create_main_conf(ngx_conf_t *cf)
+{
+    ngx_http_log_main_conf_t  *conf;
+
+    char       *rc;
+    ngx_str_t  *value;
+
+    if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t)))) {
+        return NGX_CONF_ERROR;
+    }
+
+    ngx_init_array(conf->formats, cf->pool, 5, sizeof(ngx_http_log_fmt_t),
+                  NGX_CONF_ERROR);
+
+    cf->args->nelts = 0;
+
+    if (!(value = ngx_push_array(cf->args))) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (!(value = ngx_push_array(cf->args))) {
+        return NGX_CONF_ERROR;
+    }
+
+    value->len = sizeof("combined") - 1;
+    value->data = "combined";
+
+    if (!(value = ngx_push_array(cf->args))) {
+        return NGX_CONF_ERROR;
+    }
+
+    *value = ngx_http_combined_fmt;
+
+    rc = ngx_http_log_set_format(cf, NULL, conf);
+    if (rc != NGX_CONF_OK) {
+        return rc;
+    }
+
+    return conf;
+}
+
+
+static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf)
+{
+    ngx_http_log_loc_conf_t  *conf;
+
+    ngx_test_null(conf, ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t)),
                   NGX_CONF_ERROR);
 
     return conf;
 }
 
 
-static char *ngx_http_log_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+static char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent,
+                                         void *child)
 {
-    ngx_http_log_conf_t *prev = parent;
-    ngx_http_log_conf_t *conf = child;
+    ngx_http_log_loc_conf_t *prev = parent;
+    ngx_http_log_loc_conf_t *conf = child;
+
+    ngx_http_log_t            *log;
+    ngx_http_log_fmt_t        *fmt;
+    ngx_http_log_main_conf_t  *lmcf;
+
+    if (conf->logs == NULL) {
+        if (prev->logs) {
+            conf->logs = prev->logs;
+
+        } else {
 
-    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);
+            conf->logs = ngx_create_array(cf->pool, 2, sizeof(ngx_http_log_t));
+            if (conf->logs == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            if (!(log = ngx_push_array(conf->logs))) {
+                return NGX_CONF_ERROR;
+            }
+
+            log->file = ngx_conf_open_file(cf->cycle, &http_access_log);
+            if (log->file == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            lmcf = ngx_http_conf_module_main_conf(cf, ngx_http_log_module);
+            fmt = lmcf->formats.elts;
+            /* the default "combined" format */
+            log->ops = fmt[0].ops;
         }
     }
 
@@ -189,14 +536,238 @@ static char *ngx_http_log_merge_conf(ngx
 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_http_log_loc_conf_t *llcf = conf;
 
-    ngx_str_t  *value;
+    int                        i;
+    ngx_str_t                 *value, name;
+    ngx_http_log_t            *log;
+    ngx_http_log_fmt_t        *fmt;
+    ngx_http_log_main_conf_t  *lmcf;
+
+    if (llcf->logs == NULL) {
+        if (!(llcf->logs = ngx_create_array(cf->pool, 2,
+                                            sizeof(ngx_http_log_t)))) {
+            return NGX_CONF_ERROR;
+        }
+    }
 
     value = cf->args->elts;
+    lmcf = ngx_http_conf_module_main_conf(cf, ngx_http_log_module);
 
-    ngx_test_null(lcf->file, ngx_conf_open_file(cf->cycle, &value[1]),
-                  NGX_CONF_ERROR);
+    if (!(log = ngx_push_array(llcf->logs))) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (!(log->file = ngx_conf_open_file(cf->cycle, &value[1]))) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (cf->args->nelts == 3) {
+        name = value[2];
+    } else {
+        name.len = sizeof("combined") - 1;
+        name.data = "combined";
+    }
+
+    fmt = lmcf->formats.elts;
+    for (i = 0; i < lmcf->formats.nelts; i++) {
+        if (fmt[i].name.len == name.len
+            && ngx_strcasecmp(fmt[i].name.data, name.data) == 0)
+        {
+            log->ops = fmt[i].ops;
+            return NGX_CONF_OK;
+        }
+    }
 
     return NGX_CONF_OK;
 }
+
+
+static char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd,
+                                     void *conf)
+{
+    ngx_http_log_main_conf_t *lmcf = conf;
+
+    int                         s, f, invalid;
+    char                       *data, *p, *fname;
+    size_t                      i, len, fname_len;
+    ngx_str_t                  *value, arg, *a;
+    ngx_http_log_op_t          *op;
+    ngx_http_log_fmt_t         *fmt;
+    ngx_http_log_op_name_t     *name;
+
+    value = cf->args->elts;
+#if 0
+    lmcf = ngx_http_conf_module_main_conf(cf, ngx_http_log_module);
+#endif
+
+    fmt = lmcf->formats.elts;
+    for (f = 0; f < lmcf->formats.nelts; f++) {
+        if (fmt[f].name.len == value[1].len
+            && ngx_strcmp(fmt->name.data, value[1].data) == 0)
+        {
+            return "duplicate \"log_format\" name";
+        }
+    }
+
+    if (!(fmt = ngx_push_array(&lmcf->formats))) {
+        return NGX_CONF_ERROR;
+    }
+
+    fmt->name = value[1];
+
+    if (!(fmt->ops = ngx_create_array(cf->pool, 20,
+                                      sizeof(ngx_http_log_op_t)))) {
+        return NGX_CONF_ERROR;
+    }
+
+    invalid = 0;
+    data = NULL;
+
+    for (s = 2; s < cf->args->nelts && !invalid; s++) {
+
+        i = 0;
+
+        while (i < value[s].len) {
+
+            if (!(op = ngx_push_array(fmt->ops))) {
+                return NGX_CONF_ERROR;
+            }
+
+            data = &value[s].data[i];
+
+            if (value[s].data[i] == '%') {
+                i++;
+
+                if (i == value[s].len) {
+                    invalid = 1;
+                    break;
+                }
+
+                if (value[s].data[i] == '{') {
+                    i++;
+
+                    arg.data = &value[s].data[i];
+
+                    while (i < value[s].len && value[s].data[i] != '}') {
+                        i++;
+                    }
+
+                    arg.len = &value[s].data[i] - arg.data;
+
+                    if (i == value[s].len || arg.len == 0) {
+                        invalid = 1;
+                        break;
+                    }
+
+                    i++;
+
+                } else {
+                    arg.len = 0;
+                }
+
+                fname = &value[s].data[i];
+
+                while (i < value[s].len
+                       && value[s].data[i] >= 'a'
+                       && value[s].data[i] <= 'z')
+                {
+                    i++;
+                }
+
+                fname_len = &value[s].data[i] - fname;
+
+                if (fname_len == 0) {
+                    invalid = 1;
+                    break;
+                }
+
+                for (name = ngx_http_log_fmt_ops; name->name.len; name++) {
+                    if (name->name.len == fname_len
+                        && ngx_strncmp(name->name.data, fname, fname_len) == 0)
+                    {
+                        if (name->len != NGX_HTTP_LOG_ARG) {
+                            if (arg.len) {
+                                fname[fname_len] = '\0';
+                                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                               "\"%s\" must not have argument",
+                                               data);
+                                return NGX_CONF_ERROR;
+                            }
+
+                            op->len = name->len;
+                            op->op = name->op;
+                            op->data = 0;
+
+                            break;
+                        }
+
+                        if (arg.len == 0) {
+                            fname[fname_len] = '\0';
+                            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                               "\"%s\" requires argument", 
+                                               data);
+                            return NGX_CONF_ERROR;
+                        }
+
+                        if (!(a = ngx_palloc(cf->pool, sizeof(ngx_str_t)))) {
+                            return NGX_CONF_ERROR;
+                        }
+
+                        *a = arg;
+                        name->op(NULL, (char *) op, (uintptr_t) a);
+
+                        break;
+                    }
+                }
+
+                if (name->name.len == 0) {
+                    invalid = 1;
+                    break;
+                }
+
+            } else {
+                i++;
+
+                while (i < value[s].len && value[s].data[i] != '%') {
+                    i++;
+                }
+
+                len = &value[s].data[i] - data;
+
+                if (len) {
+
+                    op->len = len;
+
+                    if (len <= sizeof(uintptr_t)) {
+                        op->op = NGX_HTTP_LOG_COPY_SHORT;
+                        op->data = 0;
+
+                        while (len--) {
+                            op->data <<= 8;
+                            op->data |= data[len];
+                        }
+
+                    } else {
+                        op->op = NGX_HTTP_LOG_COPY_LONG;
+
+                        if (!(p = ngx_palloc(cf->pool, len))) {
+                            return NGX_CONF_ERROR;
+                        }
+
+                        ngx_memcpy(p, data, len);
+                        op->data = (uintptr_t) p;
+                    }
+                }
+            }
+        }
+    }
+
+    if (invalid) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%s\"", data);
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
rename from src/http/modules/ngx_http_log_handler.h
rename to src/http/ngx_http_log_handler.h
--- a/src/http/modules/ngx_http_log_handler.h
+++ b/src/http/ngx_http_log_handler.h
@@ -7,35 +7,53 @@
 #include <ngx_http.h>
 
 
-typedef struct {
-    int   dummy;
-} ngx_http_log_conf_t;
+typedef char *(*ngx_http_log_op_pt) (ngx_http_request_t *r, char *buf,
+                                     uintptr_t data);
+
+#define NGX_HTTP_LOG_COPY_SHORT  (ngx_http_log_op_pt) 0
+#define NGX_HTTP_LOG_COPY_LONG   (ngx_http_log_op_pt) -1
+
+#define NGX_HTTP_LOG_ARG         (u_int) -1
+
+/* STUB */
+#define NGX_INT32_LEN      sizeof("4294967296") - 1
+#define NGX_OFF_LEN        sizeof("18446744073709551616") - 1
 
 
-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 {
+    size_t               len;
+    ngx_http_log_op_pt   op;
+    uintptr_t            data;
+} ngx_http_log_op_t;
 
 
 typedef struct {
-    int      type;
-    int      size;
-    char  *(*handler) (ngx_http_request_t *r, char *p);
-    int      offset;
-} ngx_http_log_code_t;
+    ngx_str_t            name;
+    ngx_array_t         *ops;        /* array of ngx_http_log_op_t */
+} ngx_http_log_fmt_t;
+
+
+typedef struct {
+    ngx_str_t            name;
+    size_t               len;
+    ngx_http_log_op_pt   op;
+} ngx_http_log_op_name_t;
+
+
+typedef struct {
+    ngx_array_t          formats;    /* array of ngx_http_log_fmt_t */
+} ngx_http_log_main_conf_t;
+
+
+typedef struct {
+    ngx_open_file_t     *file;
+    ngx_array_t         *ops;        /* array of ngx_http_log_op_t */
+} ngx_http_log_t;
+
+
+typedef struct {
+    ngx_array_t         *logs;       /* array of ngx_http_log_t */
+} ngx_http_log_loc_conf_t;
 
 
 #endif /* _NGX_HTTP_LOG_HANDLER_H_INCLUDED_ */
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -225,6 +225,7 @@ ngx_log_debug(rev->log, "IN: %08x" _ in_
         return;
     }
 
+    /* TODO: ngx_init_table */
     if (!(r->headers_out.headers = ngx_create_table(r->pool, 20))) {
         ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         ngx_http_close_connection(c);
@@ -416,6 +417,7 @@ static void ngx_http_process_request_lin
         lctx = c->log->data;
         lctx->action = "reading client request headers";
         lctx->url = r->unparsed_uri.data;
+        /* TODO: ngx_init_table */
         r->headers_in.headers = ngx_create_table(r->pool, 20);
 
         if (cscf->large_client_header
@@ -1068,7 +1070,8 @@ static void ngx_http_set_keepalive(ngx_h
 
     if (h->pos < h->last) {
 
-        /* Pipelined request.
+        /*
+         * Pipelined request.
          *
          * We do not know here whether a pipelined request is complete
          * so if the large client headers are not enabled
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -74,6 +74,7 @@ typedef struct {
     ngx_table_elt_t  *connection;
     ngx_table_elt_t  *if_modified_since;
     ngx_table_elt_t  *user_agent;
+    ngx_table_elt_t  *referer;
 
     ngx_table_elt_t  *content_length;
     ngx_table_elt_t  *accept_encoding;
@@ -202,6 +203,8 @@ struct ngx_http_request_s {
     unsigned             bypass_cache:1;
     unsigned             no_cache:1;
 
+    unsigned             error_page:1;
+
 #if 0
     unsigned             cachable:1;
 #endif
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -168,9 +168,10 @@ static ngx_str_t error_pages[] = {
 
 int ngx_http_special_response_handler(ngx_http_request_t *r, int error)
 {
-    int                        err, rc;
+    int                        err, rc, i;
     ngx_hunk_t                *h;
     ngx_chain_t               *out, **ll, *cl;
+    ngx_http_err_page_t       *err_page;
     ngx_http_core_loc_conf_t  *clcf;
 
     rc = ngx_http_discard_body(r);
@@ -181,19 +182,6 @@ int ngx_http_special_response_handler(ng
 
     r->headers_out.status = error;
 
-    if (error < NGX_HTTP_BAD_REQUEST) {
-        /* 3XX */
-        err = error - NGX_HTTP_MOVED_PERMANENTLY;
-
-    } else if (error < NGX_HTTP_INTERNAL_SERVER_ERROR) {
-        /* 4XX */
-        err = error - NGX_HTTP_BAD_REQUEST + 3;
-
-    } else {
-        /* 5XX */
-        err = error - NGX_HTTP_INTERNAL_SERVER_ERROR + 3 + 17;
-    }
-
     if (r->keepalive != 0) {
         switch (error) {
             case NGX_HTTP_BAD_REQUEST:
@@ -213,6 +201,31 @@ int ngx_http_special_response_handler(ng
         }
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    if (!r->error_page && clcf->error_pages) {
+        err_page = clcf->error_pages->elts;
+        for (i = 0; i < clcf->error_pages->nelts; i++) {
+            if (err_page[i].code == error) {
+                r->error_page = 1;
+                return ngx_http_internal_redirect(r, &err_page[i].uri, NULL);
+            }
+        }
+    }
+
+    if (error < NGX_HTTP_BAD_REQUEST) {
+        /* 3XX */
+        err = error - NGX_HTTP_MOVED_PERMANENTLY;
+
+    } else if (error < NGX_HTTP_INTERNAL_SERVER_ERROR) {
+        /* 4XX */
+        err = error - NGX_HTTP_BAD_REQUEST + 3;
+
+    } else {
+        /* 5XX */
+        err = error - NGX_HTTP_INTERNAL_SERVER_ERROR + 3 + 17;
+    }
+
     if (error_pages[err].len) {
         r->headers_out.content_length_n = error_pages[err].len
                                           + sizeof(error_tail) - 1
@@ -272,8 +285,6 @@ int ngx_http_special_response_handler(ng
     ngx_alloc_link_and_set_hunk(cl, h, r->pool, NGX_ERROR);
     ngx_chain_add_link(out, ll, cl);
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
     if (clcf->msie_padding
         && r->http_version >= NGX_HTTP_VERSION_10
         && error >= NGX_HTTP_BAD_REQUEST
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -63,6 +63,7 @@ ssize_t ngx_write_chain_to_file(ngx_file
 #define ngx_is_file(sb)          (S_ISREG(sb.st_mode))
 #define ngx_file_size(sb)        sb.st_size
 #define ngx_file_mtime(sb)       sb.st_mtime
+#define ngx_file_uniq(sb)        sb.st_ino
 
 
 #endif /* _NGX_FILES_H_INCLUDED_ */
--- a/src/os/unix/ngx_types.h
+++ b/src/os/unix/ngx_types.h
@@ -10,6 +10,8 @@
 
 typedef int          ngx_fd_t;
 typedef struct stat  ngx_file_info_t;
+typedef ino_t        ngx_file_uniq_t;
+
 
 
 #endif /* _NGX_TYPES_H_INCLUDED_ */
--- a/src/os/win32/ngx_files.h
+++ b/src/os/win32/ngx_files.h
@@ -6,7 +6,7 @@
 #include <ngx_core.h>
 
 
-/* INVALID_FILE_ATTRIBUTES specified but never defined at least in VC6SP2 */
+/* INVALID_FILE_ATTRIBUTES specified but not defined at least in MSVC6SP2 */
 #ifndef INVALID_FILE_ATTRIBUTES
 #define INVALID_FILE_ATTRIBUTES     0xFFFFFFFF
 #endif
@@ -67,6 +67,7 @@ int ngx_file_type(char *filename, ngx_fi
 #define ngx_file_size(fi)                                                   \
             (((off_t) fi.nFileSizeHigh << 32) | fi.nFileSizeLow)
 
+#define ngx_file_uniq(fi)   (*(ngx_file_uniq_t *) &fi.nFileIndexHigh)
 
 /* There are 134774 days between 1 Jan 1970 and 1 Jan 1601,
    11644473600 seconds or 11644473600,000,000,0 100-nanosecond intervals */
--- a/src/os/win32/ngx_stat.c
+++ b/src/os/win32/ngx_stat.c
@@ -1,7 +1,7 @@
 
 #include <ngx_config.h>
+#include <ngx_core.h>
 
-#include <ngx_stat.h>
 
 int ngx_file_type(char *file, ngx_file_info_t *sb)
 {
deleted file mode 100644
--- a/src/os/win32/ngx_stat.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef _NGX_STAT_H_INCLUDED_
-#define _NGX_STAT_H_INCLUDED_
-
-
-#include <windows.h>
-
-/* INVALID_FILE_ATTRIBUTES specified but never defined at least in VC6SP2 */
-#ifndef INVALID_FILE_ATTRIBUTES
-#define INVALID_FILE_ATTRIBUTES  0xFFFFFFFF
-#endif
-
-typedef BY_HANDLE_FILE_INFORMATION  ngx_file_info_t;
-
-
-#define ngx_file_type_n          "GetFileAttributes"
-
-#define ngx_is_dir(fi)          (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-
-#define ngx_stat_n               "GetFileAttributes"
-
-#define ngx_fstat(file, fd, sb)  ngx_stat(file, sb)
-#define ngx_fstat_n              "GetFileAttributes"
-
-
-/*
-int ngx_stat(char *file, ngx_stat_t *sb);
-*/
-
-
-#endif /* _NGX_STAT_H_INCLUDED_ */
--- a/src/os/win32/ngx_types.h
+++ b/src/os/win32/ngx_types.h
@@ -6,21 +6,9 @@
 #include <ngx_core.h>
 
 
-typedef unsigned __int32            u_int32_t;
-typedef __int64                     int64_t;
-
-typedef int                         ssize_t;
-typedef long                        time_t;
-
 typedef HANDLE                      ngx_fd_t;
-typedef unsigned __int64            off_t;
 typedef BY_HANDLE_FILE_INFORMATION  ngx_file_info_t;
-
-
-#define OFF_FMT    "%I64d"
-#define SIZE_FMT   "%d"
-#define SIZEX_FMT  "%x"
-#define PID_FMT    "%d"
+typedef uint64_t                    ngx_file_uniq_t;
 
 
 #endif /* _NGX_TYPES_H_INCLUDED_ */
--- a/src/os/win32/ngx_win32_config.h
+++ b/src/os/win32/ngx_win32_config.h
@@ -18,6 +18,30 @@
 #define ngx_inline   __inline
 
 
+#if 0
+typedef unsigned __int32  uint32_t;
+#else
+typedef unsigned int      uint32_t;
+#endif
+typedef __int64           int64_t;
+typedef unsigned __int64  uint64_t;
+typedef u_int             uintptr_t;
+
+typedef int               ssize_t;
+typedef long              time_t;
+typedef unsigned __int64  off_t;
+
+
+#define OFF_FMT    "%I64d"
+#define SIZE_FMT   "%d"
+#define SIZEX_FMT  "%x"
+#define PID_FMT    "%d"
+
+
+/* STUB */
+typedef uint32_t     u_int32_t;
+
+
 #ifndef HAVE_INHERITED_NONBLOCK
 #define HAVE_INHERITED_NONBLOCK  1
 #endif