nginx-0.0.1-2003-06-02-19:24:30 import
diff --git a/src/http/modules/ngx_http_charset_filter.c b/src/http/modules/ngx_http_charset_filter.c
new file mode 100644
index 0000000..baf6d9a
--- /dev/null
+++ b/src/http/modules/ngx_http_charset_filter.c
@@ -0,0 +1,124 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    ngx_str_t  default_charset;
+} ngx_http_charset_loc_conf_t;
+
+
+static void *ngx_http_charset_create_loc_conf(ngx_pool_t *pool);
+static char *ngx_http_charset_merge_loc_conf(ngx_pool_t *pool,
+                                             void *parent, void *child);
+static int ngx_http_charset_filter_init(ngx_pool_t *pool);
+
+
+static ngx_command_t  ngx_http_charset_filter_commands[] = {
+
+    {ngx_string("default_charset"),
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+     ngx_conf_set_str_slot,
+     NGX_HTTP_LOC_CONF_OFFSET,
+     offsetof(ngx_http_charset_loc_conf_t, default_charset),
+     NULL},
+
+    ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_charset_filter_module_ctx = {
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_charset_create_loc_conf,      /* create location configuration */
+    ngx_http_charset_merge_loc_conf        /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_charset_filter_module = {
+    NGX_MODULE,
+    &ngx_http_charset_filter_module_ctx,   /* module context */
+    ngx_http_charset_filter_commands,      /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    ngx_http_charset_filter_init           /* init module */
+};
+
+
+static int (*next_header_filter) (ngx_http_request_t *r);
+#if 0
+static int (*next_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch);
+#endif
+
+
+static int ngx_http_charset_header_filter(ngx_http_request_t *r)
+{
+    ngx_http_charset_loc_conf_t  *lcf;
+
+    lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
+
+    if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY
+        && r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY)
+    {
+        /* do not set charset for the redirect because NN 4.x uses this
+           charset instead of the next page charset */
+        r->headers_out.charset.len = 0;
+
+    } else if (r->headers_out.charset.len == 0) {
+        r->headers_out.charset = lcf->default_charset;
+    }
+
+    return next_header_filter(r);
+}
+
+
+#if 0
+static int ngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    ngx_log_debug(r->connection->log, "CHARSET BODY");
+    return next_body_filter(r, in);
+}
+#endif
+
+
+static int ngx_http_charset_filter_init(ngx_pool_t *pool)
+{
+    next_header_filter = ngx_http_top_header_filter;
+    ngx_http_top_header_filter = ngx_http_charset_header_filter;
+
+#if 0
+    next_body_filter = ngx_http_top_body_filter;
+    ngx_http_top_body_filter = ngx_http_charset_body_filter;
+#endif
+
+    return NGX_OK;
+}
+
+
+static void *ngx_http_charset_create_loc_conf(ngx_pool_t *pool)
+{
+    ngx_http_charset_loc_conf_t  *lcf;
+
+    ngx_test_null(lcf,
+                  ngx_pcalloc(pool, sizeof(ngx_http_charset_loc_conf_t)),
+                  NGX_CONF_ERROR);
+
+    return lcf;
+}
+
+
+static char *ngx_http_charset_merge_loc_conf(ngx_pool_t *pool,
+                                             void *parent, void *child)
+{
+    ngx_http_charset_loc_conf_t *prev = parent;
+    ngx_http_charset_loc_conf_t *conf = child;
+
+    ngx_conf_merge_str_value(conf->default_charset,
+                             prev->default_charset, "koi8-r");
+
+    return NGX_CONF_OK;
+}
diff --git a/src/http/modules/ngx_http_chunked_filter.c b/src/http/modules/ngx_http_chunked_filter.c
new file mode 100644
index 0000000..75a0cdf
--- /dev/null
+++ b/src/http/modules/ngx_http_chunked_filter.c
@@ -0,0 +1,129 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static int ngx_http_chunked_filter_init(ngx_pool_t *pool);
+
+
+static ngx_http_module_t  ngx_http_chunked_filter_module_ctx = {
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    NULL,                                  /* create location configuration */
+    NULL,                                  /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_chunked_filter_module = {
+    NGX_MODULE,
+    &ngx_http_chunked_filter_module_ctx,   /* module context */
+    NULL,                                  /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    ngx_http_chunked_filter_init           /* init module */
+};
+
+
+static int (*next_header_filter) (ngx_http_request_t *r);
+static int (*next_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch);
+
+
+static int ngx_http_chunked_header_filter(ngx_http_request_t *r)
+{
+    if (r->headers_out.content_length == -1) {
+        if (r->http_version < NGX_HTTP_VERSION_11) {
+            r->keepalive = 0;
+
+        } else {
+            r->chunked = 1;
+        }
+    }
+
+    return next_header_filter(r);
+}
+
+
+static int ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    char         *chunk;
+    size_t        size, len;
+    ngx_hunk_t   *h;
+    ngx_chain_t  *out, *ce, *te, **le;
+
+    if (in == NULL || !r->chunked) {
+        return next_body_filter(r, in);
+    }
+
+    ngx_test_null(out, ngx_alloc_chain_entry(r->pool), NGX_ERROR);
+    le = &out->next;
+
+    size = 0;
+    ce = in;
+
+    for ( ;; ) {
+
+        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
+            size += ce->hunk->last - ce->hunk->pos;
+        } else {
+            size += (size_t) (ce->hunk->file_last - ce->hunk->file_pos);
+        }
+
+        ngx_test_null(te, ngx_alloc_chain_entry(r->pool), NGX_ERROR);
+        te->hunk = ce->hunk;
+        *le = te;
+        le = &te->next;
+
+        if (ce->next == NULL) {
+            break;
+        }
+
+        ce = ce->next;
+    }
+
+    ngx_test_null(chunk, ngx_palloc(r->pool, 11), NGX_ERROR);
+    len = ngx_snprintf(chunk, 11, "%x" CRLF, size);
+
+    ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
+    h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP;
+    h->pos = chunk;
+    h->last = chunk + len;
+
+    out->hunk = h;
+
+    ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
+
+    if (ce->hunk->type & NGX_HUNK_LAST) {
+        ce->hunk->type &= ~NGX_HUNK_LAST;
+        h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_MEMORY|NGX_HUNK_LAST;
+        h->pos = CRLF "0" CRLF CRLF;
+        h->last = h->pos + 7;
+
+    } else {
+        h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_MEMORY;
+        h->pos = CRLF;
+        h->last = h->pos + 2;
+    }
+
+    ngx_test_null(te, ngx_alloc_chain_entry(r->pool), NGX_ERROR);
+    te->hunk = h;
+    te->next = NULL;
+    *le = te;
+
+    return next_body_filter(r, out);
+}
+
+
+static int ngx_http_chunked_filter_init(ngx_pool_t *pool)
+{
+    next_header_filter = ngx_http_top_header_filter;
+    ngx_http_top_header_filter = ngx_http_chunked_header_filter;
+
+    next_body_filter = ngx_http_top_body_filter;
+    ngx_http_top_body_filter = ngx_http_chunked_body_filter;
+
+    return NGX_OK;
+}
diff --git a/src/http/modules/ngx_http_index_handler.c b/src/http/modules/ngx_http_index_handler.c
index c9ad911..cc7c927 100644
--- a/src/http/modules/ngx_http_index_handler.c
+++ b/src/http/modules/ngx_http_index_handler.c
@@ -1,5 +1,16 @@
 
-#include <ngx_http_index_handler.h>
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    ngx_array_t  indices;
+    size_t       max_index_len;
+} ngx_http_index_conf_t;
+
+
+#define NGX_HTTP_DEFAULT_INDEX   "index.html"
 
 
 static int ngx_http_index_test_dir(ngx_http_request_t *r);
@@ -14,7 +25,7 @@
 static ngx_command_t ngx_http_index_commands[] = {
 
     {ngx_string("index"),
-     NGX_HTTP_LOC_CONF|NGX_CONF_ANY1,
+     NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
      ngx_http_index_set_index,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
@@ -46,8 +57,8 @@
 
 
 /*
-   Try to open first index file before the test of the directory existence
-   because the valid requests should be many more then invalid ones.
+   Try to open the first index file before the directory existence test
+   because the valid requests should be many more than invalid ones.
    If open() failed then stat() should be more quickly because some data
    is already cached in the kernel.
    Besides Win32 has ERROR_PATH_NOT_FOUND (NGX_ENOTDIR).
@@ -57,7 +68,7 @@
 
 int ngx_http_index_handler(ngx_http_request_t *r)
 {
-    int                        i, rc, test_dir;
+    int                        i, rc, test_dir, path_not_found;
     char                      *name, *file;
     ngx_str_t                  redirect, *index;
     ngx_err_t                  err;
@@ -80,6 +91,7 @@
     r->path.len = file - r->path.data;
 
     test_dir = 1;
+    path_not_found = 1;
 
     index = icf->indices.elts;
     for (i = 0; i < icf->indices.nelts; i++) {
@@ -92,7 +104,7 @@
             name = index[i].data;
         }
 
-        fd = ngx_open_file(name, NGX_FILE_RDONLY);
+        fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN);
         if (fd == NGX_INVALID_FILE) {
             err = ngx_errno;
 
@@ -100,7 +112,7 @@
               "DEBUG: " ngx_open_file_n " %s failed", name);
 
             if (err == NGX_ENOTDIR) {
-                r->path_not_found = 1;
+                path_not_found = 1;
 
             } else if (err == NGX_EACCES) {
                 r->path_err = err;
@@ -108,7 +120,7 @@
             }
 
             if (test_dir) {
-                if (r->path_not_found) {
+                if (path_not_found) {
                     r->path_err = err;
                     return NGX_HTTP_NOT_FOUND;
                 }
diff --git a/src/http/modules/ngx_http_index_handler.h b/src/http/modules/ngx_http_index_handler.h
deleted file mode 100644
index 3d2f6f0..0000000
--- a/src/http/modules/ngx_http_index_handler.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef _NGX_HTTP_INDEX_HANDLER_H_INCLUDED_
-#define _NGX_HTTP_INDEX_HANDLER_H_INCLUDED_
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_http.h>
-
-
-#define NGX_HTTP_DEFAULT_INDEX   "index.html"
-
-
-typedef struct {
-    ngx_array_t  indices;
-    size_t       max_index_len;
-} ngx_http_index_conf_t;
-
-
-extern ngx_module_t  ngx_http_index_module;
-
-
-#endif /* _NGX_HTTP_INDEX_HANDLER_H_INCLUDED_ */
diff --git a/src/http/modules/ngx_http_log_handler.c b/src/http/modules/ngx_http_log_handler.c
index 44df453..f61461c 100644
--- a/src/http/modules/ngx_http_log_handler.c
+++ b/src/http/modules/ngx_http_log_handler.c
@@ -1,38 +1,82 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_string.h>
-#include <ngx_alloc.h>
-#include <ngx_time.h>
 #include <ngx_http.h>
-#include <ngx_http_config.h>
 
 
-ngx_http_module_t  ngx_http_log_module;
+typedef struct {
+    ngx_file_t  file;
+} ngx_http_log_conf_t;
+
+
+static void *ngx_http_log_create_conf(ngx_pool_t *pool);
+static char *ngx_http_log_merge_conf(ngx_pool_t *p, void *parent, void *child);
+static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,
+                                                                   void *conf);
+
+static ngx_command_t ngx_http_log_commands[] = {
+
+    {ngx_string("access_log"),
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+     ngx_http_log_set_log,
+     NGX_HTTP_LOC_CONF_OFFSET,
+     0,
+     NULL},
+
+    ngx_null_command
+};
+
+
+ngx_http_module_t  ngx_http_log_module_ctx = {
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_log_create_conf,              /* create location configration */
+    ngx_http_log_merge_conf                /* merge location configration */
+};
+
+
+ngx_module_t  ngx_http_log_module = {
+    NGX_MODULE,
+    &ngx_http_log_module_ctx,              /* module context */
+    ngx_http_log_commands,                 /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    NULL,                                  /* init module */
+};
+
 
 
 static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-                  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+                          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
 
 
 int ngx_http_log_handler(ngx_http_request_t *r)
 {
-    size_t    len;
-    char     *line, *p;
-    ngx_tm_t  tm;
+    char                 *line, *p;
+    size_t                len;
+    ngx_tm_t              tm;
+    ngx_http_log_conf_t  *lcf;
 
     ngx_log_debug(r->connection->log, "log handler");
 
-    /* 10:%con, 1:%pipe, 22:%date, 2:"%r", 3:%status, 20:%bytes,
-       6*" ", 2/1: "\r\n" */
+    lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);
+
+    /* 10:%con, 1:%pipe, 22:%date, 2:"%r", 3:%status, 20:%bytes, 2:%user-agent,
+       7*" ", 2/1: "\r\n" */
 #if (WIN32)
-    len = 10 + 1 + 22 + 2 + 3 + 20 + 6 + 2;
+    len = 10 + 1 + 22 + 2 + 3 + 20 + 2 + 7 + 2;
 #else
-    len = 10 + 1 + 22 + 2 + 3 + 20 + 6 + 1;
+    len = 10 + 1 + 22 + 2 + 3 + 20 + 2 + 7 + 1;
 #endif
 
     len += r->connection->addr_text.len;
     len += r->request_line.len;
+    if (r->headers_in.user_agent) {
+        len += r->headers_in.user_agent->value.len;
+    }
 
     ngx_test_null(line, ngx_palloc(r->pool, len), NGX_ERROR);
     p = line;
@@ -79,13 +123,80 @@
 
     p += ngx_snprintf(p, 21, OFF_FMT, r->connection->sent);
 
+    *p++ = ' ';
+
+    *p++ = '"';
+    if (r->headers_in.user_agent) {
+        ngx_memcpy(p, r->headers_in.user_agent->value.data,
+                   r->headers_in.user_agent->value.len);
+        p += r->headers_in.user_agent->value.len;
+    }
+    *p++ = '"';
+
 #if (WIN32)
     *p++ = CR; *p++ = LF;
 #else
     *p++ = LF;
 #endif
 
-    write(1, line, p - line);
+    write(lcf->file.fd, line, p - line);
 
     return NGX_OK;
 }
+
+
+static void *ngx_http_log_create_conf(ngx_pool_t *pool)
+{
+    ngx_http_log_conf_t  *conf;
+
+    ngx_test_null(conf, ngx_pcalloc(pool, sizeof(ngx_http_log_conf_t)),
+                  NGX_CONF_ERROR);
+
+    return conf;
+}
+
+
+static char *ngx_http_log_merge_conf(ngx_pool_t *p, void *parent, void *child)
+{
+    ngx_http_log_conf_t *prev = parent;
+    ngx_http_log_conf_t *conf = child;
+
+    /* STUB */
+    *conf = *prev;
+
+    return NGX_CONF_OK;
+}
+
+
+static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,
+                                  void *conf)
+{
+    ngx_http_log_conf_t *lcf = conf;
+
+    int         len;
+    ngx_err_t   err;
+    ngx_str_t  *value;
+
+    value = cf->args->elts;
+
+    lcf->file.name.len = value[1].len;
+    lcf->file.name.data = value[1].data;
+
+    lcf->file.fd = ngx_open_file(lcf->file.name.data,
+                                 NGX_FILE_RDWR,
+                                 NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND);
+
+    if (lcf->file.fd == NGX_INVALID_FILE) {
+        err = ngx_errno;
+        len = ngx_snprintf(ngx_conf_errstr, sizeof(ngx_conf_errstr) - 1,
+                          ngx_open_file_n " \"%s\" failed (%d: ",
+                          lcf->file.name.data, err);
+        len += ngx_strerror_r(err, ngx_conf_errstr + len,
+                              sizeof(ngx_conf_errstr) - len - 1);
+        ngx_conf_errstr[len++] = ')';
+        ngx_conf_errstr[len++] = '\0';
+        return ngx_conf_errstr;
+    }
+
+    return NGX_CONF_OK;
+}
diff --git a/src/http/modules/ngx_http_log_handler.h b/src/http/modules/ngx_http_log_handler.h
new file mode 100644
index 0000000..2bda1f0
--- /dev/null
+++ b/src/http/modules/ngx_http_log_handler.h
@@ -0,0 +1,41 @@
+#ifndef _NGX_HTTP_LOG_HANDLER_H_INCLUDED_
+#define _NGX_HTTP_LOG_HANDLER_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    int   dummy;
+} ngx_http_log_conf_t;
+
+
+typedef enum {
+
+    NGX_HTTP_LOG_HANDLER = 0,
+
+#if 0
+    /* the ngx_str_t field of the request */
+    NGX_HTTP_LOG_REQUEST_STR_FIELD,
+
+    /* the ngx_str_t field of the r->headers_in */
+    NGX_HTTP_LOG_REQUEST_HEADER_IN_FIELD,
+
+    /* the ngx_str_t field of the r->headers_out */
+    NGX_HTTP_LOG_REQUEST_HEADER_OUT_FIELD,
+#endif
+
+} ngx_http_log_code_e;
+
+
+typedef struct {
+    int      type;
+    int      size;
+    char  *(*handler) (ngx_http_request_t *r, char *p);
+    int      offset;
+} ngx_http_log_code_t;
+
+
+#endif /* _NGX_HTTP_LOG_HANDLER_H_INCLUDED_ */
diff --git a/src/http/modules/ngx_http_range_filter.c b/src/http/modules/ngx_http_range_filter.c
new file mode 100644
index 0000000..d2696fd
--- /dev/null
+++ b/src/http/modules/ngx_http_range_filter.c
@@ -0,0 +1,362 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    ngx_str_t  boundary_header;
+} ngx_http_range_filter_ctx_t;
+
+
+static int ngx_http_range_filter_init(ngx_pool_t *pool);
+
+
+static ngx_http_module_t  ngx_http_range_filter_module_ctx = {
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    NULL,                                  /* create location configuration */
+    NULL,                                  /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_range_filter_module = {
+    NGX_MODULE,
+    &ngx_http_range_filter_module_ctx,     /* module context */
+    NULL,                                  /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    ngx_http_range_filter_init             /* init module */
+};
+
+
+static int (*next_header_filter) (ngx_http_request_t *r);
+static int (*next_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch);
+
+
+static int ngx_http_range_header_filter(ngx_http_request_t *r)
+{
+    int                           rc, boundary, len, i;
+    char                         *p;
+    off_t                         start, end;
+    ngx_http_range_t             *range;
+    ngx_http_range_filter_ctx_t  *ctx;
+
+    if (r->main
+        || r->http_version < NGX_HTTP_VERSION_10
+        || r->headers_out.status != NGX_HTTP_OK
+        || r->headers_out.content_length == -1
+        /* STUB: we currently support ranges for file hunks only */
+        || r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY
+        || r->headers_in.range == NULL
+        || r->headers_in.range->value.len < 7
+        || ngx_strncasecmp(r->headers_in.range->value.data, "bytes=", 6) != 0)
+    {
+        return next_header_filter(r);
+    }
+
+    ngx_init_array(r->headers_out.ranges, r->pool, 5, sizeof(ngx_http_range_t),
+                   NGX_ERROR);
+
+    rc = 0;
+    p = r->headers_in.range->value.data + 6;
+
+    for ( ;; ) {
+        start = end = 0;
+
+        while (*p == ' ') { p++; }
+
+        if (*p < '0' || *p > '9') {
+            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
+            break;
+        }
+
+        while (*p >= '0' && *p <= '9') {
+            start = start * 10 + *p++ - '0';
+        }
+
+        while (*p == ' ') { p++; }
+
+        if (*p++ != '-') {
+            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
+            break;
+        }
+
+        if (start >= r->headers_out.content_length) {
+            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
+            break;
+        }
+
+        while (*p == ' ') { p++; }
+
+        if (*p == ',' || *p == '\0') {
+            ngx_test_null(range, ngx_push_array(&r->headers_out.ranges),
+                          NGX_ERROR);
+            range->start = start;
+            range->end = r->headers_out.content_length;
+
+            if (*p++ == ',') {
+                continue;
+            }
+
+            break;
+        }
+
+        if (*p < '0' || *p > '9') {
+            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
+            break;
+        }
+
+        while (*p >= '0' && *p <= '9') {
+            end = end * 10 + *p++ - '0';
+        }
+
+        while (*p == ' ') { p++; }
+
+        if (*p != ',' && *p != '\0') {
+            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
+            break;
+        }
+
+        if (end >= r->headers_out.content_length || start >= end) {
+            rc = NGX_HTTP_RANGE_NOT_SATISFIABLE;
+            break;
+        }
+
+        ngx_test_null(range, ngx_push_array(&r->headers_out.ranges), NGX_ERROR);
+        range->start = start;
+        range->end = end + 1;
+
+        if (*p++ == ',') {
+            continue;
+        }
+
+        break;
+    }
+
+    if (rc) {
+        r->headers_out.status = rc;
+        r->headers_out.ranges.nelts = 0;
+
+        ngx_test_null(r->headers_out.content_range,
+                      ngx_push_table(r->headers_out.headers),
+                      NGX_ERROR);
+
+        ngx_test_null(r->headers_out.content_range->value.data,
+                      ngx_palloc(r->pool, 8 + 20 + 1),
+                      NGX_ERROR);
+
+        r->headers_out.content_range->value.len = 
+                        ngx_snprintf(r->headers_out.content_range->value.data,
+                                     8 + 20 + 1, "bytes */" OFF_FMT,
+                                     r->headers_out.content_length);
+
+        r->headers_out.content_length = -1;
+
+        return rc;
+
+    } else {
+        r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
+
+        if (r->headers_out.ranges.nelts == 1) {
+            ngx_test_null(r->headers_out.content_range,
+                          ngx_push_table(r->headers_out.headers),
+                          NGX_ERROR);
+
+            ngx_test_null(r->headers_out.content_range->value.data,
+                          ngx_palloc(r->pool, 6 + 20 + 1 + 20 + 1 + 20 + 1),
+                          NGX_ERROR);
+
+            r->headers_out.content_range->value.len = 
+                         ngx_snprintf(r->headers_out.content_range->value.data,
+                                      6 + 20 + 1 + 20 + 1 + 20 + 1,
+                                      "bytes " OFF_FMT "-" OFF_FMT "/" OFF_FMT,
+                                      range->start, range->end - 1,
+                                      r->headers_out.content_length);
+
+            r->headers_out.content_length = range->end - range->start;
+
+        } else {
+
+#if 0
+            /* TODO: what if no content_type ?? */
+            ngx_test_null(r->headers_out.content_type,
+                          ngx_push_table(r->headers_out.headers),
+                          NGX_ERROR);
+#endif
+
+            ngx_http_create_ctx(r, ctx, ngx_http_range_filter_module,
+                               sizeof(ngx_http_range_filter_ctx_t), NGX_ERROR);
+
+            len = 4 + 10 + 2 + 14 + r->headers_out.content_type->value.len
+                                  + 2 + 21 + 1;
+
+            if (r->headers_out.charset.len) {
+                len += 10 + r->headers_out.charset.len;
+            }
+
+            ngx_test_null(ctx->boundary_header.data, ngx_palloc(r->pool, len),
+                          NGX_ERROR);
+
+            boundary = ngx_next_temp_number(0);
+
+            if (r->headers_out.charset.len) {
+                ctx->boundary_header.len =
+                         ngx_snprintf(ctx->boundary_header.data, len,
+                                      CRLF "--%010u" CRLF
+                                      "Content-Type: %s; charset=%s" CRLF
+                                      "Content-Range: bytes ",
+                                      boundary,
+                                      r->headers_out.content_type->value.data,
+                                      r->headers_out.charset.data);
+
+                r->headers_out.charset.len = 0;
+
+            } else {
+                ctx->boundary_header.len =
+                         ngx_snprintf(ctx->boundary_header.data, len,
+                                      CRLF "--%010u" CRLF
+                                      "Content-Type: %s" CRLF
+                                      "Content-Range: bytes ",
+                                      boundary,
+                                      r->headers_out.content_type->value.data);
+            }
+
+            ngx_test_null(r->headers_out.content_type->value.data,
+                          ngx_palloc(r->pool, 31 + 10 + 1),
+                          NGX_ERROR);
+
+            r->headers_out.content_type->value.len =
+                      ngx_snprintf(r->headers_out.content_type->value.data,
+                                   31 + 10 + 1,
+                                   "multipart/byteranges; boundary=%010u",
+                                   boundary);
+
+            /* the last "CRLF--BOUNDARY--CRLF" */
+            len = 4 + 10 + 4;
+
+            range = r->headers_out.ranges.elts;
+            for (i = 0; i < r->headers_out.ranges.nelts; i++) {
+                 ngx_test_null(range[i].content_range.data,
+                               ngx_palloc(r->pool, 20 + 1 + 20 + 1 + 20 + 5),
+                               NGX_ERROR);
+
+                 range[i].content_range.len = 
+                        ngx_snprintf(range[i].content_range.data,
+                                     20 + 1 + 20 + 1 + 20 + 5,
+                                     OFF_FMT "-" OFF_FMT "/" OFF_FMT CRLF CRLF,
+                                     range[i].start, range[i].end - 1,
+                                     r->headers_out.content_length);
+
+                 len += ctx->boundary_header.len + range[i].content_range.len
+                        + range[i].end - range[i].start;
+            }
+
+            r->headers_out.content_length = len;
+        }
+    }
+
+    return next_header_filter(r);
+}
+
+
+static int ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    int                           i;
+    ngx_hunk_t                   *h;
+    ngx_chain_t                  *out, *hce, *rce, *dce, **le;
+    ngx_http_range_t             *range;
+    ngx_http_range_filter_ctx_t  *ctx;
+
+    if (r->headers_out.ranges.nelts == 0) {
+        return next_body_filter(r, in);
+    }
+
+    /* the optimized version for the static files only
+       that are passed in the single file hunk */
+
+    if (in
+        && in->hunk->type & NGX_HUNK_FILE
+        && in->hunk->type & NGX_HUNK_LAST)
+    {
+        if (r->headers_out.ranges.nelts == 1) {
+            range = r->headers_out.ranges.elts;
+            in->hunk->file_pos = range->start;
+            in->hunk->file_last = range->end;
+
+            return next_body_filter(r, in);
+        }
+
+        ctx = ngx_http_get_module_ctx(r, ngx_http_range_filter_module);
+        le = &out;
+
+        range = r->headers_out.ranges.elts;
+        for (i = 0; i < r->headers_out.ranges.nelts; i++) {
+
+            ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
+            h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_MEMORY;
+            h->pos = ctx->boundary_header.data;
+            h->last = ctx->boundary_header.data + ctx->boundary_header.len;
+
+            ngx_test_null(hce, ngx_alloc_chain_entry(r->pool), NGX_ERROR);
+            hce->hunk = h;
+
+            ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
+            h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP;
+            h->pos = range[i].content_range.data;
+            h->last = range[i].content_range.data + range[i].content_range.len;
+
+            ngx_test_null(rce, ngx_alloc_chain_entry(r->pool), NGX_ERROR);
+            rce->hunk = h;
+
+            ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
+            h->type = NGX_HUNK_FILE;
+            h->file_pos = range[i].start;
+            h->file_last = range[i].end;
+            h->file = in->hunk->file;
+
+            ngx_test_null(dce, ngx_alloc_chain_entry(r->pool), NGX_ERROR);
+            dce->hunk = h;
+            dce->next = NULL;
+
+            *le = hce;
+            hce->next = rce;
+            rce->next = dce;
+            le = &dce->next;
+        }
+
+        ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
+        h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP|NGX_HUNK_LAST;
+        ngx_test_null(h->pos, ngx_palloc(r->pool, 4 + 10 + 4), NGX_ERROR);
+        h->last = ngx_cpymem(h->pos, ctx->boundary_header.data, 4 + 10);
+        *h->last++ = '-'; *h->last++ = '-';
+        *h->last++ = CR; *h->last++ = LF;
+
+        ngx_test_null(hce, ngx_alloc_chain_entry(r->pool), NGX_ERROR);
+        hce->hunk = h;
+        hce->next = NULL;
+        *le = hce;
+
+        return next_body_filter(r, out);
+    }
+
+    /* TODO: several incoming hunks of proxied responses
+             and memory hunks on platforms that have no sendfile() */
+
+    return next_body_filter(r, in);
+}
+
+
+static int ngx_http_range_filter_init(ngx_pool_t *pool)
+{
+    next_header_filter = ngx_http_top_header_filter;
+    ngx_http_top_header_filter = ngx_http_range_header_filter;
+
+    next_body_filter = ngx_http_top_body_filter;
+    ngx_http_top_body_filter = ngx_http_range_body_filter;
+
+    return NGX_OK;
+}
diff --git a/src/http/modules/ngx_http_static_handler.c b/src/http/modules/ngx_http_static_handler.c
index bc88fc9..8ea6a0a 100644
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -2,10 +2,6 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_http.h>
-#include <ngx_http_config.h>
-#include <ngx_http_core_module.h>
-#include <ngx_http_output_filter.h>
-
 
 
 int ngx_http_static_handler(ngx_http_request_t *r)
@@ -32,7 +28,8 @@
     ctx->action = "sending response";
 
     if (r->file.fd == NGX_INVALID_FILE) {
-        r->file.fd = ngx_open_file(r->file.name.data, NGX_FILE_RDONLY);
+        r->file.fd = ngx_open_file(r->file.name.data,
+                                   NGX_FILE_RDONLY, NGX_FILE_OPEN);
 
         if (r->file.fd == NGX_INVALID_FILE) {
             err = ngx_errno;
@@ -129,6 +126,10 @@
 
 
     rc = ngx_http_send_header(r);
+    if (rc == NGX_ERROR || rc > NGX_OK) {
+        ngx_http_finalize_request(r, rc);
+        return NGX_OK;
+    }
 
     if (r->header_only) {
         if (rc == NGX_AGAIN) {
diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h
index 81a859d..4a6e3ce 100644
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -14,6 +14,7 @@
 
 #include <ngx_http_request.h>
 #include <ngx_http_config.h>
+#include <ngx_http_filter.h>
 #include <ngx_http_core_module.h>
 
 
diff --git a/src/http/ngx_http_cache.c b/src/http/ngx_http_cache.c
index f9e36f5..0c56320 100644
--- a/src/http/ngx_http_cache.c
+++ b/src/http/ngx_http_cache.c
@@ -1,5 +1,12 @@
 
 
+/*
+ * small file in malloc()ed memory, mmap()ed file, file descriptor only,
+ * file access time only (to estimate can pages pages still be in memory),
+ * translated URI (ngx_http_index_hanlder),
+ * compiled script (ngx_http_ssi_filter).
+ */
+
 
 #define NGX_HTTP_CACHE_ENTRY_DELETED  0x00000001
 #define NGX_HTTP_CACHE_ENTRY_MMAPED   0x00000002
@@ -7,6 +14,9 @@
 /* "/" -> "/index.html" in ngx_http_index_handler */
 #define NGX_HTTP_CACHE_ENTRY_URI      0x00000004
 
+/* complied script */
+#define NGX_HTTP_CACHE_ENTRY_SCRIPT   0x00000008
+
 #define NGX_HTTP_CACHE_FILTER_FLAGS   0xFFFF0000
 
 
@@ -42,7 +52,7 @@
     int                           hi;
     ngx_http_cache_hash_entry_t  *entry;
 
-    h->crc = ngx_crc32(uri->data, uri->len);
+    h->crc = ngx_crc(uri->data, uri->len);
 
     hi = h->crc % cache_hash->size;
     entry = cache_hash[hi].elts;
@@ -61,3 +71,23 @@
 
     return NGX_ERROR;
 }
+
+
+/* 32-bit crc16 */
+
+int ngx_crc(char *data, size_t len)
+{
+    u_int32_t  sum;
+
+    for (sum = 0; len; len--) {
+        /*
+         * gcc 2.95.2 x86 compiles that operator into the single rol opcode.
+         * msvc 6.0sp2 compiles it into four opcodes.
+         */
+        sum = sum >> 1 | sum << 31;
+
+        sum += *data++;
+    }
+
+    return sum;
+}
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 46f9686..b390c94 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -6,8 +6,6 @@
 #include <ngx_listen.h>
 
 #include <ngx_http.h>
-#include <ngx_http_output_filter.h>
-
 #include <nginx.h>
 
 
@@ -97,14 +95,18 @@
      NULL},
 
     {ngx_string("listen"),
+#if 0
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+#else
+     NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+#endif
      ngx_set_listen,
      NGX_HTTP_SRV_CONF_OFFSET,
      0,
      NULL},
 
     {ngx_string("server_name"),
-     NGX_HTTP_SRV_CONF|NGX_CONF_ANY1,
+     NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
      ngx_set_server_name,
      NGX_HTTP_SRV_CONF_OFFSET,
      0,
@@ -195,12 +197,16 @@
 void ngx_http_handler(ngx_http_request_t *r)
 {
     int                        rc, i;
+    ngx_http_log_ctx_t        *lcx;
     ngx_http_handler_pt       *h;
     ngx_http_core_loc_conf_t  *clcf, **clcfp;
     ngx_http_core_srv_conf_t  *cscf;
 
     r->connection->unexpected_eof = 0;
 
+    lcx = r->connection->log->data;
+    lcx->action = NULL;
+
     r->keepalive = 1;
 
     if (r->headers_in.content_length_n > 0) {
@@ -337,7 +343,8 @@
 #else
 
     if (r->file.fd == NGX_INVALID_FILE) {
-        r->file.fd = ngx_open_file(r->file.name.data, NGX_FILE_RDONLY);
+        r->file.fd = ngx_open_file(r->file.name.data,
+                                   NGX_FILE_RDONLY, NGX_FILE_OPEN);
     }
 
     if (r->file.fd == NGX_INVALID_FILE) {
@@ -367,6 +374,8 @@
                               r->file.name.data);
             }
 
+            r->file.fd = NGX_INVALID_FILE;
+
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
@@ -382,6 +391,8 @@
             ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                           ngx_close_file_n " \"%s\" failed", r->file.name.data);
         }
+
+        r->file.fd = NGX_INVALID_FILE;
 #endif
 
         /* BROKEN: need to include server name */
@@ -782,12 +793,13 @@
     ngx_http_listen_t        *l;
     ngx_http_server_name_t   *n;
 
-    /* TODO: it does not merge, it init only */
+    /* TODO: it does not merge, it inits only */
 
     if (conf->listen.nelts == 0) {
         ngx_test_null(l, ngx_push_array(&conf->listen), NGX_CONF_ERROR);
         l->addr = INADDR_ANY;
-        l->port = 8000;
+        /* STUB: getuid() should be cached */
+        l->port = (getuid() == 0) ? 80 : 8000;
         l->family = AF_INET;
     }
 
@@ -871,8 +883,7 @@
     int               i, key;
     ngx_http_type_t  *t;
 
-    ngx_conf_merge_str_value(conf->doc_root,
-                             prev->doc_root, "html");
+    ngx_conf_merge_str_value(conf->doc_root, prev->doc_root, "html");
 
     if (conf->types == NULL) {
         if (prev->types) {
diff --git a/src/http/ngx_http_event.c b/src/http/ngx_http_event.c
index 9d41584..d65fdde 100644
--- a/src/http/ngx_http_event.c
+++ b/src/http/ngx_http_event.c
@@ -1,21 +1,8 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_string.h>
-#include <ngx_files.h>
-#include <ngx_log.h>
-#include <ngx_alloc.h>
-#include <ngx_array.h>
-#include <ngx_table.h>
-#include <ngx_hunk.h>
-#include <ngx_connection.h>
 #include <ngx_event.h>
-#include <ngx_event_timer.h>
-#include <ngx_inet.h>
 #include <ngx_http.h>
-#include <ngx_http_config.h>
-#include <ngx_http_core_module.h>
-#include <ngx_http_output_filter.h>
 
 
 static void ngx_http_init_request(ngx_event_t *ev);
@@ -63,12 +50,13 @@
     { ngx_string("Content-Length"),
                             offsetof(ngx_http_headers_in_t, content_length) },
 
+    { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range) },
 #if 0
+    { ngx_string("If-Range"), offsetof(ngx_http_headers_in_t, if_range) },
+#endif
 
     { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent) },
 
-#endif
-
     { ngx_null_string, 0 }
 };
 
@@ -289,6 +277,13 @@
 
         /* the request line has been parsed successfully */
 
+        /* STUB: we need to handle such URIs */
+        if (r->complex_uri || r->unusual_uri) {
+            ngx_http_header_parse_error(r, NGX_HTTP_PARSE_INVALID_REQUEST);
+            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+            return;
+        }
+
         cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
 
         if (r->http_version >= NGX_HTTP_VERSION_10
@@ -402,7 +397,7 @@
         if (r->args.data[0] == '\0') { r->args.data = NULL; }
 #endif
 
-        if (r->http_version == NGX_HTTP_VERSION_9) {
+        if (r->http_version < NGX_HTTP_VERSION_10) {
             rev->event_handler = ngx_http_block_read;
             ngx_http_handler(r);
             return;
@@ -1436,9 +1431,14 @@
 {
     ngx_http_log_ctx_t *ctx = (ngx_http_log_ctx_t *) data;
 
-    if (ctx->url) {
+    if (ctx->action && ctx->url) {
         return ngx_snprintf(buf, len, " while %s, client: %s, URL: %s",
                             ctx->action, ctx->client, ctx->url);
+
+    } else if (ctx->action == NULL && ctx->url) {
+        return ngx_snprintf(buf, len, ", client: %s, URL: %s",
+                            ctx->client, ctx->url);
+
     } else {
         return ngx_snprintf(buf, len, " while %s, client: %s",
                             ctx->action, ctx->client);
diff --git a/src/http/ngx_http_filter.h b/src/http/ngx_http_filter.h
new file mode 100644
index 0000000..6e0c3fc
--- /dev/null
+++ b/src/http/ngx_http_filter.h
@@ -0,0 +1,18 @@
+#ifndef _NGX_HTTP_FILTER_H_INCLUDED_
+#define _NGX_HTTP_FILTER_H_INCLUDED_
+
+
+#define NGX_HTTP_FILTER_NEED_IN_MEMORY      1
+#define NGX_HTTP_FILTER_SSI_NEED_IN_MEMORY  2
+#define NGX_HTTP_FILTER_NEED_TEMP           4
+
+
+int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk);
+int ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in);
+
+
+extern int (*ngx_http_top_header_filter) (ngx_http_request_t *r);
+extern int (*ngx_http_top_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch);
+
+
+#endif /* _NGX_HTTP_FILTER_H_INCLUDED_ */
diff --git a/src/http/ngx_http_get_time.c b/src/http/ngx_http_get_time.c
index c83a5ba..45df008 100644
--- a/src/http/ngx_http_get_time.c
+++ b/src/http/ngx_http_get_time.c
@@ -1,8 +1,6 @@
 
-#include <nginx.h>
-
 #include <ngx_config.h>
-#include <ngx_types.h>
+#include <ngx_core.h>
 
 
 size_t ngx_http_get_time(char *buf, time_t t)
diff --git a/src/http/ngx_http_header_filter.c b/src/http/ngx_http_header_filter.c
index cbdbf23..583b56d 100644
--- a/src/http/ngx_http_header_filter.c
+++ b/src/http/ngx_http_header_filter.c
@@ -1,11 +1,8 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-
-#include <nginx.h>
-
 #include <ngx_http.h>
-#include <ngx_http_write_filter.h>
+#include <nginx.h>
 
 
 static int ngx_http_header_filter_init(ngx_pool_t *pool);
@@ -52,7 +49,11 @@
 #endif
 
     ngx_string("301 Moved Permanently"),
+#if 0
     ngx_string("302 Moved Temporarily"),
+#else
+    ngx_string("302 Found"),
+#endif
     ngx_null_string,  /* "303 See Other" */
     ngx_string("304 Not Modified"),
 
@@ -87,11 +88,12 @@
 
 static int ngx_http_header_filter(ngx_http_request_t *r)
 {
-    int               len, status, text, i;
-    time_t            ims;
-    ngx_hunk_t       *h;
-    ngx_chain_t      *ch;
-    ngx_table_elt_t  *header;
+    int                len, status, i;
+    time_t             ims;
+    ngx_hunk_t        *h;
+    ngx_chain_t       *ch;
+    ngx_table_elt_t   *header;
+    ngx_http_range_t  *range;
 
     if (r->http_version < NGX_HTTP_VERSION_10) {
         return NGX_OK;
@@ -170,20 +172,23 @@
         len += 37;
     }
 
+    if (r->headers_out.content_range && r->headers_out.content_range->value.len)
+    {
+        len += 15 + r->headers_out.content_range->value.len + 2;
+    }
+
     if (r->headers_out.content_length >= 0) {
         /* "Content-Length: ... \r\n", 2^64 is 20 characters */
         len += 48;
     }
 
-    text = 0;
     if (r->headers_out.content_type && r->headers_out.content_type->value.len) {
         r->headers_out.content_type->key.len = 0;
         len += 16 + r->headers_out.content_type->value.len;
-        if (ngx_strncasecmp(r->headers_out.content_type->value.data,
-                                                            "text/", 5) == 0) {
-            text = 1;
-            /* "; charset=koi8-r" */
-            len += 16;
+
+        if (r->headers_out.charset.len) {
+            /* "; charset= ... " */
+            len += 10 + r->headers_out.charset.len;
         }
     }
 
@@ -210,6 +215,11 @@
         len += 46;
     }
 
+    if (r->chunked) {
+        /* "Transfer-Encoding: chunked\r\n" */
+        len += 28;
+    }
+
     if (r->keepalive == 0) {
         /* "Connection: close\r\n" */
         len += 19;
@@ -253,6 +263,15 @@
         *(h->last++) = CR; *(h->last++) = LF;
     }
 
+
+    if (r->headers_out.content_range && r->headers_out.content_range->value.len)
+    {
+        h->last = ngx_cpymem(h->last, "Content-Range: ", 15);
+        h->last = ngx_cpymem(h->last, r->headers_out.content_range->value.data,
+                             r->headers_out.content_range->value.len);
+        *(h->last++) = CR; *(h->last++) = LF;
+    }
+
     /* 2^64 is 20 characters  */
     if (r->headers_out.content_length >= 0) {
         h->last += ngx_snprintf(h->last, 49,
@@ -263,10 +282,12 @@
     if (r->headers_out.content_type && r->headers_out.content_type->value.len) {
         h->last = ngx_cpymem(h->last, "Content-Type: ", 14);
         h->last = ngx_cpymem(h->last, r->headers_out.content_type->value.data,
-                   r->headers_out.content_type->value.len);
+                             r->headers_out.content_type->value.len);
 
-        if (text) {
-            h->last = ngx_cpymem(h->last, "; charset=koi8-r", 16);
+        if (r->headers_out.charset.len) {
+            h->last = ngx_cpymem(h->last, "; charset=", 10);
+            h->last = ngx_cpymem(h->last, r->headers_out.charset.data,
+                                 r->headers_out.charset.len);
         }
 
         *(h->last++) = CR; *(h->last++) = LF;
@@ -299,6 +320,10 @@
         *(h->last++) = CR; *(h->last++) = LF;
     }
 
+    if (r->chunked) {
+        h->last = ngx_cpymem(h->last, "Transfer-Encoding: chunked" CRLF, 28);
+    }
+
     if (r->keepalive == 0) {
         h->last = ngx_cpymem(h->last, "Connection: close" CRLF, 19);
 
diff --git a/src/http/ngx_http_output_filter.c b/src/http/ngx_http_output_filter.c
index 2db9b52..c0c4dd4 100644
--- a/src/http/ngx_http_output_filter.c
+++ b/src/http/ngx_http_output_filter.c
@@ -1,15 +1,20 @@
 
 #include <ngx_config.h>
-
 #include <ngx_core.h>
-#include <ngx_files.h>
-#include <ngx_string.h>
-#include <ngx_hunk.h>
-#include <ngx_conf_file.h>
-
 #include <ngx_http.h>
-#include <ngx_http_config.h>
-#include <ngx_http_output_filter.h>
+
+
+typedef struct {
+    size_t        hunk_size;
+} ngx_http_output_filter_conf_t;
+
+
+typedef struct {
+    ngx_hunk_t   *hunk;         /* the temporary hunk to copy */
+    ngx_chain_t  *incoming;
+    ngx_chain_t   in;           /* one chain entry for input */
+    ngx_chain_t   out;          /* one chain entry for output */
+} ngx_http_output_filter_ctx_t;
 
 
 static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src);
diff --git a/src/http/ngx_http_output_filter.h b/src/http/ngx_http_output_filter.h
deleted file mode 100644
index 2f34600..0000000
--- a/src/http/ngx_http_output_filter.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef _NGX_HTTP_OUTPUT_FILTER_H_INCLUDED_
-#define _NGX_HTTP_OUTPUT_FILTER_H_INCLUDED_
-
-
-#include <ngx_hunk.h>
-#include <ngx_conf_file.h>
-#include <ngx_http.h>
-
-
-#define NGX_HTTP_FILTER_NEED_IN_MEMORY      1
-#define NGX_HTTP_FILTER_SSI_NEED_IN_MEMORY  2
-#define NGX_HTTP_FILTER_NEED_TEMP           4
-
-
-typedef struct {
-    size_t        hunk_size;
-} ngx_http_output_filter_conf_t;
-
-
-typedef struct {
-    ngx_hunk_t   *hunk;         /* the temporary hunk to copy */
-    ngx_chain_t  *incoming;
-    ngx_chain_t   in;           /* one chain entry for input */
-    ngx_chain_t   out;          /* one chain entry for output */
-} ngx_http_output_filter_ctx_t;
-
-
-int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk);
-
-
-extern ngx_module_t  ngx_http_output_filter_module;
-
-
-#endif /* _NGX_HTTP_OUTPUT_FILTER_H_INCLUDED_ */
diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c
index a7784a3..e02805b 100644
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -175,7 +175,7 @@
             }
             break;
 
-        /* check "/." or "//" */
+        /* check "/.", "//", and "%" in URI */
         case sw_after_slash_in_uri:
             switch (ch) {
             case CR:
@@ -193,6 +193,7 @@
                 state = sw_http_09;
                 break;
             case '.':
+            case '%':
                 r->complex_uri = 1;
                 state = sw_uri;
                 break;
@@ -211,7 +212,7 @@
             }
             break;
 
-        /* check slash in URI */
+        /* check "/" and "%" in URI */
         case sw_check_uri:
             switch (ch) {
             case CR:
@@ -235,6 +236,10 @@
                 r->uri_ext = NULL;
                 state = sw_after_slash_in_uri;
                 break;
+            case '%':
+                r->complex_uri = 1;
+                state = sw_uri;
+                break;
             case '?':
                 r->args_start = p;
                 state = sw_uri;
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index 15898e8..a4ecb6b 100644
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -40,6 +40,7 @@
 
 
 #define NGX_HTTP_OK                     200
+#define NGX_HTTP_PARTIAL_CONTENT        206
 
 #define NGX_HTTP_SPECIAL_RESPONSE       300
 #define NGX_HTTP_MOVED_PERMANENTLY      301
@@ -52,6 +53,7 @@
 #define NGX_HTTP_NOT_ALLOWED            405
 #define NGX_HTTP_REQUEST_TIME_OUT       408
 #define NGX_HTTP_REQUEST_URI_TOO_LARGE  414
+#define NGX_HTTP_RANGE_NOT_SATISFIABLE  416
 
 #define NGX_HTTP_INTERNAL_SERVER_ERROR  500
 #define NGX_HTTP_NOT_IMPLEMENTED        501
@@ -79,6 +81,8 @@
     ngx_table_elt_t  *connection;
     ngx_table_elt_t  *if_modified_since;
     ngx_table_elt_t  *content_length;
+    ngx_table_elt_t  *range;
+
     ngx_table_elt_t  *accept_encoding;
 
     ngx_table_elt_t  *user_agent;
@@ -100,6 +104,13 @@
 
 
 typedef struct {
+    off_t      start;
+    off_t      end;
+    ngx_str_t  content_range;
+} ngx_http_range_t;
+
+
+typedef struct {
     int               status;
     ngx_str_t         status_line;
 
@@ -108,11 +119,14 @@
     ngx_table_elt_t  *content_type;
     ngx_table_elt_t  *location;
     ngx_table_elt_t  *last_modified;
+    ngx_table_elt_t  *content_range;
+
+    ngx_str_t         charset;
+    ngx_array_t       ranges;
 
     ngx_table_t      *headers;
 
     off_t             content_length;
-    char             *charset;
     char             *etag;
     time_t            date_time;
     time_t            last_modified_time;
@@ -140,7 +154,7 @@
 
     int  (*handler)(ngx_http_request_t *r);
 
-    time_t  lingering_time;
+    time_t               lingering_time;
 
     int                  method;
     int                  http_version;
@@ -161,46 +175,44 @@
     ngx_str_t           *server_name;
     ngx_array_t         *virtual_names;
 
-    int         filter;
 
-    char       *discarded_buffer;
+    char                *discarded_buffer;
 
-    ngx_str_t   path;
-    int         path_err;
+    ngx_str_t            path;
+    int                  path_err;
 
-    unsigned  proxy:1;
-    unsigned  cachable:1;
-    unsigned  pipeline:1;
-    unsigned  keepalive:1;
-    unsigned  lingering_close:1;
+    /* URI is not started with '/' - "GET http://" */
+    unsigned             unusual_uri:1;
+    /* URI with "/.", "%" and on Win32 with "//" */
+    unsigned             complex_uri:1;
+    unsigned             header_timeout_set:1;
 
-    unsigned  header_read:1;
-    unsigned  header_timeout_set:1;
-
-    unsigned  logging:1;
-
-    unsigned  header_only:1;
-    unsigned  unusual_uri:1;  /* URI is not started with '/' - "GET http://" */
-    unsigned  complex_uri:1;  /* URI with "/." or with "//" (WIN32) */
-    unsigned  path_not_found:1;
-#ifdef NGX_EVENT
-    unsigned  write_level_event:1;
+    unsigned             proxy:1;
+#if 0
+    unsigned             cachable:1;
 #endif
+    unsigned             pipeline:1;
 
-    int    state;
-    char  *uri_start;
-    char  *uri_end;
-    char  *uri_ext;
-    char  *args_start;
-    char  *request_start;
-    char  *request_end;
-    char  *header_name_start;
-    char  *header_name_end;
-    char  *header_start;
-    char  *header_end;
-#ifdef NGX_EVENT
-    int  (*state_handler)(ngx_http_request_t *r);
-#endif
+    unsigned             chunked:1;
+    unsigned             header_only:1;
+    unsigned             keepalive:1;
+    unsigned             lingering_close:1;
+
+    /* TODO: use filter or bits ???? */
+    int                  filter;
+
+    /* used to parse HTTP headers */
+    int                  state;
+    char                *uri_start;
+    char                *uri_end;
+    char                *uri_ext;
+    char                *args_start;
+    char                *request_start;
+    char                *request_end;
+    char                *header_name_start;
+    char                *header_name_end;
+    char                *header_start;
+    char                *header_end;
 };
 
 
diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c
new file mode 100644
index 0000000..19e582a
--- /dev/null
+++ b/src/http/ngx_http_script.c
@@ -0,0 +1,49 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+char *ngx_http_script_copy(ngx_http_request_t *r,
+                           ngx_http_script_code_t *code,
+                           char *p, size_t len)
+{
+    return ngx_cpymem(p, code->offset, code->len < len ? code->len : len);
+}
+
+
+char *ngx_http_script_header_in(ngx_http_request_t *r,
+                                ngx_http_script_code_t *code,
+                                char *p, size_t len)
+{
+    ngx_str_t *s;
+
+    s = (ngx_str_t *) (((char *) r->headers_in) + code->offset);
+
+    return ngx_cpymem(p, s->data, s->len < len ? s->len : len);
+}
+
+
+/* the log script codes */
+
+char *ngx_http_script_request_line(ngx_http_request_t *r, char *p, size_t len)
+{
+    return ngx_cpymem(p, r->request_line.data,
+                      r->request_line.len < len ? r->request_line.len : len);
+}
+
+
+char *ngx_http_script_status(ngx_http_request_t *r, char *p, size_t len)
+{
+    p += ngx_snprintf(p, len, "%d", r->headers_out.status);
+
+    return p;
+}
+
+
+char *ngx_http_script_sent(ngx_http_request_t *r, char *p, size_t len)
+{
+    p += ngx_snprintf(p, len, OFF_FMT, r->connection->sent);
+
+    return p;
+}
diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h
new file mode 100644
index 0000000..d3fd5fb
--- /dev/null
+++ b/src/http/ngx_http_script.h
@@ -0,0 +1,12 @@
+#ifndef _NGX_HTTP_SCRIPT_H_INCLUDED_
+#define _NGX_HTTP_SCRIPT_H_INCLUDED_
+
+
+typedef struct {
+    handler;
+    offset / data;
+    size;
+} ngx_http_script_code_t;
+
+
+#endif /* _NGX_HTTP_SCRIPT_H_INCLUDED_ */
diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c
index 1b71352..9a5dea1 100644
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -1,11 +1,8 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-
-#include <nginx.h>
-
 #include <ngx_http.h>
-#include <ngx_http_output_filter.h>
+#include <nginx.h>
 
 
 static char error_tail[] =
@@ -15,6 +12,14 @@
 ;
 
 
+static char error_302_page[] =
+"<html>" CRLF
+"<head><title>302 Found</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>302 Found</h1></center>" CRLF
+;
+
+
 static char error_400_page[] =
 "<html>" CRLF
 "<head><title>400 Bad Request</title></head>" CRLF
@@ -63,6 +68,14 @@
 ;
 
 
+static char error_416_page[] =
+"<html>" CRLF
+"<head><title>416 Requested Range Not Satisfiable</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>416 Requested Range Not Satisfiable</h1></center>" CRLF
+;
+
+
 static char error_500_page[] =
 "<html>" CRLF
 "<head><title>500 Internal Server Error</title></head>" CRLF
@@ -88,8 +101,9 @@
 
 
 static ngx_str_t error_pages[] = {
+    ngx_null_string,             /* 300 */
     ngx_null_string,             /* 301 */
-    ngx_null_string,             /* 302 */
+    ngx_string(error_302_page),
     ngx_null_string,             /* 303 */
 
     ngx_string(error_400_page),
@@ -108,7 +122,7 @@
     ngx_null_string,             /* 413 */
     ngx_string(error_414_page),
     ngx_null_string,             /* 415 */
-    ngx_null_string,             /* 416 */
+    ngx_string(error_416_page),
 
     ngx_string(error_500_page),
     ngx_null_string,             /* 501 */
@@ -120,35 +134,22 @@
 
 int ngx_http_special_response_handler(ngx_http_request_t *r, int error)
 {
-    int          err, len;
+    int          err;
     ngx_hunk_t  *message, *tail;
 
-    len = 0;
-
     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 + 4;
+
     } else {
-        ngx_test_null(r->headers_out.content_type,
-                      ngx_push_table(r->headers_out.headers),
-                      NGX_HTTP_INTERNAL_SERVER_ERROR);
-
-        r->headers_out.content_type->key.len = 12;
-        r->headers_out.content_type->key.data = "Content-Type";
-        r->headers_out.content_type->value.len = 9;
-        r->headers_out.content_type->value.data = "text/html";
-
-        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;
-        }
+        /* 5XX */
+        err = error - NGX_HTTP_INTERNAL_SERVER_ERROR + 4 + 17;
     }
 
     if (r->keepalive != 0) {
@@ -169,11 +170,21 @@
         }
     }
 
-    if (error_pages[err].len == 0) {
-        r->headers_out.content_length = -1;
-    } else {
+    if (error_pages[err].len) {
         r->headers_out.content_length = error_pages[err].len
-                                        + len + sizeof(error_tail);
+                                        + sizeof(error_tail);
+
+        ngx_test_null(r->headers_out.content_type,
+                      ngx_push_table(r->headers_out.headers),
+                      NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+        r->headers_out.content_type->key.len = 12;
+        r->headers_out.content_type->key.data = "Content-Type";
+        r->headers_out.content_type->value.len = 9;
+        r->headers_out.content_type->value.data = "text/html";
+
+    } else {
+        r->headers_out.content_length = -1;
     }
 
     if (ngx_http_send_header(r) == NGX_ERROR) {
diff --git a/src/http/ngx_http_write_filter.c b/src/http/ngx_http_write_filter.c
index 1844438..59d4a69 100644
--- a/src/http/ngx_http_write_filter.c
+++ b/src/http/ngx_http_write_filter.c
@@ -1,16 +1,17 @@
 
 #include <ngx_config.h>
-
 #include <ngx_core.h>
-#include <ngx_hunk.h>
-#include <ngx_conf_file.h>
-#include <ngx_connection.h>
-
-#include <ngx_event_write.h>
-
 #include <ngx_http.h>
-#include <ngx_http_config.h>
-#include <ngx_http_write_filter.h>
+
+
+typedef struct {
+    size_t        buffer_output;
+} ngx_http_write_filter_conf_t;
+
+
+typedef struct {
+    ngx_chain_t  *out;
+} ngx_http_write_filter_ctx_t;
 
 
 static void *ngx_http_write_filter_create_conf(ngx_pool_t *pool);
@@ -96,7 +97,7 @@
     /* add the new chain to the existent one */
 
     for (/* void */; in; in = in->next) {
-        ngx_test_null(ce, ngx_palloc(r->pool, sizeof(ngx_chain_t)), NGX_ERROR);
+        ngx_test_null(ce, ngx_alloc_chain_entry(r->pool), NGX_ERROR);
 
         ce->hunk = in->hunk;
         ce->next = NULL;
diff --git a/src/http/ngx_http_write_filter.h b/src/http/ngx_http_write_filter.h
deleted file mode 100644
index 492d4d8..0000000
--- a/src/http/ngx_http_write_filter.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef _NGX_HTTP_WRITE_FILTER_H_INCLUDED_
-#define _NGX_HTTP_WRITE_FILTER_H_INCLUDED_
-
-
-#include <ngx_hunk.h>
-#include <ngx_http.h>
-
-
-typedef struct {
-    size_t        buffer_output;
-} ngx_http_write_filter_conf_t;
-
-
-typedef struct {
-    ngx_chain_t  *out;
-} ngx_http_write_filter_ctx_t;
-
-
-int ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in);
-
-
-extern ngx_module_t  ngx_http_write_filter_module;
-
-
-#endif /* _NGX_HTTP_WRITE_FILTER_H_INCLUDED_ */