changeset 1:d220029ac7f3

nginx-0.0.1-2002-08-15-21:20:26 import
author Igor Sysoev <igor@sysoev.ru>
date Thu, 15 Aug 2002 17:20:26 +0000
parents 4eff17414a43
children ffffe1499bce
files src/core/nginx.c src/core/ngx_config.h src/core/ngx_hunk.c src/core/ngx_hunk.h src/core/ngx_log.c src/event/modules/ngx_overlapped_module.c src/event/ngx_event.h src/event/ngx_event_mutex.c src/event/ngx_event_read.c src/event/ngx_event_recv.c src/event/ngx_event_write.c src/http/modules/ngx_http_header_filter.c src/http/modules/ngx_http_index_handler.c src/http/modules/ngx_http_static_handler.c src/http/ngx_http.h src/http/ngx_http_event.c src/http/ngx_http_filter.h src/http/ngx_http_parse.c src/http/ngx_http_write_filter.c src/http/ngx_http_write_filter.h src/os/freebsd/ngx_os_thread.c src/os/freebsd/ngx_os_thread.h src/os/unix/ngx_recv.h src/os/unix/ngx_time.c src/os/win32/ngx_recv.h
diffstat 24 files changed, 1115 insertions(+), 120 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -25,7 +25,7 @@ int ngx_http_init_connection(void *data)
 
 int ngx_max_conn = 512;
 struct sockaddr_in ngx_addr = {0, AF_INET, 0, 0, 0};
-
+int ngx_backlog = 0;
 
 ngx_pool_t   ngx_pool;
 ngx_log_t    ngx_log;
@@ -35,10 +35,12 @@ ngx_server_t ngx_server;
 int main(int argc, char *const *argv)
 {
     char addr_text[22];
-    ngx_socket_t fd;
+    ngx_socket_t s;
     ngx_listen_t ls;
+    int            reuseaddr = 1;
 #if (WIN32)
     WSADATA      wsd;
+    unsigned long  nb = 1;
 #endif
 
 
@@ -61,16 +63,45 @@ int main(int argc, char *const *argv)
                       "WSAStartup failed");
 #endif
 
+    /* for each listening socket */
+    s = socket(AF_INET, SOCK_STREAM, 0);
+    if (s == -1)
+        ngx_log_error(NGX_LOG_EMERG, &(ngx_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, &(ngx_log), ngx_socket_errno,
+                     "setsockopt (SO_REUSEADDR) failed");
+
+#if (WIN32)
+    if (ioctlsocket(s, FIONBIO, &nb) == -1)
+        ngx_log_error(NGX_LOG_EMERG, &(ngx_log), ngx_socket_errno,
+                     "ioctlsocket (FIONBIO) failed");
+#else
+    if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
+        ngx_log_error(NGX_LOG_EMERG, &(ngx_log), ngx_socket_errno,
+                     "fcntl (O_NONBLOCK) 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);
+
+    if (bind(s, (struct sockaddr *) &ngx_addr,
+             sizeof(struct sockaddr_in)) == -1)
+        ngx_log_error(NGX_LOG_EMERG, &(ngx_log), ngx_socket_errno,
+                     "bind to %s failed", addr_text);
+
+    if (listen(s, ngx_backlog) == -1)
+        ngx_log_error(NGX_LOG_EMERG, &(ngx_log), ngx_socket_errno,
+                     "listen to %s failed", addr_text);
 
     ngx_server.buff_size = 1024;
     ngx_server.handler = ngx_http_init_connection;
 
     /* daemon */
 
-    ls.fd = fd;
+    ls.fd = s;
     ls.server = &ngx_server;
     ls.log = &ngx_log;
 
--- a/src/core/ngx_config.h
+++ b/src/core/ngx_config.h
@@ -48,8 +48,10 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <fcntl.h>
+#include <signal.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <sys/time.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
--- a/src/core/ngx_hunk.c
+++ b/src/core/ngx_hunk.c
@@ -8,12 +8,12 @@ ngx_hunk_t *ngx_get_hunk(ngx_pool_t *poo
     ngx_hunk_t *h = ngx_palloc(pool, sizeof(ngx_hunk_t));
 
 #ifndef OFF_EQUAL_PTR
-    h->pos.f = h->last.f = 0;
+    h->pos.file = h->last.file = 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->start = h->pos.mem = h->last.mem = h->pre_start + before;
+    h->end = h->last.mem + size;
     h->post_end = h->end + after;
 
     h->type = NGX_HUNK_TEMP;
@@ -28,21 +28,22 @@ ngx_hunk_t *ngx_get_hunk_before(ngx_pool
     ngx_hunk_t *h = ngx_palloc(pool, sizeof(ngx_hunk_t));
 
 #ifndef OFF_EQUAL_PTR
-    h->pos.f = h->last.f = 0;
+    h->pos.file = h->last.file = 0;
 #endif
  
-    if (hunk->type & NGX_HUNK_TEMP && hunk->pos.p - hunk->pre_start >= size) {
+    if (hunk->type & NGX_HUNK_TEMP && hunk->pos.mem - 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->end = h->post_end = hunk->pre_start = hunk->pos.mem;
+        h->start = h->pos.mem = h->last.mem = 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->pre_start = h->start = h->pos.mem = h->last.mem
+                                                      = ngx_palloc(pool, size);
         h->end = h->post_end = h->start + size;
 
         h->type = NGX_HUNK_TEMP;
@@ -58,22 +59,23 @@ ngx_hunk_t *ngx_get_hunk_after(ngx_pool_
     ngx_hunk_t *h = ngx_palloc(pool, sizeof(ngx_hunk_t));
 
 #ifndef OFF_EQUAL_PTR
-    h->pos.f = h->last.f = 0;
+    h->pos.file = h->last.file = 0;
 #endif
 
     if (hunk->type & NGX_HUNK_TEMP
-        && hunk->last.p == hunk->end
+        && hunk->last.mem == 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->pre_start = h->start = h->pos.mem = h->last.mem = hunk->post_end =
+                                                                hunk->last.mem;
         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->pre_start = h->start = h->pos.mem = h->last.mem =
+                                                        ngx_palloc(pool, size);
         h->end = h->post_end = h->start + size;
 
         h->type = NGX_HUNK_TEMP;
--- a/src/core/ngx_hunk.h
+++ b/src/core/ngx_hunk.h
@@ -7,33 +7,39 @@
 #include <ngx_alloc.h>
 
 
-/* type */
+/* hunk type */
+
+/* temp means that hunk's content can be changed */
+/* other type means that hunk's content can not be changed */
 #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
+
+/* hunk flags */
 
-#define NGX_HUNK_IN_MEMORY  (NGX_HUNK_TEMP | NGX_HUNK_MEMORY | NGX_HUNK_MMAP )
-#define NGX_HUNK_TYPE       0x0ffff
+/* in thread state flush means to write the hunk completely before return */
+/* in event state flush means to start to write the hunk */
+#define NGX_HUNK_FLUSH      0x0100
+/* last hunk */
+#define NGX_HUNK_LAST       0x0200
+/* can be used with NGX_HUNK_LAST only */
+#define NGX_HUNK_SHUTDOWN   0x0400
 
-/* flags */
-#define NGX_HUNK_SHUTDOWN   0x10000
-/* can be used with NGX_HUNK_LAST only */
+
+#define NGX_HUNK_IN_MEMORY  (NGX_HUNK_TEMP|NGX_HUNK_MEMORY|NGX_HUNK_MMAP)
+
 
 
 typedef struct ngx_hunk_s ngx_hunk_t;
 struct ngx_hunk_s {
     union {
-        char    *p;             /* start of current data */
-        off_t    f;   
+        char    *mem;           /* start of current data */
+        off_t    file;   
     } pos;
     union {
-        char    *p;             /* end of current data */
-        off_t    f;   
+        char    *mem;           /* end of current data */
+        off_t    file;   
     } last;
     int          type;
     char        *start;         /* start of hunk */
--- a/src/core/ngx_log.c
+++ b/src/core/ngx_log.c
@@ -45,6 +45,7 @@ void ngx_log_error_core(int level, ngx_l
             len += ngx_snprintf(errstr + len, sizeof(errstr) - len - 1,
                             " [%s] (%d)",
                             err_levels[level], err);
+        else
             len += ngx_snprintf(errstr + len, sizeof(errstr) - len - 1,
                             " [%s] (%X)",
                             err_levels[level], err);
--- a/src/event/modules/ngx_overlapped_module.c
+++ b/src/event/modules/ngx_overlapped_module.c
@@ -1,17 +1,41 @@
+
+
+    event = WSACreateEvent(void);
+    WSAEventSelect(s, event, FD_ACCEPT);
+
 
 int ngx_overlapped_process_events(ngx_log_t *log)
 {
     if (acceptex)
-        event = SleepEx(timer, 1);
+        n = SleepEx(timer, 1);
     else
-        event = WSAWaitForMultipleEvents(n_events, events, 0, timer, 1);
+        n = WSAWaitForMultipleEvents(nevents, events, 0, timer, 1);
+
+    if (n == WSA_WAIT_TIMEOUT)
+        close some event;
+
+    if (n == WSA_IO_COMPLETION)
+        again
 
-    if (event == WSA_IO_COMPLETION)
-        look ready array
+    /* try it with AcceptEx() on NT to detect connected sockets */
+    if (!acceptex) {
+        WSAEnumNetworkEvents(
+            sockets[n - WSA_WAIT_EVENT_0],
+            events[n - WSA_WAIT_EVENT_0],
+            net_events);
+
+        if (net_events.lNetworkEvents & FD_ACCEPT) {
+            if (net_events.iErrorCode[FD_ACCEPT_BIT] != 0)
+                accept error
+                again
+
+            ngx_event_accept(); OR post AcceptEx();
+        }
+    }
 }
 
 void CALLBACK overlapped_completion_procedure(DWORD error, DWORD nbytes,
                          LPWSAOVERLAPPED overlapped, DWORD flags)
 {
-    push overlapped;
+    run event handler
 }
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -16,8 +16,8 @@ struct ngx_event_s {
     void            *context;
     char            *action;
 
-    ngx_event_t     *prev;     /* queue in select(), poll() */
-    ngx_event_t     *next;
+    ngx_event_t     *prev;     /* queue in select(), poll(), mutex(),        */
+    ngx_event_t     *next;     /*   aio_read(), aio_write()                  */
 
     int            (*timer_handler)(ngx_event_t *ev);
     ngx_event_t     *timer_prev;
@@ -52,7 +52,7 @@ struct ngx_event_s {
 #endif
 #if (HAVE_KQUEUE)
     unsigned         eof:1;
-    int              errno;
+    int              error;
 #endif
 };
 
@@ -70,12 +70,20 @@ 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  (*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;
 
+/*
+
+NGX_LEVEL_EVENT (default)  select, poll, kqueue
+                                requires to read whole data
+NGX_ONESHOT_EVENT          kqueue
+NGX_CLEAR_EVENT            kqueue
+
+*/
 
 #if (HAVE_KQUEUE)
 
@@ -99,15 +107,21 @@ typedef struct {
 
 
 #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
+#define ngx_event_recv       ngx_event_recv_core
+
 #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
+#define ngx_event_recv       ngx_event_recv_core
+
 #endif
 
 
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_mutex.c
@@ -0,0 +1,14 @@
+
+spinlock_max depend on CPU number and mutex type.
+    1 CPU               1
+    ngx_malloc_mutex    1000 ?
+
+
+int ngx_event_mutex_trylock(ngx_mutex_t *mtx)
+{
+    for(i = mtx->spinlock_max; i; i--)
+        if (trylock(mtx->lock))
+            return 1;
+
+    return 0;
+}
rename from src/event/ngx_event_read.c
rename to src/event/ngx_event_recv.c
--- a/src/event/ngx_event_read.c
+++ b/src/event/ngx_event_recv.c
@@ -2,23 +2,28 @@
 #include <ngx_config.h>
 #include <ngx_errno.h>
 #include <ngx_log.h>
+#include <ngx_recv.h>
 #include <ngx_connection.h>
 
-int ngx_event_recv(ngx_connection_t *c, char *buf, size_t size)
+int ngx_event_recv_core(ngx_event_t *ev, char *buf, size_t size)
 {
-    int           n;
-    ngx_err_t     err;
-    ngx_event_t  *ev = c->read;
+    int                n;
+    ngx_err_t          err;
+    ngx_connection_t  *c;
+
+    c = (ngx_connection_t *) ev->data;
 
 #if (HAVE_KQUEUE)
+    ngx_log_debug(ev->log, "ngx_event_recv: eof:%d, avail:%d, err:%d" _
+                  ev->eof _ ev->available _ ev->error);
 #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);
+                              "ngx_event_recv: recv failed while %s",
+                              ev->log->action);
 
                 return -1;
             }
@@ -27,7 +32,7 @@ int ngx_event_recv(ngx_connection_t *c, 
         }
 #endif
 
-    n = recv(c->fd, buf, size, 0);
+    n = ngx_recv(c->fd, buf, size, 0);
 
     if (n == -1) {
         err = ngx_socket_errno;
--- a/src/event/ngx_event_write.c
+++ b/src/event/ngx_event_write.c
@@ -10,8 +10,8 @@
 #include <ngx_event_write.h>
 
 
-ngx_chain_t *ngx_event_writer(ngx_connection_t *cn, ngx_chain_t *in,
-                              off_t flush)
+ngx_chain_t *ngx_event_write(ngx_connection_t *cn, ngx_chain_t *in,
+                             off_t flush)
 {
     int           rc;
     char         *last;
@@ -34,25 +34,21 @@ ngx_chain_t *ngx_event_writer(ngx_connec
         header->nelts = 0;
         trailer->nelts = 0;
 
-        if (ch->hunk->type & (NGX_HUNK_IN_MEMORY | NGX_HUNK_FLUSH)) {
+        if (ch->hunk->type & NGX_HUNK_IN_MEMORY) {
             last = NULL;
             iov = NULL;
 
-            while (ch
-                   && (ch->hunk->type & (NGX_HUNK_IN_MEMORY | NGX_HUNK_FLUSH)))
+            while (ch && (ch->hunk->type & NGX_HUNK_IN_MEMORY))
             {
-                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;
+                if (last == ch->hunk->pos.mem) {
+                    iov->ngx_iov_len += ch->hunk->last.mem - ch->hunk->pos.mem;
 
                 } 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;
+                    iov->ngx_iov_base = ch->hunk->pos.mem;
+                    iov->ngx_iov_len = ch->hunk->last.mem - ch->hunk->pos.mem;
+                    last = ch->hunk->last.mem;
                 }
 
                 ch = ch->next;
@@ -70,25 +66,23 @@ ngx_chain_t *ngx_event_writer(ngx_connec
                            &sent);
         } else {
 #endif
-            if (ch && ch->hunk->type & (NGX_HUNK_IN_MEMORY | NGX_HUNK_FLUSH)) {
+            if (ch && ch->hunk->type & NGX_HUNK_IN_MEMORY) {
                 last = NULL;
                 iov = NULL;
 
-                while (ch
-                   && (ch->hunk->type & (NGX_HUNK_IN_MEMORY | NGX_HUNK_FLUSH)))
-                {
-                    if (ch->hunk->type & NGX_HUNK_FLUSH)
-                        continue;
+                while (ch && (ch->hunk->type & NGX_HUNK_IN_MEMORY)) {
 
-                    if (last == ch->hunk->pos.p) {
-                        iov->ngx_iov_len += ch->hunk->last.p - ch->hunk->pos.p;
+                    if (last == ch->hunk->pos.mem) {
+                        iov->ngx_iov_len +=
+                                        ch->hunk->last.mem - ch->hunk->pos.mem;
 
                     } 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;
+                        iov->ngx_iov_base = ch->hunk->pos.mem;
+                        iov->ngx_iov_len =
+                                        ch->hunk->last.mem - ch->hunk->pos.mem;
+                        last = ch->hunk->last.mem;
                     }
 
                     ch = ch->next;
@@ -98,8 +92,8 @@ ngx_chain_t *ngx_event_writer(ngx_connec
             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),
+                                  file->fd, file->pos.file,
+                                  (size_t) (file->last.file - file->pos.file),
                                   (ngx_iovec_t *) trailer->elts, trailer->nelts,
                                   &sent, cn->log);
             } else {
@@ -117,17 +111,18 @@ ngx_chain_t *ngx_event_writer(ngx_connec
         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;
+            if (sent >= ch->hunk->last.file - ch->hunk->pos.file) {
+                sent -= ch->hunk->last.file - ch->hunk->pos.file;
+                ch->hunk->last.file = ch->hunk->pos.file;
                     continue;
             }
 
-            ch->hunk->pos.f += sent;
+            ch->hunk->pos.file += sent;
             break;
         }
 
-    } while (flush > 0);
+    /* flush hunks if threaded state */
+    } while (cn->write->context && flush > 0);
 
     ngx_destroy_array(trailer);
     ngx_destroy_array(header);
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_header_filter.c
@@ -0,0 +1,55 @@
+
+
+typedef struct {
+    int    len;
+    char  *line;
+} line;
+
+
+static line http_codes[] = {
+    { 6, "200 OK" }
+};
+
+
+
+
+int ngx_http_header_filter(ngx_http_request_t *r)
+{
+    int  status;
+    ngx_hunk_t  *h;
+
+    ngx_test_null(h, ngx_get_hunk(r->pool, 1024, 0, 64), NGX_HTTP_FILTER_ERROR);
+
+    status = r->headers_out->status - GX_HTTP_OK;
+
+    ngx_memcpy(h->pos.mem, "HTTP/1.0 ", 9);
+    h->pos.mem += 9;
+    ngx_memcpy(h->pos.mem, http_codes[status].line, http_codes[status].len);
+    h->pos.mem += http_codes[status].len;
+    *(h->pos.mem++) = CR; *(h->pos.mem++) = LF;
+
+    memcpy(h->pos.mem, "Date: ", 6);
+    h->pos.mem += 6;
+    h->pos.mem += ngx_http_get_time(h->pos.mem, time());
+    *(h->pos.mem++) = CR; *(h->pos.mem++) = LF;
+
+    /* 2^64 is 20 characters  */
+    if (r->headers_out->content_length)
+        h->pos.mem += ngx_snprintf(h->pos.mem, 49, "Content-Length: %d" CRLF,
+                                   r->headers_out->content_length);
+
+    /* check */
+
+    memcpy(h->pos.mem, "Server: ", 8);
+    h->pos.mem += 8;
+    if (r->headers_out->server) {
+        h->pos.mem = ngx_cpystrn(h->pos.mem, r->headers_out->server,
+                                 h->last.mem - h->pos.mem);
+        check space
+    } else {
+        ngx_memcpy(h->pos.mem, NGINX_VER, sizeof(NGINX_VER));
+        h->pos.mem += sizeof(NGINX_VER);
+    }
+    *(h->pos.mem++) = CR; *(h->pos.mem++) = LF;
+    
+}
--- a/src/http/modules/ngx_http_index_handler.c
+++ b/src/http/modules/ngx_http_index_handler.c
@@ -20,10 +20,11 @@ int ngx_http_index_handler(ngx_http_requ
                     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;
+
+    ngx_test_null(name,
+                  ngx_palloc(r->pool, r->uri_end - r->uri_start + index_len
+                                      + r->server->doc_root_len),
+                  NGX_HTTP_INTERNAL_SERVER_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);
@@ -39,7 +40,7 @@ int ngx_http_index_handler(ngx_http_requ
             if (err == NGX_ENOENT)
                 return NGX_HTTP_NOT_FOUND;
             else
-                return NGX_ERROR;
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
         if (ngx_is_dir(r->stat)) {
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -0,0 +1,101 @@
+
+#include <ngx_config.h>
+
+#include <ngx_strings.h>
+#include <ngx_open.h>
+#include <ngx_stat.h>
+
+#include <ngx_http.h>
+
+int ngx_http_static_handler(ngx_http_request_t *r)
+{
+    int          index_len, err, i;
+    char        *name, *loc, *file
+    ngx_file_t   fd;
+
+    ngx_http_header_out_t  out;
+    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);
+
+    ngx_assert(r->fd, return NGX_HTTP_INTERNAL_SERVER_ERROR,
+               r->connection->log, "ngx_http_static_handler: no file");
+
+    out.status = NGX_HTTP_OK;
+    out.content_length = r->stat.sb_size;
+    out.last_modified = r->stat.sb_mtime;
+
+    /* */
+    out.content_type = "text/html";
+
+    rc = ngx_send_http_header(&out);
+    if (r->header_only)
+        return rc;
+
+    /* NGX_HTTP_INTERNAL_SERVER_ERROR is too late */
+
+    ngx_test_null(h, ngx_create_hunk(r->pool), NGX_HTTP_INTERNAL_SERVER_ERROR);
+    h->type = NGX_HUNK_FILE | NGX_HUNK_LAST;
+    h->fd = r->fd;
+    h->pos.file = 0;
+    h->end.file = r->stat.sb_size;
+
+    ngx_test_null(ch, ngx_create_chain(r->pool),
+                  NGX_HTTP_INTERNAL_SERVER_ERROR);
+    ch->hunk = h;
+    ch->next = NULL;
+
+    return ngx_http_filter(ch);
+}
+
+/*
+
+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;
+}
+
+*/
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -23,6 +23,13 @@
 
 
 typedef struct {
+    void *ctx;   /* STUB */
+} ngx_http_module_t;
+
+/* STUB */
+#define ngx_get_module_ctx(r, module)  (module)->ctx
+
+typedef struct {
     char *buff;
     char *pos;
     char *last;
--- a/src/http/ngx_http_event.c
+++ b/src/http/ngx_http_event.c
@@ -38,10 +38,7 @@ int ngx_http_init_connection(ngx_connect
     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");
@@ -62,8 +59,6 @@ int ngx_http_init_connection(ngx_connect
 #endif
 }
 
-#if 0
-
 int ngx_http_init_request(ngx_event_t *ev)
 {
     ngx_connection_t   *c = (ngx_connection_t *) ev->data;
@@ -71,11 +66,13 @@ int ngx_http_init_request(ngx_event_t *e
 
     ngx_log_debug(ev->log, "ngx_http_init_request: entered");
 
+    ngx_test_null(c->pool, ngx_create_pool(16384, ev->log), -1);
     ngx_test_null(r, ngx_pcalloc(c->pool, sizeof(ngx_http_request_t)), -1);
 
     c->data = r;
     r->connection = c;
 
+    ngx_test_null(r->pool, ngx_create_pool(16384, ev->log), -1);
     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);
@@ -99,33 +96,15 @@ int ngx_http_process_request(ngx_event_t
 
     ngx_log_debug(ev->log, "http process request");
 
-    ngx_log_debug(ev->log, "http: eof:%d, avail:%d", ev->eof, ev->available);
+    n = ngx_event_recv(ev, r->buff->last, r->buff->end - r->buff->last);
 
-    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 (n == -2)
+        return 0;
 
-        if (errno == NGX_EWOULDBLOCK) {
-            ngx_log_error(NGX_LOG_INFO, ev->log, errno,
-                         "ngx_http_process_request: "
-                         "EAGAIN while %s", ev->action);
-            return 0;
-        }
+    if (n == -1)
+        return -1;
 
-        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);
+    ngx_log_debug(ev->log, "http read %d" _ n);
 
     if (n == 0) {
         if (ev->unexpected_eof) {
@@ -138,8 +117,6 @@ int ngx_http_process_request(ngx_event_t
         return ngx_http_close_request(ev);
     }
 
-    n == r->buff->end - r->buff->last;
-
     if (!ev->read_discarded) {
         r->buff->last += n;
 
@@ -168,8 +145,8 @@ static int ngx_process_http_request_line
 
     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);
+        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";
     }
@@ -184,8 +161,8 @@ static int ngx_process_http_request_head
     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);
+        ngx_log_debug(r->connection->log, "HTTP header: '%s: %s'" _
+                     r->header_name_start _ r->header_start);
     }
 
     if (n != 2)
@@ -203,6 +180,13 @@ static int ngx_process_http_request_head
 
 static int ngx_process_http_request(ngx_http_request_t *r)
 {
+    return -1;
+}
+
+#if 0
+
+static int ngx_process_http_request(ngx_http_request_t *r)
+{
     int fd;
     struct stat sb;
     ngx_http_header_out_t  *header_out;
@@ -258,6 +242,8 @@ static int ngx_process_http_request(ngx_
     return 0;
 }
 
+#endif
+
 static int ngx_http_close_request(ngx_event_t *ev)
 {
     ngx_connection_t *c = (ngx_connection_t *) ev->data;
@@ -269,5 +255,3 @@ static int ngx_http_close_request(ngx_ev
 
     return ngx_event_close(ev);
 }
-
-#endif
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_filter.h
@@ -0,0 +1,10 @@
+#ifndef _NGX_HTTP_FILTER_H_INCLUDED_
+#define _NGX_HTTP_FILTER_H_INCLUDED_
+
+
+#define NGX_HTTP_FILTER_ERROR  -1
+#define NGX_HTTP_FILTER_AGAIN   0
+#define NGX_HTTP_FILTER_DONE    1
+
+
+#endif /* _NGX_HTTP_FILTER_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_parse.c
@@ -0,0 +1,475 @@
+
+#include <ngx_config.h>
+#include <ngx_http.h>
+
+int ngx_read_http_request_line(ngx_http_request_t *r)
+{
+    char  ch;
+    char *buff = r->buff->buff;
+    char *p = r->buff->pos;
+    enum {
+        rl_start = 0,
+        rl_space_after_method,
+        rl_spaces_before_uri,
+        rl_after_slash_in_uri,
+        rl_check_uri,
+        rl_uri,
+        rl_http_09,
+        rl_http_version,
+        rl_first_major_digit,
+        rl_major_digit,
+        rl_first_minor_digit,
+        rl_minor_digit,
+        rl_almost_done,
+        rl_done
+    } state = r->state;
+
+    while (p < r->buff->last && state < rl_done) {
+        ch = *p++;
+
+/*
+printf("\nstate: %d, pos: %x, end: %x, char: '%c' buf: %s",
+       state, p, r->buff->last, ch, p);
+*/
+
+        /* GCC complie it as jump table */
+
+        switch (state) {
+
+        /* HTTP methods: GET, HEAD, POST */
+        case rl_start:
+            switch (ch) {
+            case 'G':
+                if (p + 1 >= r->buff->last)
+                    return 0;
+
+                if (*p != 'E' || *(p + 1) != 'T')
+                    return NGX_HTTP_INVALID_METHOD;
+
+                r->method = NGX_HTTP_GET;
+                p += 2;
+                break;
+
+            case 'H':
+                if (p + 2 >= r->buff->last)
+                    return 0;
+
+                if (*p != 'E' || *(p + 1) != 'A' || *(p + 2) != 'D')
+                    return NGX_HTTP_INVALID_METHOD;
+
+                r->method = NGX_HTTP_HEAD;
+                p += 3;
+                break;
+
+            case 'P':
+                if (p + 2 >= r->buff->last)
+                    return 0;
+
+                if (*p != 'O' || *(p + 1) != 'S' || *(p + 2) != 'T')
+                    return NGX_HTTP_INVALID_METHOD;
+
+                r->method = NGX_HTTP_POST;
+                p += 3;
+                break;
+
+            default:
+                return NGX_HTTP_INVALID_METHOD;
+            }
+
+            state = rl_space_after_method;
+            break;
+
+        /* single space after method */
+        case rl_space_after_method:
+            switch (ch) {
+            case ' ':
+                state = rl_spaces_before_uri;
+                break;
+            default:
+                return NGX_HTTP_INVALID_METHOD;
+            }
+            break;
+
+        /* space* before URI */
+        case rl_spaces_before_uri:
+            switch (ch) {
+            case '/':
+                r->uri_start = p - 1;
+                state = rl_after_slash_in_uri;
+                break;
+            case ' ':
+                break;
+            default:
+                r->unusual_uri = 1;
+                r->uri_start = p - 1;
+                state = rl_uri;
+                break;
+            }
+            break;
+
+        /* check dot after slash */
+        case rl_after_slash_in_uri:
+            switch (ch) {
+            case CR:
+                r->uri_end = p - 1;
+                r->http_minor = 9;
+                state = rl_almost_done;
+                break;
+            case LF:
+                r->uri_end = p - 1;
+                r->http_minor = 9;
+                state = rl_done;
+                break;
+            case ' ':
+                r->uri_end = p - 1;
+                state = rl_http_09;
+                break;
+            case '.':
+                r->complex_uri = 1;
+                state = rl_uri;
+                break;
+            case '/':
+                r->complex_uri = 1;
+                state = rl_uri;
+                break;
+            case '?':
+                r->args_start = p;
+                state = rl_uri;
+                break;
+            default:
+                state = rl_check_uri;
+                break;
+            }
+            break;
+
+        /* check slash in URI */
+        case rl_check_uri:
+            switch (ch) {
+            case CR:
+                r->uri_end = p - 1;
+                r->http_minor = 9;
+                state = rl_almost_done;
+                break;
+            case LF:
+                r->uri_end = p - 1;
+                r->http_minor = 9;
+                state = rl_done;
+                break;
+            case ' ':
+                r->uri_end = p - 1;
+                state = rl_http_09;
+                break;
+            case '.':
+                r->uri_ext = p;
+                break;
+            case '/':
+                r->uri_ext = NULL;
+                state = rl_after_slash_in_uri;
+                break;
+            case '?':
+                r->args_start = p;
+                state = rl_uri;
+                break;
+            }
+            break;
+
+        /* URI */
+        case rl_uri:
+            switch (ch) {
+            case CR:
+                r->uri_end = p - 1;
+                r->http_minor = 9;
+                state = rl_almost_done;
+                break;
+            case LF:
+                r->uri_end = p - 1;
+                r->http_minor = 9;
+                state = rl_done;
+                break;
+            case ' ':
+                r->uri_end = p - 1;
+                state = rl_http_09;
+                break;
+            }
+            break;
+
+        /* space+ after URI */
+        case rl_http_09:
+            switch (ch) {
+            case ' ':
+                break;
+            case CR:
+                r->http_minor = 9;
+                state = rl_almost_done;
+                break;
+            case LF:
+                r->http_minor = 9;
+                state = rl_done;
+                break;
+            case 'H':
+                state = rl_http_version;
+                break;
+            default:
+                return NGX_HTTP_INVALID_REQUEST;
+            }
+            break;
+
+        /* TTP/ */
+        case rl_http_version:
+            if (p + 2 >= r->buff->last) {
+                r->state = rl_http_version;
+                r->buff->pos = p - 1;
+                return 0;
+            }
+
+            if (ch != 'T' || *p != 'T' || *(p + 1) != 'P' || *(p + 2) != '/')
+                return NGX_HTTP_INVALID_REQUEST;
+
+            p += 3;
+            state = rl_first_major_digit;
+            break;
+
+        /* first digit of major HTTP version */
+        case rl_first_major_digit:
+            if (ch < '1' || ch > '9')
+                return NGX_HTTP_INVALID_REQUEST;
+
+            r->http_major = ch - '0';
+            state = rl_major_digit;
+            break;
+
+        /* major HTTP version or dot */
+        case rl_major_digit:
+            if (ch == '.') {
+                state = rl_first_minor_digit;
+                break;
+            }
+
+            if (ch < '0' || ch > '9')
+                return NGX_HTTP_INVALID_REQUEST;
+
+            r->http_major = r->http_major * 10 + ch - '0';
+            break;
+
+        /* first digit of minor HTTP version */
+        case rl_first_minor_digit:
+            if (ch < '0' || ch > '9')
+                return NGX_HTTP_INVALID_REQUEST;
+
+            r->http_minor = ch - '0';
+
+            state = rl_minor_digit;
+            break;
+
+        /* minor HTTP version or end of request line */
+        case rl_minor_digit:
+            if (ch == CR) {
+                state = rl_almost_done;
+                break;
+            }
+
+            if (ch == LF) {
+                state = rl_done;
+                break;
+            }
+
+            if (ch < '0' || ch > '9')
+                return NGX_HTTP_INVALID_REQUEST;
+
+            r->http_minor = r->http_minor * 10 + ch - '0';
+            break;
+
+        /* end of request line */
+        case rl_almost_done:
+            switch (ch) {
+            case LF:
+                state = rl_done;
+                break;
+            default:
+                return NGX_HTTP_INVALID_METHOD;
+            }
+            break;
+        }
+    }
+
+    r->buff->pos = p;
+
+    if (state == rl_done) {
+        r->http_version = r->http_major * 1000 + r->http_minor;
+        r->state = rl_start;
+        return 1;
+    } else {
+        r->state = state;
+        return 0;
+    }
+}
+
+int ngx_read_http_header_line(ngx_http_request_t *r)
+{
+    char  c, ch;
+    char *buff = r->buff->buff;
+    char *p = r->buff->pos;
+    enum  {
+        hl_start = 0,
+        hl_name,
+        hl_space_before_value,
+        hl_value,
+        hl_space_after_value,
+        hl_almost_done,
+        header_almost_done,
+        hl_done,
+        header_done
+    } state = r->state;
+
+    while (p < r->buff->last && state < hl_done) {
+        ch = *p++;
+
+/*
+printf("\nstate: %d, pos: %x, end: %x, char: '%c' buf: %s",
+       state, p, r->buff->last, ch, p);
+*/
+
+        switch (state) {
+
+        /* first char */
+        case hl_start:
+            switch (ch) {
+            case CR:
+                r->header_end = p - 1;
+                state = header_almost_done;
+                break;
+            case LF:
+                r->header_end = p - 1;
+                state = header_done;
+                break;
+            default:
+                state = hl_name;
+                r->header_name_start = p - 1;
+
+                c = ch | 0x20;
+                if (c >= 'a' && c <= 'z')
+                    break;
+
+                if (ch == '-')
+                    break;
+
+                if (ch >= '0' && ch <= '9')
+                    break;
+
+                return NGX_HTTP_INVALID_HEADER;
+
+            }
+            break;
+
+        /* header name */
+        case hl_name:
+            c = ch | 0x20;
+            if (c >= 'a' && c <= 'z')
+                break;
+
+            if (ch == ':') {
+                r->header_name_end = p - 1;
+                state = hl_space_before_value;
+                break;
+            }
+
+            if (ch == '-')
+                break;
+
+            if (ch >= '0' && ch <= '9')
+                break;
+
+            return NGX_HTTP_INVALID_HEADER;
+
+        /* space* before header value */
+        case hl_space_before_value:
+            switch (ch) {
+            case ' ':
+                break;
+            case CR:
+                r->header_start = r->header_end = p - 1;
+                state = hl_almost_done;
+                break;
+            case LF:
+                r->header_start = r->header_end = p - 1;
+                state = hl_done;
+                break;
+            default:
+                r->header_start = p - 1;
+                state = hl_value;
+                break;
+            }
+            break;
+
+        /* header value */
+        case hl_value:
+            switch (ch) {
+            case ' ':
+                r->header_end = p - 1;
+                state = hl_space_after_value;
+                break;
+            case CR:
+                r->header_end = p - 1;
+                state = hl_almost_done;
+                break;
+            case LF:
+                r->header_end = p - 1;
+                state = hl_done;
+                break;
+            }
+            break;
+
+        /* space* before end of header line */
+        case hl_space_after_value:
+            switch (ch) {
+            case ' ':
+                break;
+            case CR:
+                state = hl_almost_done;
+                break;
+            case LF:
+                state = hl_done;
+                break;
+            default:
+                state = hl_value;
+                break;
+            }
+            break;
+
+        /* end of header line */
+        case hl_almost_done:
+            switch (ch) {
+            case LF:
+                state = hl_done;
+                break;
+            default:
+                return NGX_HTTP_INVALID_HEADER;
+            }
+            break;
+
+        /* end of header */
+        case header_almost_done:
+            switch (ch) {
+            case LF:
+                state = header_done;
+                break;
+            default:
+                return NGX_HTTP_INVALID_HEADER;
+            }
+            break;
+        }
+    }
+
+    r->buff->pos = p;
+
+    if (state == hl_done) {
+        r->state = hl_start;
+        return 1;
+    } else if (state == header_done) {
+        r->state = hl_start;
+        return 2;
+    } else {
+        r->state = state;
+        return 0;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_write_filter.c
@@ -0,0 +1,82 @@
+
+#include <ngx_config.h>
+
+#include <ngx_hunk.h>
+#include <ngx_http.h>
+#include <ngx_http_filter.h>
+#include <ngx_event_write.h>
+
+#include <ngx_http_write_filter.h>
+
+
+ngx_http_module_t  ngx_http_write_filter_module;
+
+
+/* STUB */
+static ngx_http_write_filter_ctx_t module_ctx;
+
+void ngx_http_write_filter_init()
+{
+     module_ctx.buffer_output = 10240;
+     module_ctx.out = NULL;
+
+     ngx_http_write_filter_module.ctx = &module_ctx;
+}
+/* */
+
+
+int ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    int    last;
+    off_t  size, flush;
+    ngx_chain_t  *ch, **prev, *chain;
+    ngx_http_write_filter_ctx_t  *ctx;
+
+    ctx = (ngx_http_write_filter_ctx_t *)
+                              ngx_get_module_ctx(r->main ? r->main : r,
+                                                &ngx_http_write_filter_module);
+    size = flush = 0;
+    last = 0;
+    prev = &ctx->out;
+
+    /* find size, flush point and last link of saved chain */
+    for (ch = ctx->out; ch; ch = ch->next) {
+        prev = &ch->next;
+        size += ch->hunk->last.file - ch->hunk->pos.file;
+
+        if (ch->hunk->type & NGX_HUNK_FLUSH)
+            flush = size;
+
+        if (ch->hunk->type & NGX_HUNK_LAST)
+            last = 1;
+    }
+
+    /* add new chain to existent one */
+    for (/* void */; in; in = in->next) {
+        ngx_test_null(ch, ngx_palloc(r->pool, sizeof(ngx_chain_t)),
+                      NGX_HTTP_FILTER_ERROR);
+
+        ch->hunk = in->hunk;
+        ch->next = NULL;
+        *prev = ch;
+        prev = &ch->next;
+        size += ch->hunk->last.file - ch->hunk->pos.file;
+
+        if (ch->hunk->type & NGX_HUNK_FLUSH)
+            flush = size;
+
+        if (ch->hunk->type & NGX_HUNK_LAST)
+            last = 1;
+    }
+
+    if (flush == 0 && size < ctx->buffer_output)
+        return NGX_HTTP_FILTER_DONE;
+
+    chain = ngx_event_write(r->connection, ctx->out, flush);
+    if (chain == (ngx_chain_t *) -1)
+        return NGX_HTTP_FILTER_ERROR;
+
+    ctx->out = chain;
+
+    return (chain ? NGX_HTTP_FILTER_AGAIN : NGX_HTTP_FILTER_DONE);
+}
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_write_filter.h
@@ -0,0 +1,11 @@
+#ifndef _NGX_HTTP_WRITE_FILTER_H_INCLUDED_
+#define _NGX_HTTP_WRITE_FILTER_H_INCLUDED_
+
+
+typedef struct {
+    ngx_chain_t  *out;
+    size_t        buffer_output;
+} ngx_http_write_filter_ctx_t;
+
+
+#endif /* _NGX_HTTP_WRITE_FILTER_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/freebsd/ngx_os_thread.c
@@ -0,0 +1,64 @@
+
+#include <ngx_os_thread.h>
+
+char    *ngx_stacks_start;
+char    *ngx_stacks_end;
+size_t   ngx_stack_size;
+
+
+/* handle thread-safe errno */
+static int   errno0; /* errno for main thread */
+static int  *errnos;
+
+int *__error()
+{
+    ngx_tid_t tid = ngx_gettid();
+    return tid ? &(errnos[ngx_gettid()]) : &errno0;
+}
+
+
+int ngx_create_os_thread(ngx_os_tid_t *tid, void *stack,
+                     int (*func)(void *arg), void *arg, ngx_log_t log)
+{
+    int id, err;
+
+    id = rfork_thread(RFPROC|RFMEM, stack, func, arg);
+    err = ngx_errno;
+
+    if (id == -1)
+        ngx_log_error(NGX_LOG_ERR, log, err,
+                      "ngx_create_os_thread: rfork failed");
+    else
+        *tid = id;
+
+    return err;
+}
+
+
+int ngx_create_os_thread_env(int n, size_t size, ngx_log_t log)
+{
+    char *addr;
+
+    /* create thread stacks */
+    addr = mmap(NULL, n * size, PROT_READ|PROT_WRITE, MAP_ANON, -1, NULL);
+    if (addr == MAP_FAILED) {
+        ngx_log_error(NGX_LOG_ERR, log, ngx_errno,
+                      "ngx_create_os_thread_stacks: mmap failed");
+        return -1;
+    }
+
+    nxg_stacks_start = addr;
+    nxg_stacks_end = addr + n * size;
+    nxg_stack_size = size;
+
+    /* create thread errno array */
+    ngx_test_null(errnos, ngx_calloc(n * sizeof(int)), -1);
+
+    /* create thread tid array */
+    ngx_test_null(ngx_os_tids, ngx_calloc(n * sizeof(ngx_os_tid_t)), -1);
+
+    /* allow spinlock in malloc() */
+    __isthreaded = 1;
+
+    return 0;
+}
new file mode 100644
--- /dev/null
+++ b/src/os/freebsd/ngx_os_thread.h
@@ -0,0 +1,24 @@
+#ifndef _NGX_OS_THREAD_H_INCLUDED_
+#define _NGX_OS_THREAD_H_INCLUDED_
+
+
+typedef int  ngx_os_tid_t;
+typedef int  ngx_tid_t;
+
+
+extern char   *ngx_stacks_start;
+extern char   *ngx_stacks_end;
+extern size_t  ngx_stack_size;
+
+
+static inline ngx_tid_t ngx_gettid()
+{   
+    char *sp;
+
+    __asm__ ("mov %%esp,%0" : "=r" (sp));
+    return (sp > ngx_stacks_end) ? 0:
+           (sp - ngx_stacks_start) / ngx_stack_size + 1;
+}
+
+
+#endif /* _NGX_OS_THREAD_H_INCLUDED_ */
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_recv.h
@@ -0,0 +1,10 @@
+#ifndef _NGX_RECV_H_INCLUDED_
+#define _NGX_RECV_H_INCLUDED_
+
+
+#include <errno.h>
+
+#define ngx_recv(fd, buf, size, flags)  recv(fd, buf, size, flags)
+
+
+#endif /* _NGX_RECV_H_INCLUDED_ */
--- a/src/os/unix/ngx_time.c
+++ b/src/os/unix/ngx_time.c
@@ -17,3 +17,70 @@ u_int ngx_msec(void)
     return tv.tv_sec * 1000 + tv.tv_usec / 1000;
 }
 
+
+#if 0
+
+typedef struct {
+    int busy;
+    u_int_64 msec;
+    time_t sec;
+    tm;
+    http_time_len;
+    http_time[n];    
+};
+
+volatile *ngx_time_p;
+
+ngx_time()
+{
+    p = ngx_time_p;
+}
+
+ngx_update_time()
+{
+    u_int64   msec;
+    struct timeval  tv;
+
+    gettimeofday(&tv, NULL);
+
+    msec = (unsigned long) tv.tv_sec * 1000 + tv.tv_usec / 1000;
+    p = ngx_time_p;
+
+    /* minimum can be for example 0, 10, 50 or 100 ms */
+    if (tv_sec > p->sec || msec - p->msec >= minimum) {
+        old_p = p;
+        /* max_tries < max_slots - 10,
+           max_slots should be more than max of threads */
+        for (/* void */; i < max_tries; i++) {
+            if (++p >= last_slot)
+                p = first_slot;
+
+            if (!test_and_set(p->busy)
+                break;
+        }
+
+        if (i == max_tries) {
+            ngx_log_error();
+            return;
+        }
+
+        if (tv_sec > p->sec) {
+            p->sec = tv.tv.sec;
+            p->msec = msec;
+            localtime_r(&tv.tv_sec, tm);
+            make http stirng;
+
+        } else {
+            ngx_memcpy(p->sec, old_p->sec, sizeof() - offset_of(, sec));
+            p->msec = msec;
+        }
+
+        /* here can be too seldom and non-critical race condition */
+        if (ngx_time_p == old_p)
+            ngx_time_p = p;
+
+        unlock(p->busy);
+    }
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/src/os/win32/ngx_recv.h
@@ -0,0 +1,10 @@
+#ifndef _NGX_RECV_H_INCLUDED_
+#define _NGX_RECV_H_INCLUDED_
+
+
+#include <errno.h>
+
+#define ngx_recv(fd, buf, size, flags)  recv(fd, buf, size, flags)
+
+
+#endif /* _NGX_RECV_H_INCLUDED_ */