changeset 0:4eff17414a43

nginx-0.0.1-2002-08-06-20:39:45 import The first code that uses "ngx_" prefix, the previous one used "gx_" prefix. At that point the code is not yet usable. The first draft ideas are dated back to 23.10.2001.
author Igor Sysoev <igor@sysoev.ru>
date Tue, 06 Aug 2002 16:39:45 +0000
parents
children d220029ac7f3
files src/core/nginx.c src/core/nginx.h src/core/ngx_alloc.c src/core/ngx_alloc.h src/core/ngx_array.c src/core/ngx_array.h src/core/ngx_auto_config.h src/core/ngx_config.h src/core/ngx_connection.h src/core/ngx_hunk.c src/core/ngx_hunk.h src/core/ngx_listen.c src/core/ngx_listen.h src/core/ngx_log.c src/core/ngx_log.h src/core/ngx_server.h src/core/ngx_string.c src/core/ngx_string.h src/event/modules/ngx_aio_module.c src/event/modules/ngx_kqueue_module.c src/event/modules/ngx_kqueue_module.h src/event/modules/ngx_overlapped_module.c 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_accept.h src/event/ngx_event_close.c src/event/ngx_event_close.h src/event/ngx_event_read.c src/event/ngx_event_write.c src/event/ngx_event_write.h src/http/modules/ngx_http_index_handler.c src/http/ngx_http.h src/http/ngx_http_event.c src/http/ngx_http_header.h src/os/unix/ngx_errno.h src/os/unix/ngx_sendfile.c src/os/unix/ngx_sendfile.h src/os/unix/ngx_sendv.c src/os/unix/ngx_sendv.h src/os/unix/ngx_stat.h src/os/unix/ngx_time.c src/os/unix/ngx_time.h src/os/unix/ngx_types.h src/os/win32/ngx_errno.c src/os/win32/ngx_errno.h src/os/win32/ngx_sendfile.c src/os/win32/ngx_sendfile.h src/os/win32/ngx_sendv.h src/os/win32/ngx_stat.c src/os/win32/ngx_stat.h src/os/win32/ngx_time.h src/os/win32/ngx_types.h
diffstat 55 files changed, 3253 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/core/nginx.c
@@ -0,0 +1,118 @@
+
+#include <nginx.h>
+
+#include <ngx_config.h>
+#include <ngx_string.h>
+#include <ngx_log.h>
+#include <ngx_alloc.h>
+#include <ngx_server.h>
+#include <ngx_connection.h>
+#include <ngx_listen.h>
+
+/*
+#include <ngx_http.h>
+*/
+
+
+#if !(WIN32)
+static int ngx_options(int argc, char *const *argv);
+#endif
+
+char *ngx_root = "/home/is/work/xml/xml/html";
+
+int ngx_http_init_connection(void *data);
+
+
+int ngx_max_conn = 512;
+struct sockaddr_in ngx_addr = {0, AF_INET, 0, 0, 0};
+
+
+ngx_pool_t   ngx_pool;
+ngx_log_t    ngx_log;
+ngx_server_t ngx_server;
+
+
+int main(int argc, char *const *argv)
+{
+    char addr_text[22];
+    ngx_socket_t fd;
+    ngx_listen_t ls;
+#if (WIN32)
+    WSADATA      wsd;
+#endif
+
+
+    ngx_log.log_level = NGX_LOG_DEBUG;
+    ngx_pool.log = &ngx_log;
+    ngx_addr.sin_port = htons(8000);
+    ngx_addr.sin_family = AF_INET;
+
+#if !(WIN32)
+    if (ngx_options(argc, argv) == -1)
+        ngx_log_error(NGX_LOG_EMERG, (&ngx_log), 0, "invalid argument");
+#endif
+
+    ngx_log_debug((&ngx_log), "%d, %s:%d" _ ngx_max_conn _
+                 inet_ntoa(ngx_addr.sin_addr) _ ntohs(ngx_addr.sin_port));
+
+#if (WIN32)
+    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
+        ngx_log_error(NGX_LOG_EMERG, (&ngx_log), ngx_socket_errno,
+                      "WSAStartup failed");
+#endif
+
+    ngx_snprintf(ngx_cpystrn(addr_text, inet_ntoa(ngx_addr.sin_addr), 16),
+                 7, ":%d", ntohs(ngx_addr.sin_port));
+    fd = ngx_listen((struct sockaddr *) &ngx_addr, -1, &ngx_log, addr_text);
+
+    ngx_server.buff_size = 1024;
+    ngx_server.handler = ngx_http_init_connection;
+
+    /* daemon */
+
+    ls.fd = fd;
+    ls.server = &ngx_server;
+    ls.log = &ngx_log;
+
+    /* fork */
+
+    ngx_worker(&ls, 1, &ngx_pool, &ngx_log);
+}
+
+#if !(WIN32)
+extern char *optarg;
+
+static int ngx_options(int argc, char *const *argv)
+{
+    char ch, *pos;
+    int port;
+
+    while ((ch = getopt(argc, argv, "l:c:")) != -1) {
+        switch (ch) {
+        case 'l':
+            if (pos = strchr(optarg, ':')) {
+                *(pos) = '\0';
+                if ((port = atoi(pos + 1)) <= 0)
+                    return -1;
+                ngx_addr.sin_port = htons(port);
+            }
+
+            if ((ngx_addr.sin_addr.s_addr = inet_addr(optarg)) == INADDR_NONE)
+                return -1;
+            break;
+
+        case 'c':
+            if ((ngx_max_conn = atoi(optarg)) <= 0)
+                return -1;
+            break;
+
+        case '?':
+        default:
+            return -1;
+        }
+
+    }
+
+    return 0;
+}
+#endif
new file mode 100644
--- /dev/null
+++ b/src/core/nginx.h
@@ -0,0 +1,8 @@
+#ifndef _NGINX_H_INCLUDED_
+#define _NGINX_H_INCLUDED_
+
+
+extern char *gx_root;
+
+
+#endif /* _NGINX_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_alloc.c
@@ -0,0 +1,136 @@
+
+#include <ngx_config.h>
+
+#include <ngx_errno.h>
+#include <ngx_log.h>
+#include <ngx_alloc.h>
+
+
+void *ngx_alloc(size_t size, ngx_log_t *log)
+{
+    void *p;
+
+    p = malloc(size);
+    if (p == NULL)
+        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+                     "ngx_alloc: malloc %d bytes failed", size);
+    return p;
+}
+
+void *ngx_calloc(size_t size, ngx_log_t *log)
+{
+    void *p;
+
+    p = ngx_alloc(size, log);
+    if (p)
+        ngx_memzero(p, size);
+
+    return p;
+}
+
+ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log)
+{
+    ngx_pool_t *p;
+
+    ngx_test_null(p, ngx_alloc(size, log), NULL);
+
+    p->last = (char *) p + sizeof(ngx_pool_t);
+    p->end = (char *) p + size;
+    p->next = NULL;
+    p->large = NULL;
+    p->log = log;
+
+    return p;
+}
+
+void ngx_destroy_pool(ngx_pool_t *pool)
+{
+    ngx_pool_t        *p, *n;
+    ngx_pool_large_t  *l;
+
+    for (l = pool->large; l; l = l->next)
+        free(l->alloc);
+
+    for (p = pool, n = pool->next; /* void */; p = n, n = n->next) {
+        free(p);
+
+        if (n == NULL)
+            break;
+    }
+}
+
+void *ngx_palloc(ngx_pool_t *pool, size_t size)
+{
+    void              *m;
+    ngx_pool_t        *p, *n;
+    ngx_pool_large_t  *large, *last;
+
+    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;
+
+                return m;
+            }
+
+            if (n == NULL)
+                break;
+        }
+
+        /* alloc new pool block */
+        ngx_test_null(n, ngx_create_pool(p->end - (char *) p, p->log), NULL);
+        p->next = n;
+        m = n->last;
+        n->last += size;
+        return m;
+
+    /* alloc large block */
+    } else {
+        large = NULL;
+        last = NULL;
+
+        if (pool->large) {
+            for (last = pool->large; /* void */; last = last->next) {
+                if (last->alloc == NULL) {
+                    large = last;
+                    last = NULL;
+                    break;
+                }
+
+                if (last->next == NULL)
+                    break;
+            }
+        }
+
+        if (large == NULL) {
+            ngx_test_null(large, ngx_palloc(pool, sizeof(ngx_pool_large_t)),
+                          NULL);
+        }
+
+        ngx_test_null(p, ngx_alloc(size, pool->log), NULL);
+
+        if (pool->large == NULL) {
+            pool->large = large;
+
+        } else if (last) {
+            last->next = large;
+        }
+
+        large->alloc = p;
+
+        return p;
+    }
+}
+
+void *ngx_pcalloc(ngx_pool_t *pool, size_t size)
+{
+    void *p;
+
+    p = ngx_palloc(pool, size);
+    if (p)
+        ngx_memzero(p, size);
+
+    return p;
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_alloc.h
@@ -0,0 +1,42 @@
+#ifndef _NGX_ALLOC_H_INCLUDED_
+#define _NGX_ALLOC_H_INCLUDED_
+
+
+#include <ngx_config.h>
+
+#include <ngx_log.h>
+
+
+#define NGX_MAX_ALLOC_FROM_POOL (8192 - sizeof(ngx_pool_t))
+#define NGX_DEFAULT_POOL_SIZE   (16 * 1024)
+
+#define ngx_test_null(p, alloc, rc)  if ((p = alloc) == NULL) return rc
+
+
+typedef struct ngx_pool_large_s  ngx_pool_large_t;
+struct ngx_pool_large_s {
+    ngx_pool_large_t  *next;
+    void              *alloc;
+};
+
+typedef struct ngx_pool_s  ngx_pool_t;
+struct ngx_pool_s {
+    char              *last;
+    char              *end;
+    ngx_pool_t        *next;
+    ngx_pool_large_t  *large;
+    ngx_log_t         *log;
+};
+
+
+void *ngx_alloc(size_t size, ngx_log_t *log);
+void *ngx_calloc(size_t size, ngx_log_t *log);
+
+ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
+void ngx_destroy_pool(ngx_pool_t *pool);
+
+void *ngx_palloc(ngx_pool_t *pool, size_t size);
+void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
+
+
+#endif /* _NGX_ALLOC_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_array.c
@@ -0,0 +1,69 @@
+
+#include <ngx_config.h>
+
+#include <ngx_alloc.h>
+#include <ngx_array.h>
+
+ngx_array_t *ngx_create_array(ngx_pool_t *p, int n, size_t size)
+{
+    ngx_array_t *a;
+
+    a = ngx_palloc(p, sizeof(ngx_array_t));
+    if (a == NULL)
+        return NULL;
+
+    a->elts = ngx_palloc(p, n * size);
+    if (a->elts == NULL)
+        return NULL;
+
+    a->pool = p;
+    a->nelts = 0;
+    a->nalloc = n;
+    a->size = size;
+
+    return a;
+}
+
+void ngx_destroy_array(ngx_array_t *a)
+{
+    ngx_pool_t *p = a->pool;
+
+    if (a->elts + a->size * a->nalloc == p->last)
+        p->last -= a->size * a->nalloc;
+
+    if ((char *) a + sizeof(ngx_array_t) == p->last)
+        p->last = (char *) a;
+}
+
+void *ngx_push_array(ngx_array_t *a)
+{
+    void *elt;
+
+    /* array is full */
+    if (a->nelts == a->nalloc) {
+        ngx_pool_t *p = a->pool;
+
+        /* array allocation is the last in the pool */
+        if (a->elts + a->size * a->nelts == p->last
+            && (unsigned) (p->end - p->last) >= a->size)
+        {
+            p->last += a->size;
+            a->nalloc++;
+
+        /* allocate new array */
+        } else {
+            void *new = ngx_palloc(p, 2 * a->nalloc * a->size);
+            if (new == NULL)
+                return NULL;
+
+            memcpy(new, a->elts, a->nalloc * a->size);
+            a->elts = new;
+            a->nalloc *= 2;
+        }
+    }
+
+    elt = a->elts + a->size * a->nelts;
+    a->nelts++;
+
+    return elt;
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_array.h
@@ -0,0 +1,23 @@
+#ifndef _NGX_ARRAY_H_INCLUDED_
+#define _NGX_ARRAY_H_INCLUDED_
+
+
+#include <ngx_config.h>
+
+#include <ngx_alloc.h>
+
+typedef struct {
+    char       *elts;
+    int         nelts;
+    size_t      size;
+    int         nalloc;
+    ngx_pool_t *pool;
+} ngx_array_t;
+
+
+ngx_array_t *ngx_create_array(ngx_pool_t *p, int n, size_t size);
+void ngx_destroy_array(ngx_array_t *a);
+void *ngx_push_array(ngx_array_t *a);
+
+
+#endif /* _NGX_ARRAY_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_auto_config.h
@@ -0,0 +1,4 @@
+
+#ifndef OFF_EQUAL_PTR
+#define OFF_EQUAL_PTR 0
+#endif
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_config.h
@@ -0,0 +1,130 @@
+#ifndef _NGX_CONFIG_H_INCLUDED_
+#define _NGX_CONFIG_H_INCLUDED_
+
+
+#include <ngx_auto_config.h>
+
+/*
+   auto_conf
+   ngx_inline inline __inline __inline__
+*/
+
+#define FD_SETSIZE  1024
+
+
+#ifdef _WIN32
+
+#define WIN32 1
+
+#include <winsock2.h>
+#include <mswsock.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+
+#define ngx_inline   __inline
+
+#define ngx_memzero  ZeroMemory
+
+#define ngx_close_socket closesocket
+
+#ifndef HAVE_WIN32_TRANSMITPACKETS
+#define HAVE_WIN32_TRANSMITPACKETS  1
+#define HAVE_WIN32_TRANSMITFILE     0
+#endif
+
+#ifndef HAVE_WIN32_TRANSMITFILE
+#define HAVE_WIN32_TRANSMITFILE  1
+#endif
+
+#if (HAVE_WIN32_TRANSMITPACKETS) || (HAVE_WIN32_TRANSMITFILE)
+#define HAVE_SENDFILE  1
+#endif
+
+#else /* POSIX */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define ngx_inline   inline
+
+#define ngx_memzero  bzero
+
+#define ngx_close_socket close
+
+#endif /* POSIX */
+
+
+
+#define LF     10
+#define CR     13
+#define CRLF   "\x0d\x0a"
+
+
+
+#if defined SO_ACCEPTFILTER || defined TCP_DEFER_ACCEPT
+
+#ifndef HAVE_DEFERRED_ACCEPT
+#define HAVE_DEFERRED_ACCEPT  1
+#endif
+
+#endif
+
+
+
+#ifdef __FreeBSD__
+
+#include <osreldate.h>
+
+#if __FreeBSD_version >= 300007
+
+#ifndef HAVE_FREEBSD_SENDFILE
+#define HAVE_FREEBSD_SENDFILE  1
+#endif
+
+#ifndef HAVE_FREEBSD_SENDFILE_NBYTES_BUG
+#define HAVE_FREEBSD_SENDFILE_NBYTES_BUG  2
+#endif
+
+#endif
+
+#if (__FreeBSD__ == 4 && __FreeBSD_version >= 460100) \
+    || __FreeBSD_version == 460001
+    || __FreeBSD_version >= 500029
+
+#if (HAVE_FREEBSD_SENDFILE_NBYTES_BUG == 2)
+#define HAVE_FREEBSD_SENDFILE_NBYTES_BUG  0
+#endif
+
+#endif
+
+#if (HAVE_FREEBSD_SENDFILE)
+#define HAVE_SENDFILE  1
+#endif
+
+
+#if (__FreeBSD__ == 4 && __FreeBSD_version >= 410000) \
+    || __FreeBSD_version >= 500011
+
+#ifndef HAVE_KQUEUE
+#define HAVE_KQUEUE  1
+#include <sys/event.h>
+#endif
+
+#endif
+
+
+#endif /* __FreeBSD__ */
+
+
+#endif /* _NGX_CONFIG_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_connection.h
@@ -0,0 +1,54 @@
+#ifndef _NGX_CONNECTION_H_INCLUDED_
+#define _NGX_CONNECTION_H_INCLUDED_
+
+#include <ngx_log.h>
+#include <ngx_alloc.h>
+#include <ngx_server.h>
+
+typedef struct ngx_connection_s  ngx_connection_t;
+
+#ifdef NGX_EVENT
+#include <ngx_event.h>
+#endif
+
+struct ngx_connection_s {
+    ngx_socket_t      fd;
+    void            *data;
+
+#ifdef NGX_EVENT
+    ngx_event_t      *read;
+    ngx_event_t      *write;
+#endif
+
+    ngx_log_t        *log;
+    ngx_server_t     *server;
+    ngx_server_t     *servers;
+    ngx_pool_t       *pool;
+};
+
+
+/*
+
+cached file
+    int      fd;       -2 unused, -1 closed (but read or mmaped), >=0 open
+    char    *name;
+
+    void    *buf;      addr if read or mmaped
+                       aiocb* if aio_read
+                       OVERLAPPED if TransmitFile or TransmitPackets
+                       NULL if sendfile
+
+    size_t   buf_size; for plain read
+    off_t    offset;   for plain read
+
+    size_t   size;
+    time_t   mod;
+    char    *last_mod; 'Sun, 17 Mar 2002 19:39:50 GMT'
+    char    *etag;     '"a6d08-1302-3c94f106"'
+    char    *len;      '4866'
+
+EV_VNODE        should notify by some signal if diretory tree is changed
+                or stat if aged >= N seconds (big enough)
+*/
+
+#endif /* _NGX_CONNECTION_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_hunk.c
@@ -0,0 +1,85 @@
+
+#include <ngx_types.h>
+#include <ngx_hunk.h>
+
+
+ngx_hunk_t *ngx_get_hunk(ngx_pool_t *pool, int size, int before, int after)
+{
+    ngx_hunk_t *h = ngx_palloc(pool, sizeof(ngx_hunk_t));
+
+#ifndef OFF_EQUAL_PTR
+    h->pos.f = h->last.f = 0;
+#endif
+
+    h->pre_start = ngx_palloc(pool, size + before + after);
+    h->start = h->pos.p = h->last.p = h->pre_start + before;
+    h->end = h->last.p + size;
+    h->post_end = h->end + after;
+
+    h->type = NGX_HUNK_TEMP;
+    h->tag = 0;
+    h->fd = (ngx_file_t) -1;
+
+    return h;
+}
+    
+ngx_hunk_t *ngx_get_hunk_before(ngx_pool_t *pool, ngx_hunk_t *hunk, int size)
+{    
+    ngx_hunk_t *h = ngx_palloc(pool, sizeof(ngx_hunk_t));
+
+#ifndef OFF_EQUAL_PTR
+    h->pos.f = h->last.f = 0;
+#endif
+ 
+    if (hunk->type & NGX_HUNK_TEMP && hunk->pos.p - hunk->pre_start >= size) {
+        /* keep hunk->start unchanged - used in restore */
+        h->pre_start = hunk->pre_start;
+        h->end = h->post_end = hunk->pre_start = hunk->pos.p;
+        h->start = h->pos.p = h->last.p = h->end - size;
+
+        h->type = NGX_HUNK_TEMP;
+        h->tag = 0;
+        h->fd = (ngx_file_t) -1;
+
+    } else {
+        h->pre_start = h->start = h->pos.p = h->last.p = ngx_palloc(pool, size);
+        h->end = h->post_end = h->start + size;
+
+        h->type = NGX_HUNK_TEMP;
+        h->tag = 0;
+        h->fd = (ngx_file_t) -1;
+    }
+
+    return h;
+}
+
+ngx_hunk_t *ngx_get_hunk_after(ngx_pool_t *pool, ngx_hunk_t *hunk, int size)
+{
+    ngx_hunk_t *h = ngx_palloc(pool, sizeof(ngx_hunk_t));
+
+#ifndef OFF_EQUAL_PTR
+    h->pos.f = h->last.f = 0;
+#endif
+
+    if (hunk->type & NGX_HUNK_TEMP
+        && hunk->last.p == hunk->end
+        && hunk->post_end - hunk->end >= size)
+    {
+        h->post_end = hunk->post_end;
+        h->pre_start = h->start = h->pos.p = h->last.p = hunk->post_end =
+                                                                  hunk->last.p;
+        h->type = NGX_HUNK_TEMP;
+        h->tag = 0;
+        h->fd = (ngx_file_t) -1;
+
+    } else {
+        h->pre_start = h->start = h->pos.p = h->last.p = ngx_palloc(pool, size);
+        h->end = h->post_end = h->start + size;
+
+        h->type = NGX_HUNK_TEMP;
+        h->tag = 0;
+        h->fd = (ngx_file_t) -1;
+    }
+
+    return h;
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_hunk.h
@@ -0,0 +1,57 @@
+#ifndef _NGX_CHUNK_H_INCLUDED_
+#define _NGX_CHUNK_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_alloc.h>
+
+
+/* type */
+#define NGX_HUNK_TEMP       0x0001
+#define NGX_HUNK_MEMORY     0x0002
+#define NGX_HUNK_MMAP       0x0004
+#define NGX_HUNK_FILE       0x0008
+#define NGX_HUNK_FLUSH      0x0010
+/* in thread state flush means to write the hunk completely before return
+   in event-driven state flush means to start to write the hunk */
+#define NGX_HUNK_LAST       0x0020
+
+#define NGX_HUNK_IN_MEMORY  (NGX_HUNK_TEMP | NGX_HUNK_MEMORY | NGX_HUNK_MMAP )
+#define NGX_HUNK_TYPE       0x0ffff
+
+/* flags */
+#define NGX_HUNK_SHUTDOWN   0x10000
+/* can be used with NGX_HUNK_LAST only */
+
+
+typedef struct ngx_hunk_s ngx_hunk_t;
+struct ngx_hunk_s {
+    union {
+        char    *p;             /* start of current data */
+        off_t    f;   
+    } pos;
+    union {
+        char    *p;             /* end of current data */
+        off_t    f;   
+    } last;
+    int          type;
+    char        *start;         /* start of hunk */
+    char        *end;           /* end of hunk */
+    char        *pre_start;     /* start of pre-allocated hunk */
+    char        *post_end;      /* end of post-allocated hunk */
+    int          tag;
+    ngx_file_t   fd;
+};
+
+typedef struct ngx_chain_s  ngx_chain_t;
+struct ngx_chain_s {
+    ngx_hunk_t  *hunk;
+    ngx_chain_t *next;
+};
+
+
+ngx_hunk_t *ngx_get_hunk(ngx_pool_t *pool, int size, int before, int after);
+
+
+#endif /* _NGX_CHUNK_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_listen.c
@@ -0,0 +1,44 @@
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_errno.h>
+#include <ngx_log.h>
+#include <ngx_listen.h>
+
+ngx_socket_t ngx_listen(struct sockaddr *addr, int backlog,
+                        ngx_log_t *log, char *addr_text)
+{
+    ngx_socket_t   s;
+    int            reuseaddr = 1;
+#if (WIN32)
+    unsigned long  nb = 1;
+#endif
+
+    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, "socket failed");
+
+    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                   (const void *) &reuseaddr, sizeof(int)) == -1)
+        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                     "ngx_listen: setsockopt (SO_REUSEADDR) failed");
+
+#if (WIN32)
+    if (ioctlsocket(s, FIONBIO, &nb) == -1)
+        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                     "ngx_listen: ioctlsocket (FIONBIO) failed");
+#else
+    if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
+        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                     "ngx_listen: fcntl (O_NONBLOCK) failed");
+#endif
+
+    if (bind(s, (struct sockaddr *) addr, sizeof(struct sockaddr_in)) == -1)
+        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                     "ngx_listen: bind to %s failed", addr_text);
+
+    if (listen(s, backlog) == -1)
+        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                     "ngx_listen: listen to %s failed", addr_text);
+
+    return s;
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_listen.h
@@ -0,0 +1,9 @@
+#ifndef _NGX_LISTEN_H_INCLUDED_
+#define _NGX_LISTEN_H_INCLUDED_
+
+
+ngx_socket_t ngx_listen(struct sockaddr *addr, int backlog,
+                        ngx_log_t *log, char *addr_text);
+
+
+#endif /* _NGX_LISTEN_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_log.c
@@ -0,0 +1,116 @@
+
+/*
+   TODO: log pid and tid
+*/
+
+/*
+   "[time as ctime()] [alert] 412:3 (32)Broken pipe: anything"
+
+   "[time as ctime()] [alert] (32)Broken pipe: anything"
+   "[time as ctime()] [alert] anything"
+*/
+
+#include <ngx_config.h>
+#include <ngx_errno.h>
+#include <ngx_time.h>
+#include <ngx_string.h>
+#include <ngx_log.h>
+
+
+static const char *err_levels[] = {
+    "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug"
+};
+
+#if (HAVE_VARIADIC_MACROS)
+void ngx_log_error_core(int level, ngx_log_t *log, ngx_err_t err,
+                        const char *fmt, ...)
+#else
+void ngx_log_error_core(int level, ngx_log_t *log, ngx_err_t err,
+                        const char *fmt, va_list args)
+#endif
+{
+    char       errstr[MAX_ERROR_STR];
+    ngx_tm_t   tm;
+    size_t     len;
+#if (HAVE_VARIADIC_MACROS)
+    va_list    args;
+#endif
+
+    ngx_localtime(&tm);
+    len = ngx_snprintf(errstr, sizeof(errstr), "%02d:%02d:%02d",
+                       tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);
+
+    if (err) {
+        if ((unsigned) err < 0x80000000)
+            len += ngx_snprintf(errstr + len, sizeof(errstr) - len - 1,
+                            " [%s] (%d)",
+                            err_levels[level], err);
+            len += ngx_snprintf(errstr + len, sizeof(errstr) - len - 1,
+                            " [%s] (%X)",
+                            err_levels[level], err);
+
+        len += ngx_strerror_r(err, errstr + len, sizeof(errstr) - len - 1);
+        if (len < sizeof(errstr) - 2) {
+            errstr[len++] = ':';
+            errstr[len++] = ' ';
+        } else {
+            len = sizeof(errstr) - 2;
+        }
+
+    } else {
+        len += ngx_snprintf(errstr + len, sizeof(errstr) - len - 1,
+                            " [%s] ", err_levels[level]);
+    }
+
+#if (HAVE_VARIADIC_MACROS)
+    va_start(args, fmt);
+    len += ngx_vsnprintf(errstr + len, sizeof(errstr) - len - 1, fmt, args);
+    va_end(args);
+#else
+    len += ngx_vsnprintf(errstr + len, sizeof(errstr) - len - 1, fmt, args);
+#endif
+
+    if (len > sizeof(errstr) - 2)
+        len = sizeof(errstr) - 2;
+    errstr[len] = '\n';
+    errstr[len + 1] = '\0';
+
+    fputs(errstr, stderr);
+
+    if (level == NGX_LOG_EMERG)
+        exit(1);
+}
+
+#if !(HAVE_VARIADIC_MACROS)
+
+void ngx_log_error(int level, ngx_log_t *log, ngx_err_t err,
+                   const char *fmt, ...)
+{
+    va_list    args;
+
+    if (log->log_level >= level) {
+        va_start(args, fmt);
+        ngx_log_error_core(level, log, err, fmt, args);
+        va_end(args);
+    }
+}
+
+void ngx_log_debug_core(ngx_log_t *log, const char *fmt, ...)
+{
+    va_list    args;
+
+    va_start(args, fmt);
+    ngx_log_error_core(NGX_LOG_DEBUG, log, 0, fmt, args);
+    va_end(args);
+}
+
+void ngx_assert_core(ngx_log_t *log, const char *fmt, ...)
+{
+    va_list    args;
+
+    va_start(args, fmt);
+    ngx_log_error_core(NGX_LOG_ALERT, log, 0, fmt, args);
+    va_end(args);
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_log.h
@@ -0,0 +1,118 @@
+#ifndef _NGX_LOG_H_INCLUDED_
+#define _NGX_LOG_H_INCLUDED_
+
+
+#include <ngx_errno.h>
+
+typedef enum {
+    NGX_LOG_EMERG = 0,
+    NGX_LOG_ALERT,
+    NGX_LOG_CRIT,
+    NGX_LOG_ERR,
+    NGX_LOG_WARN,
+    NGX_LOG_NOTICE,
+    NGX_LOG_INFO,
+    NGX_LOG_DEBUG
+} ngx_log_e;
+
+/*
+    "... while ", action = "reading client request headers"
+    "... while reading client request headers"
+    "... while ", action = "reading client request headers"
+                  context: pop3 user account
+    "... while reading client command for 'john_doe'"
+*/
+
+typedef struct {
+    int    log_level;
+    char  *action;
+    char  *context;
+/*  char  *func(ngx_log_t *log); */
+} ngx_log_t;
+
+#define MAX_ERROR_STR	2048
+
+#define _               ,
+
+
+#if (HAVE_GCC_VARIADIC_MACROS)
+
+#define HAVE_VARIADIC_MACROS  1
+
+#define ngx_log_error(level, log, args...) \
+        if (log->log_level >= level) ngx_log_error_core(level, log, args)
+
+#ifdef NGX_DEBUG
+#define ngx_log_debug(log, args...) \
+    if (log->log_level == NGX_LOG_DEBUG) \
+        ngx_log_error_core(NGX_LOG_DEBUG, log, 0, args)
+#else
+#define ngx_log_debug(log, args...)
+#endif
+
+#define ngx_assert(assert, fallback, log, args...) \
+        if (!(assert)) { \
+            if (log->log_level >= NGX_LOG_ALERT) \
+                ngx_log_error_core(NGX_LOG_ALERT, log, 0, args); \
+            fallback; \
+        }
+
+void ngx_log_error_core(int level, ngx_log_t *log, ngx_err_t err,
+                        const char *fmt, ...);
+
+#elif (HAVE_C99_VARIADIC_MACROS)
+
+#define HAVE_VARIADIC_MACROS  1
+
+#define ngx_log_error(level, log, ...) \
+        if (log->log_level >= level) ngx_log_error_core(level, log, __VA_ARGS__)
+
+#ifdef NGX_DEBUG
+#define ngx_log_debug(log, ...) \
+    if (log->log_level == NGX_LOG_DEBUG) \
+        ngx_log_error_core(NGX_LOG_DEBUG, log, 0, __VA_ARGS__)
+#else
+#define ngx_log_debug(log, ...)
+#endif
+
+#define ngx_assert(assert, fallback, log, ...) \
+        if (!(assert)) { \
+            if (log->log_level >= NGX_LOG_ALERT) \
+                ngx_log_error_core(NGX_LOG_ALERT, log, 0, __VA_ARGS__); \
+            fallback; \
+        }
+
+void ngx_log_error_core(int level, ngx_log_t *log, ngx_err_t err,
+                        const char *fmt, ...);
+
+#else /* NO VARIADIC MACROS */
+
+#include <stdarg.h>
+
+#ifdef NGX_DEBUG
+#define ngx_log_debug(log, text) \
+    if (log->log_level == NGX_LOG_DEBUG) \
+        ngx_log_debug_core(log, text)
+#else
+#define ngx_log_debug(log, text)
+#endif
+
+#define ngx_assert(assert, fallback, log, text) \
+        if (!(assert)) { \
+            if (log->log_level >= NGX_LOG_ALERT) \
+                ngx_assert_core(log, text); \
+            fallback; \
+        }
+
+void ngx_log_error(int level, ngx_log_t *log, ngx_err_t err,
+                   const char *fmt, ...);
+void ngx_log_error_core(int level, ngx_log_t *log, ngx_err_t err,
+                        const char *fmt, va_list args);
+void ngx_log_debug_core(ngx_log_t *log, const char *fmt, ...);
+void ngx_assert_core(ngx_log_t *log, const char *fmt, ...);
+
+
+#endif /* VARIADIC MACROS */
+
+
+#endif /* _NGX_LOG_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_server.h
@@ -0,0 +1,30 @@
+#ifndef _NGX_SERVER_H_INCLUDED_
+#define _NGX_SERVER_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_alloc.h>
+
+typedef struct {
+    int          log_level;
+    ngx_pool_t  *pool;
+    int        (*handler)(void *data);
+    int          buff_size;
+} ngx_server_t;
+
+
+typedef struct {
+    ngx_socket_t  fd;
+
+    ngx_log_t    *log;
+    ngx_server_t *server;
+
+    unsigned      shared:1;
+#if (HAVE_DEFERRED_ACCEPT)
+    unsigned      accept_filter:1;
+#endif
+} ngx_listen_t;
+
+
+#endif /* _NGX_SERVER_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_string.c
@@ -0,0 +1,21 @@
+
+#include <ngx_config.h>
+#include <ngx_string.h>
+
+
+char *ngx_cpystrn(char *dst, char *src, size_t n)
+{
+    if (n == 0)
+        return dst;
+
+    for (/* void */; --n; dst++, src++) {
+        *dst = *src;
+
+        if (*dst == '\0')
+            return dst;
+    }
+
+    *dst = '\0';
+
+    return dst;
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_string.h
@@ -0,0 +1,26 @@
+#ifndef _NGX_STRING_H_INCLUDED_
+#define _NGX_STRING_H_INCLUDED_
+
+
+#include <ngx_config.h>
+
+
+#if (WIN32)
+
+#define ngx_snprintf              _snprintf
+#define ngx_vsnprintf             _vsnprintf
+
+#else
+
+#define ngx_snprintf              snprintf
+#define ngx_vsnprintf             vsnprintf
+
+#endif
+
+#define ngx_memcpy(dst, src, n)   memcpy(dst, src, n)
+#define ngx_cpymem(dst, src, n)   memcpy(dst, src, n) + n
+
+char *ngx_cpystrn(char *dst, char *src, size_t n);
+
+
+#endif /* _NGX_STRING_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/event/modules/ngx_aio_module.c
@@ -0,0 +1,37 @@
+
+int ngx_posix_aio_process_events(ngx_log_t *log)
+{
+    unmask signal
+
+    listen via signal;
+
+    aio_suspend()/aiowait()/aio_waitcomplete();
+
+    mask signal
+
+    if (ngx_socket_errno == NGX_EINTR)
+        look listen
+        select()/accept() nb listen sockets
+    else
+        aio
+}
+
+int ngx_posix_aio_process_events(ngx_log_t *log)
+{
+    unmask signal
+
+    /* BUG: signal can be delivered before select() */
+
+    select(listen);
+
+    mask signal
+
+    if (ngx_socket_errno == NGX_EINTR)
+        look ready array
+}
+
+void aio_sig_handler(int signo, siginfo_t *siginfo, void *context)
+{
+    push siginfo->si_value.sival_ptr
+}
+
new file mode 100644
--- /dev/null
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2002 Igor Sysoev, http://sysoev.ru
+ */
+
+/*
+    NEED ? : unify change_list and event_list:
+       event_list = change_list;
+*/
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_log.h>
+#include <ngx_connection.h>
+#include <ngx_event.h>
+#include <ngx_kqueue_module.h>
+
+#if (USE_KQUEUE) && !(HAVE_KQUEUE)
+#error "kqueue is not supported on this platform"
+#endif
+
+static void ngx_add_timer(ngx_event_t *ev, u_int timer);
+static void ngx_inline ngx_del_timer(ngx_event_t *ev);
+
+
+static int              kq;
+static struct kevent   *change_list, *event_list;
+static int              nchanges, nevents;
+
+static ngx_event_t      timer_queue;
+
+void ngx_kqueue_init(int max_connections, ngx_log_t *log)
+{
+    int size = sizeof(struct kevent) * 512;
+
+    nchanges = 0;
+    nevents = 512;
+
+    if ((kq = kqueue()) == -1)
+        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+                      "ngx_kqueue_init: kqueue failed");
+
+    change_list = ngx_alloc(size, log);
+    event_list = ngx_alloc(size, log);
+
+    timer_queue.timer_prev = &timer_queue;
+    timer_queue.timer_next = &timer_queue;
+
+#if !(USE_KQUEUE)
+    ngx_event_actions.add = ngx_kqueue_add_event;
+    ngx_event_actions.del = ngx_kqueue_del_event;
+    ngx_event_actions.process = ngx_kqueue_process_events;
+#endif
+
+}
+
+int ngx_kqueue_add_event(ngx_event_t *ev, int event, u_int flags)
+{
+    if (event == NGX_TIMER_EVENT) {
+        ngx_add_timer(ev, flags);
+        return 0;
+    }
+
+    return ngx_kqueue_set_event(ev, event, EV_ADD | flags);
+}
+
+int ngx_kqueue_del_event(ngx_event_t *ev, int event)
+{
+    if (event == NGX_TIMER_EVENT) {
+        ngx_del_timer(ev);
+        return 0;
+    }
+
+    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 };
+    ngx_connection_t *cn = (ngx_connection_t *) ev->data;
+
+    ngx_log_debug(ev->log, "ngx_kqueue_set_event: %d: ft:%d f:%08x" _
+                  cn->fd _ filter _ flags);
+
+    if (nchanges >= nevents) {
+        ngx_log_error(NGX_LOG_WARN, ev->log, 0,
+                      "ngx_kqueue_set_event: change list is filled up");
+
+        if (kevent(kq, change_list, nchanges, NULL, 0, &ts) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+                          "ngx_kqueue_set_event: kevent failed");
+            return -1;
+        }
+        nchanges = 0;
+    }
+
+    change_list[nchanges].ident = cn->fd;
+    change_list[nchanges].filter = filter;
+    change_list[nchanges].flags = flags;
+    change_list[nchanges].fflags = 0;
+    change_list[nchanges].data = 0;
+    change_list[nchanges].udata = ev;
+    nchanges++;
+
+    return 0;
+}
+
+int ngx_kqueue_process_events(ngx_log_t *log)
+{
+    int              events, i;
+    u_int            timer = 0, delta = 0;
+    ngx_event_t      *ev, *nx;
+    struct timeval   tv;
+    struct timespec  ts, *tp = NULL;
+
+    if (timer_queue.timer_next != &timer_queue) {
+        timer = timer_queue.timer_next->timer_delta;
+        ts.tv_sec = timer / 1000;
+        ts.tv_nsec = (timer % 1000) * 1000000;
+        tp = &ts;
+        gettimeofday(&tv, NULL);
+        delta = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+    }
+
+    ngx_log_debug(log, "ngx_kqueue_process_events: timer: %d" _ timer);
+
+    if ((events = kevent(kq, change_list, nchanges, event_list, nevents, tp))
+                == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "ngx_kqueue_process_events: kevent failed");
+        return -1;
+    }
+
+    nchanges = 0;
+
+    if (timer) {
+        gettimeofday(&tv, NULL);
+        delta = tv.tv_sec * 1000 + tv.tv_usec / 1000 - delta;
+
+    } else {
+        ngx_assert((events != 0), return -1, log,
+                   "ngx_kqueue_process_events: "
+                   "kevent returns no events without timeout");
+    }
+
+    ngx_log_debug(log, "ngx_kqueue_process_events: "
+                       "timer: %d, delta: %d" _ timer _ delta);
+
+    if (timer) {
+        if (delta >= timer) {
+            for (ev = timer_queue.timer_next;
+                 ev != &timer_queue && delta >= ev->timer_delta;
+                 /* void */)
+            {
+                delta -= ev->timer_delta;
+                nx = ev->timer_next;
+                ngx_del_timer(ev);
+                if (ev->timer_handler(ev) == -1)
+                    ev->close_handler(ev);
+                ev = nx;
+            }
+
+        } else {
+           timer_queue.timer_next->timer_delta -= delta;
+        }
+    }
+
+    for (i = 0; i < events; i++) {
+
+        ngx_log_debug(log, "ngx_kqueue_process_events: kevent: "
+                      "%d: ft:%d f:%08x ff:%08x d:%d ud:%08x" _
+                      event_list[i].ident _ event_list[i].filter _
+                      event_list[i].flags _ event_list[i].fflags _
+                      event_list[i].data _ event_list[i].udata);
+
+        if (event_list[i].flags & EV_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, log, event_list[i].data,
+                          "ngx_kqueue_process_events: kevent error");
+            continue;
+        }
+
+        ev = (ngx_event_t *) event_list[i].udata;
+
+        switch (event_list[i].filter) {
+
+        case EVFILT_READ:
+        case EVFILT_WRITE:
+            ev->ready = 1;
+            ev->available = event_list[i].data;
+
+            if (event_list[i].flags & EV_EOF) {
+                ev->eof = 1;
+                ev->error = event_list[i].fflags;
+            }
+
+            if (ev->event_handler(ev) == -1)
+                ev->close_handler(ev);
+
+            break;
+
+        default:
+            ngx_assert(0, /* void */, log,
+                       "ngx_kqueue_process_events: unknown filter %d" _
+                       event_list[i].filter);
+        }
+    }
+
+    return 0;
+}
+
+static void ngx_add_timer(ngx_event_t *ev, u_int timer)
+{
+    ngx_event_t *e;
+
+    for (e = timer_queue.timer_next;
+         e != &timer_queue && timer > e->timer_delta;
+         e = e->timer_next)
+        timer -= e->timer_delta;
+
+    ev->timer_delta = timer;
+
+    ev->timer_next = e;
+    ev->timer_prev = e->timer_prev;
+
+    e->timer_prev->timer_next = ev;
+    e->timer_prev = ev;
+}
+
+static void ngx_inline ngx_del_timer(ngx_event_t *ev)
+{
+    if (ev->timer_prev)
+        ev->timer_prev->timer_next = ev->timer_next;
+
+    if (ev->timer_next) {
+        ev->timer_next->timer_prev = ev->timer_prev;
+        ev->timer_prev = NULL;
+    }
+
+    if (ev->timer_prev)
+        ev->timer_next = NULL;
+}
new file mode 100644
--- /dev/null
+++ b/src/event/modules/ngx_kqueue_module.h
@@ -0,0 +1,16 @@
+#ifndef _NGX_KQUEUE_MODULE_H_INCLUDED_
+#define _NGX_KQUEUE_MODULE_H_INCLUDED_
+
+
+#include <ngx_types.h>
+#include <ngx_log.h>
+#include <ngx_event.h>
+
+void 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_set_event(ngx_event_t *ev, int filter, u_int flags);
+int ngx_kqueue_process_events(ngx_log_t *log);
+
+
+#endif /* _NGX_KQUEUE_MODULE_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/event/modules/ngx_overlapped_module.c
@@ -0,0 +1,17 @@
+
+int ngx_overlapped_process_events(ngx_log_t *log)
+{
+    if (acceptex)
+        event = SleepEx(timer, 1);
+    else
+        event = WSAWaitForMultipleEvents(n_events, events, 0, timer, 1);
+
+    if (event == WSA_IO_COMPLETION)
+        look ready array
+}
+
+void CALLBACK overlapped_completion_procedure(DWORD error, DWORD nbytes,
+                         LPWSAOVERLAPPED overlapped, DWORD flags)
+{
+    push overlapped;
+}
new file mode 100644
--- /dev/null
+++ b/src/event/modules/ngx_select_module.c
@@ -0,0 +1,349 @@
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_log.h>
+#include <ngx_time.h>
+#include <ngx_connection.h>
+#include <ngx_event.h>
+#include <ngx_select_module.h>
+
+static fd_set       master_read_fds;
+static fd_set       master_write_fds;
+static fd_set       work_read_fds;
+static fd_set       work_write_fds;
+
+#if (WIN32)
+static int          max_read;
+static int          max_write;
+#else
+static int          max_fd;
+#endif
+
+static ngx_event_t  event_queue;
+static ngx_event_t  timer_queue;
+
+
+static void ngx_add_timer(ngx_event_t *ev, u_int timer);
+static void ngx_inline ngx_del_timer(ngx_event_t *ev);
+
+static fd_set *ngx_select_get_fd_set(ngx_socket_t fd, int event,
+                                     ngx_log_t *log);
+
+void ngx_select_init(int max_connections, ngx_log_t *log)
+{
+#if (WIN32)
+    if (max_connections > FD_SETSIZE)
+        ngx_log_error(NGX_LOG_EMERG, log, 0,
+                      "ngx_select_init: maximum number of descriptors "
+                      "supported by select() is %d",
+                      FD_SETSIZE);
+#else
+    if (max_connections >= FD_SETSIZE)
+        ngx_log_error(NGX_LOG_EMERG, log, 0,
+                      "ngx_select_init: maximum descriptor number"
+                      "supported by select() is %d",
+                      FD_SETSIZE - 1);
+#endif
+
+    FD_ZERO(&master_read_fds);
+    FD_ZERO(&master_write_fds);
+
+    event_queue.prev = &event_queue;
+    event_queue.next = &event_queue;
+
+    timer_queue.timer_prev = &timer_queue;
+    timer_queue.timer_next = &timer_queue;
+
+    ngx_event_actions.add = ngx_select_add_event;
+    ngx_event_actions.del = ngx_select_del_event;
+    ngx_event_actions.process = ngx_select_process_events;
+
+#if (WIN32)
+    max_read = max_write = 0;
+#else
+    max_fd = -1;
+#endif
+}
+
+int ngx_select_add_event(ngx_event_t *ev, int event, u_int flags)
+{
+    fd_set *fds;
+    ngx_connection_t *cn = (ngx_connection_t *) ev->data;
+
+    if (event == NGX_TIMER_EVENT) {
+        ngx_add_timer(ev, flags);
+        return 0;
+    }
+
+    ngx_assert((flags != NGX_ONESHOT_EVENT), return -1, ev->log,
+               "ngx_select_add_event: NGX_ONESHOT_EVENT is not supported");
+
+    fds = ngx_select_get_fd_set(cn->fd, event, ev->log);
+    if (fds == NULL)
+        return -1;
+
+    ev->prev = &event_queue;
+    ev->next = event_queue.next;
+    event_queue.next->prev = ev;
+    event_queue.next = ev;
+
+    FD_SET(cn->fd, fds);
+
+#if (WIN32)
+    switch (event) {
+    case NGX_READ_EVENT:
+        max_read++;
+        break;
+    case NGX_WRITE_EVENT:
+        max_write++;
+        break;
+    }
+#else
+    if (max_fd != -1 && max_fd < cn->fd)
+        max_fd = cn->fd;
+#endif
+
+    return 0;
+}
+
+int ngx_select_del_event(ngx_event_t *ev, int event)
+{
+    fd_set *fds;
+    ngx_connection_t *cn = (ngx_connection_t *) ev->data;
+
+    if (event == NGX_TIMER_EVENT) {
+        ngx_del_timer(ev);
+        return 0;
+    }
+
+    fds = ngx_select_get_fd_set(cn->fd, event, ev->log);
+    if (fds == NULL)
+        return -1;
+
+    if (ev->prev)
+        ev->prev->next = ev->next;
+
+    if (ev->next) {
+        ev->next->prev = ev->prev;
+        ev->prev = NULL;
+    }
+
+    if (ev->prev)
+        ev->next = NULL;
+
+    FD_CLR(cn->fd, fds);
+
+#if (WIN32)
+    switch (event) {
+    case NGX_READ_EVENT:
+        max_read--;
+        break;
+    case NGX_WRITE_EVENT:
+        max_write--;
+        break;
+    }
+#else
+    if (max_fd == cn->fd)
+        max_fd = -1;
+#endif
+
+    return 0;
+}
+
+static fd_set *ngx_select_get_fd_set(ngx_socket_t fd, int event, ngx_log_t *log)
+{
+    ngx_log_debug(log, "ngx_select_get_fd_set: %d %d" _ fd _ event);
+
+#if !(WIN32)
+    if (fd >= FD_SETSIZE) {
+        ngx_log_error(NGX_LOG_ERR, log, 0,
+                      "ngx_select_get_event: maximum descriptor number"
+                      "supported by select() is %d",
+                      FD_SETSIZE - 1);
+        return NULL;
+    }
+#endif
+
+    switch (event) {
+    case NGX_READ_EVENT:
+#if (WIN32)
+        if (max_read >= FD_SETSIZE) {
+            ngx_log_error(NGX_LOG_ERR, log, 0,
+                          "ngx_select_get_event: maximum number of descriptors "
+                          "supported by select() is %d",
+                          FD_SETSIZE);
+            return NULL;
+        }
+#endif
+        return &master_read_fds;
+
+    case NGX_WRITE_EVENT:
+#if (WIN32)
+        if (max_write >= FD_SETSIZE) {
+            ngx_log_error(NGX_LOG_ERR, log, 0,
+                          "ngx_select_get_event: maximum number of descriptors "
+                          "supported by select() is %d",
+                          FD_SETSIZE);
+            return NULL;
+        }
+#endif
+        return &master_write_fds;
+
+    default:
+        ngx_assert(0, return NULL, log,
+                      "ngx_select_get_fd_set: invalid event %d" _ event);
+    }
+
+    return NULL;
+}
+
+int ngx_select_process_events(ngx_log_t *log)
+{
+    int                ready, found;
+    u_int              timer, delta;
+    ngx_event_t       *ev, *nx;
+    ngx_connection_t  *cn;
+    struct timeval     tv, *tp;
+
+    work_read_fds = master_read_fds;
+    work_write_fds = master_write_fds;
+
+    if (timer_queue.timer_next != &timer_queue) {
+        timer = timer_queue.timer_next->timer_delta;
+        tv.tv_sec = timer / 1000;
+        tv.tv_usec = (timer % 1000) * 1000;
+        tp = &tv;
+
+        delta = ngx_msec();
+
+    } else {
+        timer = 0;
+        tp = NULL;
+        delta = 0;
+    }
+
+#if !(WIN32)
+    if (max_fd == -1) {
+        for (ev = event_queue.next; ev != &event_queue; ev = ev->next) {
+            cn = (ngx_connection_t *) ev->data;
+            if (max_fd < cn->fd)
+                max_fd = cn->fd;
+        }
+
+        ngx_log_debug(log, "ngx_select_process_events: change max_fd: %d" _
+                      max_fd);
+    }
+#endif
+
+    ngx_log_debug(log, "ngx_select_process_events: timer: %d" _ timer);
+
+#if (WIN32)
+    if ((ready = select(0, &work_read_fds, &work_write_fds, NULL, tp))
+#else
+    if ((ready = select(max_fd + 1, &work_read_fds, &work_write_fds, NULL, tp))
+#endif
+               == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
+                     "ngx_select_process_events: select failed");
+        return -1;
+    }
+
+    ngx_log_debug(log, "ngx_select_process_events: ready %d" _ ready);
+
+    if (timer) {
+        delta = ngx_msec() - delta;
+
+    } else {
+        ngx_assert((ready != 0), return -1, log,
+                   "ngx_select_process_events: "
+                   "select returns no events without timeout");
+    }
+
+    ngx_log_debug(log, "ngx_select_process_events: "
+                       "timer: %d, delta: %d" _ timer _ delta);
+
+    if (timer) {
+        if (delta >= timer) {
+            for (ev = timer_queue.timer_next;
+                 ev != &timer_queue && delta >= ev->timer_delta;
+                 /* void */)
+            {
+                delta -= ev->timer_delta;
+                nx = ev->timer_next;
+                ngx_del_timer(ev);
+                if (ev->timer_handler(ev) == -1)
+                    ev->close_handler(ev);
+                ev = nx;
+            }
+
+        } else {
+           timer_queue.timer_next->timer_delta -= delta;
+        }
+    }
+
+    for (ev = event_queue.next; ev != &event_queue; ev = ev->next) {
+        cn = (ngx_connection_t *) ev->data;
+        found = 0;
+
+        if (ev->write) {
+            if (FD_ISSET(cn->fd, &work_write_fds)) {
+                ngx_log_debug(log, "ngx_select_process_events: write %d" _
+                              cn->fd);
+                found = 1;
+            }
+
+        } else {
+            if (FD_ISSET(cn->fd, &work_read_fds)) {
+                ngx_log_debug(log, "ngx_select_process_events: read %d" _
+                              cn->fd);
+                found = 1;
+            }
+        }
+
+        if (found) {
+            ev->ready = 1;
+            if (ev->event_handler(ev) == -1)
+                ev->close_handler(ev);
+
+            ready--;
+        }
+
+    }
+
+    ngx_assert((ready == 0), return 0, log,
+               "ngx_select_process_events: ready != events");
+
+    return 0;
+}
+
+static void ngx_add_timer(ngx_event_t *ev, u_int timer)
+{
+    ngx_event_t *e;
+
+    for (e = timer_queue.timer_next;
+         e != &timer_queue && timer > e->timer_delta;
+         e = e->timer_next)
+        timer -= e->timer_delta;
+
+    ev->timer_delta = timer;
+
+    ev->timer_next = e;
+    ev->timer_prev = e->timer_prev;
+
+    e->timer_prev->timer_next = ev;
+    e->timer_prev = ev;
+}
+
+static void ngx_inline ngx_del_timer(ngx_event_t *ev)
+{
+    if (ev->timer_prev)
+        ev->timer_prev->timer_next = ev->timer_next;
+
+    if (ev->timer_next) {
+        ev->timer_next->timer_prev = ev->timer_prev;
+        ev->timer_prev = NULL;
+    }
+
+    if (ev->timer_prev)
+        ev->timer_next = NULL;
+}
new file mode 100644
--- /dev/null
+++ b/src/event/modules/ngx_select_module.h
@@ -0,0 +1,16 @@
+#ifndef _NGX_SELECT_MODULE_H_INCLUDED_
+#define _NGX_SELECT_MODULE_H_INCLUDED_
+
+
+#include <ngx_types.h>
+#include <ngx_log.h>
+#include <ngx_event.h>
+
+void 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_set_event(ngx_event_t *ev, int filter, u_int flags);
+int ngx_select_process_events(ngx_log_t *log);
+
+
+#endif /* _NGX_SELECT_MODULE_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event.c
@@ -0,0 +1,87 @@
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_log.h>
+#include <ngx_alloc.h>
+#include <ngx_connection.h>
+#include <ngx_event.h>
+#include <ngx_event_accept.h>
+
+#include <ngx_select_module.h>
+#if (HAVE_KQUEUE)
+#include <ngx_kqueue_module.h>
+#endif
+
+
+ngx_connection_t    *ngx_connections;
+ngx_event_t         *ngx_read_events, *ngx_write_events;
+
+#if !(USE_KQUEUE)
+
+#if 1
+ngx_event_type_e     ngx_event_type = NGX_SELECT_EVENT;
+#else
+ngx_event_type_e     ngx_event_type = NGX_KQUEUE_EVENT;
+#endif
+
+ngx_event_actions_t  ngx_event_actions;
+
+/* ngx_event_type_e order */
+static void (*ngx_event_init[]) (int max_connections, ngx_log_t *log) = {
+    ngx_select_init,
+#if (HAVE_POLL)
+    ngx_poll_init,
+#endif
+#if (HAVE_KQUEUE)
+    ngx_kqueue_init
+#endif
+};
+
+#endif /* USE_KQUEUE */
+
+
+void ngx_worker(ngx_listen_t *sock, int n, ngx_pool_t *pool, ngx_log_t *log)
+{
+    int  i, fd;
+
+    /* per group */
+    int max_connections = 512;
+
+    ngx_init_events(max_connections, log);
+
+    ngx_read_events = ngx_alloc(sizeof(ngx_event_t) * max_connections, log);
+    ngx_write_events = ngx_alloc(sizeof(ngx_event_t) * max_connections, log);
+    ngx_connections = ngx_alloc(sizeof(ngx_connection_t)
+                                                     * max_connections, log);
+
+    /* for each listening socket */
+    for (i = 0; i < n; i++) {
+        fd = sock[i].fd;
+
+        ngx_memzero(&ngx_read_events[fd], sizeof(ngx_event_t));
+        ngx_memzero(&ngx_write_events[fd], sizeof(ngx_event_t));
+        ngx_memzero(&ngx_connections[fd], sizeof(ngx_connection_t));
+
+        ngx_connections[fd].fd = fd;
+        ngx_connections[fd].server = sock[i].server;
+        ngx_connections[fd].read = (void *) &ngx_read_events[fd].data;
+        ngx_read_events[fd].data = &ngx_connections[fd];
+        ngx_read_events[fd].log = ngx_connections[fd].log = sock[i].log;
+        ngx_read_events[fd].data = &ngx_connections[fd];
+        ngx_read_events[fd].event_handler = &ngx_event_accept;
+        ngx_read_events[fd].listening = 1;
+
+        ngx_read_events[fd].available = 0;
+
+#if (HAVE_DEFERRED_ACCEPT)
+        ngx_read_events[fd].accept_filter = sock->accept_filter;
+#endif
+        ngx_add_event(&ngx_read_events[fd], NGX_READ_EVENT, 0);
+    }
+
+    while (1) {
+        ngx_log_debug(log, "ngx_worker cycle");
+
+        ngx_process_events(log);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event.h
@@ -0,0 +1,127 @@
+#ifndef _NGX_EVENT_H_INCLUDED_
+#define _NGX_EVENT_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_log.h>
+
+typedef struct ngx_event_s       ngx_event_t;
+
+struct ngx_event_s {
+    void            *data;
+
+    int            (*event_handler)(ngx_event_t *ev);
+    int            (*close_handler)(ngx_event_t *ev);
+    void            *context;
+    char            *action;
+
+    ngx_event_t     *prev;     /* queue in select(), poll() */
+    ngx_event_t     *next;
+
+    int            (*timer_handler)(ngx_event_t *ev);
+    ngx_event_t     *timer_prev;
+    ngx_event_t     *timer_next;
+
+    u_int            timer_delta;
+    u_int            timer;
+
+    ngx_log_t       *log;
+
+    int              available; /* kqueue only:                              */
+                                /*   accept: number of sockets that wait     */
+                                /*           to be accepted                  */
+                                /*   read:   bytes to read                   */
+                                /*   write:  available space in buffer       */
+                                /* otherwise:                                */
+                                /*   accept: 1 if accept many, 0 otherwise   */
+
+    /* flags - int are probably faster on write then bits ??? */
+    unsigned         listening:1;
+    unsigned         write:1;
+
+    unsigned         ready:1;
+    unsigned         timedout:1;
+    unsigned         process:1;
+    unsigned         read_discarded:1;
+
+    unsigned         unexpected_eof:1;
+
+#if (HAVE_DEFERRED_ACCEPT)
+    unsigned         accept_filter:1;
+#endif
+#if (HAVE_KQUEUE)
+    unsigned         eof:1;
+    int              errno;
+#endif
+};
+
+typedef enum {
+    NGX_SELECT_EVENT = 0,
+#if (HAVE_POLL)
+    NGX_POLL_EVENT,
+#endif
+#if (HAVE_KQUEUE)
+    NGX_KQUEUE_EVENT,
+#endif
+} ngx_event_type_e ;
+
+typedef struct {
+    int  (*add)(ngx_event_t *ev, int event, u_int flags);
+    int  (*del)(ngx_event_t *ev, int event);
+    int  (*process)(ngx_log_t *log);
+/*
+    int  (*read)(ngx_event_t *ev, char *buf, size_t size);
+    int  (*write)(ngx_event_t *ev, char *buf, size_t size);
+*/
+} ngx_event_actions_t;
+
+
+#if (HAVE_KQUEUE)
+
+#define NGX_READ_EVENT     EVFILT_READ
+#define NGX_WRITE_EVENT    EVFILT_WRITE
+#define NGX_TIMER_EVENT    (-EVFILT_SYSCOUNT - 1)
+
+#define NGX_ONESHOT_EVENT  EV_ONESHOT
+#define NGX_CLEAR_EVENT    EV_CLEAR
+
+#else
+
+#define NGX_READ_EVENT     0
+#define NGX_WRITE_EVENT    1
+#define NGX_TIMER_EVENT    2
+
+#define NGX_ONESHOT_EVENT  1
+#define NGX_CLEAR_EVENT    2
+
+#endif
+
+
+#if (USE_KQUEUE)
+#define ngx_init_events      ngx_kqueue_init
+#define ngx_process_events   ngx_kqueue_process_events
+#define ngx_add_event        ngx_kqueue_add_event
+#define ngx_del_event        ngx_kqueue_del_event
+#else
+#define ngx_init_events     (ngx_event_init[ngx_event_type])
+#define ngx_process_events   ngx_event_actions.process
+#define ngx_add_event        ngx_event_actions.add
+#define ngx_del_event        ngx_event_actions.del
+#endif
+
+
+extern ngx_event_t          *ngx_read_events;
+extern ngx_event_t          *ngx_write_events;
+extern ngx_connection_t     *ngx_connections;
+
+#if !(USE_KQUEUE)
+extern ngx_event_actions_t   ngx_event_actions;
+extern ngx_event_type_e      ngx_event_type;
+#endif
+
+
+void ngx_worker(ngx_listen_t *sock, int n, ngx_pool_t *pool, ngx_log_t *log);
+
+
+#endif /* _NGX_EVENT_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_accept.c
@@ -0,0 +1,83 @@
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_log.h>
+#include <ngx_connection.h>
+#include <ngx_event.h>
+#include <ngx_event_close.h>
+#include <ngx_event_accept.h>
+
+
+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_log_debug(ev->log, "ngx_event_accept: accept ready: %d" _
+                  ev->available);
+        
+    ev->ready = 0;
+  
+    do {
+        if ((s = accept(cn->fd, (struct sockaddr *) &addr, &addrlen)) == -1) {
+            err = ngx_socket_errno;
+            if (err == NGX_EAGAIN) {
+                ngx_log_error(NGX_LOG_INFO, ev->log, err,
+                             "ngx_event_accept: EAGAIN while accept");
+                return 0;
+            }
+            
+            ngx_log_error(NGX_LOG_ERR, ev->log, err,
+                         "ngx_event_accept: accept failed");
+            /* if we return -1 listen socket would be closed */
+            return 0;
+        }
+ 
+        ngx_log_debug(ev->log, "ngx_event_accept: accepted socket: %d" _ s);
+
+        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));
+
+        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];
+
+        ngx_connections[s].fd = s;
+        ngx_read_events[s].unexpected_eof = 1;
+        ngx_write_events[s].ready = 1;
+
+        ngx_write_events[s].timer = ngx_read_events[s].timer = 10000;
+
+        ngx_write_events[s].timer_handler =
+            ngx_read_events[s].timer_handler = ngx_event_close;
+
+        ngx_write_events[s].close_handler =
+            ngx_read_events[s].close_handler = ngx_event_close;
+
+        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;
+    
+#if (HAVE_DEFERRED_ACCEPT)
+        if (ev->accept_filter)
+            ngx_read_events[s].ready = 1;
+#endif
+
+        cn->server->handler(&ngx_connections[s]);
+
+#if (HAVE_KQUEUE)
+#if !(USE_KQUEUE)
+        if (ngx_event_type == NGX_KQUEUE_EVENT)
+#endif
+            ev->available--;
+#endif
+    } while (ev->available);
+  
+    return 0;
+}
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_accept.h
@@ -0,0 +1,10 @@
+#ifndef _NGX_EVENT_ACCEPT_H_INCLUDED_
+#define _NGX_EVENT_ACCEPT_H_INCLUDED_
+
+
+#include <ngx_event.h>
+
+int ngx_event_accept(ngx_event_t *ev);
+
+
+#endif /* _NGX_EVENT_ACCEPT_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_close.c
@@ -0,0 +1,29 @@
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_connection.h>
+#include <ngx_event_close.h>
+
+
+int ngx_event_close(ngx_event_t *ev)
+{
+    int rc;
+    ngx_connection_t *cn = (ngx_connection_t *) ev->data;
+
+    ngx_assert((cn->fd != -1), return -1, ev->log,
+               "ngx_event_close: already closed");
+
+    if ((rc = ngx_close_socket(cn->fd)) == -1)
+        ngx_log_error(NGX_LOG_ERR, ev->log, ngx_socket_errno,
+                      "ngx_event_close: close failed");
+
+    if (cn->read->next)
+        ngx_del_event(cn->read, NGX_READ_EVENT);
+
+    if (cn->write->next)
+        ngx_del_event(cn->write, NGX_WRITE_EVENT);
+
+    cn->fd = -1;
+
+    return rc;
+}
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_close.h
@@ -0,0 +1,10 @@
+#ifndef _NGX_EVENT_CLOSE_H_INCLUDED_
+#define _NGX_EVENT_CLOSE_H_INCLUDED_
+
+
+#include <ngx_event.h>
+
+int ngx_event_close(ngx_event_t *ev);
+
+
+#endif /* _NGX_EVENT_CLOSE_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_read.c
@@ -0,0 +1,55 @@
+
+#include <ngx_config.h>
+#include <ngx_errno.h>
+#include <ngx_log.h>
+#include <ngx_connection.h>
+
+int ngx_event_recv(ngx_connection_t *c, char *buf, size_t size)
+{
+    int           n;
+    ngx_err_t     err;
+    ngx_event_t  *ev = c->read;
+
+#if (HAVE_KQUEUE)
+#if !(USE_KQUEUE)
+    if (ngx_event_type == NGX_KQUEUE_EVENT)
+#endif
+        if (ev->eof && ev->available == 0) {
+            if (ev->error) {
+                ngx_log_error(NGX_LOG_ERR, ev->log, ev->error,
+                             "ngx_event_recv: recv failed while %s",
+                             ev->log->action);
+
+                return -1;
+            }
+
+            return 0;
+        }
+#endif
+
+    n = recv(c->fd, buf, size, 0);
+
+    if (n == -1) {
+        err = ngx_socket_errno;
+
+        if (err == NGX_EAGAIN) {
+            ngx_log_error(NGX_LOG_INFO, ev->log, err,
+                          "ngx_event_recv: EAGAIN while %s", ev->log->action);
+            return -2;
+        }
+
+        ngx_log_error(NGX_LOG_INFO, ev->log, err,
+                      "ngx_event_recv: recv failed while %s", ev->log->action);
+
+        return -1;
+    }
+
+#if (HAVE_KQUEUE)
+#if !(USE_KQUEUE)
+    if (ngx_event_type == NGX_KQUEUE_EVENT)
+#endif
+        ev->available -= n;
+#endif
+
+    return n;
+}
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_write.c
@@ -0,0 +1,136 @@
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_alloc.h>
+#include <ngx_array.h>
+#include <ngx_hunk.h>
+#include <ngx_connection.h>
+#include <ngx_sendv.h>
+#include <ngx_sendfile.h>
+#include <ngx_event_write.h>
+
+
+ngx_chain_t *ngx_event_writer(ngx_connection_t *cn, ngx_chain_t *in,
+                              off_t flush)
+{
+    int           rc;
+    char         *last;
+    off_t         sent;
+    ngx_iovec_t  *iov;
+    ngx_array_t  *header, *trailer;
+    ngx_hunk_t   *file;
+    ngx_chain_t  *ch;
+
+    ch = in;
+    file = NULL;
+
+    ngx_test_null(header, ngx_create_array(cn->pool, 10, sizeof(ngx_iovec_t)),
+                  (ngx_chain_t *) -1);
+
+    ngx_test_null(trailer, ngx_create_array(cn->pool, 10, sizeof(ngx_iovec_t)),
+                  (ngx_chain_t *) -1);
+
+    do {
+        header->nelts = 0;
+        trailer->nelts = 0;
+
+        if (ch->hunk->type & (NGX_HUNK_IN_MEMORY | NGX_HUNK_FLUSH)) {
+            last = NULL;
+            iov = NULL;
+
+            while (ch
+                   && (ch->hunk->type & (NGX_HUNK_IN_MEMORY | NGX_HUNK_FLUSH)))
+            {
+                if (ch->hunk->type & NGX_HUNK_FLUSH)
+                    continue;
+
+                if (last == ch->hunk->pos.p) {
+                    iov->ngx_iov_len += ch->hunk->last.p - ch->hunk->pos.p;
+
+                } else {
+                    ngx_test_null(iov, ngx_push_array(header),
+                                  (ngx_chain_t *) -1);
+                    iov->ngx_iov_base = ch->hunk->pos.p;
+                    iov->ngx_iov_len = ch->hunk->last.p - ch->hunk->pos.p;
+                    last = ch->hunk->last.p;
+                }
+
+                ch = ch->next;
+            }
+        }
+
+        if (ch && (ch->hunk->type & NGX_HUNK_FILE)) {
+            file = ch->hunk;
+            ch = ch->next;
+        }
+
+#if (HAVE_MAX_SENDFILE_IOVEC)
+        if (file && header->nelts > HAVE_MAX_SENDFILE_IOVEC) {
+            rc = ngx_sendv(cn->fd, (ngx_iovec_t *) header->elts, header->nelts,
+                           &sent);
+        } else {
+#endif
+            if (ch && ch->hunk->type & (NGX_HUNK_IN_MEMORY | NGX_HUNK_FLUSH)) {
+                last = NULL;
+                iov = NULL;
+
+                while (ch
+                   && (ch->hunk->type & (NGX_HUNK_IN_MEMORY | NGX_HUNK_FLUSH)))
+                {
+                    if (ch->hunk->type & NGX_HUNK_FLUSH)
+                        continue;
+
+                    if (last == ch->hunk->pos.p) {
+                        iov->ngx_iov_len += ch->hunk->last.p - ch->hunk->pos.p;
+
+                    } else {
+                        ngx_test_null(iov, ngx_push_array(trailer),
+                                      (ngx_chain_t *) -1);
+                        iov->ngx_iov_base = ch->hunk->pos.p;
+                        iov->ngx_iov_len = ch->hunk->last.p - ch->hunk->pos.p;
+                        last = ch->hunk->last.p;
+                    }
+
+                    ch = ch->next;
+                }
+            }
+
+            if (file) {
+                rc = ngx_sendfile(cn->fd,
+                                  (ngx_iovec_t *) header->elts, header->nelts,
+                                  file->fd, file->pos.f,
+                                          (size_t) (file->last.f - file->pos.f),
+                                  (ngx_iovec_t *) trailer->elts, trailer->nelts,
+                                  &sent, cn->log);
+            } else {
+                rc = ngx_sendv(cn->fd, (ngx_iovec_t *) header->elts,
+                               header->nelts, (size_t *) &sent);
+            }
+#if (HAVE_MAX_SENDFILE_IOVEC)
+        }
+#endif
+        /* save sent for logging */
+
+        if (rc == -1)
+            return (ngx_chain_t *) -1;
+
+        flush -= sent;
+
+        for (ch = in; ch && !(ch->hunk->type & NGX_HUNK_LAST); ch = ch->next) {
+            if (sent >= ch->hunk->last.f - ch->hunk->pos.f) {
+                sent -= ch->hunk->last.f - ch->hunk->pos.f;
+                ch->hunk->last.f = ch->hunk->pos.f;
+                    continue;
+            }
+
+            ch->hunk->pos.f += sent;
+            break;
+        }
+
+    } while (flush > 0);
+
+    ngx_destroy_array(trailer);
+    ngx_destroy_array(header);
+
+    return ch;
+}
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_write.h
@@ -0,0 +1,13 @@
+#ifndef _NGX_EVENT_WRITE_H_INCLUDED_
+#define _NGX_EVENT_WRITE_H_INCLUDED_
+
+
+#include <ngx_types.h>
+#include <ngx_hunk.h>
+#include <ngx_connection.h>
+
+ngx_chain_t *ngx_event_write(ngx_connection_t *cn, ngx_chain_t *in,
+                             off_t flush);
+
+
+#endif /* _NGX_EVENT_WRITE_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_index_handler.c
@@ -0,0 +1,123 @@
+
+#include <ngx_config.h>
+
+#include <ngx_strings.h>
+#include <ngx_open.h>
+#include <ngx_stat.h>
+
+#include <ngx_http.h>
+
+int ngx_http_index_handler(ngx_http_request_t *r)
+{
+    int          index_len, err, i;
+    char        *name, *loc, *file
+    ngx_file_t   fd;
+
+    ngx_http_index_t  *index;
+    ngx_http_index_handler_loc_conf_t  *cf;
+
+    cf = (ngx_http_index_handler_loc_conf_t *)
+                    ngx_get_module_loc_conf(r, &ngx_http_index_handler_module);
+
+    index_len = (*(r->uri_end - 1) == '/') ? cf->max_index_len : 0;
+    name = ngx_palloc(r->pool, r->uri_end - r->uri_start + index_len
+                              + r->server->doc_root_len);
+    if (name == NULL)
+        return NGX_ERROR;
+
+    loc = ngx_cpystrn(name, r->server->doc_root, r->server->doc_root_len);
+    file = ngx_cpystrn(loc, r->uri_start, r->uri_end - r->uri_start + 1);
+
+    /* URI without / on the end - check directory */
+    if (index_len == 0) {
+
+        if (ngx_stat(name, &r->stat) == -1) {
+            err = ngx_errno;
+            ngx_log_error(GX_LOG_ERR, r->connection->log, err,
+                         "ngx_http_handler: " ngx_stat_n " %s failed", name);
+
+            if (err == NGX_ENOENT)
+                return NGX_HTTP_NOT_FOUND;
+            else
+                return NGX_ERROR;
+        }
+
+        if (ngx_is_dir(r->stat)) {
+            *file++ = '/';
+            *file = '\0';
+            r->headers_out->location = loc;
+            return NGX_HTTP_MOVED_PERMANENTLY;
+        }
+
+        r->file = name;
+        r->stat_valid = 1;
+
+        return NGX_OK;
+    }
+
+    /* look for index file */
+    index = (ngx_http_index_t *) cf->indices->elts;
+    for (i = 0; i < cf->indices->nelts; i++) {
+        ngx_memcpy(file, index[i].name; index[i].len);
+
+        fd = ngx_open(name, O_RDONLY);
+        if (fd != -1) {
+            r->file = name; 
+            r->fd = fd; 
+            return NGX_OK;
+        }
+    }
+
+    return NGX_HTTP_FORBIDDEN;
+}
+
+/*
+
+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;
+}
+
+*/
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http.h
@@ -0,0 +1,84 @@
+#ifndef _NGX_HTTP_H_INCLUDED_
+#define _NGX_HTTP_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_connection.h>
+
+
+#define NGX_SYS_ERROR             -1
+#define NGX_HTTP_INVALID_METHOD   -2
+#define NGX_HTTP_INVALID_REQUEST  -3
+#define NGX_HTTP_INVALID_HEADER   -4
+
+#define NGX_HTTP_GET   1
+#define NGX_HTTP_HEAD  2
+#define NGX_HTTP_POST  3
+
+#define NGX_HTTP_CONN_CLOSE       0
+#define NGX_HTTP_CONN_KEEP_ALIVE  1
+
+#define NGX_HTTP_OK               200
+
+
+typedef struct {
+    char *buff;
+    char *pos;
+    char *last;
+    char *end;
+} ngx_buff_t;
+
+typedef struct {
+    int     status;
+    int     connection;
+    size_t  content_length;
+    char   *content_type;
+    char   *charset;
+    char   *etag;
+    time_t  date;
+    time_t  last_modified;
+} ngx_http_header_out_t;
+
+typedef struct ngx_http_request_s ngx_http_request_t;
+
+struct ngx_http_request_s {
+    int    method;
+
+    int    http_version;
+    int    http_major;
+    int    http_minor;
+
+    char  *uri;
+    ngx_http_request_t *main;
+
+    ngx_connection_t *connection;
+    ngx_buff_t  *buff;
+    ngx_pool_t  *pool;
+
+    /* internal */
+    unsigned  unusual_uri:1;
+    unsigned  complex_uri:1;
+
+    int    state;
+    char  *uri_start;
+    char  *uri_end;
+    char  *uri_ext;
+    char  *args_start;
+    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
+};
+
+
+#define NGX_INDEX "index.html"
+
+
+int ngx_http_init_connection(ngx_connection_t *c);
+
+
+#endif /* _NGX_HTTP_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_event.c
@@ -0,0 +1,273 @@
+
+#include <ngx_config.h>
+#include <ngx_log.h>
+#include <ngx_alloc.h>
+#include <ngx_hunk.h>
+#include <ngx_connection.h>
+
+#include <ngx_http.h>
+
+/*
+ngx_read should check errors (if we ask) and return
+   -2 EAGAIN
+   -1 error
+    0 EOF
+   >0 number of bytes
+*/
+
+       int ngx_http_init_connection(ngx_connection_t *c);
+static int ngx_http_init_request(ngx_event_t *ev);
+static int ngx_http_process_request(ngx_event_t *ev);
+
+static int ngx_process_http_request_line(ngx_http_request_t *r);
+static int ngx_process_http_request_header(ngx_http_request_t *r);
+
+static int ngx_process_http_request(ngx_http_request_t *r);
+
+static int ngx_http_close_request(ngx_event_t *ev);
+
+/*
+    returns
+    -1 if error
+     0 need more data or EOF (filter is deleted)
+     1 there is unread data
+*/
+
+int ngx_http_init_connection(ngx_connection_t *c)
+{
+    ngx_event_t  *ev;
+
+    ev = c->read;
+/*
+    ev->event_handler = ngx_http_init_request;
+*/
+    ev->event_handler = NULL;
+    ev->log->action = "reading client request line";
+
+    ngx_log_debug(ev->log, "ngx_http_init_connection: entered");
+
+    /* XXX: ev->timer ? */
+    if (ngx_add_event(ev, NGX_TIMER_EVENT, ev->timer) == -1)
+        return -1;
+
+#if (HAVE_DEFERRED_ACCEPT)
+    if (ev->ready)
+        return ngx_http_init_request(ev);
+    else
+#endif
+#if (NGX_CLEAR_EVENT)
+        return ngx_add_event(ev, NGX_READ_EVENT, NGX_CLEAR_EVENT);
+#else
+        return ngx_add_event(ev, NGX_READ_EVENT, NGX_ONESHOT_EVENT);
+#endif
+}
+
+#if 0
+
+int ngx_http_init_request(ngx_event_t *ev)
+{
+    ngx_connection_t   *c = (ngx_connection_t *) ev->data;
+    ngx_http_request_t *r;
+
+    ngx_log_debug(ev->log, "ngx_http_init_request: entered");
+
+    ngx_test_null(r, ngx_pcalloc(c->pool, sizeof(ngx_http_request_t)), -1);
+
+    c->data = r;
+    r->connection = c;
+
+    ngx_test_null(r->buff, ngx_palloc(r->pool, sizeof(ngx_buff_t)), -1);
+    ngx_test_null(r->buff->buff,
+                  ngx_pcalloc(r->pool, sizeof(c->server->buff_size)), -1);
+
+    r->buff->pos = r->buff->last = r->buff->buff;
+    r->buff->end = r->buff->buff + c->server->buff_size;
+
+    r->state_handler = ngx_process_http_request_line;
+
+    ev->event_handler = ngx_http_process_request;
+    ev->close_handler = ngx_http_close_request;
+    c->write->close_handler = ngx_http_close_request;
+    return ngx_http_process_request(ev);
+}
+
+int ngx_http_process_request(ngx_event_t *ev)
+{
+    int n;
+    ngx_connection_t *c = (ngx_connection_t *) ev->data;
+    ngx_http_request_t *r = (ngx_http_request_t *) c->data;
+
+    ngx_log_debug(ev->log, "http process request");
+
+    ngx_log_debug(ev->log, "http: eof:%d, avail:%d", ev->eof, ev->available);
+
+    if (ev->eof && ev->available == 0) {
+        if (ev->err_no)
+            ngx_log_error(NGX_LOG_ERR, ev->log, ev->err_no,
+                         "ngx_http_process_request: "
+                         "read failed while %s", ev->action);
+
+        return -1;
+    }
+
+    if ((n = read(c->fd, r->buff->last, r->buff->end - r->buff->last)) == -1) {
+
+        if (errno == NGX_EWOULDBLOCK) {
+            ngx_log_error(NGX_LOG_INFO, ev->log, errno,
+                         "ngx_http_process_request: "
+                         "EAGAIN while %s", ev->action);
+            return 0;
+        }
+
+        ngx_log_error(NGX_LOG_ERR, ev->log, errno,
+                     "ngx_http_process_request: "
+                     "read failed while %s", ev->action);
+        return -1;
+    }
+
+    ngx_log_debug(ev->log, "http read %d", n);
+
+    if (n == 0) {
+        if (ev->unexpected_eof) {
+            ngx_log_error(NGX_LOG_INFO, ev->log, 0,
+                         "ngx_http_process_request: "
+                         "connection is closed while %s", ev->action);
+            return -1;
+        }
+
+        return ngx_http_close_request(ev);
+    }
+
+    n == r->buff->end - r->buff->last;
+
+    if (!ev->read_discarded) {
+        r->buff->last += n;
+
+        /* state_handlers are called in following order:
+            ngx_process_http_request_line()
+            ngx_process_http_request_header() */
+
+        do {
+            if ((r->state_handler)(r) < 0)
+                return -1;
+        } while (r->buff->pos < r->buff->last);
+    }
+
+    if (ngx_del_event(ev, NGX_TIMER_EVENT) == -1)
+        return -1;
+
+    if (ngx_add_event(ev, NGX_TIMER_EVENT, ev->timer) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int ngx_process_http_request_line(ngx_http_request_t *r)
+{
+    int n;
+
+    if ((n = ngx_read_http_request_line(r)) == 1) {
+        *r->uri_end = '\0';
+        ngx_log_debug(r->connection->log, "HTTP: %d, %d, %s",
+                     r->method, r->http_version, r->uri_start);
+        r->state_handler = ngx_process_http_request_header;
+        r->connection->read->action = "reading client request headers";
+    }
+
+    return n;
+}
+
+static int ngx_process_http_request_header(ngx_http_request_t *r)
+{
+    int n;
+
+    while ((n = ngx_read_http_header_line(r)) == 1) {
+        *r->header_name_end = '\0';
+        *r->header_end = '\0';
+        ngx_log_debug(r->connection->log, "HTTP header: '%s: %s'",
+                     r->header_name_start, r->header_start);
+    }
+
+    if (n != 2)
+        return n;
+
+    r->state_handler = NULL;
+    r->connection->read->action = "reading client request body";
+
+    r->connection->read->read_discarded = 1;
+    r->connection->read->unexpected_eof = 0;
+    ngx_log_debug(r->connection->log, "HTTP header done");
+
+    return ngx_process_http_request(r);
+}
+
+static int ngx_process_http_request(ngx_http_request_t *r)
+{
+    int fd;
+    struct stat sb;
+    ngx_http_header_out_t  *header_out;
+    ngx_chunk_t            *header, *ch;
+
+    int index = (*(r->uri_end - 1) == '/') ? sizeof(NGX_INDEX) : 1;
+    char *name = ngx_palloc(r->pool,
+                           r->uri_end - r->uri_start + strlen(ngx_root) + index);
+    strcpy(name, ngx_root);
+    strcat(name, r->uri_start);
+    if (*(r->uri_end - 1) == '/')
+        strcat(name, NGX_INDEX);
+
+    ngx_log_debug(r->connection->log, "HTTP URI: '%s'", name);
+
+    if ((fd = open(name, O_RDONLY)) == -1) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, errno,
+                     "open %s failed", name);
+        return -1;
+    }
+
+    if (fstat(fd, &sb) == -1) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, errno,
+                     "fstat %s failed", name);
+        return -1;
+    }
+
+    header_out = ngx_palloc(r->pool, sizeof(ngx_http_header_out_t));
+
+    header_out->status = NGX_HTTP_OK;
+    header_out->content_length = sb.st_size;
+    header_out->last_modified = sb.st_mtime;
+    header_out->content_type = "text/html";
+    header_out->charset = "koi8-r";
+    header_out->date = time(NULL);
+    header_out->connection = NGX_HTTP_CONN_CLOSE;
+
+/*
+    header_out->connection = NGX_HTTP_CONN_KEEP_ALIVE;
+    r->connection->read->event_handler = ngx_http_init_request;
+*/
+
+    header = ngx_http_header(r, header_out);
+    ch = ngx_palloc(r->pool, sizeof(ngx_chunk_t));
+    ch->ident = fd;
+    ch->offset = 0;
+    ch->size = sb.st_size;
+    ch->next = NULL;
+    header->next = ch;
+
+    ngx_event_write(r->connection, header);
+
+    return 0;
+}
+
+static int ngx_http_close_request(ngx_event_t *ev)
+{
+    ngx_connection_t *c = (ngx_connection_t *) ev->data;
+
+    ngx_log_debug(ev->log, "http close");
+
+    ngx_del_event(c->read, NGX_TIMER_EVENT);
+    ngx_del_event(c->write, NGX_TIMER_EVENT);
+
+    return ngx_event_close(ev);
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_header.h
@@ -0,0 +1,2 @@
+
+gx_chunk_t *gx_http_header(gx_http_request_t *r, gx_http_header_out_t *out);
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_errno.h
@@ -0,0 +1,21 @@
+#ifndef _NGX_ERRNO_H_INCLUDED_
+#define _NGX_ERRNO_H_INCLUDED_
+
+
+#include <errno.h>
+#include <string.h>
+
+typedef int               ngx_err_t;
+
+#define NGX_ENOENT        ENOENT
+#define NGX_EINTR         EINTR
+#define NGX_EAGAIN        EWOULDBLOCK
+
+#define ngx_errno         errno
+#define ngx_socket_errno  errno
+
+#define ngx_strerror_r(err, errstr, size)  \
+             ngx_cpystrn(errstr, strerror(err), size) - (errstr)
+
+
+#endif /* _NGX_ERRNO_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_sendfile.c
@@ -0,0 +1,65 @@
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_errno.h>
+#include <ngx_log.h>
+#include <ngx_sendv.h>
+#include <ngx_sendfile.h>
+
+/*
+  TODO:
+    FreeBSD:
+       check sent if errno == EINTR then should return right sent.
+*/
+
+/*
+  returns
+      0 done
+     -1 error
+*/
+
+#if (HAVE_FREEBSD_SENDFILE)
+
+int ngx_sendfile(ngx_socket_t s,
+                 ngx_iovec_t *headers, int hdr_cnt,
+                 ngx_file_t fd, off_t offset, size_t nbytes,
+                 ngx_iovec_t *trailers, int trl_cnt,
+                 off_t *sent,
+                 ngx_log_t *log)
+{
+    int             rc, i;
+    ngx_err_t       err;
+    struct sf_hdtr  hdtr;
+
+    hdtr.headers = headers;
+    hdtr.hdr_cnt = hdr_cnt;
+    hdtr.trailers = trailers;
+    hdtr.trl_cnt = trl_cnt;
+
+#if (HAVE_FREEBSD_SENDFILE_NBYTES_BUG)
+    for (i = 0; i < hdr_cnt; i++)
+        nbytes += headers[i].iov_len;
+#endif
+
+    rc = sendfile(fd, s, offset, nbytes, &hdtr, sent, 0);
+
+    if (rc == -1) {
+        err = ngx_socket_errno;
+        if (err != NGX_EAGAIN && err != NGX_EINTR) {
+            ngx_log_error(NGX_LOG_ERR, log, err,
+                         "ngx_sendfile: sendfile failed");
+            return -1;
+
+        } else {
+            ngx_log_error(NGX_LOG_INFO, log, err,
+                         "ngx_sendfile: sendfile sent only %qd bytes", *sent);
+        }
+    }
+
+    ngx_log_debug(log, "ngx_sendfile: %d, @%qd %d:%qd" _
+                  rc _ offset _ nbytes _ *sent);
+
+    return 0;
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_sendfile.h
@@ -0,0 +1,17 @@
+#ifndef _NGX_SENDFILE_H_INCLUDED_
+#define _NGX_SENDFILE_H_INCLUDED_
+
+
+#include <ngx_types.h>
+#include <ngx_log.h>
+#include <ngx_sendv.h>
+
+int ngx_sendfile(ngx_socket_t s,
+                 ngx_iovec_t *headers, int hdr_cnt,
+                 ngx_file_t fd, off_t offset, size_t nbytes,
+                 ngx_iovec_t *trailers, int trl_cnt,
+                 off_t *sent,
+                 ngx_log_t *log);
+
+
+#endif /* _NGX_SENDFILE_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_sendv.c
@@ -0,0 +1,14 @@
+
+#include <ngx_types.h>
+#include <ngx_sendv.h>
+
+ssize_t ngx_sendv(ngx_socket_t s, ngx_iovec_t *iovec, int n, size_t *sent)
+{
+     ssize_t rc = writev(s, iovec, n);
+
+     if (rc == -1)
+         return -1;
+
+     *sent = rc;
+     return 0;
+}
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_sendv.h
@@ -0,0 +1,14 @@
+#ifndef _NGX_SENDV_H_INCLUDED_
+#define _NGX_SENDV_H_INCLUDED_
+
+
+#include <ngx_types.h>
+
+typedef struct iovec  ngx_iovec_t;
+#define ngx_iov_base  iov_base
+#define ngx_iov_len   iov_len
+
+ssize_t ngx_sendv(ngx_socket_t s, ngx_iovec_t *iovec, int n, size_t *sent);
+
+
+#endif /* _NGX_SENDV_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_stat.h
@@ -0,0 +1,19 @@
+#ifndef _NGX_STAT_H_INCLUDED_
+#define _NGX_STAT_H_INCLUDED_
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+typedef struct stat  ngx_stat_t;
+
+#define ngx_is_dir(sb)           (S_ISDIR(sb.st_mode))
+
+#define ngx_stat(file, sb)       stat(file, sb)
+#define ngx_stat_n               "stat"
+
+#define ngx_fstat(file, fd, sb)  fstat(fd, sb)
+#define ngx_fstat_n              "stat"
+
+
+#endif /* _NGX_STAT_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_time.c
@@ -0,0 +1,19 @@
+
+#include <ngx_config.h>
+#include <ngx_time.h>
+
+void ngx_localtime(ngx_tm_t *tm)
+{
+    time_t clock = time(NULL);
+    localtime_r(&clock, tm);
+}
+
+u_int ngx_msec(void)
+{
+    struct timeval  tv;
+
+    gettimeofday(&tv, NULL);
+
+    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_time.h
@@ -0,0 +1,22 @@
+#ifndef _NGX_TIME_H_INCLUDED_
+#define _NGX_TIME_H_INCLUDED_
+
+
+#include <ngx_config.h>
+
+typedef struct tm      ngx_tm_t;
+
+#define ngx_tm_sec     tm_sec
+#define ngx_tm_min     tm_min
+#define ngx_tm_hour    tm_hour
+#define ngx_tm_mday    tm_mday
+#define ngx_tm_mon     tm_mon
+#define ngx_tm_year    tm_year
+#define ngx_tm_wday    tm_wday
+
+void ngx_localtime(ngx_tm_t *tm);
+
+u_int ngx_msec(void);
+
+
+#endif /* _NGX_TIME_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_types.h
@@ -0,0 +1,12 @@
+#ifndef _NGX_TYPES_H_INCLUDED_
+#define _NGX_TYPES_H_INCLUDED_
+
+
+#include <ngx_config.h>
+
+
+typedef int  ngx_file_t;
+typedef int  ngx_socket_t;
+
+
+#endif /* _NGX_TYPES_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/win32/ngx_errno.c
@@ -0,0 +1,38 @@
+
+/*
+   TODO:
+     add WSA error messages
+     test for English only messages
+*/
+
+#include <ngx_config.h>
+#include <ngx_string.h>
+#include <ngx_errno.h>
+
+int ngx_strerror_r(ngx_err_t err, char *errstr, size_t size)
+{
+    int len;
+
+    len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
+                        | FORMAT_MESSAGE_IGNORE_INSERTS,
+                        NULL, err,
+                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                        errstr, size, NULL);
+
+    /* add WSA error messages */
+
+    if (len == 0) {
+
+        len = ngx_snprintf(errstr, size,
+                           "FormatMessage error:(%d)", GetLastError());
+        return len;
+
+    }
+
+    /* remove ".\r\n\0" */
+    while (errstr[len] == '\0' || errstr[len] == CR
+           || errstr[len] == LF || errstr[len] == '.')
+        --len;
+
+    return ++len;
+}
new file mode 100644
--- /dev/null
+++ b/src/os/win32/ngx_errno.h
@@ -0,0 +1,18 @@
+#ifndef _NGX_ERRNO_H_INCLUDED_
+#define _NGX_ERRNO_H_INCLUDED_
+
+
+#include <ngx_config.h>
+
+typedef DWORD             ngx_err_t;
+
+#define ngx_errno         GetLastError()
+#define ngx_socket_errno  WSAGetLastError()
+
+#define NGX_ENOENT        ERROR_FILE_NOT_FOUND
+#define NGX_EAGAIN        WSAEWOULDBLOCK
+
+int ngx_strerror_r(ngx_err_t err, char *errstr, size_t size);
+
+
+#endif /* _NGX_ERRNO_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/win32/ngx_sendfile.c
@@ -0,0 +1,89 @@
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_errno.h>
+#include <ngx_log.h>
+#include <ngx_sendv.h>
+#include <ngx_sendfile.h>
+
+/*
+  TODO:
+       various flags
+       TransmitPackets
+*/
+
+/*
+  returns
+      0 done
+     -1 error
+*/
+
+#if (HAVE_WIN32_TRANSMITFILE)
+
+int ngx_sendfile(ngx_socket_t s,
+                 ngx_iovec_t *headers, int hdr_cnt,
+                 ngx_file_t fd, off_t offset, size_t nbytes,
+                 ngx_iovec_t *trailers, int trl_cnt,
+                 off_t *sent,
+                 ngx_log_t *log)
+{
+    int                    tfrc, rc;
+    ngx_err_t              tf_err, err;
+    OVERLAPPED             olp;
+    TRANSMIT_FILE_BUFFERS  tfb, *ptfb;
+
+    tf_err = 0;
+    err = 0;
+
+    olp.Internal = 0;
+    olp.InternalHigh = 0;
+    olp.Offset = (DWORD) offset;
+    olp.OffsetHigh = (DWORD) (offset >> 32);
+    olp.hEvent = NULL;
+
+    if (headers || trailers) {
+        tfb.Head = headers->ngx_iov_base;
+        tfb.HeadLength = headers->ngx_iov_len;
+        tfb.Tail = trailers->ngx_iov_base;
+        tfb.TailLength = trailers->ngx_iov_len;
+        ptfb = &tfb;
+
+    } else {
+        ptfb = NULL;
+    }
+
+    tfrc = TransmitFile(s, fd, nbytes, 0, &olp, ptfb, 0);
+
+    if (tfrc == 0)
+        tf_err = ngx_socket_errno;
+
+    /* set sent */
+    rc = WSAGetOverlappedResult(s, &olp, (unsigned long *) sent, 0, NULL);
+
+    ngx_log_debug(log, "ngx_sendfile: %d, @%qd %d:%qd" _
+                  tfrc _ offset _ nbytes _ *sent);
+
+    if (rc == 0) {
+        err = ngx_socket_errno;
+        ngx_log_error(NGX_LOG_ERR, log, err,
+                     "ngx_sendfile: WSAGetOverlappedResult failed");
+    }
+
+    if (tfrc == 0) {
+        if (tf_err != NGX_EAGAIN) {
+            ngx_log_error(NGX_LOG_ERR, log, tf_err,
+                          "ngx_sendfile: TransmitFile failed");
+            return -1;
+        }
+
+        ngx_log_error(NGX_LOG_INFO, log, tf_err,
+                     "ngx_sendfile: TransmitFile sent only %qd bytes", *sent);
+    }
+
+    if (rc == 0)
+        return -1;
+
+    return 0;
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/src/os/win32/ngx_sendfile.h
@@ -0,0 +1,18 @@
+#ifndef _NGX_SENDFILE_H_INCLUDED_
+#define _NGX_SENDFILE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_types.h>
+#include <ngx_log.h>
+#include <ngx_sendv.h>
+
+int ngx_sendfile(ngx_socket_t s,
+                 ngx_iovec_t *headers, int hdr_cnt,
+                 ngx_file_t fd, off_t offset, size_t nbytes,
+                 ngx_iovec_t *trailers, int trl_cnt,
+                 off_t *sent,
+                 ngx_log_t *log);
+
+
+#endif /* _NGX_SENDFILE_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/win32/ngx_sendv.h
@@ -0,0 +1,14 @@
+#ifndef _NGX_SENDV_H_INCLUDED_
+#define _NGX_SENDV_H_INCLUDED_
+
+
+#include <ngx_config.h>
+
+typedef WSABUF        ngx_iovec_t;
+#define ngx_iov_base  buf
+#define ngx_iov_len   len
+
+#define ngx_sendv(s, iovec, n, sent)  WSASend(s, iovec, n, sent, 0, NULL, NULL)
+
+
+#endif /* _NGX_SENDV_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/win32/ngx_stat.c
@@ -0,0 +1,15 @@
+
+#include <windows.h>
+
+#include <ngx_stat.h>
+
+int ngx_stat(char *file, ngx_stat_t *sb)
+{
+    *sb = GetFileAttributes(file);
+
+    if (*sb == INVALID_FILE_ATTRIBUTES) {
+        return -1;
+    }
+
+    return 0;
+}
new file mode 100644
--- /dev/null
+++ b/src/os/win32/ngx_stat.h
@@ -0,0 +1,26 @@
+#ifndef _NGX_STAT_H_INCLUDED_
+#define _NGX_STAT_H_INCLUDED_
+
+
+#include <windows.h>
+
+/* INVALID_FILE_ATTRIBUTES specified but never defined */
+#ifndef INVALID_FILE_ATTRIBUTES
+#define INVALID_FILE_ATTRIBUTES  0xFFFFFFFF
+#endif
+
+typedef DWORD  ngx_stat_t;
+
+
+#define ngx_is_dir(sb)           (*sb & FILE_ATTRIBUTE_DIRECTORY) 
+
+#define ngx_stat_n               "GetFileAttributes"
+
+#define ngx_fstat(file, fd, sb)  ngx_stat(file, sb)
+#define ngx_fstat_n              "GetFileAttributes"
+
+
+int ngx_stat(char *file, ngx_stat_t *sb);
+
+
+#endif /* _NGX_STAT_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/win32/ngx_time.h
@@ -0,0 +1,21 @@
+#ifndef _NGX_TIME_H_INCLUDED_
+#define _NGX_TIME_H_INCLUDED_
+
+
+#include <windows.h>
+
+typedef SYSTEMTIME     ngx_tm_t;
+
+#define ngx_tm_sec     wSecond
+#define ngx_tm_min     wMinute
+#define ngx_tm_hour    wHour
+#define ngx_tm_mday    wDay
+#define ngx_tm_mon     wMonth
+#define ngx_tm_year    wYear
+#define ngx_tm_wday    wDayOfWeek
+
+#define ngx_localtime  GetLocalTime
+#define ngx_msec       GetTickCount
+
+
+#endif /* _NGX_TIME_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/win32/ngx_types.h
@@ -0,0 +1,14 @@
+#ifndef _NGX_TYPES_H_INCLUDED_
+#define _NGX_TYPES_H_INCLUDED_
+
+
+#include <ngx_config.h>
+
+
+typedef HANDLE            ngx_file_t;
+typedef SOCKET            ngx_socket_t;
+typedef long              time_t;
+typedef unsigned __int64  off_t;
+
+
+#endif /* _NGX_TYPES_H_INCLUDED_ */