changeset 190:02a715e85df1

nginx-0.0.1-2003-11-19-00:34:08 import
author Igor Sysoev <igor@sysoev.ru>
date Tue, 18 Nov 2003 21:34:08 +0000
parents c966c09be66b
children 71ce40b3c37b
files src/core/nginx.c src/core/ngx_conf_file.h src/core/ngx_file.c src/core/ngx_file.h src/core/ngx_garbage_collector.c src/core/ngx_garbage_collector.h src/core/ngx_times.c src/core/ngx_times.h src/http/modules/ngx_http_static_handler.c src/http/modules/proxy/ngx_http_proxy_cache.c src/http/modules/proxy/ngx_http_proxy_handler.c src/http/modules/proxy/ngx_http_proxy_handler.h src/http/modules/proxy/ngx_http_proxy_header.c src/http/modules/proxy/ngx_http_proxy_upstream.c src/http/ngx_http.h src/http/ngx_http_cache.c src/http/ngx_http_cache.h src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_request.h src/os/unix/ngx_files.c src/os/unix/ngx_freebsd_config.h
diffstat 22 files changed, 261 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -6,7 +6,8 @@
 
 
 /* STUB */
-void stub_init(ngx_log_t *log);
+void stub_init(ngx_cycle_t *cycle);
+
 
 
 static ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle, ngx_log_t *log);
@@ -97,10 +98,6 @@ int main(int argc, char *const *argv)
         return 1;
     }
 
-#if 0
-    stub_init(log);
-#endif
-
     ngx_max_module = 0;
     for (i = 0; ngx_modules[i]; i++) {
         ngx_modules[i]->index = ngx_max_module++;
@@ -261,6 +258,18 @@ static ngx_cycle_t *ngx_init_cycle(ngx_c
     cycle->old_cycle = old_cycle;
 
 
+    n = old_cycle ? old_cycle->pathes.nelts : 10;
+    cycle->pathes.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *));
+    if (cycle->pathes.elts == NULL) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+    cycle->pathes.nelts = 0;
+    cycle->pathes.size = sizeof(ngx_path_t *);
+    cycle->pathes.nalloc = n;
+    cycle->pathes.pool = pool;
+
+
     n = old_cycle ? old_cycle->open_files.nelts : 20;
     cycle->open_files.elts = ngx_pcalloc(pool, n * sizeof(ngx_open_file_t));
     if (cycle->open_files.elts == NULL) {
@@ -455,6 +464,8 @@ ngx_log_debug(log, "OPEN: %d:%s" _ file[
         }
     }
 
+    stub_init(cycle);
+
     if (old_cycle == NULL) {
         return cycle;
     }
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -87,6 +87,7 @@ struct ngx_cycle_s {
     ngx_log_t         *log;
     ngx_array_t        listening;
     ngx_array_t        open_files;
+    ngx_array_t        pathes;
 
     int                connection_n;
     ngx_connection_t  *connections;
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -202,8 +202,14 @@ char *ngx_conf_set_path_slot(ngx_conf_t 
         return "is duplicate";
     }
 
-    ngx_test_null(path, ngx_pcalloc(cf->pool, sizeof(ngx_path_t)), NULL);
+    /* TODO: check duplicate in cf->cycle->pathes */
 
+    ngx_test_null(path, ngx_pcalloc(cf->pool, sizeof(ngx_path_t)),
+                  NGX_CONF_ERROR);
+
+    *pp = path;
+
+    ngx_test_null(pp, ngx_push_array(&cf->cycle->pathes), NGX_CONF_ERROR);
     *pp = path;
 
     value = (ngx_str_t *) cf->args->elts;
@@ -225,5 +231,7 @@ char *ngx_conf_set_path_slot(ngx_conf_t 
         path->level[i++] = 0;
     }
 
+    path->gc_handler = cmd->post;
+
     return NGX_CONF_OK;
 }
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -5,6 +5,10 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 
+typedef struct ngx_path_s  ngx_path_t;
+
+#include <ngx_garbage_collector.h>
+
 
 struct ngx_file_s {
     ngx_fd_t         fd;
@@ -12,6 +16,7 @@ struct ngx_file_s {
     ngx_file_info_t  info;
 
     off_t            offset;
+    off_t            sys_offset;
 
     ngx_log_t       *log;
 
@@ -20,11 +25,12 @@ struct ngx_file_s {
 
 #define NGX_MAX_PATH_LEVEL  3
 
-typedef struct {
-    ngx_str_t  name;
-    int        len;
-    int        level[3];
-} ngx_path_t;
+struct ngx_path_s {
+    ngx_str_t           name;
+    int                 len;
+    int                 level[3];
+    ngx_gc_handler_pt   gc_handler;
+};
 
 
 typedef struct {
--- a/src/core/ngx_garbage_collector.c
+++ b/src/core/ngx_garbage_collector.c
@@ -1,24 +1,11 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-
-
-typedef struct ngx_gc_s  ngx_gc_t;
-
-typedef int (*ngx_gc_handler_pt) (ngx_gc_t *ctx, ngx_str_t *name,
-                                  ngx_dir_t *dir);
+#include <ngx_garbage_collector.h>
 
 
-static int ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name,
-                                              ngx_dir_t *dir);
-
-struct ngx_gc_s {
-    ngx_path_t         *path;
-    u_int               deleted;
-    off_t               freed;
-    ngx_gc_handler_pt   handler;
-    ngx_log_t          *log;
-};
+int ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name,
+                                       ngx_dir_t *dir);
 
 
 static int ngx_collect_garbage(ngx_gc_t *ctx, ngx_str_t *dname, int level);
@@ -64,27 +51,20 @@ void garbage_collector()
 #endif
 
 
-void stub_init(ngx_log_t *log)
+void stub_init(ngx_cycle_t *cycle)
 {
-    ngx_gc_t    *ctx;
-    ngx_path_t   path;
-
-    if (!(ctx = ngx_alloc(sizeof(ngx_gc_t), log))) {
-        return;
-    }
+    int           i;
+    ngx_gc_t      ctx;
+    ngx_path_t  **path;
 
-    path.name.len = 4;
-    path.name.data = "temp";
-    path.len = 5;
-    path.level[0] = 1;
-    path.level[1] = 2;
-    path.level[2] = 0;
+    path = cycle->pathes.elts;
+    for (i = 0; i < cycle->pathes.nelts; i++) {
+        ctx.path = path[i];
+        ctx.log = cycle->log;
+        ctx.handler = path[i]->gc_handler;
 
-    ctx->path = &path;
-    ctx->log = log;
-    ctx->handler = ngx_garbage_collector_temp_handler;
-
-    ngx_collect_garbage(ctx, &path.name, 0);
+        ngx_collect_garbage(&ctx, &path[i]->name, 0);
+    }
 }
 
 
@@ -254,8 +234,8 @@ ngx_log_debug(ctx->log, "file %s" _ fnam
 }
 
 
-static int ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name,
-                                              ngx_dir_t *dir)
+int ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name,
+                                       ngx_dir_t *dir)
 {
     /*
      * we use mtime only and do not use atime because:
@@ -279,5 +259,6 @@ static int ngx_garbage_collector_temp_ha
 
     ctx->deleted++;
     ctx->freed += ngx_de_size(dir);
+
     return NGX_OK;
 }
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_garbage_collector.h
@@ -0,0 +1,24 @@
+#ifndef _NGX_GARBAGE_COLLECTOR_H_INCLUDED_
+#define _NGX_GARBAGE_COLLECTOR_H_INCLUDED_
+
+
+typedef struct ngx_gc_s  ngx_gc_t;
+
+typedef int (*ngx_gc_handler_pt) (ngx_gc_t *ctx, ngx_str_t *name,
+                                  ngx_dir_t *dir);
+
+
+struct ngx_gc_s {
+    ngx_path_t         *path;
+    u_int               deleted;
+    off_t               freed;
+    ngx_gc_handler_pt   handler;
+    ngx_log_t          *log;
+};
+
+
+int ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name, 
+                                       ngx_dir_t *dir);
+
+
+#endif /* _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ */
--- a/src/core/ngx_times.c
+++ b/src/core/ngx_times.c
@@ -41,12 +41,6 @@ void ngx_init_time()
 }
 
 
-time_t ngx_time()
-{
-    return ngx_cached_time;
-}
-
-
 void ngx_time_update()
 {
     ngx_tm_t  tm;
--- a/src/core/ngx_times.h
+++ b/src/core/ngx_times.h
@@ -7,11 +7,12 @@
 
 
 void ngx_init_time();
-time_t ngx_time();
 void ngx_time_update();
 size_t ngx_http_time(char *buf, time_t t);
 void ngx_gmtime(time_t t, ngx_tm_t *tp);
 
+#define ngx_time()   ngx_cached_time
+
 
 extern time_t     ngx_cached_time;
 extern ngx_str_t  ngx_cached_err_log_time;
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -200,8 +200,10 @@ ngx_log_debug(r->connection->log, "HTTP 
 
         *last++ = '/';
         *last = '\0';
+#if 0
         r->headers_out.location->key.len = 8;
         r->headers_out.location->key.data = "Location" ;
+#endif
         r->headers_out.location->value.len = last - location;
         r->headers_out.location->value.data = location;
 
--- a/src/http/modules/proxy/ngx_http_proxy_cache.c
+++ b/src/http/modules/proxy/ngx_http_proxy_cache.c
@@ -55,6 +55,7 @@ int ngx_http_proxy_get_cached_response(n
     p->header_in->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module;
 
     c->ctx.buf = p->header_in; 
+    c->ctx.log = r->connection->log;
 
     return ngx_http_proxy_process_cached_response(p,
                                           ngx_http_cache_get_file(r, &c->ctx));
@@ -341,8 +342,7 @@ static void ngx_http_proxy_cache_look_co
 
     *ctx = p->cache->ctx;
 
-    rc = ngx_http_cache_open_file(p->request, ctx,
-                                  ngx_file_uniq(&p->cache->ctx.file.info));
+    rc = ngx_http_cache_open_file(ctx, ngx_file_uniq(&p->cache->ctx.file.info));
 
     if (rc == NGX_HTTP_CACHE_THE_SAME) {
         p->try_busy_lock = 1;
--- a/src/http/modules/proxy/ngx_http_proxy_handler.c
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.c
@@ -114,14 +114,14 @@ static ngx_command_t  ngx_http_proxy_com
       ngx_conf_set_path_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_proxy_loc_conf_t, cache_path),
-      NULL },
+      ngx_garbage_collector_http_cache_handler },
 
     { ngx_string("proxy_temp_path"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
       ngx_conf_set_path_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_proxy_loc_conf_t, temp_path),
-      NULL },
+      ngx_garbage_collector_temp_handler },
 
     { ngx_string("proxy_temp_file_write_size"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
@@ -255,6 +255,8 @@ ngx_http_header_t ngx_http_proxy_headers
                        offsetof(ngx_http_proxy_headers_in_t, content_length) },
     { ngx_string("Last-Modified"),
                         offsetof(ngx_http_proxy_headers_in_t, last_modified) },
+    { ngx_string("Location"),
+                             offsetof(ngx_http_proxy_headers_in_t, location) },
     { ngx_string("Accept-Ranges"),
                         offsetof(ngx_http_proxy_headers_in_t, accept_ranges) },
 
@@ -932,6 +934,7 @@ static char *ngx_http_proxy_set_pass(ngx
     clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
     lcf->upstream->location = &clcf->name;
     clcf->handler = ngx_http_proxy_handler;
+    clcf->auto_redirect = 1;
 
     return NULL;
 }
--- a/src/http/modules/proxy/ngx_http_proxy_handler.h
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.h
@@ -116,6 +116,7 @@ typedef struct {
     ngx_table_elt_t                 *content_type;
     ngx_table_elt_t                 *content_length;
     ngx_table_elt_t                 *last_modified;
+    ngx_table_elt_t                 *location;
     ngx_table_elt_t                 *accept_ranges;
 
     off_t                            content_length_n;
--- a/src/http/modules/proxy/ngx_http_proxy_header.c
+++ b/src/http/modules/proxy/ngx_http_proxy_header.c
@@ -5,6 +5,9 @@
 #include <ngx_http_proxy_handler.h>
 
 
+static int ngx_http_proxy_rewrite_location_header(ngx_http_proxy_ctx_t *p,
+                                                  ngx_table_elt_t *loc);
+
 int ngx_http_proxy_copy_header(ngx_http_proxy_ctx_t *p,
                                ngx_http_proxy_headers_in_t *headers_in)
 {
@@ -36,6 +39,16 @@ int ngx_http_proxy_copy_header(ngx_http_
             if (&h[i] == headers_in->server && !p->lcf->pass_server) {
                 continue;
             } 
+    
+            if (&h[i] == headers_in->location) {
+                if (ngx_http_proxy_rewrite_location_header(p, &h[i])
+                                                                  == NGX_ERROR)
+                {
+                    return NGX_ERROR;
+                }
+
+                continue;
+            } 
         }
     
         if (&h[i] == headers_in->content_type) {
@@ -79,3 +92,47 @@ int ngx_http_proxy_copy_header(ngx_http_
 
     return NGX_OK;
 }
+
+
+static int ngx_http_proxy_rewrite_location_header(ngx_http_proxy_ctx_t *p,
+                                                  ngx_table_elt_t *loc)
+{
+    char                            *last;
+    ngx_http_request_t              *r;
+    ngx_http_proxy_upstream_conf_t  *uc;
+
+    r = p->request;
+    uc = p->lcf->upstream;
+
+    r->headers_out.location = ngx_http_add_header(&r->headers_out,
+                                                  ngx_http_headers_out);
+    if (r->headers_out.location == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (uc->url.len > loc->value.len
+        || ngx_rstrncmp(loc->value.data, uc->url.data, uc->url.len) != 0)
+    {
+        *r->headers_out.location = *loc;
+        return NGX_OK;
+    }
+
+    /* TODO: proxy_reverse */
+
+    r->headers_out.location->value.len = uc->location->len
+                                         + (loc->value.len - uc->url.len) + 1;
+    r->headers_out.location->value.data =
+                       ngx_palloc(r->pool, r->headers_out.location->value.len); 
+
+    if (r->headers_out.location->value.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    last = ngx_cpymem(r->headers_out.location->value.data,
+                      uc->location->data, uc->location->len);
+
+    ngx_cpystrn(last, loc->value.data + uc->url.len,
+                loc->value.len - uc->url.len + 1);
+
+    return NGX_OK;
+}
--- a/src/http/modules/proxy/ngx_http_proxy_upstream.c
+++ b/src/http/modules/proxy/ngx_http_proxy_upstream.c
@@ -1182,8 +1182,8 @@ ngx_log_debug(p->request->connection->lo
     }
 
     if (p->request->connection->write->eof) {
-        ngx_http_proxy_finalize_request(p, status ? status:
-                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);
+        ngx_http_proxy_finalize_request(p, NGX_HTTP_CLIENT_CLOSED_REQUEST);
+        return;
     }
 
     if (status) {
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -4,6 +4,8 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
+#include <ngx_garbage_collector.h>
+
 #include <ngx_http_request.h>
 #include <ngx_http_config.h>
 #include <ngx_http_cache.h>
--- a/src/http/ngx_http_cache.c
+++ b/src/http/ngx_http_cache.c
@@ -3,6 +3,7 @@
 #include <ngx_core.h>
 #include <ngx_http.h>
 
+
 #include <md5.h>
 
 #if (HAVE_OPENSSL_MD5)
@@ -41,14 +42,13 @@ ngx_log_debug(r->connection->log, "FILE:
 
     /* TODO: look open files cache */
 
-    return ngx_http_cache_open_file(r, ctx, 0);
+    return ngx_http_cache_open_file(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)
+int ngx_http_cache_open_file(ngx_http_cache_ctx_t *ctx, ngx_file_uniq_t uniq)
 {
     ssize_t                   n;
     ngx_err_t                 err;
@@ -64,14 +64,14 @@ int ngx_http_cache_open_file(ngx_http_re
             return NGX_DECLINED;
         }
 
-        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
                       ngx_open_file_n " \"%s\" failed", ctx->file.name.data);
         return NGX_ERROR;
     }
 
     if (uniq) {
         if (ngx_fd_info(ctx->file.fd, &ctx->file.info) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+            ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
                           ngx_fd_info_n " \"%s\" failed", ctx->file.name.data);
 
             return NGX_ERROR;
@@ -79,7 +79,7 @@ int ngx_http_cache_open_file(ngx_http_re
 
         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_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
                               ngx_close_file_n " \"%s\" failed",
                               ctx->file.name.data);
             }
@@ -96,7 +96,7 @@ int ngx_http_cache_open_file(ngx_http_re
     }
 
     if (n <= ctx->header_size) {
-        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+        ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
                       "cache file \"%s\" is too small", ctx->file.name.data);
         return NGX_ERROR;
     }
@@ -108,17 +108,18 @@ int ngx_http_cache_open_file(ngx_http_re
     ctx->length = h->length;
 
     if (h->key_len > (size_t) (ctx->buf->last - ctx->buf->pos)) {
-        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+        ngx_log_error(NGX_LOG_ALERT, ctx->log, 0,
                       "cache file \"%s\" is probably invalid",
                       ctx->file.name.data);
         return NGX_DECLINED;
     }
 
-    if (h->key_len != ctx->key.len
-        || ngx_strncmp(h->key, ctx->key.data, h->key_len) != 0)
+    if (ctx->key.len
+        && (h->key_len != ctx->key.len
+            || ngx_strncmp(h->key, ctx->key.data, h->key_len) != 0))
     {
         h->key[h->key_len] = '\0';
-        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+        ngx_log_error(NGX_LOG_ALERT, ctx->log, 0,
                           "md5 collision: \"%s\" and \"%s\"",
                           h->key, ctx->key.data);
         return NGX_DECLINED;
@@ -127,7 +128,7 @@ int ngx_http_cache_open_file(ngx_http_re
     ctx->buf->last += n;
 
     if (ctx->expires < ngx_time()) {
-ngx_log_debug(r->connection->log, "EXPIRED");
+ngx_log_debug(ctx->log, "EXPIRED");
         return NGX_HTTP_CACHE_STALE;
     }
 
@@ -137,6 +138,50 @@ ngx_log_debug(r->connection->log, "EXPIR
 }
 
 
+int ngx_garbage_collector_http_cache_handler(ngx_gc_t *gc, ngx_str_t *name,
+                                             ngx_dir_t *dir)
+{
+    int                   rc;
+    char                  data[sizeof(ngx_http_cache_header_t)];
+    ngx_hunk_t            buf;
+    ngx_http_cache_ctx_t  ctx;
+
+    ctx.file.fd = NGX_INVALID_FILE;
+    ctx.file.name = *name;
+    ctx.file.log = gc->log;
+
+    ctx.header_size = sizeof(ngx_http_cache_header_t);
+    ctx.buf = &buf;
+    ctx.log = gc->log;
+    ctx.key.len = 0;
+
+    buf.type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP;
+    buf.pos = data;
+    buf.last = data;
+    buf.start = data;
+    buf.end = data + sizeof(ngx_http_cache_header_t);
+
+    rc = ngx_http_cache_open_file(&ctx, 0);
+
+    /* TODO: NGX_AGAIN */
+
+    if (rc != NGX_ERROR && rc != NGX_DECLINED && rc != NGX_HTTP_CACHE_STALE) {
+        return NGX_OK;
+    }
+
+    if (ngx_delete_file(name->data) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_CRIT, gc->log, ngx_errno,
+                      ngx_delete_file_n " \"%s\" failed", name->data);
+        return NGX_ERROR;
+    }
+
+    gc->deleted++;
+    gc->freed += ngx_de_size(dir);
+
+    return NGX_OK;
+}
+
+
 int ngx_http_cache_update_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx,
                                ngx_str_t *temp_file)
 {
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -43,6 +43,7 @@ typedef struct {
     off_t        length;
     ssize_t      header_size;
     size_t       file_start;
+    ngx_log_t   *log;
 } ngx_http_cache_ctx_t;
 
 
@@ -52,8 +53,9 @@ typedef struct {
 
 
 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_open_file(ngx_http_cache_ctx_t *ctx, ngx_file_uniq_t uniq);
+int ngx_garbage_collector_http_cache_handler(ngx_gc_t *gc, ngx_str_t *name,
+                                             ngx_dir_t *dir);
 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_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -369,16 +369,27 @@ static void ngx_http_run_phases(ngx_http
 int ngx_http_find_location_config(ngx_http_request_t *r)
 {
     int                            i, rc;
+    ngx_str_t                     *auto_redirect;
     ngx_http_core_loc_conf_t      *clcf, **clcfp;
     ngx_http_core_srv_conf_t      *cscf;
 
     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+    auto_redirect = NULL;
 
     clcfp = cscf->locations.elts;
     for (i = 0; i < cscf->locations.nelts; i++) {
 #if 0
 ngx_log_debug(r->connection->log, "trans: %s" _ clcfp[i]->name.data);
 #endif
+        if (clcfp[i]->auto_redirect
+            && r->uri.len == clcfp[i]->name.len - 1
+            && ngx_strncmp(r->uri.data, clcfp[i]->name.data,
+                           clcfp[i]->name.len - 1) == 0)
+        {
+            auto_redirect = &clcfp[i]->name;
+            continue;
+        }
+
         if (r->uri.len < clcfp[i]->name.len) {
             continue;
         }
@@ -386,6 +397,7 @@ ngx_log_debug(r->connection->log, "trans
         rc = ngx_strncmp(r->uri.data, clcfp[i]->name.data, clcfp[i]->name.len);
 
         if (rc < 0) {
+            /* locations are lexicographically sorted */
             break;
         }
 
@@ -406,6 +418,22 @@ ngx_log_debug(r->connection->log, "trans
         r->sendfile = 1;
     }
 
+    if (auto_redirect) {
+        if (!(r->headers_out.location =
+                   ngx_http_add_header(&r->headers_out, ngx_http_headers_out)))
+        {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+#if 0
+        r->headers_out.location->key.len = 8;
+        r->headers_out.location->key.data = "Location";
+#endif
+        r->headers_out.location->value = *auto_redirect;
+
+        return NGX_HTTP_MOVED_PERMANENTLY;
+    }
+
     if (clcf->handler) {
         /*
          * if the location already has content handler then skip
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -128,6 +128,8 @@ typedef struct {
     int           msie_padding;            /* msie_padding */
     ngx_array_t  *error_pages;             /* error_page */
 
+    unsigned      auto_redirect:1;
+
     ngx_log_t    *err_log;
 } ngx_http_core_loc_conf_t;
 
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -51,9 +51,11 @@
 
 /*
  * HTTP does not define a code for the case when a client closed a connection
- * while we are processing request so we introduce own code to log this case
+ * while we are processing request so we introduce own code to log such
+ * situation when client has closed a connection before we even try to
+ * send HTTP header to it
  */
-#define NGX_HTTP_CLIENT_CLOSED_REQUEST     420
+#define NGX_HTTP_CLIENT_CLOSED_REQUEST     499
 
 #define NGX_HTTP_INTERNAL_SERVER_ERROR     500
 #define NGX_HTTP_NOT_IMPLEMENTED           501
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -25,13 +25,15 @@ ssize_t ngx_read_file(ngx_file_t *file, 
 
 #else
 
-    if (file->offset != offset) {
+    if (file->sys_offset != offset) {
         if (lseek(file->fd, offset, SEEK_SET) == -1) {
             ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() failed");
             return NGX_ERROR;
         }
     }
 
+    file->sys_offset = offset;
+
     n = read(file->fd, buf, size);
 
     if (n == -1) {
@@ -39,6 +41,8 @@ ssize_t ngx_read_file(ngx_file_t *file, 
         return NGX_ERROR;
     }
 
+    file->sys_offset += n;
+
 #endif
 
     file->offset += n;
@@ -68,13 +72,15 @@ ssize_t ngx_write_file(ngx_file_t *file,
 
 #else
 
-    if (file->offset != offset) {
+    if (file->sys_offset != offset) {
         if (lseek(file->fd, offset, SEEK_SET) == -1) {
             ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() failed");
             return NGX_ERROR;
         }
     }
 
+    file->sys_offset = offset;
+
     n = write(file->fd, buf, size);
 
     if (n == -1) {
@@ -88,6 +94,8 @@ ssize_t ngx_write_file(ngx_file_t *file,
         return NGX_ERROR;
     }
 
+    file->sys_offset += n;
+
 #endif
 
     file->offset += n;
@@ -150,6 +158,8 @@ ssize_t ngx_write_chain_to_file(ngx_file
         }
     }
 
+    file->sys_offset = offset;
+
     n = writev(file->fd, io.elts, io.nelts);
 
     if (n == -1) {
@@ -163,6 +173,7 @@ ssize_t ngx_write_chain_to_file(ngx_file
         return NGX_ERROR;
     }
 
+    file->sys_offset += n;
     file->offset += n;
 
     return n;
--- a/src/os/unix/ngx_freebsd_config.h
+++ b/src/os/unix/ngx_freebsd_config.h
@@ -93,8 +93,8 @@ typedef uint32_t   socklen_t;
 
 
 /* STUB */
-#define HAVE_PREAD         0
-#define HAVE_PWRITE        0
+#define HAVE_PREAD         1
+#define HAVE_PWRITE        1
 #define HAVE_LOCALTIME_R   1