changeset 26:53cb81681040

nginx-0.0.1-2002-12-15-09:25:09 import
author Igor Sysoev <igor@sysoev.ru>
date Sun, 15 Dec 2002 06:25:09 +0000
parents a8b156554dfe
children a8d5abe713e6
files src/core/nginx.c src/core/nginx.h src/core/ngx_alloc.c src/core/ngx_config.h src/core/ngx_connection.h src/core/ngx_file.h src/core/ngx_inet.c src/core/ngx_inet.h src/core/ngx_string.h src/event/modules/ngx_kqueue_module.c src/event/modules/ngx_kqueue_module.h src/event/modules/ngx_select_module.c src/event/modules/ngx_select_module.h src/event/ngx_event.c src/event/ngx_event.h src/event/ngx_event_accept.c src/event/ngx_event_close.c src/event/ngx_event_write.c src/http/modules/ngx_http_event_proxy_handler.c src/http/modules/ngx_http_index_handler.c src/http/modules/ngx_http_log_handler.c src/http/modules/ngx_http_static_handler.c src/http/ngx_http.h src/http/ngx_http_config.c src/http/ngx_http_config.h src/http/ngx_http_core.c src/http/ngx_http_event.c src/http/ngx_http_get_time.c src/http/ngx_http_header.h src/http/ngx_http_header_filter.c src/http/ngx_http_modules.c src/http/ngx_http_output_filter.c src/http/ngx_http_output_filter.h src/http/ngx_http_parse.c src/http/ngx_http_parse_time.c src/http/ngx_http_special_response.c src/http/ngx_http_write_filter.c src/os/unix/ngx_files.h src/os/unix/ngx_sendv.c src/os/win32/ngx_files.c src/os/win32/ngx_files.h src/os/win32/ngx_sendv.c src/os/win32/ngx_time.h
diffstat 43 files changed, 983 insertions(+), 588 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -32,6 +32,8 @@ ngx_log_t     ngx_log;
 ngx_pool_t   *ngx_pool;
 
 
+int ngx_connection_counter;
+
 ngx_array_t  *ngx_listening_sockets;
 
 
@@ -43,7 +45,9 @@ int main(int argc, char *const *argv)
     ngx_pool = ngx_create_pool(16 * 1024, &ngx_log);
     /* */
 
+#if !(WIN32)
     ngx_set_signals(&ngx_log);
+#endif
 
     ngx_init_sockets(&ngx_log);
 
@@ -73,6 +77,7 @@ int main(int argc, char *const *argv)
     return 0;
 }
 
+#if !(WIN32)
 static void ngx_set_signals(ngx_log_t *log)
 {
     struct sigaction sa;
@@ -86,6 +91,7 @@ static void ngx_set_signals(ngx_log_t *l
         exit(1);
     }
 }
+#endif
 
 static void ngx_open_listening_sockets(ngx_log_t *log)
 {
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -5,4 +5,7 @@
 #define  NGINX_VER  "nginx/0.0.1"
 
 
+extern int ngx_connection_counter;
+
+
 #endif /* _NGINX_H_INCLUDED_ */
--- a/src/core/ngx_alloc.c
+++ b/src/core/ngx_alloc.c
@@ -3,6 +3,7 @@
 
 #include <ngx_errno.h>
 #include <ngx_log.h>
+#include <ngx_string.h>
 #include <ngx_alloc.h>
 
 
@@ -15,7 +16,7 @@ void *ngx_alloc(size_t size, ngx_log_t *
         ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                       "malloc() %d bytes failed", size);
 
-    ngx_log_debug(log, "malloc: %x:%d" _ p _ size);
+    ngx_log_debug(log, "malloc: %08x:%d" _ p _ size);
 
     return p;
 }
@@ -52,12 +53,12 @@ void ngx_destroy_pool(ngx_pool_t *pool)
     ngx_pool_large_t  *l;
 
     for (l = pool->large; l; l = l->next) {
-        ngx_log_debug(pool->log, "free: %x" _ l->alloc);
+        ngx_log_debug(pool->log, "free: %08x" _ l->alloc);
         free(l->alloc);
     }
 
     for (p = pool, n = pool->next; /* void */; p = n, n = n->next) {
-        ngx_log_debug(pool->log, "free: %x" _ p);
+        ngx_log_debug(pool->log, "free: %08x" _ p);
         free(p);
 
         if (n == NULL)
@@ -74,9 +75,10 @@ void *ngx_palloc(ngx_pool_t *pool, size_
     if (size <= NGX_MAX_ALLOC_FROM_POOL) {
 
         for (p = pool, n = pool->next; /* void */; p = n, n = n->next) {
-            if ((size_t) (p->end - p->last) >= size) {
-                m = p->last;
-                p->last += size;
+            if ((size_t) (p->end - ngx_align(p->last)) >= size) {
+                m = ngx_align(p->last);
+                p->last = ngx_align(p->last);
+                p->last += size ;
 
                 return m;
             }
--- a/src/core/ngx_config.h
+++ b/src/core/ngx_config.h
@@ -12,6 +12,13 @@
 #define FD_SETSIZE  1024
 
 
+/* auto_conf */
+#define NGX_ALIGN       (4 - 1)
+#define NGX_ALIGN_TYPE  unsigned
+
+#define ngx_align(p)    (char *) (((NGX_ALIGN_TYPE) p + NGX_ALIGN) & ~NGX_ALIGN)
+
+
 #ifdef _WIN32
 
 #define WIN32 1
@@ -25,9 +32,6 @@
 
 #define ngx_inline   __inline
 
-#define ngx_memzero  ZeroMemory
-
-#define ngx_close_socket closesocket
 
 #ifndef HAVE_INHERITED_NONBLOCK
 #define HAVE_INHERITED_NONBLOCK  1
@@ -66,9 +70,6 @@
 
 #define ngx_inline   inline
 
-#define ngx_memzero  bzero
-
-#define ngx_close_socket close
 
 #endif /* POSIX */
 
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -44,6 +44,8 @@ struct ngx_connection_s {
     ngx_hunk_t       *buffer;
     unsigned int      post_accept_timeout;
 
+    int               number;
+
     unsigned          unexpected_eof:1;
 };
 
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -4,12 +4,18 @@
 
 #include <ngx_files.h>
 #include <ngx_log.h>
+#include <ngx_string.h>
 
 typedef struct ngx_file_s  ngx_file_t;
 
 struct ngx_file_s {
-    ngx_fd_t      fd;
-    ngx_log_t    *log;
+    ngx_fd_t         fd;
+    ngx_str_t        name;
+    ngx_file_info_t  info;
+
+    ngx_log_t       *log;
+
+    unsigned         info_valid:1;
 };
 
 
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_inet.c
@@ -0,0 +1,18 @@
+
+#include <ngx_config.h>
+#include <ngx_string.h>
+#include <ngx_inet.h>
+
+
+size_t ngx_inet_ntop(int family, char *addr, char *text, size_t len)
+{
+    if (family != AF_INET)
+        return 0;
+
+    return ngx_snprintf(text, len > INET_ADDRSTRLEN ? INET_ADDRSTRLEN : len,
+                        "%u.%u.%u.%u",
+                        (unsigned char) addr[0],
+                        (unsigned char) addr[1],
+                        (unsigned char) addr[2],
+                        (unsigned char) addr[3]);
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_inet.h
@@ -0,0 +1,8 @@
+#ifndef _NGX_INET_H_INCLUDED_
+#define _NGX_INET_H_INCLUDED_
+
+
+size_t ngx_inet_ntop(int family, char *addr, char *text, size_t len);
+
+
+#endif /* _NGX_INET_H_INCLUDED_ */
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -6,17 +6,24 @@
 
 
 typedef struct {
-    int   len;
-    char *data;
+    size_t  len;
+    char   *data;
 } ngx_str_t;
 
 #if (WIN32)
 
+
+#define ngx_memzero               ZeroMemory
+
+#define strcasecmp                stricmp
+
 #define ngx_snprintf              _snprintf
 #define ngx_vsnprintf             _vsnprintf
 
 #else
 
+#define ngx_memzero               bzero
+
 #define ngx_snprintf              snprintf
 #define ngx_vsnprintf             vsnprintf
 
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -16,12 +16,14 @@
 #endif
 
 
-
+/* should be per-thread */
 static int              kq;
 static struct kevent   *change_list, *event_list;
 static int              nchanges, nevents;
 
 static ngx_event_t      timer_queue;
+/* */
+
 
 int ngx_kqueue_init(int max_connections, ngx_log_t *log)
 {
@@ -53,6 +55,7 @@ int ngx_kqueue_init(int max_connections,
     return NGX_OK;
 }
 
+
 int ngx_kqueue_add_event(ngx_event_t *ev, int event, u_int flags)
 {
     ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1: 0;
@@ -60,22 +63,33 @@ int ngx_kqueue_add_event(ngx_event_t *ev
     return ngx_kqueue_set_event(ev, event, EV_ADD | flags);
 }
 
-int ngx_kqueue_del_event(ngx_event_t *ev, int event)
+
+int ngx_kqueue_del_event(ngx_event_t *ev, int event, u_int flags)
 {
     ngx_event_t *e;
 
-    if (ev->index <= nchanges && change_list[ev->index].udata == ev) {
-        change_list[ev->index] = change_list[nchanges];
-        e = (ngx_event_t *) change_list[ev->index].udata;
-        e->index = ev->index;
-        nchanges--;
+    if (ev->index < nchanges && change_list[ev->index].udata == ev) {
+
+        ngx_connection_t *cn = (ngx_connection_t *) ev->data;
+        ngx_log_debug(ev->log, "kqueue del event: %d: ft:%d" _
+                      cn->fd _ event);
+
+        if (ev->index < --nchanges) {
+            e = (ngx_event_t *) change_list[nchanges].udata;
+            change_list[ev->index] = change_list[nchanges];
+            e->index = ev->index;
+        }
 
         return NGX_OK;
     }
 
+    if (flags & NGX_CLOSE_EVENT)
+        return NGX_OK;
+
     return ngx_kqueue_set_event(ev, event, EV_DELETE);
 }
 
+
 int ngx_kqueue_set_event(ngx_event_t *ev, int filter, u_int flags)
 {
     struct timespec  ts = { 0, 0 };
@@ -110,13 +124,18 @@ int ngx_kqueue_set_event(ngx_event_t *ev
     return NGX_OK;
 }
 
+
 int ngx_kqueue_process_events(ngx_log_t *log)
 {
     int              events, i;
-    u_int            timer = 0, delta = 0;
+    u_int            timer, delta;
     ngx_event_t      *ev;
     struct timeval   tv;
-    struct timespec  ts, *tp = NULL;
+    struct timespec  ts, *tp;
+
+    timer = 0;
+    delta = 0;
+    tp = NULL;
 
     if (timer_queue.timer_next != &timer_queue) {
         timer = timer_queue.timer_next->timer_delta;
@@ -212,6 +231,7 @@ int ngx_kqueue_process_events(ngx_log_t 
     return NGX_OK;
 }
 
+
 void ngx_kqueue_add_timer(ngx_event_t *ev, ngx_msec_t timer)
 {
     ngx_event_t *e;
--- a/src/event/modules/ngx_kqueue_module.h
+++ b/src/event/modules/ngx_kqueue_module.h
@@ -8,7 +8,7 @@
 
 int ngx_kqueue_init(int max_connections, ngx_log_t *log);
 int ngx_kqueue_add_event(ngx_event_t *ev, int event, u_int flags);
-int ngx_kqueue_del_event(ngx_event_t *ev, int event);
+int ngx_kqueue_del_event(ngx_event_t *ev, int event, u_int flags);
 int ngx_kqueue_set_event(ngx_event_t *ev, int filter, u_int flags);
 void ngx_kqueue_add_timer(ngx_event_t *ev, ngx_msec_t timer);
 int ngx_kqueue_process_events(ngx_log_t *log);
--- a/src/event/modules/ngx_select_module.c
+++ b/src/event/modules/ngx_select_module.c
@@ -8,24 +8,26 @@
 #include <ngx_event.h>
 #include <ngx_select_module.h>
 
-static fd_set       master_read_fd_set;
-static fd_set       master_write_fd_set;
-static fd_set       work_read_fd_set;
-static fd_set       work_write_fd_set;
+
+/* should be per-thread */
+static fd_set         master_read_fd_set;
+static fd_set         master_write_fd_set;
+static fd_set         work_read_fd_set;
+static fd_set         work_write_fd_set;
 
 #if (WIN32)
-static int          max_read;
-static int          max_write;
+static int            max_read;
+static int            max_write;
 #else
-static int          max_fd;
+static int            max_fd;
 #endif
 
-static int          nevents;
+static int            nevents;
 
 static ngx_event_t  **event_index;
 static ngx_event_t  **ready_index;
 static ngx_event_t    timer_queue;
-
+/* */
 
 static fd_set *ngx_select_get_fd_set(ngx_socket_t fd, int event,
                                      ngx_log_t *log);
@@ -121,12 +123,15 @@ int ngx_select_add_event(ngx_event_t *ev
     return NGX_OK;
 }
 
-int ngx_select_del_event(ngx_event_t *ev, int event)
+int ngx_select_del_event(ngx_event_t *ev, int event, u_int flags)
 {
     ngx_connection_t *c;
     c = (ngx_connection_t *) ev->data;
 
-    ngx_log_debug(c->log, "del event: %d" _ c->fd);
+    if (ev->index == NGX_INVALID_INDEX)
+        return NGX_OK;
+
+    ngx_log_debug(c->log, "del event: %d, %d" _ c->fd _ event);
 
 #if (WIN32)
     if (event == NGX_READ_EVENT) {
@@ -148,13 +153,13 @@ int ngx_select_del_event(ngx_event_t *ev
         max_fd = -1;
 #endif
 
-    nevents--;
-
-    if (ev->index < nevents) {
+    if (ev->index < --nevents) {
         event_index[ev->index] = event_index[nevents];
         event_index[ev->index]->index = ev->index;
     }
 
+    ev->index = NGX_INVALID_INDEX;
+
     return NGX_OK;
 }
 
@@ -162,7 +167,7 @@ int ngx_select_process_events(ngx_log_t 
 {
     int                i, ready, found, nready;
     u_int              timer, delta;
-    ngx_event_t       *ev, *nx;
+    ngx_event_t       *ev;
     ngx_connection_t  *c;
     struct timeval     tv, *tp;
 
@@ -195,6 +200,15 @@ int ngx_select_process_events(ngx_log_t 
     }
 #endif
 
+#if 1
+    /* DEBUG */
+    for (i = 0; i < nevents; i++) {
+        ev = event_index[i];
+        c = (ngx_connection_t *) ev->data;
+        ngx_log_debug(log, "select: %d" _ c->fd);
+    }
+#endif
+
     ngx_log_debug(log, "select timer: %d" _ timer);
 
 #if (WIN32)
@@ -222,17 +236,17 @@ int ngx_select_process_events(ngx_log_t 
 
     if (timer) {
         if (delta >= timer) {
-            for (ev = timer_queue.timer_next;
-                 ev != &timer_queue && delta >= ev->timer_delta;
-                 /* void */)
-            {
+            for ( ;; ) {
+                ev = timer_queue.timer_next;
+
+                if (ev == &timer_queue || delta < ev->timer_delta)
+                    break;
+
                 delta -= ev->timer_delta;
-                nx = ev->timer_next;
                 ngx_del_timer(ev);
                 ev->timedout = 1;
-                if (ev->event_handler(ev) == -1)
+                if (ev->event_handler(ev) == NGX_ERROR)
                     ev->close_handler(ev);
-                ev = nx;
             }
 
         } else {
@@ -249,15 +263,13 @@ int ngx_select_process_events(ngx_log_t 
 
         if (ev->write) {
             if (FD_ISSET(c->fd, &work_write_fd_set)) {
-                ngx_log_debug(log, "select write %d" _
-                              c->fd);
+                ngx_log_debug(log, "select write %d" _ c->fd);
                 found = 1;
             }
 
         } else {
             if (FD_ISSET(c->fd, &work_read_fd_set)) {
-                ngx_log_debug(log, "select read %d" _
-                              c->fd);
+                ngx_log_debug(log, "select read %d" _ c->fd);
                 found = 1;
             }
         }
@@ -274,13 +286,14 @@ int ngx_select_process_events(ngx_log_t 
 
         if (ev->oneshot) {
             ngx_del_timer(ev);
+
             if (ev->write)
-                ngx_select_del_event(ev, NGX_WRITE_EVENT);
+                ngx_select_del_event(ev, NGX_WRITE_EVENT, 0);
             else
-                ngx_select_del_event(ev, NGX_READ_EVENT);
+                ngx_select_del_event(ev, NGX_READ_EVENT, 0);
         }
 
-        if (ev->event_handler(ev) == -1)
+        if (ev->event_handler(ev) == NGX_ERROR)
             ev->close_handler(ev);
 
         ready--;
--- a/src/event/modules/ngx_select_module.h
+++ b/src/event/modules/ngx_select_module.h
@@ -8,7 +8,7 @@
 
 int ngx_select_init(int max_connections, ngx_log_t *log);
 int ngx_select_add_event(ngx_event_t *ev, int event, u_int flags);
-int ngx_select_del_event(ngx_event_t *ev, int event);
+int ngx_select_del_event(ngx_event_t *ev, int event, u_int flags);
 int ngx_select_set_event(ngx_event_t *ev, int filter, u_int flags);
 void ngx_select_add_timer(ngx_event_t *ev, ngx_msec_t timer);
 int ngx_select_process_events(ngx_log_t *log);
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -21,7 +21,7 @@ ngx_event_t         *ngx_read_events, *n
 
 #if !(USE_KQUEUE)
 
-#if 0
+#if 1
 ngx_event_type_e     ngx_event_type = NGX_SELECT_EVENT;
 #else
 ngx_event_type_e     ngx_event_type = NGX_KQUEUE_EVENT;
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -10,6 +10,8 @@
 #include <ngx_alloc.h>
 #include <ngx_array.h>
 
+#define NGX_INVALID_INDEX  0x80000000
+
 typedef struct ngx_event_s       ngx_event_t;
 
 struct ngx_event_s {
@@ -79,7 +81,7 @@ typedef enum {
 
 typedef struct {
     int  (*add)(ngx_event_t *ev, int event, u_int flags);
-    int  (*del)(ngx_event_t *ev, int event);
+    int  (*del)(ngx_event_t *ev, int event, u_int flags);
     void (*timer)(ngx_event_t *ev, ngx_msec_t timer);
     int  (*process)(ngx_log_t *log);
     int  (*read)(ngx_event_t *ev, char *buf, size_t size);
@@ -96,8 +98,12 @@ NGX_ONESHOT_EVENT          select, poll,
 NGX_CLEAR_EVENT            kqueue
 NGX_AIO_EVENT              overlapped, aio_read, aioread
                                 no need to add or delete events
+
+NGX_CLOSE_EVENT            kqueue: kqueue deletes events for file that closed
 */
 
+#define NGX_CLOSE_EVENT    1
+
 #if (HAVE_KQUEUE)
 
 #define NGX_READ_EVENT     EVFILT_READ
@@ -124,15 +130,12 @@ NGX_AIO_EVENT              overlapped, a
 
 #endif
 
-
 #if (USE_KQUEUE)
 
 #define ngx_init_events      ngx_kqueue_init
 #define ngx_process_events   ngx_kqueue_process_events
-#define ngx_kqueue_add_event(ev, event)                                       \
-            ngx_kqueue_set_event(ev, event, EV_ADD | flags)
-#define ngx_kqueue_del_event(ev, event)                                       \
-            ngx_kqueue_set_event(ev, event, EV_DELETE)
+#define ngx_add_event        ngx_kqueue_add_event
+#define ngx_del_event        ngx_kqueue_add_event
 #define ngx_add_timer        ngx_kqueue_add_timer
 #define ngx_event_recv       ngx_event_recv_core
 
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -1,3 +1,5 @@
+
+#include <nginx.h>
 
 #include <ngx_config.h>
 #include <ngx_core.h>
@@ -11,11 +13,12 @@
 
 int ngx_event_accept(ngx_event_t *ev)
 {
-    ngx_err_t           err;
-    ngx_socket_t        s;
-    struct sockaddr_in  addr;
-    int addrlen = sizeof(struct sockaddr_in);
-    ngx_connection_t *cn = (ngx_connection_t *) ev->data;
+    ngx_err_t          err;
+    ngx_socket_t       s;
+    ngx_event_t       *rev, *wev;
+    ngx_connection_t  *c, *ac;
+
+    ac = (ngx_connection_t *) ev->data;
             
     ngx_log_debug(ev->log, "ngx_event_accept: accept ready: %d" _
                   ev->available);
@@ -23,69 +26,73 @@ int ngx_event_accept(ngx_event_t *ev)
     ev->ready = 0;
   
     do {
-        if ((s = accept(cn->fd, cn->sockaddr, &cn->socklen)) == -1) {
+        if ((s = accept(ac->fd, ac->sockaddr, &ac->socklen)) == -1) {
             err = ngx_socket_errno;
             if (err == NGX_EAGAIN) {
                 ngx_log_error(NGX_LOG_INFO, ev->log, err,
                              "ngx_event_accept: EAGAIN while accept %s",
-                             cn->addr_text);
+                             ac->addr_text);
                 return NGX_OK;
             }
 
             ngx_log_error(NGX_LOG_ERR, ev->log, err,
-                         "ngx_event_accept: accept %s failed", cn->addr_text);
+                         "ngx_event_accept: accept %s failed", ac->addr_text);
             /* if we return NGX_ERROR listen socket would be closed */
             return NGX_OK;
         }
 
-        ngx_log_debug(ev->log, "ngx_event_accept: accept: %d" _ s);
-
 #if !(HAVE_INHERITED_NONBLOCK)
         if (ngx_nonblocking(s) == -1)
             ngx_log_error(NGX_LOG_ERR, log, ngx_socket_errno,
                           ngx_nonblocking_n "failed");
 #endif
 
-        ngx_memzero(&ngx_read_events[s], sizeof(ngx_event_t));
-        ngx_memzero(&ngx_write_events[s], sizeof(ngx_event_t));
-        ngx_memzero(&ngx_connections[s], sizeof(ngx_connection_t));
+        rev = &ngx_read_events[s];
+        wev = &ngx_write_events[s];
+        c = &ngx_connections[s];
+
+        ngx_memzero(rev, sizeof(ngx_event_t));
+        ngx_memzero(wev, sizeof(ngx_event_t));
+        ngx_memzero(c, sizeof(ngx_connection_t));
 
-        ngx_connections[s].sockaddr = cn->sockaddr;
-        ngx_connections[s].family = cn->family;
-        ngx_connections[s].socklen = cn->socklen;
-        ngx_connections[s].addr = cn->addr;
-        ngx_connections[s].addr_text.len = cn->addr_text.len;
-        ngx_connections[s].post_accept_timeout = cn->post_accept_timeout;
+        c->sockaddr = ac->sockaddr;
+        c->family = ac->family;
+        c->socklen = ac->socklen;
+        c->addr = ac->addr;
+        c->addr_text.len = ac->addr_text.len;
+        c->post_accept_timeout = ac->post_accept_timeout;
 
-        ngx_read_events[s].data = ngx_write_events[s].data
-                                                         = &ngx_connections[s];
-        ngx_connections[s].read = &ngx_read_events[s];
-        ngx_connections[s].write = &ngx_write_events[s];
+        rev->index = wev->index = NGX_INVALID_INDEX;
 
-        ngx_connections[s].fd = s;
-        ngx_connections[s].unexpected_eof = 1;
-        ngx_write_events[s].write = 1;
-        ngx_write_events[s].ready = 1;
+        rev->data = wev->data = c;
+        c->read = rev;
+        c->write = wev;
 
-        ngx_write_events[s].timer = ngx_read_events[s].timer = 10000;
+        c->fd = s;
+        c->unexpected_eof = 1;
+        wev->write = 1;
+        wev->ready = 1;
 
-        ngx_write_events[s].timer_handler =
-            ngx_read_events[s].timer_handler = ngx_event_close_connection;
+        wev->timer = rev->timer = 10000;
+        wev->timer_handler = rev->timer_handler = ngx_event_close_connection;
+        wev->close_handler = rev->close_handler = ngx_event_close_connection;
 
-        ngx_write_events[s].close_handler =
-            ngx_read_events[s].close_handler = ngx_event_close_connection;
+        c->server = ac->server;
+        c->servers = ac->servers;
+        c->log = rev->log = wev->log = ev->log;
 
-        ngx_connections[s].server = cn->server;
-        ngx_connections[s].servers = cn->servers;
-        ngx_connections[s].log =
-            ngx_read_events[s].log = ngx_write_events[s].log = ev->log;
+        /* STUB: x86: SP: xadd, MT: lock xadd, MP: lock xadd, shared */
+        c->number = ngx_connection_counter++;
+
+        ngx_log_debug(ev->log, "ngx_event_accept: accept: %d, %d" _
+                                s _ c->number);
 
 #if (HAVE_DEFERRED_ACCEPT)
         if (ev->accept_filter)
-            ngx_read_events[s].ready = 1;
+            rev->ready = 1;
 #endif
 
-        cn->handler(&ngx_connections[s]);
+        ac->handler(c);
 
 #if (HAVE_KQUEUE)
 #if !(USE_KQUEUE)
--- a/src/event/ngx_event_close.c
+++ b/src/event/ngx_event_close.c
@@ -11,21 +11,20 @@ int ngx_event_close_connection(ngx_event
     int rc;
     ngx_connection_t *c = (ngx_connection_t *) ev->data;
 
+    ngx_log_debug(c->log, "CLOSE: %d" _ c->fd);
+
     ngx_assert((c->fd != -1), return NGX_ERROR, c->log,
                "ngx_event_close: already closed");
 
     ngx_destroy_pool(c->pool);
 
+    ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
+    ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT);
+
     if ((rc = ngx_close_socket(c->fd)) == -1)
         ngx_log_error(NGX_LOG_ERR, c->log, ngx_socket_errno,
                       "ngx_event_close: close failed");
 
-    if (c->read->next)
-        ngx_del_event(c->read, NGX_READ_EVENT);
-
-    if (c->write->next)
-        ngx_del_event(c->write, NGX_WRITE_EVENT);
-
     c->fd = -1;
 
     return rc;
--- a/src/event/ngx_event_write.c
+++ b/src/event/ngx_event_write.c
@@ -98,8 +98,7 @@ ngx_chain_t *ngx_event_write(ngx_connect
                                   (ngx_iovec_t *) trailer->elts, trailer->nelts,
                                   &sent, c->log);
             } else {
-                rc = ngx_sendv(c, (ngx_iovec_t *) header->elts,
-                               header->nelts);
+                rc = ngx_sendv(c, (ngx_iovec_t *) header->elts, header->nelts);
 
                 sent = rc > 0 ? rc: 0;
 
--- a/src/http/modules/ngx_http_event_proxy_handler.c
+++ b/src/http/modules/ngx_http_event_proxy_handler.c
@@ -309,7 +309,9 @@ static int ngx_http_proxy_read_response_
 
     if (n == 0) {
         ngx_log_debug(c->log, "CLOSE proxy");
-        ngx_del_event(ev, NGX_READ_EVENT);
+#if 0
+        ngx_del_event(ev, NGX_READ_EVENT, NGX_CLOSE_EVENT);
+#endif
         ngx_event_close_connection(ev);
 
         p->hunk_n = 0;
@@ -439,7 +441,9 @@ static int ngx_http_proxy_read_response_
 
     if (n == 0) {
         ngx_log_debug(c->log, "CLOSE proxy");
-        ngx_del_event(ev, NGX_READ_EVENT);
+#if 0
+        ngx_del_event(ev, NGX_READ_EVENT, NGX_CLOSE_EVENT);
+#endif
         ngx_event_close_connection(ev);
 
         p->hunk_n = 0;
--- a/src/http/modules/ngx_http_index_handler.c
+++ b/src/http/modules/ngx_http_index_handler.c
@@ -16,7 +16,16 @@ static void *ngx_http_index_merge_conf(n
 static char *ngx_http_index_set_index(ngx_pool_t *p, void *conf,
                                       ngx_str_t *value);
 
-static ngx_command_t ngx_http_index_commands[];
+
+static ngx_command_t ngx_http_index_commands[] = {
+
+    {"index", ngx_http_index_set_index, NULL,
+     NGX_HTTP_LOC_CONF, NGX_CONF_ITERATE,
+     "set index files"},
+
+    {NULL}
+
+};
 
 
 ngx_http_module_t  ngx_http_index_module = {
@@ -33,17 +42,6 @@ ngx_http_module_t  ngx_http_index_module
 };
 
 
-static ngx_command_t ngx_http_index_commands[] = {
-
-    {"index", ngx_http_index_set_index, NULL,
-     NGX_HTTP_LOC_CONF, NGX_CONF_ITERATE,
-     "set index files"},
-
-    {NULL}
-
-};
-
-
 int ngx_http_index_handler(ngx_http_request_t *r)
 {
     int          i;
@@ -71,10 +69,14 @@ int ngx_http_index_handler(ngx_http_requ
         ngx_memcpy(file, index[i].data, index[i].len + 1);
 
         fd = ngx_open_file(name, NGX_FILE_RDONLY);
-        if (fd == -1) {
+        if (fd == NGX_INVALID_FILE) {
             err = ngx_errno;
             if (err == NGX_ENOENT)
                 continue;
+#if (WIN32)
+            if (err == ERROR_PATH_NOT_FOUND)
+                continue;
+#endif
 
             ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
                           ngx_open_file_n " %s failed", name);
@@ -82,9 +84,9 @@ int ngx_http_index_handler(ngx_http_requ
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
-        r->filename.len = r->server->doc_root_len + r->uri.len + index[i].len;
-        r->filename.data = name; 
-        r->fd = fd; 
+        r->file.name.len = r->server->doc_root_len + r->uri.len + index[i].len;
+        r->file.name.data = name; 
+        r->file.fd = fd; 
 
         loc.len = r->uri.len + index[i].len;
         return ngx_http_internal_redirect(r, loc);
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_log_handler.c
@@ -0,0 +1,79 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_string.h>
+#include <ngx_alloc.h>
+#include <ngx_time.h>
+#include <ngx_http.h>
+
+
+ngx_http_module_t  ngx_http_log_module;
+
+
+static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+                  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+
+int ngx_http_log_handler(ngx_http_request_t *r)
+{
+    size_t    len;
+    char     *line, *p;
+    ngx_tm_t  tm;
+
+#if (WIN32)
+    len = 2 + 22 + 3 + 20 + 5 + 20 + 2;
+#else
+    len = 2 + 22 + 3 + 20 + 5 + 20 + 1;
+#endif
+
+    len += r->connection->addr_text.len;
+    len += r->request_line.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++ = ' ';
+
+    ngx_localtime(&tm);
+
+    *p++ = '[';
+    p += ngx_snprintf(p, 21, "%02d/%s/%d:%02d:%02d:%02d",
+                      tm.ngx_tm_mday, months[tm.ngx_tm_mon],
+                      tm.ngx_tm_year + 1900,
+                      tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);
+
+    *p++ = ']';
+
+    *p++ = ' ';
+
+    *p++ = '"';
+    ngx_memcpy(p, r->request_line.data, r->request_line.len);
+    p += r->request_line.len;
+    *p++ = '"';
+
+    *p++ = ' ';
+
+    p += ngx_snprintf(p, 4, "%d", r->headers_out.status);
+
+    *p++ = ' ';
+
+    p += ngx_snprintf(p, 21, QD_FMT, r->connection->sent);
+
+    *p++ = ' ';
+
+    p += ngx_snprintf(p, 21, "%u", r->connection->number);
+
+#if (WIN32)
+    *p++ = CR; *p++ = LF;
+#else
+    *p++ = LF;
+#endif
+
+    write(1, line, len);
+
+    return NGX_OK;
+}
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -24,51 +24,84 @@ void ngx_http_static_init()
 
 int ngx_http_static_handler(ngx_http_request_t *r)
 {
-    int rc;
+    int  rc;
+    ngx_err_t    err;
     ngx_hunk_t  *h;
     ngx_http_log_ctx_t  *ctx;
 
-/*
+#if 0
     ngx_http_event_static_handler_loc_conf_t  *cf;
 
     cf = (ngx_http_event_static_handler_loc_conf_t *)
              ngx_get_module_loc_conf(r, &ngx_http_event_static_handler_module);
 
-*/
+#endif
 
     ngx_http_discard_body(r);
     ctx = r->connection->log->data;
     ctx->action = "sending response";
 
-    if (r->fd != -1)
-        r->fd = ngx_open_file(r->filename.data, NGX_FILE_RDONLY);
+    if (r->file.fd == NGX_INVALID_FILE)
+        r->file.fd = ngx_open_file(r->file.name.data, NGX_FILE_RDONLY);
 
-    if (r->fd == -1) {
+    if (r->file.fd == NGX_INVALID_FILE) {
+        err = ngx_errno;
         ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
                       "ngx_http_static_handler: "
-                      ngx_open_file_n " %s failed", r->filename.data);
+                      ngx_open_file_n " %s failed", r->file.name.data);
 
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        if (err == NGX_ENOENT)
+            return NGX_HTTP_NOT_FOUND;
+#if (WIN32)
+        else if (err == ERROR_PATH_NOT_FOUND)
+            return NGX_HTTP_NOT_FOUND;
+#endif
+        else
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    if (ngx_stat_fd(r->fd, &r->fileinfo) == -1) {
+    if (!r->file.info_valid) {
+        if (ngx_stat_fd(r->file.fd, &r->file.info) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
+                          "ngx_http_static_handler: "
+                          ngx_stat_fd_n " %s failed", r->file.name.data);
+
+            if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR)
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
+                              "ngx_http_static_handler: "
+                              ngx_close_file_n " %s failed", r->file.name.data);
+
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        r->file.info_valid = 1;
+    }
+
+#if !(WIN32) /* it's probably Unix specific */
+
+    if (!ngx_is_file(r->file.info)) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
                       "ngx_http_static_handler: "
-                      ngx_stat_fd_n " %s failed", r->filename.data);
+                      "%s is not regular file", r->file.name.data);
 
-        /* close fd */
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR)
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
+                          "ngx_http_static_handler: "
+                          ngx_close_file_n " %s failed", r->file.name.data);
+
+        return NGX_HTTP_NOT_FOUND;
     }
 
+#endif
+
     r->headers_out.status = NGX_HTTP_OK;
-    r->headers_out.content_length = ngx_file_size(r->fileinfo);
-/*
-    r->headers_out.last_modified = ngx_file_mtime(r->fileinfo);
-*/
+    r->headers_out.content_length = ngx_file_size(r->file.info);
+    r->headers_out.last_modified_time = ngx_file_mtime(r->file.info);
 
     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";
 
@@ -90,83 +123,29 @@ int ngx_http_static_handler(ngx_http_req
         r->headers_out.content_type->value.len = 25;
         r->headers_out.content_type->value.data = "text/html; charset=koi8-r";
     }
+    /**/
 
-    /* STUB */
-    rc = ngx_http_header_filter(r);
-/*
-    rc = ngx_send_http_header(r);
-*/
+    /* we need to allocate them before header would be sent */
+    ngx_test_null(h, ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)),
+                  NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+    ngx_test_null(h->file, ngx_pcalloc(r->pool, sizeof(ngx_file_t)),
+                  NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+    rc = ngx_http_send_header(r);
     if (r->header_only)
         return rc;
 
-    /* TODO: NGX_HTTP_INTERNAL_SERVER_ERROR is too late */
-
-    /* STUB */
-    ngx_test_null(h, ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)),
-                  NGX_HTTP_INTERNAL_SERVER_ERROR);
-
     h->type = NGX_HUNK_FILE|NGX_HUNK_LAST;
     h->pos.file = 0;
-    h->last.file = ngx_file_size(r->fileinfo);
+    h->last.file = ngx_file_size(r->file.info);
 
-    /* STUB */
-    ngx_test_null(h->file, ngx_pcalloc(r->pool, sizeof(ngx_file_t)),
-                  NGX_HTTP_INTERNAL_SERVER_ERROR);
-    h->file->fd = r->fd;
+    h->file->fd = r->file.fd;
     h->file->log = r->connection->log;
 
     rc = ngx_http_output_filter(r, h);
+
     ngx_log_debug(r->connection->log, "0 output_filter: %d" _ rc);
+
     return rc;
 }
-
-#if 0
-
-static void *ngx_create_index_config()
-{
-    ngx_http_index_handler_loc_conf_t  *cf;
-
-    ngx_check_null(cf, ngx_alloc(p, sizeof(ngx_http_index_handler_loc_conf)),
-                   NULL);
-
-    cf->indices = ngx_create_array(p, sizeof(ngx_http_index_t), 5);
-    if (cf->indices == NULL)
-        return NULL;
-
-    cf->max_index_len = 0;
-
-    return cf;
-}
-
-static void *ngx_merge_index_config()
-{
-    if (p->indices->nelts > 0) {
-
-        copy and check dups
-
-        if (c->max_index_len < c->max_index_len)
-            c->max_index_len < c->max_index_len);
-    }
-}
-
-static void *ngx_set_index()
-{
-    if (*conf == NULL) {
-        cf = ngx_create_index_conf();
-        if (cf == NULL)
-            return "can not create config";
-    }
-
-    while (args) {
-       index = ngx_push_array(cf->indices);
-       index->name = arg;
-       index->len = ngx_strlen(arg) + 1;
-
-       if (cf->max_index_len < index->len)
-           cf->max_index_len = index->len;
-    }
-
-    *conf = cf;
-}
-
-#endif
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -32,11 +32,13 @@
 
 #define NGX_HTTP_OK                     200
 #define NGX_HTTP_SPECIAL_RESPONSE       300
-#define NGX_HTTP_MOVED_PERMANENTLY      302
+#define NGX_HTTP_MOVED_PERMANENTLY      301
+#define NGX_HTTP_MOVED_TEMPORARILY      302
+#define NGX_HTTP_NOT_MODIFIED           304
 #define NGX_HTTP_BAD_REQUEST            400
 #define NGX_HTTP_NOT_FOUND              404
 #define NGX_HTTP_REQUEST_URI_TOO_LARGE  414
-#define NGX_HTTP_INTERNAL_SERVER_ERROR  503
+#define NGX_HTTP_INTERNAL_SERVER_ERROR  500
 
 
 #define NGX_HTTP_STATIC_HANDLER     0
@@ -59,21 +61,25 @@ typedef struct {
     time_t         lingering_time;
 } ngx_http_server_t;
 
+
 typedef struct {
     int    len;
     char  *data;
     int    offset;
 } ngx_http_header_t;
 
+
 typedef struct {
     ngx_table_elt_t  *host;
     ngx_table_elt_t  *connection;
+    ngx_table_elt_t  *if_modified_since;
     ngx_table_elt_t  *user_agent;
     ngx_table_elt_t  *accept_encoding;
 
     ngx_table_t      *headers;
 } ngx_http_headers_in_t;
 
+
 typedef struct {
     int               status;
     ngx_str_t         status_line;
@@ -93,12 +99,18 @@ typedef struct {
     time_t            last_modified_time;
 } ngx_http_headers_out_t;
 
+
 typedef struct ngx_http_request_s ngx_http_request_t;
 
 struct ngx_http_request_s {
-    ngx_str_t  filename;
+    ngx_file_t  file;
 
+#if 0
+    ngx_str_t   filename;
+    ngx_file_info_t fileinfo;
     ngx_fd_t  fd;
+    int    filename_len;
+#endif
 
     void  **ctx;
     void  **loc_conf;
@@ -110,11 +122,8 @@ struct ngx_http_request_s {
     ngx_http_headers_in_t   headers_in;
     ngx_http_headers_out_t  headers_out;
 
-    int    filename_len;
     int  (*handler)(ngx_http_request_t *r);
 
-    ngx_file_info_t fileinfo;
-
     int    method;
 
     time_t  lingering_time;
@@ -146,7 +155,7 @@ struct ngx_http_request_s {
 
     unsigned  header_only:1;
     unsigned  unusual_uri:1;  /* URI is not started with '/' - "GET http://" */
-    unsigned  complex_uri:1;  /* URI with "./" or with "//" */
+    unsigned  complex_uri:1;  /* URI with "/." or with "//" (WIN32) */
 
     int    state;
     char  *uri_start;
@@ -163,6 +172,7 @@ struct ngx_http_request_s {
 #endif
 };
 
+
 typedef struct {
     char  *action;
     char  *client;
@@ -181,10 +191,20 @@ typedef struct {
 
     int             (*translate_handler)(ngx_http_request_t *r);
 
-    int             (*init_output_body_filter)(int (**next_filter)
+    int             (*output_header_filter) (ngx_http_request_t *r);
+    int             (*next_output_header_filter) (ngx_http_request_t *r);
+
+    int             (*output_body_filter)();
+    int             (*next_output_body_filter)
+                                      (ngx_http_request_t *r, ngx_chain_t *ch);
+
+#if 0
+    int             (*next_output_body_filter)(int (**next_filter)
                                      (ngx_http_request_t *r, ngx_chain_t *ch));
+#endif
 } ngx_http_module_t;
 
+
 #define NGX_HTTP_MODULE  0
 
 #define ngx_get_module_loc_conf(r, module)  r->loc_conf[module.index]
@@ -204,10 +224,14 @@ typedef struct {
 
 /* STUB */
 int ngx_http_init(ngx_pool_t *pool, ngx_log_t *log);
+/**/
 
 int ngx_http_init_connection(ngx_connection_t *c);
 
 
+int ngx_http_discard_body(ngx_http_request_t *r);
+
+
 extern int ngx_max_module;
 
 extern ngx_http_module_t *ngx_http_modules[];
--- a/src/http/ngx_http_config.c
+++ b/src/http/ngx_http_config.c
@@ -9,6 +9,8 @@
 
 int ngx_max_module;
 
+int (*ngx_http_top_header_filter) (ngx_http_request_t *r);
+
 /* STUB: gobal srv and loc conf */
 void **ngx_srv_conf;
 void **ngx_loc_conf;
@@ -53,13 +55,27 @@ int ngx_http_init_modules(ngx_pool_t *po
 int ngx_http_init_filters(ngx_pool_t *pool, ngx_http_module_t **modules)
 {
     int i;
-    int (*filter)(ngx_http_request_t *r, ngx_chain_t *ch);
+    int (*ohf)(ngx_http_request_t *r);
+    int (*obf)(ngx_http_request_t *r, ngx_chain_t *ch);
 
-    filter = ngx_http_write_filter;
+    ohf = NULL;
 
     for (i = 0; modules[i]; i++) {
-        if (modules[i]->init_output_body_filter)
-            modules[i]->init_output_body_filter(&filter);
+        if (modules[i]->output_header_filter) {
+            modules[i]->next_output_header_filter = ohf;
+            ohf = modules[i]->output_header_filter;
+        }
+    }
+
+    ngx_http_top_header_filter = ohf;
+
+    obf = NULL;
+
+    for (i = 0; modules[i]; i++) {
+        if (modules[i]->output_body_filter) {
+            modules[i]->next_output_body_filter = obf;
+            obf = modules[i]->output_body_filter;
+        }
     }
 }
 
--- a/src/http/ngx_http_config.h
+++ b/src/http/ngx_http_config.h
@@ -10,6 +10,8 @@
 int ngx_http_config_modules(ngx_pool_t *pool, ngx_http_module_t **modules);
 
 
+extern int (*ngx_http_top_header_filter) (ngx_http_request_t *r);
+
 extern void **ngx_srv_conf;
 extern void **ngx_loc_conf;
 
--- a/src/http/ngx_http_core.c
+++ b/src/http/ngx_http_core.c
@@ -18,7 +18,16 @@ static void *ngx_http_core_create_loc_co
 static int ngx_http_core_translate_handler(ngx_http_request_t *r);
 
 
-static ngx_command_t ngx_http_core_commands[];
+static ngx_command_t ngx_http_core_commands[] = {
+
+    {"send_timeout", ngx_conf_set_time_slot,
+     offsetof(ngx_http_core_loc_conf_t, send_timeout),
+     NGX_HTTP_LOC_CONF, NGX_CONF_TAKE1,
+     "set timeout for sending response"},
+
+    {NULL}
+
+};
 
 
 ngx_http_module_t  ngx_http_core_module = {
@@ -35,18 +44,6 @@ ngx_http_module_t  ngx_http_core_module 
 };
 
 
-static ngx_command_t ngx_http_core_commands[] = {
-
-    {"send_timeout", ngx_conf_set_time_slot,
-     offsetof(ngx_http_core_loc_conf_t, send_timeout),
-     NGX_HTTP_LOC_CONF, NGX_CONF_TAKE1,
-     "set timeout for sending response"},
-
-    {NULL}
-
-};
-
-
 int ngx_http_handler(ngx_http_request_t *r)
 {
     int  rc, i;
@@ -95,22 +92,54 @@ static int ngx_http_core_translate_handl
         return NGX_OK;
     }
 
-    r->filename.len = r->server->doc_root_len + r->uri.len + 2;
+    r->file.name.len = r->server->doc_root_len + r->uri.len + 2;
 
-    ngx_test_null(r->filename.data,
-                  ngx_palloc(r->pool, r->filename.len + 1),
+    ngx_test_null(r->file.name.data,
+                  ngx_palloc(r->pool, r->file.name.len + 1),
                   NGX_HTTP_INTERNAL_SERVER_ERROR);
 
-    loc = ngx_cpystrn(r->filename.data, r->server->doc_root,
+    loc = ngx_cpystrn(r->file.name.data, r->server->doc_root,
                       r->server->doc_root_len);
     last = ngx_cpystrn(loc, r->uri.data, r->uri.len + 1);
 
-    ngx_log_debug(r->connection->log, "HTTP filename: '%s'" _ r->filename.data);
+    ngx_log_debug(r->connection->log, "HTTP filename: '%s'" _
+                  r->file.name.data);
+
+#if (WIN32)
 
-    if (ngx_file_type(r->filename.data, &r->fileinfo) == -1) {
+    /* There is no way to open file or directory in Win32 with
+       one syscall: CreateFile() returns ERROR_ACCESS_DENIED on directory,
+       so we need to check its type before opening */
+
+#if 0 /* OLD: ngx_file_type() is to be removed */
+    if (ngx_file_type(r->file.name.data, &r->file.info) == -1) {
+#endif
+
+    r->file.info.dwFileAttributes = GetFileAttributes(r->file.name.data);
+    if (r->file.info.dwFileAttributes == INVALID_FILE_ATTRIBUTES) {
         err = ngx_errno;
         ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
-                      ngx_file_type_n " %s failed", r->filename.data);
+                      "ngx_http_core_translate_handler: "
+                      ngx_file_type_n " %s failed", r->file.name.data);
+
+        if (err == ERROR_FILE_NOT_FOUND)
+            return NGX_HTTP_NOT_FOUND;
+        else if (err == ERROR_PATH_NOT_FOUND)
+            return NGX_HTTP_NOT_FOUND;
+        else
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+#else
+
+    if (r->file.fd == NGX_INVALID_FILE)
+        r->file.fd = ngx_open_file(r->file.name.data, NGX_FILE_RDONLY);
+
+    if (r->file.fd == NGX_INVALID_FILE) {
+        err = ngx_errno;
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
+                      "ngx_http_static_handler: "
+                      ngx_open_file_n " %s failed", r->file.name.data);
 
         if (err == NGX_ENOENT)
             return NGX_HTTP_NOT_FOUND;
@@ -118,8 +147,33 @@ static int ngx_http_core_translate_handl
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    if (ngx_is_dir(r->fileinfo)) {
-        ngx_log_debug(r->connection->log, "HTTP DIR: '%s'" _ r->filename.data);
+    if (!r->file.info_valid) {
+        if (ngx_stat_fd(r->file.fd, &r->file.info) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
+                          "ngx_http_static_handler: "
+                          ngx_stat_fd_n " %s failed", r->file.name.data);
+
+            if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR)
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
+                              "ngx_http_static_handler: "
+                              ngx_close_file_n " %s failed", r->file.name.data);
+
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        r->file.info_valid = 1;
+    }
+#endif
+
+    if (ngx_is_dir(r->file.info)) {
+        ngx_log_debug(r->connection->log, "HTTP DIR: '%s'" _ r->file.name.data);
+
+#if !(WIN32)
+        if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR)
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
+                          "ngx_http_static_handler: "
+                          ngx_close_file_n " %s failed", r->file.name.data);
+#endif
 
         /* BROKEN: need to include server name */
 
@@ -144,6 +198,11 @@ static int ngx_http_core_translate_handl
 }
 
 
+int ngx_http_send_header(ngx_http_request_t *r)
+{
+    return (*ngx_http_top_header_filter)(r);
+}
+
 
 int ngx_http_redirect(ngx_http_request_t *r, int redirect)
 {
@@ -162,17 +221,27 @@ int ngx_http_error(ngx_http_request_t *r
 
     /* log request */
 
+    ngx_http_special_response(r, error);
     return ngx_http_close_request(r);
 }
 
 
 int ngx_http_close_request(ngx_http_request_t *r)
 {
-    ngx_assert((r->fd != -1), /* void */; , r->connection->log,
-               "file already closed");
+    ngx_log_debug(r->connection->log, "CLOSE#: %d" _ r->file.fd);
+
+    ngx_http_log_handler(r);
+
+    ngx_assert((r->file.fd != NGX_INVALID_FILE), /* void */ ; ,
+               r->connection->log, "file already closed");
 
-    if (r->fd != -1) {
-        if (ngx_close_file(r->fd) == -1)
+    if (r->file.fd != NGX_INVALID_FILE) {
+/* STUB WIN32 */
+#if (WIN32)
+        if (ngx_close_file(r->file.fd) == 0)
+#else
+        if (ngx_close_file(r->file.fd) == -1)
+#endif
             ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
                           ngx_close_file_n " failed");
     }
--- a/src/http/ngx_http_event.c
+++ b/src/http/ngx_http_event.c
@@ -13,6 +13,7 @@
 #include <ngx_table.h>
 #include <ngx_hunk.h>
 #include <ngx_connection.h>
+#include <ngx_inet.h>
 #include <ngx_http.h>
 #include <ngx_http_config.h>
 #include <ngx_http_core.h>
@@ -33,19 +34,14 @@ static int ngx_http_process_request_line
 static int ngx_http_process_request_headers(ngx_http_request_t *r);
 static int ngx_http_process_request_header_line(ngx_http_request_t *r);
 
-static int ngx_http_event_handler(ngx_http_request_t *r);
-static int ngx_http_block_read(ngx_event_t *ev);
-
-
-static int ngx_http_read_discarded_body(ngx_event_t *ev);
-
-int ngx_http_handler(ngx_http_request_t *r);
-static int ngx_http_set_default_handler(ngx_http_request_t *r);
+static int ngx_http_event_request_handler(ngx_http_request_t *r);
 
 static int ngx_http_writer(ngx_event_t *ev);
-static int ngx_http_set_lingering_close(ngx_http_request_t *r);
+static int ngx_http_block_read(ngx_event_t *ev);
+static int ngx_http_read_discarded_body(ngx_event_t *ev);
 static int ngx_http_keepalive_handler(ngx_event_t *ev);
-static int ngx_http_lingering_close(ngx_event_t *ev);
+static int ngx_http_set_lingering_close(ngx_http_request_t *r);
+static int ngx_http_lingering_close_handler(ngx_event_t *ev);
 
 #if 0
 int ngx_http_special_response(ngx_http_request_t *r, int error);
@@ -70,6 +66,8 @@ static char *header_errors[] = {
 static ngx_http_header_t headers_in[] = {
     { 4, "Host", offsetof(ngx_http_headers_in_t, host) },
     { 10, "Connection", offsetof(ngx_http_headers_in_t, connection) },
+    { 17, "If-Modified-Since",
+                           offsetof(ngx_http_headers_in_t,if_modified_since) },
 
     { 10, "User-Agent", offsetof(ngx_http_headers_in_t, user_agent) },
 
@@ -106,15 +104,11 @@ int ngx_http_init_connection(ngx_connect
     ngx_test_null(c->addr_text.data, ngx_palloc(c->pool, c->addr_text.len),
                   NGX_ERROR);
 
-    /* STUB: should be ngx_inet_ntop() */
-#if (WIN32)
-    c->addr_text.data = inet_ntoa((struct in_addr *)
-                                              ((char *)c->sockaddr + c->addr));
-#else
-    inet_ntop(c->family, (char *)c->sockaddr + c->addr,
-              c->addr_text.data, c->addr_text.len);
-#endif
-    /**/
+    ngx_test_null(c->addr_text.len,
+                  ngx_inet_ntop(c->family,
+                                (char *)c->sockaddr + c->addr,
+                                c->addr_text.data, c->addr_text.len),
+                  NGX_ERROR);
 
     ngx_test_null(ctx, ngx_pcalloc(c->pool, sizeof(ngx_http_log_ctx_t)),
                   NGX_ERROR);
@@ -166,6 +160,7 @@ static int ngx_http_init_request(ngx_eve
     c->data = r;
     r->connection = c;
     r->server = srv;
+    r->file.fd = NGX_INVALID_FILE;
 
     /* STUB */
     r->srv_conf = ngx_srv_conf;
@@ -189,6 +184,10 @@ static int ngx_http_init_request(ngx_eve
     ngx_test_null(r->ctx, ngx_pcalloc(r->pool, sizeof(void *) * ngx_max_module),
                   ngx_http_close_request(r));
 
+    r->headers_out.headers = ngx_create_table(r->pool, 10);
+    r->headers_out.content_length = -1;
+    r->headers_out.last_modified_time = -1;
+
     ev->event_handler = ngx_http_process_request_header;
     r->state_handler = ngx_http_process_request_line;
     r->header_timeout = 1;
@@ -262,7 +261,7 @@ static int ngx_http_process_request_head
     }
 
     if (rc == NGX_OK)
-        return ngx_http_event_handler(r);
+        return ngx_http_event_request_handler(r);
     else
         return rc;
 }
@@ -280,7 +279,8 @@ static int ngx_http_process_request_line
     c = r->connection;
 
     if (rc == NGX_OK) {
-        r->uri.len = r->uri_end - r->uri_start;
+        r->uri.len = (r->args_start ? r->args_start - 1 : r->uri_end)
+                                                                - r->uri_start;
         ngx_test_null(r->uri.data, ngx_palloc(r->pool, r->uri.len + 1),
                       ngx_http_close_request(r));
         ngx_cpystrn(r->uri.data, r->uri_start, r->uri.len + 1);
@@ -309,7 +309,8 @@ static int ngx_http_process_request_line
         /* */
 
         if (r->uri_ext) {
-            r->exten.len = r->uri_end - r->uri_ext;
+            r->exten.len = (r->args_start ? r->args_start - 1 : r->uri_end)
+                                                                  - r->uri_ext;
             ngx_test_null(r->exten.data,
                           ngx_palloc(r->pool, r->exten.len + 1), 
                           ngx_http_close_request(r));
@@ -326,8 +327,6 @@ static int ngx_http_process_request_line
         /* TODO: check too long URI - no space for header, compact buffer */
 
         r->headers_in.headers = ngx_create_table(r->pool, 10);
-        /* THINK: when to create out.headers ? */
-        r->headers_out.headers = ngx_create_table(r->pool, 10);
 
         r->state_handler = ngx_http_process_request_headers;
         ctx = r->connection->log->data;
@@ -372,7 +371,14 @@ static int ngx_http_process_request_head
 
         } else if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
             ngx_log_debug(r->connection->log, "HTTP header done");
-            return NGX_OK;
+
+            if (r->http_version > NGX_HTTP_VERSION_10
+                && r->headers_in.host == NULL)
+            {
+                return ngx_http_error(r, NGX_HTTP_BAD_REQUEST);
+            } else {
+                return NGX_OK;
+            }
 
         } else if (rc == NGX_AGAIN) {
             return NGX_AGAIN;
@@ -422,7 +428,7 @@ static int ngx_http_process_request_head
 }
 
 
-static int ngx_http_event_handler(ngx_http_request_t *r)
+static int ngx_http_event_request_handler(ngx_http_request_t *r)
 {
     int rc;
     ngx_msec_t  timeout;
@@ -494,181 +500,6 @@ static int ngx_http_event_handler(ngx_ht
 }
 
 
-static int ngx_http_block_read(ngx_event_t *ev)
-{
-    ngx_log_debug(ev->log, "http read blocked");
-
-    ev->blocked = 1;
-    return ngx_del_event(ev, NGX_READ_EVENT);
-}
-
-
-
-/* FIND PLACE ******************** */
-
-void ngx_http_discard_body(ngx_http_request_t *r)
-{
-    ngx_log_debug(r->connection->log, "set discard body");
-
-    ngx_del_timer(r->connection->read);
-
-    if (r->client_content_length)
-        r->connection->read->event_handler = ngx_http_read_discarded_body;
-}
-
-
-static int ngx_http_read_discarded_body(ngx_event_t *ev)
-{
-    size_t   size;
-    ssize_t  n;
-    ngx_connection_t    *c;
-    ngx_http_request_t  *r;
-
-    c = (ngx_connection_t *) ev->data;
-    r = (ngx_http_request_t *) c->data;
-
-    ngx_log_debug(ev->log, "http read discarded body");
-
-    if (ev->timedout)
-        return NGX_ERROR;
-
-    if (r->discarded_buffer == NULL)
-        ngx_test_null(r->discarded_buffer,
-                      ngx_palloc(r->pool, r->server->discarded_buffer_size),
-                      NGX_ERROR);
-
-    size = r->client_content_length;
-    if (size > r->server->discarded_buffer_size)
-        size = r->server->discarded_buffer_size;
-
-    n = ngx_event_recv(c, r->discarded_buffer, size);
-    if (n == NGX_ERROR)
-        return NGX_ERROR;
-
-    if (n == NGX_AGAIN)
-        return NGX_OK;
-
-    r->client_content_length -= n;
-    /* XXX: what if r->client_content_length == 0 ? */
-    return NGX_OK;
-}
-
-
-static int ngx_http_discarded_read(ngx_event_t *ev)
-{
-    ssize_t n;
-    ngx_connection_t    *c;
-    ngx_http_request_t  *r;
-
-    c = (ngx_connection_t *) ev->data;
-    r = (ngx_http_request_t *) c->data;
-
-    ngx_log_debug(ev->log, "http discarded read");
-
-    if (ev->timedout)
-        return NGX_ERROR;
-
-    if (r->discarded_buffer == NULL)
-        ngx_test_null(r->discarded_buffer,
-                      ngx_palloc(r->pool, r->server->discarded_buffer_size),
-                      NGX_ERROR);
-
-    n = ngx_event_recv(c, r->discarded_buffer,
-                       r->server->discarded_buffer_size);
-
-    return n;
-}
-
-/* ******************** */
-
-
-#if 0
-int ngx_http_handler(ngx_http_request_t *r)
-{
-    int  rc;
-
-    r->connection->unexpected_eof = 0;
-    r->lingering_close = 1;
-
-    /* STUB: should find handler */
-#if 1
-    r->filter = NGX_HTTP_FILTER_NEED_IN_MEMORY;
-#endif
-    rc = ngx_http_set_default_handler(r);
-
-    if (rc >= NGX_HTTP_SPECIAL_RESPONSE)
-        return ngx_http_special_response(r, rc);
-
-    rc = r->handler(r);
-
-    return rc;
-}
-#endif
-
-
-#if 0
-static int ngx_http_set_default_handler(ngx_http_request_t *r)
-{
-    ngx_err_t  err;
-    char      *name, *loc, *file;
-
-#if 0
-    /* STUB */
-    r->handler = ngx_http_proxy_handler;
-    return NGX_OK;
-#endif
-
-/*  NO NEEDED
-    ngx_test_null(r->headers_out,
-                  ngx_pcalloc(r->pool, sizeof(ngx_http_headers_out_t)),
-                  NGX_HTTP_INTERNAL_SERVER_ERROR);
-*/
-
-    if (*(r->uri_end - 1) == '/') {
-        r->handler = ngx_http_index_handler;
-        return NGX_OK;
-    }
-
-    /* 20 bytes is spare space for some index name, i.e. index.html */
-    r->filename_len = r->uri_end - r->uri_start + r->server->doc_root_len + 20;
-
-    ngx_test_null(r->filename,
-                  ngx_palloc(r->pool, r->filename_len),
-                  NGX_HTTP_INTERNAL_SERVER_ERROR);
-
-    r->location = ngx_cpystrn(r->filename, r->server->doc_root,
-                              r->server->doc_root_len);
-    file = ngx_cpystrn(r->location, r->uri_start,
-                       r->uri_end - r->uri_start + 1);
-
-    ngx_log_debug(r->connection->log, "HTTP filename: '%s'" _ r->filename);
-
-    if (ngx_file_type(r->filename, &r->fileinfo) == -1) {
-        err = ngx_errno;
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
-                      ngx_file_type_n " %s failed", r->filename);
-
-        if (err == NGX_ENOENT)
-            return NGX_HTTP_NOT_FOUND;
-        else
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-    if (ngx_is_dir(r->fileinfo)) {
-        ngx_log_debug(r->connection->log, "HTTP DIR: '%s'" _ r->filename);
-        *file++ = '/';
-        *file = '\0';
-        r->headers_out.location = r->location;
-        return NGX_HTTP_MOVED_PERMANENTLY;
-    }
-
-    r->handler = ngx_http_static_handler;
-
-    return NGX_OK;
-}
-#endif
-
-
 static int ngx_http_writer(ngx_event_t *ev)
 {
     int rc;
@@ -735,35 +566,93 @@ static int ngx_http_writer(ngx_event_t *
 }
 
 
-static int ngx_http_set_lingering_close(ngx_http_request_t *r)
+static int ngx_http_block_read(ngx_event_t *ev)
 {
-    r->lingering_time = ngx_time() + r->server->lingering_time;
-    r->connection->read->event_handler = ngx_http_lingering_close;
+    ngx_log_debug(ev->log, "http read blocked");
+
+    ev->blocked = 1;
+    return ngx_del_event(ev, NGX_READ_EVENT, 0);
+}
+
+
+int ngx_http_discard_body(ngx_http_request_t *r)
+{
+    ngx_log_debug(r->connection->log, "set discard body");
 
     ngx_del_timer(r->connection->read);
-    ngx_add_timer(r->connection->read, r->server->lingering_timeout);
 
-#if (HAVE_CLEAR_EVENT)
-    if (ngx_add_event(r->connection->read, NGX_READ_EVENT,
-                      NGX_CLEAR_EVENT) == NGX_ERROR) {
-#else
-    if (ngx_add_event(r->connection->read, NGX_READ_EVENT,
-                      NGX_ONESHOT_EVENT) == NGX_ERROR) {
-#endif
-       return ngx_http_close_request(r);
-    }
-
-    if (ngx_shutdown_socket(r->connection->fd, NGX_WRITE_SHUTDOWN) == NGX_ERROR)
-    {
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_socket_errno,
-                      ngx_shutdown_socket_n " failed");
-        return ngx_http_close_request(r);
-    }
+    if (r->client_content_length)
+        r->connection->read->event_handler = ngx_http_read_discarded_body;
 
     return NGX_OK;
 }
 
 
+static int ngx_http_read_discarded_body(ngx_event_t *ev)
+{
+    size_t   size;
+    ssize_t  n;
+    ngx_connection_t    *c;
+    ngx_http_request_t  *r;
+
+    c = (ngx_connection_t *) ev->data;
+    r = (ngx_http_request_t *) c->data;
+
+    ngx_log_debug(ev->log, "http read discarded body");
+
+    if (ev->timedout)
+        return NGX_ERROR;
+
+    if (r->discarded_buffer == NULL)
+        ngx_test_null(r->discarded_buffer,
+                      ngx_palloc(r->pool, r->server->discarded_buffer_size),
+                      NGX_ERROR);
+
+    size = r->client_content_length;
+    if (size > r->server->discarded_buffer_size)
+        size = r->server->discarded_buffer_size;
+
+    n = ngx_event_recv(c, r->discarded_buffer, size);
+    if (n == NGX_ERROR)
+        return NGX_ERROR;
+
+    if (n == NGX_AGAIN)
+        return NGX_OK;
+
+    r->client_content_length -= n;
+    /* XXX: what if r->client_content_length == 0 ? */
+    return NGX_OK;
+}
+
+
+#if 0
+static int ngx_http_discarded_read(ngx_event_t *ev)
+{
+    ssize_t n;
+    ngx_connection_t    *c;
+    ngx_http_request_t  *r;
+
+    c = (ngx_connection_t *) ev->data;
+    r = (ngx_http_request_t *) c->data;
+
+    ngx_log_debug(ev->log, "http discarded read");
+
+    if (ev->timedout)
+        return NGX_ERROR;
+
+    if (r->discarded_buffer == NULL)
+        ngx_test_null(r->discarded_buffer,
+                      ngx_palloc(r->pool, r->server->discarded_buffer_size),
+                      NGX_ERROR);
+
+    n = ngx_event_recv(c, r->discarded_buffer,
+                       r->server->discarded_buffer_size);
+
+    return n;
+}
+#endif
+
+
 static int ngx_http_keepalive_handler(ngx_event_t *ev)
 {
     ssize_t n;
@@ -772,7 +661,7 @@ static int ngx_http_keepalive_handler(ng
 
     c = (ngx_connection_t *) ev->data;
 
-    ngx_log_debug(ev->log, "http keepalive");
+    ngx_log_debug(ev->log, "http keepalive handler");
 
     if (ev->timedout)
         return NGX_DONE;
@@ -800,17 +689,46 @@ static int ngx_http_keepalive_handler(ng
 }
 
 
-static int ngx_http_lingering_close(ngx_event_t *ev)
+static int ngx_http_set_lingering_close(ngx_http_request_t *r)
 {
-    ssize_t  n;
-    ngx_msec_t   timer;
+    r->lingering_time = ngx_time() + r->server->lingering_time;
+    r->connection->read->event_handler = ngx_http_lingering_close_handler;
+
+    ngx_del_timer(r->connection->read);
+    ngx_add_timer(r->connection->read, r->server->lingering_timeout);
+
+#if (HAVE_CLEAR_EVENT)
+    if (ngx_add_event(r->connection->read, NGX_READ_EVENT,
+                      NGX_CLEAR_EVENT) == NGX_ERROR) {
+#else
+    if (ngx_add_event(r->connection->read, NGX_READ_EVENT,
+                      NGX_ONESHOT_EVENT) == NGX_ERROR) {
+#endif
+       return ngx_http_close_request(r);
+    }
+
+    if (ngx_shutdown_socket(r->connection->fd, NGX_WRITE_SHUTDOWN) == -1)
+    {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_socket_errno,
+                      ngx_shutdown_socket_n " failed");
+        return ngx_http_close_request(r);
+    }
+
+    return NGX_OK;
+}
+
+
+static int ngx_http_lingering_close_handler(ngx_event_t *ev)
+{
+    ssize_t              n;
+    ngx_msec_t           timer;
     ngx_connection_t    *c;
     ngx_http_request_t  *r;
 
     c = (ngx_connection_t *) ev->data;
     r = (ngx_http_request_t *) c->data;
 
-    ngx_log_debug(ev->log, "http lingering close");
+    ngx_log_debug(ev->log, "http lingering close handler");
 
     if (ev->timedout)
         return NGX_DONE;
--- a/src/http/ngx_http_get_time.c
+++ b/src/http/ngx_http_get_time.c
@@ -2,6 +2,7 @@
 #include <nginx.h>
 
 #include <ngx_config.h>
+#include <ngx_types.h>
 
 
 ngx_http_get_time(char *buf, time_t t)
deleted file mode 100644
--- a/src/http/ngx_http_header.h
+++ /dev/null
@@ -1,2 +0,0 @@
-
-gx_chunk_t *gx_http_header(gx_http_request_t *r, gx_http_header_out_t *out);
--- a/src/http/ngx_http_header_filter.c
+++ b/src/http/ngx_http_header_filter.c
@@ -9,7 +9,7 @@
 #include <ngx_http.h>
 
 
-#if 0
+static int ngx_http_header_filter(ngx_http_request_t *r);
 
 ngx_http_module_t  ngx_http_header_filter_module = {
     NGX_HTTP_MODULE,
@@ -21,33 +21,40 @@ ngx_http_module_t  ngx_http_header_filte
     NULL,                                  /* init module */
     NULL,                                  /* translate handler */
 
-    ngx_http_header_filter_init            /* init output header filter */
-    NULL                                   /* init output body filter */
+    ngx_http_header_filter,                /* output header filter */
+    NULL,                                  /* next output header filter */
+    NULL,                                  /* output body filter */
+    NULL                                   /* next output body filter */
 };
 
-#endif
-
 
 static char server_string[] = "Server: " NGINX_VER CRLF;
 
 
 static ngx_str_t http_codes[] = {
+
     { 6,  "200 OK" },
 
     { 21, "301 Moved Permanently" },
+    { 21, "302 Moved Temporarily" },
+    { 0,  NULL },
+    { 16, "304 Not Modified" },
 
     { 15, "400 Bad Request" },
     { 0,  NULL },
     { 0,  NULL },
     { 13, "403 Forbidden" },
-    { 13, "404 Not Found" }
+    { 13, "404 Not Found" },
+
+    { 25, "500 Internal Server Error" }
 };
 
 
 
-int ngx_http_header_filter(ngx_http_request_t *r)
+static int ngx_http_header_filter(ngx_http_request_t *r)
 {
     int  len, status, i;
+    time_t            ims;
     ngx_hunk_t       *h;
     ngx_chain_t      *ch;
     ngx_table_elt_t  *header;
@@ -55,10 +62,30 @@ int ngx_http_header_filter(ngx_http_requ
     if (r->http_version < NGX_HTTP_VERSION_10)
         return NGX_OK;
 
-    /* 9 is for "HTTP/1.1 ", 2 is for trailing "\r\n"
+    /* 9 is for "HTTP/1.x ", 2 is for trailing "\r\n"
        and 2 is for end of header */
     len = 9 + 2 + 2;
 
+    if (r->headers_in.if_modified_since && r->headers_out.status == NGX_HTTP_OK)
+    {
+        /* TODO: check LM header */
+        if (r->headers_out.last_modified_time) {
+            ims = ngx_http_parse_time(
+                                  r->headers_in.if_modified_since->value.data,
+                                  r->headers_in.if_modified_since->value.len);
+
+            ngx_log_debug(r->connection->log, "%d %d" _
+                          ims _ r->headers_out.last_modified_time);
+
+            if (ims != NGX_ERROR && ims >= r->headers_out.last_modified_time) {
+                r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
+                r->headers_out.content_length = -1;
+                r->headers_out.content_type->key.len = 0;
+                r->header_only = 1;
+            }
+        }
+    }
+
     /* status line */
     if (r->headers_out.status_line.len) {
         len += r->headers_out.status_line.len;
@@ -69,8 +96,12 @@ int ngx_http_header_filter(ngx_http_requ
         else if (r->headers_out.status < NGX_HTTP_BAD_REQUEST)
             status = r->headers_out.status - NGX_HTTP_MOVED_PERMANENTLY + 1;
 
+        else if (r->headers_out.status < NGX_HTTP_INTERNAL_SERVER_ERROR)
+            status = r->headers_out.status - NGX_HTTP_BAD_REQUEST + 1 + 4;
+
         else
-            status = r->headers_out.status - NGX_HTTP_BAD_REQUEST + 1 + 1;
+            status = r->headers_out.status
+                                 - NGX_HTTP_INTERNAL_SERVER_ERROR + 1 + 4 + 5;
 
         len += http_codes[status].len;
     }
@@ -99,6 +130,14 @@ int ngx_http_header_filter(ngx_http_requ
         len += r->headers_out.content_type.len + 16;
 #endif
 
+    if (r->headers_out.last_modified && r->headers_out.last_modified->key.len) {
+        len += r->headers_out.last_modified->key.len
+               + r->headers_out.last_modified->value.len + 2;
+    } else if (r->headers_out.last_modified_time != -1) {
+        /* "Last-Modified: ... \r\n"; */
+        len += 46;
+    }
+
     if (r->keepalive)
         len += 24;
     else
@@ -114,7 +153,7 @@ int ngx_http_header_filter(ngx_http_requ
 
     ngx_test_null(h, ngx_create_temp_hunk(r->pool, len, 0, 64), NGX_ERROR);
 
-    /* "HTTP/1.1 " */
+    /* "HTTP/1.x " */
     ngx_memcpy(h->last.mem, "HTTP/1.1 ", 9);
     h->last.mem += 9;
 
@@ -159,6 +198,17 @@ int ngx_http_header_filter(ngx_http_requ
     }
 #endif
 
+    if (!(r->headers_out.last_modified
+          && r->headers_out.last_modified->key.len)
+        && r->headers_out.last_modified_time != -1)
+    {
+        ngx_memcpy(h->last.mem, "Last-Modified: ", 15);
+        h->last.mem += 15;
+        h->last.mem += ngx_http_get_time(h->last.mem,
+                                         r->headers_out.last_modified_time);
+        *(h->last.mem++) = CR; *(h->last.mem++) = LF;
+    }
+
     if (r->keepalive) {
         ngx_memcpy(h->last.mem, "Connection: keep-alive" CRLF, 24);
         h->last.mem += 24;
@@ -181,9 +231,17 @@ int ngx_http_header_filter(ngx_http_requ
         *(h->last.mem++) = CR; *(h->last.mem++) = LF;
     }
 
+    /* STUB */
+    *(h->last.mem) = '\0';
+    ngx_log_debug(r->connection->log, "%s\n" _ h->pos.mem);
+    /**/
+
     /* end of HTTP header */
     *(h->last.mem++) = CR; *(h->last.mem++) = LF;
 
+    if (r->header_only)
+        h->type |= NGX_HUNK_LAST;
+
     ngx_test_null(ch, ngx_palloc(r->pool, sizeof(ngx_chain_t)), NGX_ERROR);
 
     ch->hunk = h;
--- a/src/http/ngx_http_modules.c
+++ b/src/http/ngx_http_modules.c
@@ -1,12 +1,18 @@
 
 #include <ngx_http.h>
 
+extern ngx_http_module_t ngx_http_header_filter_module;
+
 extern ngx_http_module_t ngx_http_write_filter_module;
 extern ngx_http_module_t ngx_http_output_filter_module;
+
 extern ngx_http_module_t ngx_http_core_module;
 extern ngx_http_module_t ngx_http_index_module;
 
 ngx_http_module_t *ngx_http_modules[] = {
+
+    &ngx_http_header_filter_module,
+
     &ngx_http_write_filter_module,
     &ngx_http_output_filter_module,
 
--- a/src/http/ngx_http_output_filter.c
+++ b/src/http/ngx_http_output_filter.c
@@ -9,29 +9,15 @@
 #include <ngx_http_output_filter.h>
 
 
+int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk);
 static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src);
+#if 0
 static int ngx_http_output_filter_init(
             int (**next_filter)(ngx_http_request_t *r, ngx_chain_t *ch));
+#endif
 static void *ngx_http_output_filter_create_conf(ngx_pool_t *pool);
 
 
-static ngx_command_t ngx_http_output_filter_commands[];
-
-
-ngx_http_module_t  ngx_http_output_filter_module = {
-    NGX_HTTP_MODULE,
-
-    NULL,                                  /* create server config */
-    ngx_http_output_filter_create_conf,    /* create location config */
-    ngx_http_output_filter_commands,       /* module directives */
-
-    NULL,                                  /* init module */
-    NULL,                                  /* translate handler */
-
-    ngx_http_output_filter_init            /* init output body filter */
-};
-
-
 static ngx_command_t ngx_http_output_filter_commands[] = {
 
     {"output_buffer", ngx_conf_set_size_slot,
@@ -44,8 +30,27 @@ static ngx_command_t ngx_http_output_fil
 };
 
 
+ngx_http_module_t  ngx_http_output_filter_module = {
+    NGX_HTTP_MODULE,
+
+    NULL,                                  /* create server config */
+    ngx_http_output_filter_create_conf,    /* create location config */
+    ngx_http_output_filter_commands,       /* module directives */
+
+    NULL,                                  /* init module */
+    NULL,                                  /* translate handler */
+
+    NULL,                                  /* output header filter */
+    NULL,                                  /* next output header filter */
+    ngx_http_output_filter,                /* output body filter */
+    NULL                                   /* next output body filter */
+};
+
+
+#if 0
 static int (*ngx_http_output_next_filter)(ngx_http_request_t *r,
                                           ngx_chain_t *ch);
+#endif
 
 
 int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk)
@@ -66,7 +71,9 @@ int ngx_http_output_filter(ngx_http_requ
                             ngx_http_output_filter_module,
                             sizeof(ngx_http_output_filter_ctx_t));
 
+#if 0
         ctx->next_filter = ngx_http_output_next_filter;
+#endif
     }
 
     if (hunk && (hunk->type & NGX_HUNK_LAST))
@@ -87,7 +94,11 @@ int ngx_http_output_filter(ngx_http_requ
 
             /* our hunk is still busy */
             if (ctx->hunk->pos.mem < ctx->hunk->last.mem) {
+                rc = ngx_http_output_filter_module.
+                                              next_output_body_filter(r, NULL);
+#if 0
                 rc = ctx->next_filter(r, NULL);
+#endif
 
             /* our hunk is free */
             } else {
@@ -110,7 +121,7 @@ int ngx_http_output_filter(ngx_http_requ
                         if (ce->hunk->type & NGX_HUNK_FILE)
                             break;
 
-                        if ((ce->hunk->type & NGX_HUNK_MEMORY|NGX_HUNK_MMAP)
+                        if ((ce->hunk->type & (NGX_HUNK_MEMORY|NGX_HUNK_MMAP))
                             && (r->filter & NGX_HTTP_FILTER_NEED_TEMP))
                             break;
                     }
@@ -121,7 +132,11 @@ int ngx_http_output_filter(ngx_http_requ
                     ctx->out.next = NULL;
                 }
 
+                rc = ngx_http_output_filter_module.
+                                         next_output_body_filter(r, &ctx->out);
+#if 0
                 rc = ctx->next_filter(r, &ctx->out);
+#endif;
             }
 
             /* delete completed hunks from input chain */
@@ -139,7 +154,11 @@ int ngx_http_output_filter(ngx_http_requ
         } else {
 
             if (hunk == NULL) {
+                rc = ngx_http_output_filter_module.
+                                              next_output_body_filter(r, NULL);
+#if 0
                 rc = ctx->next_filter(r, NULL);
+#endif;
 
             } else {
 
@@ -147,7 +166,7 @@ int ngx_http_output_filter(ngx_http_requ
                 if (((r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY)
                         && (hunk->type & NGX_HUNK_FILE))
                     || ((r->filter & NGX_HTTP_FILTER_NEED_TEMP)
-                        && (hunk->type & NGX_HUNK_MEMORY|NGX_HUNK_MMAP))
+                        && (hunk->type & (NGX_HUNK_MEMORY|NGX_HUNK_MMAP)))
                    ) {
 
                     /* out hunk is still busy */
@@ -155,7 +174,11 @@ int ngx_http_output_filter(ngx_http_requ
                         ngx_add_hunk_to_chain(ctx->in, hunk, r->pool,
                                               NGX_ERROR);
 
+                        rc = ngx_http_output_filter_module.
+                                              next_output_body_filter(r, NULL);
+#if 0
                         rc = ctx->next_filter(r, NULL);
+#endif
 
                     } else {
                         if (ctx->hunk == NULL) {
@@ -201,7 +224,11 @@ int ngx_http_output_filter(ngx_http_requ
                             ctx->out.hunk = ctx->hunk;
                             ctx->out.next = NULL;
 
+                            rc = ngx_http_output_filter_module.
+                                         next_output_body_filter(r, &ctx->out);
+#if 0
                             rc = ctx->next_filter(r, &ctx->out);
+#endif
                         }
                     }
 
@@ -209,7 +236,11 @@ int ngx_http_output_filter(ngx_http_requ
                     ctx->out.hunk = hunk;
                     ctx->out.next = NULL;
 
+                    rc = ngx_http_output_filter_module.
+                                         next_output_body_filter(r, &ctx->out);
+#if 0
                     rc = ctx->next_filter(r, &ctx->out);
+#endif
                 }
             }
         }
@@ -269,6 +300,11 @@ static int ngx_http_output_filter_copy_h
         dst->last.mem += size;
     }
 
+#if 1
+    if (src->type & NGX_HUNK_LAST) 
+        dst->type |= NGX_HUNK_LAST;
+#endif
+
     return NGX_OK;
 }
 
@@ -286,6 +322,7 @@ static void *ngx_http_output_filter_crea
     return conf;
 }
 
+#if 0
 static int ngx_http_output_filter_init(
             int (**next_filter)(ngx_http_request_t *r, ngx_chain_t *ch))
 {
@@ -294,3 +331,4 @@ static int ngx_http_output_filter_init(
 
     return NGX_OK;
 }
+#endif
--- a/src/http/ngx_http_output_filter.h
+++ b/src/http/ngx_http_output_filter.h
@@ -15,7 +15,9 @@ typedef struct {
 } ngx_http_output_filter_conf_t;
 
 typedef struct {
+#if 0
     int         (*next_filter)(ngx_http_request_t *r, ngx_chain_t *ch);
+#endif
     ngx_hunk_t   *hunk;
     ngx_chain_t  *in;
     ngx_chain_t   out;
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -110,7 +110,7 @@ printf("\nstate: %d, pos: %x, end: %x, c
             }
             break;
 
-        /* check dot after slash */
+        /* check "/." or "//" */
         case sw_after_slash_in_uri:
             switch (ch) {
             case CR:
@@ -132,8 +132,9 @@ printf("\nstate: %d, pos: %x, end: %x, c
                 state = sw_uri;
                 break;
             case '/':
+#if (WIN32)
                 r->complex_uri = 1;
-                state = sw_uri;
+#endif
                 break;
             case '?':
                 r->args_start = p;
--- a/src/http/ngx_http_parse_time.c
+++ b/src/http/ngx_http_parse_time.c
@@ -1,7 +1,8 @@
 
-#include <time.h>
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_types.h>
 
-#define NGX_ERROR  -1
 
 static int mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 
@@ -16,6 +17,7 @@ time_t ngx_http_parse_time(char *value, 
         isoc      /* Tue Dec 10 23:50:13 2002    */
     } fmt;
 
+    fmt = 0;
     end = value + len;
 
     for (p = value; p < end; p++) {
@@ -182,31 +184,38 @@ time_t ngx_http_parse_time(char *value, 
                + (*(p + 2) - '0') * 10 + *(p + 3) - '0';
     }
 
+#if 0
     printf("%d.%d.%d %d:%d:%d\n", day, month + 1, year, hour, min, sec);
+#endif
 
-    if (hour > 23 || min > 60 || sec > 60)
+    if (hour > 23 || min > 59 || sec > 59)
          return NGX_ERROR;
 
     if (day == 29 && month == 1) {
         if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0))
             return NGX_ERROR;
 
-    } else if (day > mday[month])
+    } else if (day > mday[month]) {
         return NGX_ERROR;
     }
 
     if (sizeof(time_t) <= 4 && year >= 2038)
         return NGX_ERROR;
 
+    /* shift new year to 1st March, needed for Gauss's formula */
     if (--month <= 0) {
        month += 12;
        year -= 1;
     }
-
-    return year / 4 - year / 100 + year / 400
-           + 367 * month / 12 + day + year * 365 - 719499;
+           /* Gauss's formula for days from 1 March 1 BC */
+    return (365 * year + year / 4 - year / 100 + year / 400
+                                               + 367 * month / 12 + day - 31
+           /* 719527 days are between 1 March 1 BC and 1 March 1970,
+              31 and 28 days in Jan and Feb 1970  */
+            - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;
 }
 
+#if 0
 char zero[] = "Sun, 01 Jan 1970 08:49:30";
 char one[]  = "Sunday, 11-Dec-02 08:49:30";
 char two[]  = "Sun Mar 1 08:49:37 2000";
@@ -228,3 +237,5 @@ main()
     rc = ngx_http_parse_time(thr, sizeof(thr) - 1);
     printf("rc: %d\n", rc);
 }
+
+#endif
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -1,21 +1,91 @@
 
+#include <nginx.h>
 
 #include <ngx_config.h>
-#if 0
 #include <ngx_core.h>
-#endif
+#include <ngx_string.h>
 #include <ngx_http.h>
 
+static char error_tail[] =
+"<hr><center>" NGINX_VER "</center>" CRLF
+"</body>" CRLF
+"</html>" CRLF
+;
+
+static char error_400_page[] =
+"<html>" CRLF
+"<head><title>400 Bad Request</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>400 Bad Request</h1></center>" CRLF
+;
+
+static char error_404_page[] =
+"<html>" CRLF
+"<head><title>404 Not Found</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>404 Not Found</h1></center>" CRLF
+;
+
+
+static ngx_str_t error_pages[] = {
+    { 0, NULL},  /* 301 */
+    { 0, NULL},  /* 302 */
+    { 0, NULL},  /* 303 */
+    { 0, NULL},  /* 304 */
+
+    { sizeof(error_400_page) - 1, error_400_page },
+    { 0, NULL},  /* 401 */
+    { 0, NULL},  /* 402 */
+    { 0, NULL},  /* 403 */
+    { sizeof(error_404_page) - 1, error_404_page },
+
+    { 0, NULL}   /* 500 */
+};
 
 int ngx_http_special_response(ngx_http_request_t *r, int error)
 {
-    switch (error) {
+    int  rc, err, len;
+    ngx_hunk_t  *message, *tail;
+
+    len = 0;
+
+    r->headers_out.status = error;
+
+    if (error < NGX_HTTP_BAD_REQUEST)
+        err = error - NGX_HTTP_MOVED_PERMANENTLY;
+
+    else if (error < NGX_HTTP_INTERNAL_SERVER_ERROR)
+        err = error - NGX_HTTP_BAD_REQUEST + 4;
+
+    else
+        err = NGX_HTTP_INTERNAL_SERVER_ERROR + 4 + 5;
+
+    if (error_pages[err].len == 0)
+        r->headers_out.content_length = -1;
+    else
+        r->headers_out.content_length = error_pages[err].len
+                                        + len + sizeof(error_tail);
 
-    default:
-        r->headers_out.status = error;
-        return ngx_http_header_filter(r);
+    ngx_http_send_header(r);
+
+    if (error_pages[err].len == 0)
+        return NGX_OK;
+
+    ngx_test_null(message, ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)),
+                  NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+    message->type = NGX_HUNK_MEMORY;
+    message->pos.mem = error_pages[err].data;
+    message->last.mem = error_pages[err].data + error_pages[err].len;
 
-    }
+    rc = ngx_http_output_filter(r, message);
+
+    ngx_test_null(tail, ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)),
+                  NGX_HTTP_INTERNAL_SERVER_ERROR);
 
-    return ngx_http_error(r, error);
+    tail->type = NGX_HUNK_MEMORY|NGX_HUNK_LAST;
+    tail->pos.mem = error_tail;
+    tail->last.mem = error_tail + sizeof(error_tail);
+
+    rc = ngx_http_output_filter(r, tail);
 }
--- a/src/http/ngx_http_write_filter.c
+++ b/src/http/ngx_http_write_filter.c
@@ -9,10 +9,23 @@
 #include <ngx_http_write_filter.h>
 
 
-static ngx_command_t ngx_http_write_filter_commands[];
+int ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in);
 
 static void *ngx_http_write_filter_create_conf(ngx_pool_t *pool);
 
+
+static ngx_command_t ngx_http_write_filter_commands[] = {
+
+    {"write_buffer", ngx_conf_set_size_slot,
+     offsetof(ngx_http_write_filter_conf_t, buffer_output),
+     NGX_HTTP_LOC_CONF, NGX_CONF_TAKE1, 
+     "set write filter size to buffer output"},
+
+    {NULL}
+
+};
+
+
 ngx_http_module_t  ngx_http_write_filter_module = {
     NGX_HTTP_MODULE,
 
@@ -23,19 +36,10 @@ ngx_http_module_t  ngx_http_write_filter
     NULL,                                  /* init module */
     NULL,                                  /* translate handler */
 
-    NULL                                   /* init output body filter */
-};
-
-
-static ngx_command_t ngx_http_write_filter_commands[] = {
-
-    {"write_buffer", ngx_conf_set_size_slot,
-     offsetof(ngx_http_write_filter_conf_t, buffer_output),
-     NGX_HTTP_LOC_CONF, NGX_CONF_TAKE1, 
-     "set write filter size to buffer output"},
-
-    {NULL}
-
+    NULL,                                  /* output header filter */
+    NULL,                                  /* next output header filter */
+    ngx_http_write_filter,                 /* output body filter */
+    NULL,                                  /* next output body filter */
 };
 
 
@@ -69,7 +73,7 @@ int ngx_http_write_filter(ngx_http_reque
                       ch->hunk->type _ ch->hunk->pos.file _
                       ch->hunk->last.file - ch->hunk->pos.file);
 
-        if (ch->hunk->type & NGX_HUNK_FLUSH|NGX_HUNK_RECYCLED)
+        if (ch->hunk->type & (NGX_HUNK_FLUSH|NGX_HUNK_RECYCLED))
             flush = size;
 
         if (ch->hunk->type & NGX_HUNK_LAST)
@@ -90,7 +94,7 @@ int ngx_http_write_filter(ngx_http_reque
                       ch->hunk->type _ ch->hunk->pos.file _
                       ch->hunk->last.file - ch->hunk->pos.file);
 
-        if (ch->hunk->type & NGX_HUNK_FLUSH|NGX_HUNK_RECYCLED)
+        if (ch->hunk->type & (NGX_HUNK_FLUSH|NGX_HUNK_RECYCLED))
             flush = size;
 
         if (ch->hunk->type & NGX_HUNK_LAST)
@@ -101,6 +105,8 @@ int ngx_http_write_filter(ngx_http_reque
                    ngx_get_module_loc_conf(r->main ? r->main : r,
                                                 ngx_http_write_filter_module);
 
+    ngx_log_debug(r->connection->log, "l:%d f:%d" _ last _ flush);
+
     if (!last && flush == 0 && size < conf->buffer_output)
         return NGX_OK;
 
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -6,6 +6,9 @@
 #include <sys/stat.h>
 
 typedef int                      ngx_fd_t;
+#define NGX_INVALID_FILE         -1
+#define NGX_FILE_ERROR           -1
+
 typedef struct stat              ngx_file_info_t;
 
 
@@ -27,6 +30,7 @@ typedef struct stat              ngx_fil
 #define ngx_stat_fd_n            "fstat()"
 
 #define ngx_is_dir(sb)           (S_ISDIR(sb.st_mode))
+#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
 
--- a/src/os/unix/ngx_sendv.c
+++ b/src/os/unix/ngx_sendv.c
@@ -8,21 +8,21 @@
 
 ssize_t ngx_sendv(ngx_connection_t *c, ngx_iovec_t *iovec, int n)
 {
-     ssize_t rc;
-     ngx_err_t err;
+    ssize_t rc;
+    ngx_err_t err;
 
-     rc = writev(c->fd, iovec, n);
+    rc = writev(c->fd, iovec, n);
 
-     if (rc == -1) {
-         err = ngx_socket_errno;
-         if (err == NGX_EAGAIN) {
-             ngx_log_error(NGX_LOG_INFO, c->log, err, "sendv() eagain");
-             return NGX_AGAIN;
-         }
+    if (rc == -1) {
+        err = ngx_socket_errno;
+        if (err == NGX_EAGAIN) {
+            ngx_log_error(NGX_LOG_INFO, c->log, err, "sendv() eagain");
+            return NGX_AGAIN;
+        }
 
-         ngx_log_error(NGX_LOG_ERR, c->log, err, "sendv() failed");
-         return NGX_ERROR;
-     }
+        ngx_log_error(NGX_LOG_ERR, c->log, err, "sendv() failed");
+        return NGX_ERROR;
+    }
 
-     return rc;
+    return rc;
 }
--- a/src/os/win32/ngx_files.c
+++ b/src/os/win32/ngx_files.c
@@ -14,5 +14,3 @@ ssize_t ngx_read_file(ngx_file_t *file, 
 
     return n;
 }
-
-
--- a/src/os/win32/ngx_files.h
+++ b/src/os/win32/ngx_files.h
@@ -11,6 +11,9 @@
 #endif
 
 typedef HANDLE                      ngx_fd_t;
+#define NGX_INVALID_FILE            INVALID_HANDLE_VALUE
+#define NGX_FILE_ERROR              0
+
 typedef unsigned __int64            off_t;
 
 typedef BY_HANDLE_FILE_INFORMATION  ngx_file_info_t;
@@ -25,33 +28,32 @@ typedef BY_HANDLE_FILE_INFORMATION  ngx_
 
 #define NGX_FILE_RDONLY             GENERIC_READ
 
+#define ngx_close_file              CloseHandle
+#define ngx_close_file_n            "CloseHandle()"
 
 int ngx_file_type(char *filename, ngx_file_info_t *fi);
-#define ngx_file_type_n          "GetFileAttributes"
+#define ngx_file_type_n             "GetFileAttributes"
 
-#define ngx_stat_fd(fd, fi)     GetFileInformationByHandle(fd, fi)
-#define ngx_stat_fd_n           "GetFileInformationByHandle"
+#define ngx_stat_fd(fd, fi)        GetFileInformationByHandle(fd, fi)
+#define ngx_stat_fd_n              "GetFileInformationByHandle"
 
-#define ngx_is_dir(fi)          (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+#define ngx_is_dir(fi)      (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+#define ngx_is_file(fi)     !(fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 
 #define ngx_file_size(fi)                                                   \
-            fi.nFileSizeLow
-
-/*
-#define ngx_file_size(fi)                                                   \
-            ((off_t) fi.nFileSizeHigh << 32 & fi.nFileSizeLow)
-*/
-
-#define ngx_file_mtime(fi)       fi.ftLastWriteTime
-
-/*
-1970 - 1601:
-	116444736000000000
-	19DB1DED53E8000
-*/
+            (((off_t) fi.nFileSizeHigh << 32) | fi.nFileSizeLow)
 
 
-#define ngx_read_file_n             "ReadFile()"
+/* There are 134774 days between 1 Jan 1970 and 1 Jan 1601,
+   11644473600 seconds or 11644473600,000,000,0 100-nanosecond intervals */
+
+#define ngx_file_mtime(fi)                                                  \
+   (time_t) (((((unsigned __int64) fi.ftLastWriteTime.dwHighDateTime << 32) \
+                                 | fi.ftLastWriteTime.dwLowDateTime)        \
+                                          - 116444736000000000) / 10000000)
+
+
+#define ngx_read_file_n            "ReadFile()"
 
 
 #endif /* _NGX_FILES_H_INCLUDED_ */
--- a/src/os/win32/ngx_sendv.c
+++ b/src/os/win32/ngx_sendv.c
@@ -6,29 +6,39 @@
 #include <ngx_log.h>
 #include <ngx_sendv.h>
 
+#include <ngx_string.h>
+
 ssize_t ngx_sendv(ngx_connection_t *c, ngx_iovec_t *iovec, int n)
 {
-     int        rc;
-     size_t     sent;
-     ngx_err_t  err;
+    int         rc;
+    size_t      sent;
+    ngx_err_t   err;
 
-     ngx_log_debug(c->log, "WSASend() start");
+#if 0
+    /* STUB: WSABUF must be 4-byte aligned. Undocumented WSAEINVAL error */
+    ngx_iovec_t iov[10];
+    ngx_memcpy(iov, iovec, n * sizeof(ngx_iovec_t));
+#endif
 
-     rc = WSASend(c->fd, iovec, n, &sent, 0, NULL, NULL);
+    sent = 0;
 
-     ngx_log_debug(c->log, "WSASend() done");
+    ngx_log_debug(c->log, "WSASend: %d, %d, %08x" _ c->fd _ n _ iovec);
+
+    rc = WSASend(c->fd, iovec, n, &sent, 0, NULL, NULL);
 
-     if (rc == -1) {
-         err = ngx_socket_errno;
+    ngx_log_debug(c->log, "WSASend() done");
+
+    if (rc == SOCKET_ERROR) {
+        err = ngx_socket_errno;
 
-         if (err == NGX_EAGAIN) {
-             ngx_log_error(NGX_LOG_INFO, c->log, err, "WSASend() eagain");
-             return NGX_AGAIN;
-         }
+        if (err == NGX_EAGAIN) {
+            ngx_log_error(NGX_LOG_INFO, c->log, err, "WSASend() eagain");
+            return NGX_AGAIN;
+        }
 
-         ngx_log_error(NGX_LOG_ERR, c->log, err, "WSASend() failed");
-         return NGX_ERROR;
-     }
+        ngx_log_error(NGX_LOG_ERR, c->log, err, "WSASend() failed");
+        return NGX_ERROR;
+    }
 
-     return sent;
+    return sent;
 }
--- a/src/os/win32/ngx_time.h
+++ b/src/os/win32/ngx_time.h
@@ -21,5 +21,8 @@ typedef FILETIME       ngx_mtime_t;
 #define ngx_localtime  GetLocalTime
 #define ngx_msec       GetTickCount
 
+/* STUB */
+#define ngx_time()  time(NULL)
+
 
 #endif /* _NGX_TIME_H_INCLUDED_ */