changeset 67:5a7d1aaa1618

nginx-0.0.1-2003-03-11-23:38:13 import
author Igor Sysoev <igor@sysoev.ru>
date Tue, 11 Mar 2003 20:38:13 +0000
parents 4876cd4a36bb
children d549fdc17d7e
files src/core/ngx_conf_file.c src/core/ngx_hunk.c src/core/ngx_hunk.h src/core/ngx_string.h src/event/modules/ngx_kqueue_module.c src/event/ngx_event.h src/event/ngx_event_close.c src/event/ngx_event_recv.c src/http/modules/ngx_http_event_proxy_handler.c src/http/modules/ngx_http_event_proxy_handler.h src/http/modules/ngx_http_static_handler.c src/http/ngx_http.c src/http/ngx_http.h src/http/ngx_http_core_module.c src/http/ngx_http_event.c src/http/ngx_http_header_filter.c src/http/ngx_http_output_filter.c src/http/ngx_http_parse.c src/http/ngx_http_special_response.c src/http/ngx_http_write_filter.c src/os/unix/ngx_errno.h src/os/unix/ngx_freebsd_write_chain.c src/os/win32/ngx_errno.h
diffstat 23 files changed, 766 insertions(+), 463 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -206,7 +206,7 @@ static int ngx_conf_read_token(ngx_conf_
 
     cf->args->nelts = 0;
     h = cf->conf_file->hunk;
-    start = h->pos.mem;
+    start = h->pos;
 
 #if 0
 ngx_log_debug(cf->log, "TOKEN START");
@@ -214,31 +214,31 @@ ngx_log_debug(cf->log, "TOKEN START");
 
     for ( ;; ) {
 
-        if (h->pos.mem >= h->last.mem) {
+        if (h->pos >= h->last) {
             if (cf->conf_file->file.offset
                                   >= ngx_file_size(cf->conf_file->file.info)) {
                 return NGX_CONF_FILE_DONE;
             }
 
-            if (h->pos.mem - start) {
-                ngx_memcpy(h->start, start, h->pos.mem - start);
+            if (h->pos - start) {
+                ngx_memcpy(h->start, start, h->pos - start);
             }
 
             n = ngx_read_file(&cf->conf_file->file,
-                              h->start + (h->pos.mem - start),
-                              h->end - (h->start + (h->pos.mem - start)),
+                              h->start + (h->pos - start),
+                              h->end - (h->start + (h->pos - start)),
                               cf->conf_file->file.offset);
 
             if (n == NGX_ERROR) {
                 return NGX_ERROR;
             }
 
-            h->pos.mem = h->start + (h->pos.mem - start);
+            h->pos = h->start + (h->pos - start);
             start = h->start;
-            h->last.mem = h->pos.mem + n;
+            h->last = h->pos + n;
         }
 
-        ch = *h->pos.mem++;
+        ch = *h->pos++;
 
 #if 0
 ngx_log_debug(cf->log, "%d:%d:%d:%d:%d '%c'" _
@@ -282,7 +282,7 @@ ngx_log_debug(cf->log, "%d:%d:%d:%d:%d '
                 continue;
             }
 
-            start = h->pos.mem - 1;
+            start = h->pos - 1;
 
             switch (ch) {
 
@@ -363,11 +363,11 @@ ngx_log_debug(cf->log, "%d:%d:%d:%d:%d '
             if (found) {
                 ngx_test_null(word, ngx_push_array(cf->args), NGX_ERROR);
                 ngx_test_null(word->data,
-                              ngx_palloc(cf->pool, h->pos.mem - start + 1),
+                              ngx_palloc(cf->pool, h->pos - start + 1),
                               NGX_ERROR);
 
                 for (dst = word->data, src = start, len = 0;
-                     src < h->pos.mem - 1;
+                     src < h->pos - 1;
                      len++)
                 {
                     if (*src == '\\') {
--- a/src/core/ngx_hunk.c
+++ b/src/core/ngx_hunk.c
@@ -10,16 +10,14 @@ ngx_hunk_t *ngx_create_temp_hunk(ngx_poo
 
     ngx_test_null(h, ngx_palloc(pool, sizeof(ngx_hunk_t)), NULL);
 
-#if !(HAVE_OFFSET_EQUAL_PTR)
-    h->pos.file = h->last.file = 0;
-#endif
+    ngx_test_null(h->pre_start, ngx_palloc(pool, size + before + after), NULL);
 
-    ngx_test_null(h->pre_start, ngx_palloc(pool, size + before + after), NULL);
-    h->start = h->pos.mem = h->last.mem = h->pre_start + before;
-    h->end = h->last.mem + size;
+    h->start = h->pos = h->last = h->pre_start + before;
+    h->file_pos = h->file_last = 0;
+    h->end = h->last + size;
     h->post_end = h->end + after;
 
-    h->type = NGX_HUNK_TEMP;
+    h->type = NGX_HUNK_TEMP|NGX_HUNK_IN_MEMORY;
     h->tag = 0;
     h->file = NULL;
 
@@ -32,26 +30,24 @@ ngx_hunk_t *ngx_create_hunk_before(ngx_p
 
     ngx_test_null(h, ngx_palloc(pool, sizeof(ngx_hunk_t)), NULL);
 
-#if !(HAVE_OFFSET_EQUAL_PTR)
-    h->pos.file = h->last.file = 0;
-#endif
- 
-    if (hunk->type & NGX_HUNK_TEMP && hunk->pos.mem - hunk->pre_start >= size) {
+    if (hunk->type & NGX_HUNK_TEMP && hunk->pos - 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.mem;
-        h->start = h->pos.mem = h->last.mem = h->end - size;
+        h->end = h->post_end = hunk->pre_start = hunk->pos;
+        h->start = h->pos = h->last = h->end - size;
+        h->file_pos = h->file_last = 0;
 
-        h->type = NGX_HUNK_TEMP;
+        h->type = NGX_HUNK_TEMP|NGX_HUNK_IN_MEMORY;
         h->tag = 0;
         h->file = NULL;
 
     } else {
         ngx_test_null(h->pre_start, ngx_palloc(pool, size), NULL);
-        h->start = h->pos.mem = h->last.mem = h->pre_start; 
+        h->start = h->pos = h->last = h->pre_start; 
         h->end = h->post_end = h->start + size;
+        h->file_pos = h->file_last = 0;
 
-        h->type = NGX_HUNK_TEMP;
+        h->type = NGX_HUNK_TEMP|NGX_HUNK_IN_MEMORY;
         h->tag = 0;
         h->file = NULL;
     }
@@ -65,27 +61,26 @@ ngx_hunk_t *ngx_create_hunk_after(ngx_po
 
     ngx_test_null(h, ngx_palloc(pool, sizeof(ngx_hunk_t)), NULL);
 
-#if !(HAVE_OFFSET_EQUAL_PTR)
-    h->pos.file = h->last.file = 0;
-#endif
-
     if (hunk->type & NGX_HUNK_TEMP
-        && hunk->last.mem == hunk->end
+        && hunk->last == hunk->end
         && hunk->post_end - hunk->end >= size)
     {
         h->post_end = hunk->post_end;
-        h->pre_start = h->start = h->pos.mem = h->last.mem = hunk->post_end =
-                                                                hunk->last.mem;
-        h->type = NGX_HUNK_TEMP;
+        h->pre_start = h->start = h->pos = h->last = hunk->post_end =
+                                                                hunk->last;
+        h->file_pos = h->file_last = 0;
+
+        h->type = NGX_HUNK_TEMP|NGX_HUNK_IN_MEMORY;
         h->tag = 0;
         h->file = NULL;
 
     } else {
         ngx_test_null(h->pre_start, ngx_palloc(pool, size), NULL);
-        h->start = h->pos.mem = h->last.mem = h->pre_start; 
+        h->start = h->pos = h->last = h->pre_start; 
         h->end = h->post_end = h->start + size;
+        h->file_pos = h->file_last = 0;
 
-        h->type = NGX_HUNK_TEMP;
+        h->type = NGX_HUNK_TEMP|NGX_HUNK_IN_MEMORY;
         h->tag = 0;
         h->file = NULL;
     }
--- a/src/core/ngx_hunk.h
+++ b/src/core/ngx_hunk.h
@@ -10,34 +10,34 @@
 
 /* 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
+/* the hunk is in memory */
+#define NGX_HUNK_IN_MEMORY  0x0001
+/* the hunk's content can be changed */
+#define NGX_HUNK_TEMP       0x0002
+/* the hunk's content is in cache and can not be changed */
+#define NGX_HUNK_MEMORY     0x0004
+/* the hunk's content is mmap()ed and can not be changed */
+#define NGX_HUNK_MMAP       0x0008
+
+#define NGX_HUNK_RECYCLED   0x0010
+
+/* the hunk is in file */
+#define NGX_HUNK_FILE       0x0100
 
 /* hunk flags */
 
 /* 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
+#define NGX_HUNK_FLUSH      0x1000
 /* last hunk */
-#define NGX_HUNK_LAST       0x0200
-#if 0
-/* can be used with NGX_HUNK_LAST only */
-#define NGX_HUNK_SHUTDOWN   0x0400 /
-#endif
+#define NGX_HUNK_LAST       0x2000
 
-#define NGX_HUNK_RECYCLED   0x0800
-
-
-#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 {
+#if 0
     union {
         char    *mem;           /* start of current data */
         off_t    file;   
@@ -46,6 +46,13 @@ struct ngx_hunk_s {
         char    *mem;           /* end of current data */
         off_t    file;   
     } last;
+#endif
+
+    char        *pos;
+    char        *last;
+    off_t        file_pos;
+    off_t        file_last;
+
     int          type;
     char        *start;         /* start of hunk */
     char        *end;           /* end of hunk */
@@ -67,6 +74,10 @@ struct ngx_chain_s {
 #define NGX_CHAIN_ERROR     (ngx_chain_t *) NGX_ERROR
 
 
+#define ngx_hunk_in_memory_only(h)                                           \
+         ((h->type & (NGX_HUNK_IN_MEMORY|NGX_HUNK_FILE)) == NGX_HUNK_IN_MEMORY)
+
+
 ngx_hunk_t *ngx_create_temp_hunk(ngx_pool_t *pool, int size,
                                  int before, int after);
 
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -12,6 +12,7 @@ typedef struct {
 
 
 #define ngx_string(str)  { sizeof(str) - 1, str }
+#define ngx_null_string  { 0, NULL }
 
 
 #if (WIN32)
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -74,12 +74,17 @@ int ngx_kqueue_init(int max_connections,
 
     ngx_event_flags = NGX_HAVE_LEVEL_EVENT
                      |NGX_HAVE_ONESHOT_EVENT
+
 #if (HAVE_CLEAR_EVENT)
                      |NGX_HAVE_CLEAR_EVENT
+#else
+                     |NGX_USE_LEVEL_EVENT
 #endif
+
 #if (HAVE_LOWAT_EVENT)
                      |NGX_HAVE_LOWAT_EVENT
 #endif
+
                      |NGX_HAVE_KQUEUE_EVENT;
 
     ngx_write_chain_proc = ngx_freebsd_write_chain;
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -56,7 +56,6 @@ struct ngx_event_s {
                                 /* otherwise:                                */
                                 /*   accept: 1 if accept many, 0 otherwise   */
 
-    unsigned         level:1;
     unsigned         oneshot:1;
 
 #if 0
@@ -73,6 +72,7 @@ struct ngx_event_s {
     unsigned         process:1;
     unsigned         read_discarded:1;
 
+    unsigned         ignore_econnreset:1;
     unsigned         unexpected_eof:1;
 
 #if (HAVE_DEFERRED_ACCEPT)
@@ -149,7 +149,7 @@ typedef struct {
 #define NGX_HAVE_LEVEL_EVENT    1
 
 /* Event filter is deleted after notification - select, poll, kqueue.
-   Using /dev/poll, epoll it can be implemented with additional syscall */
+   Using /dev/poll it can be implemented with additional syscall */
 #define NGX_HAVE_ONESHOT_EVENT  2
 
 /* Event filter notifies only changes and initial level - kqueue */
@@ -159,21 +159,24 @@ typedef struct {
 #define NGX_HAVE_KQUEUE_EVENT   8
 
 /* Event filter supports low water mark - kqueue's NOTE_LOWAT,
-   early kqueue implementations have no NOTE_LOWAT so we need separate flag */
-#define NGX_HAVE_LOWAT_EVENT    16
+   early kqueue implementations have no NOTE_LOWAT so we need a separate flag */
+#define NGX_HAVE_LOWAT_EVENT    0x00000010
 
 /* Event filter notifies only changes (edges) but not initial level - epoll */
-#define NGX_HAVE_EDGE_EVENT     32
+#define NGX_HAVE_EDGE_EVENT     0x00000020
 
 /* No need to add or delete event filters - rt signals */
-#define NGX_HAVE_SIGIO_EVENT    64
+#define NGX_HAVE_SIGIO_EVENT    0x00000040
 
 /* No need to add or delete event filters - overlapped, aio_read, aioread */
-#define NGX_HAVE_AIO_EVENT      128
+#define NGX_HAVE_AIO_EVENT      0x00000080
 
-/* Need to add socket or halde only once - i/o completion port.
-   It also requires to set HAVE_AIO_EVENT and NGX_HAVE_AIO_EVENT */
-#define NGX_HAVE_IOCP_EVENT     256
+/* Need to add socket or handle only once - i/o completion port.
+   It also requires HAVE_AIO_EVENT and NGX_HAVE_AIO_EVENT to be set */
+#define NGX_HAVE_IOCP_EVENT     0x00000100
+
+
+#define NGX_USE_LEVEL_EVENT     0x00010000
 
 
 /* Event filter is deleted before closing file. Has no meaning
--- a/src/event/ngx_event_close.c
+++ b/src/event/ngx_event_close.c
@@ -13,15 +13,20 @@ int ngx_event_close_connection(ngx_event
     int rc;
     ngx_connection_t *c = (ngx_connection_t *) ev->data;
 
-    ngx_log_debug(c->log, "CLOSE: %d" _ c->fd);
+    ngx_log_debug(c->log, "close connection: %d" _ c->fd);
 
     ngx_assert((c->fd != -1), return NGX_ERROR, c->log,
                "ngx_event_close: already closed");
 
-    ngx_destroy_pool(c->pool);
+    if (c->read->timer_set) {
+        ngx_del_timer(c->read);
+        c->read->timer_set = 0;
+    }
 
-    ngx_del_timer(c->read);
-    ngx_del_timer(c->write);
+    if (c->write->timer_set) {
+        ngx_del_timer(c->write);
+        c->write->timer_set = 0;
+    }
 
     ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
     ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT);
@@ -32,5 +37,7 @@ int ngx_event_close_connection(ngx_event
 
     c->fd = -1;
 
+    ngx_destroy_pool(c->pool);
+
     return rc;
 }
--- a/src/event/ngx_event_recv.c
+++ b/src/event/ngx_event_recv.c
@@ -10,23 +10,34 @@ ssize_t ngx_event_recv_core(ngx_connecti
 {
     int                n;
     ngx_err_t          err;
+    ngx_event_t       *ev;
 
-    if (c->read->timedout) {
+    ev = c->read;
+
+    if (ev->timedout) {
         ngx_set_socket_errno(NGX_ETIMEDOUT);
         ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, "recv() failed");
         return NGX_ERROR;
     }
 
 #if (HAVE_KQUEUE)
-    ngx_log_debug(c->log, "ngx_event_recv: eof:%d, avail:%d, err:%d" _
-                  c->read->eof _ c->read->available _ c->read->error);
+    if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
+        ngx_log_debug(c->log, "ngx_event_recv: eof:%d, avail:%d, err:%d" _
+                      ev->eof _ ev->available _ ev->error);
+    }
 #endif
 
 #if (USE_KQUEUE)
 
-    if (c->read->eof && c->read->available == 0) {
-        if (c->read->error) {
-            ngx_log_error(NGX_LOG_ERR, c->log, c->read->error,
+    if (ev->eof && ev->available == 0) {
+        if (ev->error) {
+            ngx_set_socket_errno(ev->error);
+
+            if (ev->error == NGX_ECONNRESET && ev->ignore_econnreset) {
+                return 0;
+            }
+
+            ngx_log_error(NGX_LOG_ERR, c->log, ev->error,
                           "recv() failed");
             return NGX_ERROR;
         }
@@ -36,10 +47,16 @@ ssize_t ngx_event_recv_core(ngx_connecti
 
 #elif (HAVE_KQUEUE)
 
-    if (ngx_event_type == NGX_HAVE_KQUEUE_EVENT) {
-        if (c->read->eof && c->read->available == 0) {
-            if (c->read->error) {
-                ngx_log_error(NGX_LOG_ERR, c->log, c->read->error,
+    if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
+        if (ev->eof && ev->available == 0) {
+            if (ev->error) {
+                ngx_set_socket_errno(ev->error);
+
+                if (ev->error == NGX_ECONNRESET && ev->ignore_econnreset) {
+                    return 0;
+                }
+
+                ngx_log_error(NGX_LOG_ERR, c->log, ev->error,
                               "recv() failed");
                 return NGX_ERROR;
             }
@@ -55,6 +72,10 @@ ssize_t ngx_event_recv_core(ngx_connecti
     if (n == -1) {
         err = ngx_socket_errno;
 
+        if (ev->error == NGX_ECONNRESET && ev->ignore_econnreset) {
+            return 0;
+        }
+
         if (err == NGX_EAGAIN) {
             ngx_log_error(NGX_LOG_INFO, c->log, err, "recv() returns EAGAIN");
             return NGX_AGAIN;
@@ -66,12 +87,12 @@ ssize_t ngx_event_recv_core(ngx_connecti
 
 #if (USE_KQUEUE)
 
-    c->read->available -= n;
+    ev->available -= n;
 
 #elif (HAVE_KQUEUE)
 
-    if (ngx_event_type == NGX_HAVE_KQUEUE_EVENT) {
-        c->read->available -= n;
+    if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
+        ev->available -= n;
     }
 
 #endif
--- a/src/http/modules/ngx_http_event_proxy_handler.c
+++ b/src/http/modules/ngx_http_event_proxy_handler.c
@@ -4,12 +4,53 @@
 #include <ngx_string.h>
 #include <ngx_file.h>
 #include <ngx_hunk.h>
+#include <ngx_conf_file.h>
 #include <ngx_event_write.h>
+
 #include <ngx_http.h>
+#include <ngx_http_config.h>
+#include <ngx_http_core_module.h>
 #include <ngx_http_output_filter.h>
 #include <ngx_http_event_proxy_handler.h>
 
-ngx_http_module_t  ngx_http_proxy_module_ctx;
+
+static ngx_command_t  ngx_http_proxy_commands[] = {
+
+    {ngx_string("proxy_large_header"),
+     NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+     ngx_conf_set_flag_slot,
+     NGX_HTTP_LOC_CONF_OFFSET,
+     offsetof(ngx_http_proxy_loc_conf_t, large_header)},
+
+    {ngx_null_string, 0, NULL, 0, 0}
+};
+
+
+static ngx_http_module_t  ngx_http_proxy_module_ctx = {
+    NGX_HTTP_MODULE,
+
+    NULL,                                  /* create server config */
+    NULL,                                  /* init server config */
+    NULL,                                  /* create location config */
+    NULL,                                  /* merge location config */
+
+    NULL,                                  /* translate handler */
+
+    NULL,                                  /* output header filter */
+    NULL,                                  /* next output header filter */
+    NULL,                                  /* output body filter */
+    NULL                                   /* next output body filter */
+};
+
+
+ngx_module_t  ngx_http_proxy_module = {
+    0,                                     /* module index */
+    &ngx_http_proxy_module_ctx,            /* module context */
+    ngx_http_proxy_commands,               /* module directives */
+    NGX_HTTP_MODULE_TYPE,                  /* module type */
+    NULL                                   /* init module */
+};
+
 
 
 static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_request_t *r);
@@ -18,6 +59,7 @@ static int ngx_http_proxy_connect(ngx_ht
                                   char *addr_text);
 static int ngx_http_proxy_send_request(ngx_event_t *ev);
 
+static int ngx_http_proxy_init_response(ngx_event_t *ev);
 static int ngx_http_proxy_read_response_header(ngx_event_t *ev);
 static int ngx_http_proxy_process_status_line(ngx_http_request_t *r,
                                                ngx_http_proxy_ctx_t *p);
@@ -45,12 +87,13 @@ int ngx_http_proxy_handler(ngx_http_requ
 
     if (p == NULL) {
         ngx_http_create_ctx(r, p, ngx_http_proxy_module_ctx,
-                            sizeof(ngx_http_proxy_ctx_t));
+                            sizeof(ngx_http_proxy_ctx_t),
+                            NGX_HTTP_INTERNAL_SERVER_ERROR);
     }
 
     chain = ngx_http_proxy_create_request(r);
     if (chain == NULL) {
-        return NGX_ERROR;
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
     p->out = chain;
@@ -74,8 +117,13 @@ static ngx_chain_t *ngx_http_proxy_creat
 
     /* 2 is for "\r\n" after request line
        and 2 is for "\r\n" at the header end */
+
+    /* STUB: "method p->url HTTP/1.0" length */
+
     len = r->request_line.len + 2 + 2;
 
+    /* TODO: Host length */
+
     /* "Connection: close\r\n" */
     len += sizeof(conn_close) - 1;
 
@@ -98,12 +146,16 @@ static ngx_chain_t *ngx_http_proxy_creat
     ngx_test_null(hunk, ngx_create_temp_hunk(r->pool, len, 0, 0), NULL);
     ngx_add_hunk_to_chain(chain, hunk, r->pool, NULL);
 
-    ngx_memcpy(hunk->last.mem, r->request_line.data, r->request_line.len);
-    hunk->last.mem += r->request_line.len;
-    *(hunk->last.mem++) = CR; *(hunk->last.mem++) = LF;
+    /* STUB: "method p->url HTTP/1.0" */
 
-    ngx_memcpy(hunk->last.mem, conn_close, sizeof(conn_close) - 1);
-    hunk->last.mem += sizeof(conn_close) - 1;
+    ngx_memcpy(hunk->last, r->request_line.data, r->request_line.len);
+    hunk->last += r->request_line.len;
+    *(hunk->last++) = CR; *(hunk->last++) = LF;
+
+    /* TODO: Host header */
+
+    ngx_memcpy(hunk->last, conn_close, sizeof(conn_close) - 1);
+    hunk->last += sizeof(conn_close) - 1;
 
     for (i = 0; i < r->headers_in.headers->nelts; i++) {
         if (&header[i] == r->headers_in.host) {
@@ -114,25 +166,25 @@ static ngx_chain_t *ngx_http_proxy_creat
             continue;
         }
 
-        ngx_memcpy(hunk->last.mem, header[i].key.data, header[i].key.len);
-        hunk->last.mem += header[i].key.len;
+        ngx_memcpy(hunk->last, header[i].key.data, header[i].key.len);
+        hunk->last += header[i].key.len;
 
-        *(hunk->last.mem++) = ':'; *(hunk->last.mem++) = ' ';
+        *(hunk->last++) = ':'; *(hunk->last++) = ' ';
 
-        ngx_memcpy(hunk->last.mem, header[i].value.data, header[i].value.len);
-        hunk->last.mem += header[i].value.len;
+        ngx_memcpy(hunk->last, header[i].value.data, header[i].value.len);
+        hunk->last += header[i].value.len;
 
-        *(hunk->last.mem++) = CR; *(hunk->last.mem++) = LF;
+        *(hunk->last++) = CR; *(hunk->last++) = LF;
 
         ngx_log_debug(r->connection->log, "proxy: '%s: %s'" _
                       header[i].key.data _ header[i].value.data);
     }
 
     /* add "\r\n" at the header end */
-    *(hunk->last.mem++) = CR; *(hunk->last.mem++) = LF;
+    *(hunk->last++) = CR; *(hunk->last++) = LF;
 
-    /* STUB */ *(hunk->last.mem++) = '\0';
-    ngx_log_debug(r->connection->log, "PROXY:\n'%s'" _ hunk->pos.mem);
+    /* STUB */ *(hunk->last++) = '\0';
+    ngx_log_debug(r->connection->log, "PROXY:\n'%s'" _ hunk->pos);
 
     return chain;
 }
@@ -142,7 +194,7 @@ static int ngx_http_proxy_connect(ngx_ht
                                   struct sockaddr_in *addr,
                                   char *addr_text)
 {
-    int                  rc;
+    int                  rc, event;
     ngx_err_t            err;
     ngx_socket_t         s;
     ngx_event_t         *rev, *wev;
@@ -157,7 +209,7 @@ static int ngx_http_proxy_connect(ngx_ht
     if (s == -1) {
         ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,
                       ngx_socket_n " failed");
-        return NGX_ERROR;
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
 #if 0
@@ -172,7 +224,7 @@ static int ngx_http_proxy_connect(ngx_ht
                               ngx_close_socket_n " failed");
             }
 
-            return NGX_ERROR;
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
     }
 #endif
@@ -186,7 +238,7 @@ static int ngx_http_proxy_connect(ngx_ht
                           ngx_close_socket_n " failed");
         }
 
-        return NGX_ERROR;
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
     rc = connect(s, (struct sockaddr *) addr, sizeof(struct sockaddr_in));
@@ -201,7 +253,7 @@ static int ngx_http_proxy_connect(ngx_ht
                               ngx_close_socket_n " failed");
             }
 
-            return NGX_ERROR;
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
     }
 
@@ -226,21 +278,48 @@ static int ngx_http_proxy_connect(ngx_ht
 
     ngx_test_null(pc->pool,
                   ngx_create_pool(/* STUB */ 1024 /**/, pc->log),
-                  NGX_ERROR);
+                  NGX_HTTP_INTERNAL_SERVER_ERROR);
 
     wev->event_handler = ngx_http_proxy_send_request;
-    rev->event_handler = ngx_http_proxy_read_response_header;
+    rev->event_handler = ngx_http_proxy_init_response;
 
-#if (HAVE_CLEAR_EVENT)
+#if (USE_KQUEUE)
+
     if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
 #else
-    if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT) != NGX_OK) {
-#endif
-        return NGX_ERROR;
+
+#if (HAVE_CLEAR_EVENT) /* kqueue */
+
+    if (ngx_event_flags & NGX_HAVE_CLEAR_EVENT) {
+        event = NGX_CLEAR_EVENT;
+
+    } else {
+        event = NGX_LEVEL_EVENT;
     }
 
+#else /* select, poll, /dev/poll */
+
+    event = NGX_LEVEL_EVENT;
+
+#endif
+
+    if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+#endif /* USE_KQUEUE */
+
+    /* TODO: aio, iocp */
+
+    /* The connection is in a progress */
     if (rc == -1) {
-        return ngx_add_event(wev, NGX_WRITE_EVENT, NGX_ONESHOT_EVENT);
+        /* TODO: oneshot */
+        if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_ONESHOT_EVENT) != NGX_OK) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
     }
 
     wev->write = 1;
@@ -262,7 +341,7 @@ static int ngx_http_proxy_send_request(n
     p = (ngx_http_proxy_ctx_t *)
                          ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
 
-    chain = ngx_event_write(c, p->out, 0);
+    chain = ngx_write_chain(c, p->out, 0);
     if (chain == (ngx_chain_t *) -1) {
         return NGX_ERROR;
     }
@@ -273,7 +352,7 @@ static int ngx_http_proxy_send_request(n
 }
 
 
-static int ngx_http_proxy_read_response_header(ngx_event_t *ev)
+static int ngx_http_proxy_init_response(ngx_event_t *ev)
 {
     int                    n;
     ngx_hunk_t           **ph;
@@ -281,48 +360,126 @@ static int ngx_http_proxy_read_response_
     ngx_http_request_t    *r;
     ngx_http_proxy_ctx_t  *p;
 
+    c = (ngx_connection_t *) ev->data;
+    r = (ngx_http_request_t *) c->data;
+
     if (ev->timedout) {
-        return NGX_ERROR;
+        return ngx_http_proxy_error(r, p, NGX_HTTP_GATEWAY_TIME_OUT);
     }
 
+    p = (ngx_http_proxy_ctx_t *)
+                         ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
+
+    ngx_test_null(p->header_in,
+                  ngx_create_temp_hunk(r->pool,
+                                       /* STUB */ 1024 /**/, 0, 0),
+                  ngx_http_proxy_error(r, p, NGX_HTTP_INTERNAL_SERVER_ERROR));
+
+    p->header_in->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
+
+    ngx_test_null(p->headers_in,
+                  ngx_palloc(r->pool, sizeof(ngx_http_proxy_headers_in_t)),
+                  ngx_http_proxy_error(r, p, NGX_HTTP_INTERNAL_SERVER_ERROR));
+
+    ngx_init_array(p->hunks, r->pool,
+                   /* STUB */ 10 /**/,
+                   sizeof(ngx_hunk_t *),
+                   ngx_http_proxy_error(r, p, NGX_HTTP_INTERNAL_SERVER_ERROR));
+
+    ngx_test_null(ph, ngx_push_array(&p->hunks), NGX_ERROR);
+    *ph = p->header_in;
+
+    p->state_handler = ngx_http_proxy_process_status_line;
+
+    return ngx_http_proxy_read_response_header(ev);
+}
+
+
+static int ngx_http_proxy_read_response_header(ngx_event_t *ev)
+{
+    int                         n;
+    ngx_hunk_t                **ph;
+    ngx_connection_t           *c;
+    ngx_http_request_t         *r;
+    ngx_http_proxy_ctx_t       *p;
+    ngx_http_proxy_loc_conf_t  *lcf;
+
     c = (ngx_connection_t *) ev->data;
     r = (ngx_http_request_t *) c->data;
     p = (ngx_http_proxy_ctx_t *)
-                         ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
-
-    if (p->header_in == NULL) {
-        ngx_test_null(p->header_in,
-                      ngx_create_temp_hunk(r->pool,
-                                           /* STUB */ 1024 /**/, 0, 0),
-                      NGX_ERROR);
-
-        p->header_in->type = NGX_HUNK_MEMORY;
+                         ngx_http_get_module_ctx(r, ngx_http_proxy_module);
 
-        ngx_test_null(p->headers_in,
-                      ngx_palloc(r->pool, sizeof(ngx_http_proxy_headers_in_t)),
-                      NGX_ERROR);
-
-        ngx_init_array(p->hunks, r->pool,
-                       /* STUB */ 10 /**/,
-                       sizeof(ngx_hunk_t *),
-                       NGX_ERROR);
-
-        ngx_test_null(ph, ngx_push_array(&p->hunks), NGX_ERROR);
-        *ph = p->header_in;
-
-        p->state_handler = ngx_http_proxy_process_status_line;
+    if (ev->timedout) {
+        return ngx_http_proxy_error(r, p, NGX_HTTP_GATEWAY_TIME_OUT);
     }
 
-    n = ngx_event_recv(c, p->header_in->last.mem,
-                       p->header_in->end - p->header_in->last.mem);
+    lcf = (ngx_http_proxy_loc_conf_t *)
+                        ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
+
+
+#if 0
+
+    do {
+        n = ngx_event_recv(c, p->header_in->last,
+                           p->header_in->end - p->header_in->last;
+
+        if (n == NGX_AGAIN) {
+            if (ev->timer_set) {
+                ngx_del_timer(ev);
+            } else {
+                ev->timer_set = 1;
+            }
+
+            ngx_add_timer(ev, lcf->timeout);
+            return NGX_AGAIN;
+        }
+
+        if (n == NGX_ERROR) {
+            ngx_http_proxy_close_request(r, p);
+            return ngx_http_error(r, NGX_HTTP_BAD_GATEWAY);
+        }
+
+        ngx_log_debug(c->log, "http proxy read %d" _ n);
+
+        if (n == 0) {
+            ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                          "client has prematurely closed connection");
+            ngx_http_proxy_close_request(r, p);
+        }
+
+        p->header_in->last += n;
+
+        if (lcf->large_header && p->header_in->end == p->header_in->last) {
+            again = 1;
+        } else {
+            again = 0;
+        }
+
+
+
+
+#if (HAVE_AIO_EVENT) /* aio, iocp */
+
+        if (ngx_event_flags & NGX_HAVE_AIO_EVENT) {
+            again = 1;
+        }
+
+#endif
+
+    } while (rc == NGX_AGAIN && again);
+
+#endif
+
+    n = ngx_event_recv(c, p->header_in->last,
+                       p->header_in->end - p->header_in->last);
 
     ngx_log_debug(c->log, "READ:%d" _ n);
 
-    p->header_in->last.mem += n;
+    p->header_in->last += n;
 
     /* STUB */
-    *p->header_in->last.mem = '\0';
-    ngx_log_debug(c->log, "PROXY:\n'%s'" _ p->header_in->pos.mem);
+    *p->header_in->last = '\0';
+    ngx_log_debug(c->log, "PROXY:\n'%s'" _ p->header_in->pos);
     /**/
 
     if (n == 0) {
@@ -355,13 +512,14 @@ static int ngx_http_proxy_read_response_
 #endif
 
     ev->event_handler = ngx_http_proxy_read_response_body;
-    if (p->header_in->end - p->header_in->last.mem == 0) {
+    if (p->header_in->end - p->header_in->last == 0) {
         return ngx_http_proxy_read_response_body(ev);
     }
 
     return NGX_WAITING;
 }
 
+
 static int ngx_http_proxy_process_status_line(ngx_http_request_t *r,
                                               ngx_http_proxy_ctx_t *p)
 {
@@ -380,7 +538,7 @@ static int ngx_http_proxy_process_status
         p->state_handler = NULL;
     }
 
-    if (p->header_in->last.mem >= p->header_in->end) {
+    if (p->header_in->last >= p->header_in->end) {
         rc = NGX_HTTP_PARSE_TOO_LONG_STATUS_LINE;
 
     } else if (rc == NGX_AGAIN) {
@@ -390,6 +548,7 @@ static int ngx_http_proxy_process_status
     /* STUB */ return NGX_ERROR;
 }
 
+
 #if 0
 static int ngx_http_proxy_process_response_header(ngx_http_request_t *r,
                                                   ngx_http_proxy_ctx_t *p)
@@ -398,6 +557,7 @@ static int ngx_http_proxy_process_respon
 }
 #endif
 
+
 static int ngx_http_proxy_read_response_body(ngx_event_t *ev)
 {
     int                    n;
@@ -419,7 +579,7 @@ static int ngx_http_proxy_read_response_
 
     if (p->hunks.nelts > 0) {
         h = ((ngx_hunk_t **) p->hunks.elts)[p->hunks.nelts - 1];
-        left = h->end - h->last.mem;
+        left = h->end - h->last;
 
     } else {
         h = NULL;
@@ -453,13 +613,13 @@ static int ngx_http_proxy_read_response_
                                                /* STUB */ 4096 /**/, 0, 0),
                           NGX_ERROR);
 
-            h->type = NGX_HUNK_MEMORY;
+            h->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
             *ph = h;
         }
 
         if (h != NULL) {
-            buf = h->last.mem;
-            size = h->end - h->last.mem;
+            buf = h->last;
+            size = h->end - h->last;
 
         } else {
             buf = (char *) &buf;
@@ -478,12 +638,12 @@ static int ngx_http_proxy_read_response_
             return NGX_ERROR;
         }
 
-        h->last.mem += n;
-        left = h->end - h->last.mem;
+        h->last += n;
+        left = h->end - h->last;
 
         /* STUB */
-        *h->last.mem = '\0';
-        ngx_log_debug(c->log, "PROXY:\n'%s'" _ h->pos.mem);
+        *h->last = '\0';
+        ngx_log_debug(c->log, "PROXY:\n'%s'" _ h->pos);
         /**/
 
     } while (n > 0 && left == 0);
@@ -503,6 +663,7 @@ static int ngx_http_proxy_read_response_
     /* STUB */ return NGX_WAITING;
 }
 
+
 static int ngx_http_proxy_write_to_client(ngx_event_t *ev)
 {
     int  rc;
@@ -536,6 +697,15 @@ static int ngx_http_proxy_write_to_clien
 }
 
 
+static int ngx_http_proxy_error(ngx_http_request_t *r, ngx_http_proxy_ctx_t *p,
+                                int error)
+{
+    ngx_event_close_connection(p->connection->read);
+
+    return ngx_http_error(r, error);
+}
+
+
 static int ngx_read_http_proxy_status_line(ngx_http_proxy_ctx_t *ctx)
 {
     char   ch;
@@ -554,21 +724,16 @@ static int ngx_read_http_proxy_status_li
     } state;
 
     state = ctx->state;
-    p = ctx->header_in->pos.mem;
-
-    while (p < ctx->header_in->last.mem && state < sw_done) {
-        ch = *p++;
+    p = ctx->header_in->pos;
 
-#if 0
-fprintf(stderr, "state: %d, pos: %x, end: %x, char: '%c', status: %d\n",
-        state, p, ctx->header_in->last.mem, ch, ctx->status);
-#endif
+    while (p < ctx->header_in->last && state < sw_done) {
+        ch = *p++;
 
         switch (state) {
 
         /* "HTTP/" */
         case sw_start:
-            if (p + 3 >= ctx->header_in->last.mem) {
+            if (p + 3 >= ctx->header_in->last) {
                 return NGX_AGAIN;
             }
 
@@ -684,7 +849,7 @@ fprintf(stderr, "state: %d, pos: %x, end
         }
     }
 
-    ctx->header_in->pos.mem = p;
+    ctx->header_in->pos = p;
 
     if (state == sw_done) {
         if (ctx->request_end == NULL) {
--- a/src/http/modules/ngx_http_event_proxy_handler.h
+++ b/src/http/modules/ngx_http_event_proxy_handler.h
@@ -14,6 +14,12 @@ typedef struct {
     int dummy;
 } ngx_http_proxy_headers_in_t;
 
+
+typedef struct {
+    int         large_header;
+} ngx_http_proxy_loc_conf_t;
+
+
 typedef struct ngx_http_proxy_ctx_s  ngx_http_proxy_ctx_t;
 
 struct ngx_http_proxy_ctx_s {
@@ -24,6 +30,7 @@ struct ngx_http_proxy_ctx_s {
 
     int           hunk_n;
 
+    ngx_connection_t  *connection;
     ngx_http_proxy_headers_in_t  *headers_in;
 
     ngx_hunk_t  *header_in;
@@ -36,7 +43,11 @@ struct ngx_http_proxy_ctx_s {
 };
 
 
-extern ngx_http_module_t  ngx_http_proxy_module;
+extern ngx_module_t  ngx_http_proxy_module;
+
+
+static int ngx_http_proxy_error(ngx_http_request_t *r, ngx_http_proxy_ctx_t *p,
+                                int error);
 
 
 #endif /* _NGX_HTTP_PROXY_HANDLER_H_INCLUDED_ */
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -144,8 +144,8 @@ int ngx_http_static_handler(ngx_http_req
 #if 1
 
     h->type = NGX_HUNK_FILE|NGX_HUNK_LAST;
-    h->pos.file = 0;
-    h->last.file = ngx_file_size(r->file.info);
+    h->file_pos = 0;
+    h->file_last = ngx_file_size(r->file.info);
 
     h->file->fd = r->file.fd;
     h->file->log = r->connection->log;
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -27,11 +27,6 @@ int  ngx_http_large_client_header = 1;
 
 int  ngx_http_url_in_error_log = 1;
 
-/* STUB: per location */
-int  ngx_http_lingering_timeout = 5000;
-int  ngx_http_lingering_time = 30;
-/**/
-
 
 ngx_array_t  ngx_http_index_handlers;
 
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -47,29 +47,17 @@
 #define NGX_HTTP_NOT_FOUND              404
 #define NGX_HTTP_REQUEST_URI_TOO_LARGE  414
 #define NGX_HTTP_INTERNAL_SERVER_ERROR  500
+#define NGX_HTTP_NOT_IMPLEMENTED        501
+#define NGX_HTTP_BAD_GATEWAY            502
+#define NGX_HTTP_SERVICE_UNAVAILABLE    503
+#define NGX_HTTP_GATEWAY_TIME_OUT       504
+
 
 
 #define NGX_HTTP_STATIC_HANDLER     0
 #define NGX_HTTP_DIRECTORY_HANDLER  1
 
 
-
-typedef struct {
-    char          *doc_root;
-    size_t         doc_root_len;
-
-    size_t         connection_pool_size;
-    size_t         request_pool_size;
-
-    size_t         header_buffer_size;
-    size_t         discarded_buffer_size;
-
-    ngx_msec_t     header_timeout;
-    ngx_msec_t     lingering_timeout;
-    time_t         lingering_time;
-} ngx_http_server_t;
-
-
 typedef struct {
     size_t  len;
     char   *data;
@@ -232,9 +220,9 @@ typedef struct {
 #define ngx_http_get_module_loc_conf(r, module)  r->loc_conf[module.index]
 #define ngx_http_get_module_ctx(r, module)       r->ctx[module.index]
 
-#define ngx_http_create_ctx(r, cx, module, size)                              \
+#define ngx_http_create_ctx(r, cx, module, size, error)                       \
             do {                                                              \
-                ngx_test_null(cx, ngx_pcalloc(r->pool, size), NGX_ERROR);     \
+                ngx_test_null(cx, ngx_pcalloc(r->pool, size), error);         \
                 r->ctx[module.index] = cx;                                    \
             } while (0)
 
@@ -279,9 +267,6 @@ extern int  ngx_http_client_header_buffe
 extern int  ngx_http_large_client_header;
 extern int  ngx_http_discarded_buffer_size;
 
-extern int  ngx_http_lingering_timeout;
-extern int  ngx_http_lingering_time;
-
 extern int  ngx_http_url_in_error_log;
 
 extern ngx_array_t  ngx_http_index_handlers;
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -96,6 +96,18 @@ static ngx_command_t  ngx_http_core_comm
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_core_loc_conf_t, send_timeout)},
 
+    {ngx_string("lingering_time"),
+     NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+     ngx_conf_set_time_slot,
+     NGX_HTTP_LOC_CONF_OFFSET,
+     offsetof(ngx_http_core_loc_conf_t, lingering_time)},
+
+    {ngx_string("lingering_timeout"),
+     NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+     ngx_conf_set_time_slot,
+     NGX_HTTP_LOC_CONF_OFFSET,
+     offsetof(ngx_http_core_loc_conf_t, lingering_timeout)},
+
     {ngx_string(""), 0, NULL, 0, 0}
 };
 
@@ -138,7 +150,7 @@ int ngx_http_handler(ngx_http_request_t 
     r->connection->unexpected_eof = 0;
 
     r->lingering_close = 1;
-    r->keepalive = 1;
+    r->keepalive = 0;
 
 #if 1
     r->filter = NGX_HTTP_FILTER_NEED_IN_MEMORY;
@@ -264,6 +276,11 @@ ngx_log_debug(r->connection->log, "trans
          }
     }
 
+#if 0
+    /* STUB */ r->handler = ngx_http_proxy_handler;
+    return NGX_OK;
+#endif
+
     if (r->uri.data[r->uri.len - 1] == '/') {
         r->handler = ngx_http_core_index_handler;
         return NGX_OK;
@@ -504,44 +521,38 @@ int ngx_http_error(ngx_http_request_t *r
 
 int ngx_http_close_request(ngx_http_request_t *r)
 {
+    ngx_connection_t    *c;
     ngx_http_log_ctx_t  *ctx;
 
-    ngx_log_debug(r->connection->log, "CLOSE#: %d" _ r->file.fd);
+    c = r->connection;
 
     ngx_http_log_handler(r);
 
-    ngx_assert((r->file.fd != NGX_INVALID_FILE), /* void */ ; ,
-               r->connection->log, "file already closed");
-
     if (r->file.fd != NGX_INVALID_FILE) {
         if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+            ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                           ngx_close_file_n " failed");
         }
     }
 
-/*
-    if (r->logging)
-        ngx_http_log_request(r);
-*/
-
-    ctx = (ngx_http_log_ctx_t *) r->connection->log->data;
-
     /* ctx->url was allocated from r->pool */
+    ctx = (ngx_http_log_ctx_t *) c->log->data;
     ctx->url = NULL;
 
     ngx_destroy_pool(r->pool);
 
-    ngx_log_debug(r->connection->log, "http closed");
-
-    if (r->connection->read->timer_set) {
-        ngx_del_timer(r->connection->read);
+    if (c->read->timer_set) {
+        ngx_del_timer(c->read);
+        c->read->timer_set = 0;
     }
 
-    if (r->connection->write->timer_set) {
-        ngx_del_timer(r->connection->write);
+    if (c->write->timer_set) {
+        ngx_del_timer(c->write);
+        c->write->timer_set = 0;
     }
 
+    ngx_log_debug(c->log, "http closed");
+
     return NGX_DONE;
 }
 
@@ -780,7 +791,7 @@ static void *ngx_http_core_create_loc_co
 
     lcf->send_timeout = 10;
     lcf->discarded_buffer_size = 1500;
-    lcf->lingering_time = 30;
+    lcf->lingering_time = 30000;
     lcf->lingering_timeout = 5000;
 
 /*
@@ -809,8 +820,8 @@ static char *ngx_set_listen(ngx_conf_t *
     args = (ngx_str_t *) cf->args->elts;
 
     ls->port = atoi(args[1].data);
-    if (ls->port < 0) {
-        return "port must be greater or equal to zero";
+    if (ls->port < 1 || ls->port > 65536) {
+        return "port must be between 1 and 65535";
     }
 
     return NGX_CONF_OK;
--- a/src/http/ngx_http_event.c
+++ b/src/http/ngx_http_event.c
@@ -130,7 +130,6 @@ int ngx_http_init_connection(ngx_connect
 
     /* select, poll, /dev/poll */
 
-    ev->level = 1;
     return ngx_add_event(ev, NGX_READ_EVENT, NGX_LEVEL_EVENT);
 
 #endif /* USE_KQUEUE */
@@ -190,7 +189,7 @@ static int ngx_http_init_request(ngx_eve
 
 static int ngx_http_process_request_header(ngx_event_t *ev)
 {
-    int                  n, rc;
+    int                  n, rc, again;
     ngx_connection_t    *c;
     ngx_http_request_t  *r;
     ngx_http_log_ctx_t  *ctx;
@@ -200,18 +199,22 @@ static int ngx_http_process_request_head
 
     ngx_log_debug(ev->log, "http process request");
 
-#if (HAVE_AIO_EVENT)
     do {
-#endif
 
         if (r->header_read) {
+            if (r->header_in->end - r->header_in->last == 0) {
+                again = 1;
+            } else {
+                again = 0;
+            }
+
             r->header_read = 0;
             ngx_log_debug(ev->log, "http preread %d" _
-                          r->header_in->last.mem - r->header_in->pos.mem);
+                          r->header_in->last - r->header_in->pos);
 
         } else {
-            n = ngx_event_recv(c, r->header_in->last.mem,
-                               r->header_in->end - r->header_in->last.mem);
+            n = ngx_event_recv(c, r->header_in->last,
+                               r->header_in->end - r->header_in->last);
 
             if (n == NGX_AGAIN) {
                 if (!r->header_timeout_set) {
@@ -228,8 +231,9 @@ static int ngx_http_process_request_head
                 return NGX_AGAIN;
             }
 
-            if (n == NGX_ERROR)
+            if (n == NGX_ERROR) {
                 return ngx_http_close_request(r);
+            }
 
             ngx_log_debug(ev->log, "http read %d" _ n);
 
@@ -239,7 +243,14 @@ static int ngx_http_process_request_head
                 return ngx_http_close_request(r);
             }
 
-            r->header_in->last.mem += n;
+            r->header_in->last += n;
+
+            if (ngx_http_large_client_header
+                                  && r->header_in->end == r->header_in->last) {
+                again = 1;
+            } else {
+                again = 0;
+            }
         }
 
         /* the state_handlers are called in the following order:
@@ -250,16 +261,22 @@ static int ngx_http_process_request_head
             /* state_handlers return NGX_OK when the whole header done */
             rc = (r->state_handler)(r);
 
-            if (rc == NGX_ERROR)
+            if (rc == NGX_ERROR) {
                 return rc;
+            }
 
-        } while (rc == NGX_AGAIN
-                 && r->header_in->pos.mem < r->header_in->last.mem);
+        } while (rc == NGX_AGAIN && r->header_in->pos < r->header_in->last);
 
 #if (HAVE_AIO_EVENT) /* aio, iocp */
-    } while (rc == NGX_AGAIN && ngx_event_flags & NGX_HAVE_AIO_EVENT);
+
+        if (ngx_event_flags & NGX_HAVE_AIO_EVENT) {
+            again = 1;
+        }
+
 #endif
 
+    } while (rc == NGX_AGAIN && again);
+
     if (rc == NGX_OK) {
         /* HTTP header done */
 
@@ -308,7 +325,7 @@ static int ngx_http_process_request_line
                       ngx_http_close_request(r));
         ngx_cpystrn(r->uri.data, r->uri_start, r->uri.len + 1);
 
-        /* if the large client headers is enabled then
+        /* if the large client headers are enabled then
            we need to copy a request line */
 
         r->request_line.len = r->request_end - r->request_start;
@@ -376,12 +393,12 @@ static int ngx_http_process_request_line
 
     /* NGX_AGAIN: a request line parsing is still not complete */
 
-    if (r->header_in->last.mem == r->header_in->end) {
+    if (r->header_in->last == r->header_in->end) {
 
         /* If it's a pipelined request and a request line is not complete
            then we need to copy it to the start of r->header_in hunk.
-           We need to copy it here only if the large client header
-           is enabled otherwise a request line had been already copied
+           We need to copy it here only if the large client headers
+           are enabled otherwise a request line had been already copied
            to the start of r->header_in hunk in ngx_http_set_keepalive() */
 
         if (ngx_http_large_client_header) {
@@ -394,10 +411,10 @@ static int ngx_http_process_request_line
             }
 
             ngx_memcpy(r->header_in->start, r->request_start,
-                       r->header_in->last.mem - r->request_start);
+                       r->header_in->last - r->request_start);
 
-            r->header_in->pos.mem -= offset;
-            r->header_in->last.mem -= offset;
+            r->header_in->pos -= offset;
+            r->header_in->last -= offset;
             r->request_start = r->header_in->start;
             r->request_end -= offset;
             r->uri_start -= offset;
@@ -437,7 +454,7 @@ static int ngx_http_process_request_head
 
             return NGX_AGAIN;
 
-        /* the whole header has been parsed successfully */
+        /* a whole header has been parsed successfully */
         } else if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
             ngx_log_debug(r->connection->log, "HTTP header done");
 
@@ -468,9 +485,9 @@ static int ngx_http_process_request_head
 
         /* NGX_AGAIN: a header line parsing is still not complete */
 
-        if (r->header_in->last.mem == r->header_in->end) {
+        if (r->header_in->last == r->header_in->end) {
 
-            /* if the large client header is enabled then
+            /* if the large client headers are enabled then
                 we need to compact r->header_in hunk */
 
             if (ngx_http_large_client_header) {
@@ -483,10 +500,10 @@ static int ngx_http_process_request_head
                 }
 
                 ngx_memcpy(r->header_in->start, r->header_name_start,
-                           r->header_in->last.mem - r->header_name_start);
+                           r->header_in->last - r->header_name_start);
 
-                r->header_in->last.mem -= offset;
-                r->header_in->pos.mem -= offset;
+                r->header_in->last -= offset;
+                r->header_in->pos -= offset;
                 r->header_name_start = r->header_in->start;
                 r->header_name_end -= offset;
                 r->header_start -= offset;
@@ -512,7 +529,7 @@ static int ngx_http_process_request_head
 
     ngx_test_null(h, ngx_push_table(r->headers_in.headers), NGX_ERROR);
 
-    /* if large client header is enabled then
+    /* if large client headers are enabled then
        we need to copy header name and value */
 
     h->key.len = r->header_name_end - r->header_name_start;
@@ -631,13 +648,11 @@ static int ngx_http_event_request_handle
 
         } else {
             event = NGX_LEVEL_EVENT;
-            wev->level = 1;
         }
 
 #else /* select, poll, /dev/poll */
 
         event = NGX_LEVEL_EVENT;
-        wev->level = 1;
 
 #endif
 
@@ -751,11 +766,11 @@ static int ngx_http_block_read(ngx_event
 
 #else
 
-    if (ev->level) {       /* select, poll, /dev/poll */
+    if (ngx_event_flags & NGX_USE_LEVEL_EVENT) { /* select, poll, /dev/poll */
         ev->blocked = 1;
         return ngx_del_event(ev, NGX_READ_EVENT, 0);
 
-    } else {               /* kqueue, epoll */
+    } else {                                     /* kqueue, epoll */
         return NGX_OK;
     }
 
@@ -831,7 +846,7 @@ static int ngx_http_read_discarded_body(
 
 static int ngx_http_set_keepalive(ngx_http_request_t *r)
 {
-    int                  len;
+    int                  len, blocked;
     ngx_hunk_t          *h;
     ngx_event_t         *rev, *wev;
     ngx_connection_t    *c;
@@ -841,33 +856,37 @@ static int ngx_http_set_keepalive(ngx_ht
     rev = c->read;
     wev = c->write;
 
-    if (rev->blocked && rev->level) {
+    ctx = (ngx_http_log_ctx_t *) c->log->data;
+    ctx->action = "closing request";
+    ngx_http_close_request(r);
+
+    if (rev->blocked && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
         if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT) == NGX_ERROR) {
             return NGX_ERROR;
         }
+        blocked = 1;
         rev->blocked = 0;
-    }
 
-    ctx = (ngx_http_log_ctx_t *) c->log->data;
-    ctx->action = "closing request";
-    ngx_http_close_request(r);
+    } else {
+        blocked = 0;
+    }
 
     h = c->buffer;
 
     /* pipelined request */
-    if (h->pos.mem < h->last.mem) {
+    if (h->pos < h->last) {
 
         /* We do not know here whether pipelined request is complete
-           so we need to copy it to the start of c->buffer if the large
-           client header is not enabled. This copy should be rare
-           because clients that support pipelined requests (Mozilla 1.x,
-           Opera 6.x) are still rare */
+           so if large client headers are not enabled
+           we need to copy the data to the start of c->buffer.
+           This copy should be rare because clients that support
+           pipelined requests (Mozilla 1.x, Opera 6.x) are still rare */
 
         if (!ngx_http_large_client_header) {
-            len = h->last.mem - h->pos.mem; 
-            ngx_memcpy(h->start, h->pos.mem, len);
-            h->pos.mem = h->start;
-            h->last.mem = h->start + len;
+            len = h->last - h->pos; 
+            ngx_memcpy(h->start, h->pos, len);
+            h->pos = h->start;
+            h->last = h->start + len;
         }
 
         c->pipeline = 1;
@@ -877,10 +896,10 @@ static int ngx_http_set_keepalive(ngx_ht
 
     c->pipeline = 0;
 
-    h->pos.mem = h->last.mem = h->start;
+    h->pos = h->last = h->start;
     rev->event_handler = ngx_http_keepalive_handler;
 
-    if (wev->active && wev->level) {
+    if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
         if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
             return NGX_ERROR;
         }
@@ -890,7 +909,13 @@ static int ngx_http_set_keepalive(ngx_ht
 
 #if (HAVE_AIO_EVENT) /* aio, iocp */
 
-    if (ngx_event_flags & NGX_HAVE_AIO_EVENT) {
+    if ((ngx_event_flags & NGX_HAVE_AIO_EVENT) || blocked) {
+        return ngx_http_keepalive_handler(rev);
+    }
+
+#else
+
+    if (blocked) {
         return ngx_http_keepalive_handler(rev);
     }
 
@@ -913,11 +938,13 @@ static int ngx_http_keepalive_handler(ng
     if (ev->timedout)
         return NGX_DONE;
 
-    /* TODO: MSIE closes keepalive connection with ECONNRESET
-             so we need to handle here this error
-             1) in INFO (not ERR) level, 2) with time elapsed */
-    n = ngx_event_recv(c, c->buffer->last.mem,
-                       c->buffer->end - c->buffer->last.mem);
+    /* MSIE closes keepalive connection with RST flag
+       so we ignore ECONNRESET here */
+
+    ev->ignore_econnreset = 1;
+    ngx_set_socket_errno(0);
+    n = ngx_event_recv(c, c->buffer->last, c->buffer->end - c->buffer->last);
+    ev->ignore_econnreset = 0;
 
     if (n == NGX_AGAIN || n == NGX_ERROR)
         return n;
@@ -926,12 +953,12 @@ static int ngx_http_keepalive_handler(ng
     ev->log->handler = NULL;
 
     if (n == 0) {
-        ngx_log_error(NGX_LOG_INFO, ev->log, 0,
+        ngx_log_error(NGX_LOG_INFO, ev->log, ngx_socket_errno,
                       "client %s closed keepalive connection", ctx->client);
         return NGX_DONE;
     }
 
-    c->buffer->last.mem += n;
+    c->buffer->last += n;
     ev->log->handler = ngx_http_log_error;
     ctx->action = "reading client request line";
 
@@ -941,57 +968,67 @@ static int ngx_http_keepalive_handler(ng
 
 static int ngx_http_set_lingering_close(ngx_http_request_t *r)
 {
-    ngx_event_t  *ev;
-    ngx_connection_t    *c;
+    int                        blocked;
+    ngx_event_t               *rev, *wev;
+    ngx_connection_t          *c;
     ngx_http_core_loc_conf_t  *lcf;
 
     c = r->connection;
-    ev = r->connection->read;
+    rev = c->read;
+    wev = c->write;
 
     lcf = (ngx_http_core_loc_conf_t *)
                      ngx_http_get_module_loc_conf(r, ngx_http_core_module_ctx);
 
-    r->lingering_time = ngx_time() + lcf->lingering_time;
+    r->lingering_time = ngx_time() + lcf->lingering_time / 1000;
     r->connection->read->event_handler = ngx_http_lingering_close_handler;
 
-    if (ev->timer_set) {
-        ngx_del_timer(ev);
+    if (rev->timer_set) {
+        ngx_del_timer(rev);
     } else {
-        ev->timer_set = 1;
+        rev->timer_set = 1;
     }
 
-    ngx_add_timer(ev, lcf->lingering_timeout);
+    ngx_add_timer(rev, lcf->lingering_timeout);
 
-    if (ev->blocked) {
-        if (ngx_event_flags & NGX_HAVE_LEVEL_EVENT) {
-            if (ngx_add_event(ev, NGX_READ_EVENT, NGX_LEVEL_EVENT)
-                                                                == NGX_ERROR) {
-                return ngx_http_close_request(r);
-            }
+    if (rev->blocked && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
+        if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT) == NGX_ERROR) {
+            return ngx_http_close_request(r);
+        }
+        blocked = 1;
+        rev->blocked = 0;
+
+    } else {
+        blocked = 0;
+    }
+
+    if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
+        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
+            return ngx_http_close_request(r);
         }
     }
 
-    if (ngx_shutdown_socket(r->connection->fd, NGX_WRITE_SHUTDOWN) == -1) {
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_socket_errno,
+    if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) {
+        ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,
                       ngx_shutdown_socket_n " failed");
         return ngx_http_close_request(r);
     }
 
 #if (HAVE_AIO_EVENT) /* aio, iocp */
-    if (ngx_event_flags & NGX_HAVE_AIO_EVENT) {
-        return ngx_http_lingering_close_handler(ev);
+
+    if ((ngx_event_flags & NGX_HAVE_AIO_EVENT) || blocked) {
+        return ngx_http_lingering_close_handler(rev);
     }
+
+#else
+
+    if (blocked) {
+        return ngx_http_lingering_close_handler(rev);
+    }
+
 #endif
 
-#if (HAVE_CLEAR_EVENT) /* kqueue */ || (HAVE_EDGE_EVENT) /* epoll */
-    if (ngx_event_flags & (NGX_HAVE_CLEAR_EVENT|NGX_HAVE_EDGE_EVENT)) {
-        return NGX_OK;
-    }
-#endif
-
-    /* select, poll, /dev/poll */
-
-    return ngx_del_event(c->write, NGX_WRITE_EVENT, 0);
+    return NGX_OK;
 }
 
 
@@ -1021,9 +1058,14 @@ static int ngx_http_lingering_close_hand
                      ngx_http_get_module_loc_conf(r, ngx_http_core_module_ctx);
 
     if (r->discarded_buffer == NULL) {
-        if ((size_t)(r->header_in->end - r->header_in->last.mem)
+
+        /* TODO: r->header_in->start (if large headers are enabled)
+                 or the end of parsed header (otherwise)
+                 instead of r->header_in->last */
+
+        if ((size_t)(r->header_in->end - r->header_in->last)
                                                >= lcf->discarded_buffer_size) {
-            r->discarded_buffer = r->header_in->last.mem;
+            r->discarded_buffer = r->header_in->last;
 
         } else {
             ngx_test_null(r->discarded_buffer,
--- a/src/http/ngx_http_header_filter.c
+++ b/src/http/ngx_http_header_filter.c
@@ -48,22 +48,37 @@ static char server_string[] = "Server: "
 static ngx_str_t http_codes[] = {
 
     ngx_string("200 OK"),
-#if 0
-    { 6,  "200 OK" },
-#endif
+
+    ngx_string("301 Moved Permanently"),
+    ngx_string("302 Moved Temporarily"),
+    ngx_null_string,  /* 303 */
+    ngx_string("304 Not Modified"),
 
-    { 21, "301 Moved Permanently" },
-    { 21, "302 Moved Temporarily" },
-    { 0,  NULL },
-    { 16, "304 Not Modified" },
+    ngx_string("400 Bad Request"),
+    ngx_null_string,  /* 401 */
+    ngx_null_string,  /* 402 */
+    ngx_string("403 Forbidden"),
+    ngx_string("404 Not Found"),
+    ngx_null_string,  /* 405 */
+    ngx_null_string,  /* 406 */
+    ngx_null_string,  /* 407 */
+    ngx_string("408 Request Time-out"),
+    ngx_null_string,  /* 409 */
+    ngx_null_string,  /* 410 */
+    ngx_string("411 Length Required"),
+    ngx_null_string,  /* 412 */
+    ngx_string("413 Request Entity Too Large"),
+    ngx_null_string,  /* "414 Request-URI Too Large" but we never send it
+                         because we treat such requests as HTTP/0.9 requests
+                         and send only the body without the header */
+    ngx_null_string,  /* 415 */
+    ngx_string("416 Requested Range Not Satisfiable"),
 
-    { 15, "400 Bad Request" },
-    { 0,  NULL },
-    { 0,  NULL },
-    { 13, "403 Forbidden" },
-    { 13, "404 Not Found" },
-
-    { 25, "500 Internal Server Error" }
+    ngx_string("500 Internal Server Error"),
+    ngx_string("501 Method Not Implemented"),
+    ngx_string("502 Bad Gateway"),
+    ngx_string("503 Service Temporarily Unavailable"),
+    ngx_string("504 Gateway Time-out")
 };
 
 
@@ -124,7 +139,7 @@ static int ngx_http_header_filter(ngx_ht
 
         } else {
             status = r->headers_out.status
-                                 - NGX_HTTP_INTERNAL_SERVER_ERROR + 1 + 4 + 5;
+                                 - NGX_HTTP_INTERNAL_SERVER_ERROR + 1 + 4 + 17;
         }
 
         len += http_codes[status].len;
@@ -181,49 +196,49 @@ static int ngx_http_header_filter(ngx_ht
     ngx_test_null(h, ngx_create_temp_hunk(r->pool, len, 0, 64), NGX_ERROR);
 
     /* "HTTP/1.x " */
-    ngx_memcpy(h->last.mem, "HTTP/1.1 ", 9);
-    h->last.mem += 9;
+    ngx_memcpy(h->last, "HTTP/1.1 ", 9);
+    h->last += 9;
 
     /* status line */
     if (r->headers_out.status_line.len) {
-        ngx_memcpy(h->last.mem, r->headers_out.status_line.data,
+        ngx_memcpy(h->last, r->headers_out.status_line.data,
                    r->headers_out.status_line.len);
-        h->last.mem += r->headers_out.status_line.len;
+        h->last += r->headers_out.status_line.len;
 
     } else {
-        ngx_memcpy(h->last.mem, http_codes[status].data,
+        ngx_memcpy(h->last, http_codes[status].data,
                    http_codes[status].len);
-        h->last.mem += http_codes[status].len;
+        h->last += http_codes[status].len;
     }
-    *(h->last.mem++) = CR; *(h->last.mem++) = LF;
+    *(h->last++) = CR; *(h->last++) = LF;
 
     if (!(r->headers_out.server && r->headers_out.server->key.len)) {
-        ngx_memcpy(h->last.mem, server_string, sizeof(server_string) - 1);
-        h->last.mem += sizeof(server_string) - 1;
+        ngx_memcpy(h->last, server_string, sizeof(server_string) - 1);
+        h->last += sizeof(server_string) - 1;
     }
 
     if (!(r->headers_out.date && r->headers_out.date->key.len)) {
-        ngx_memcpy(h->last.mem, "Date: ", 6);
-        h->last.mem += 6;
-        h->last.mem += ngx_http_get_time(h->last.mem, time(NULL));
-        *(h->last.mem++) = CR; *(h->last.mem++) = LF;
+        ngx_memcpy(h->last, "Date: ", 6);
+        h->last += 6;
+        h->last += ngx_http_get_time(h->last, time(NULL));
+        *(h->last++) = CR; *(h->last++) = LF;
     }
 
     /* 2^64 is 20 characters  */
     if (r->headers_out.content_length >= 0) {
-        h->last.mem += ngx_snprintf(h->last.mem, 49,
+        h->last += ngx_snprintf(h->last, 49,
                                     "Content-Length: " OFF_FMT CRLF,
                                     r->headers_out.content_length);
     }
 
 #if 0
     if (r->headers_out.content_type.len) {
-        ngx_memcpy(h->last.mem, "Content-Type: ", 14);
-        h->last.mem += 14;
-        ngx_memcpy(h->last.mem, r->headers_out.content_type.data,
+        ngx_memcpy(h->last, "Content-Type: ", 14);
+        h->last += 14;
+        ngx_memcpy(h->last, r->headers_out.content_type.data,
                    r->headers_out.content_type.len);
-        h->last.mem += r->headers_out.content_type.len;
-        *(h->last.mem++) = CR; *(h->last.mem++) = LF;
+        h->last += r->headers_out.content_type.len;
+        *(h->last++) = CR; *(h->last++) = LF;
     }
 #endif
 
@@ -231,20 +246,20 @@ static int ngx_http_header_filter(ngx_ht
           && r->headers_out.last_modified->key.len)
         && r->headers_out.last_modified_time != -1)
     {
-        ngx_memcpy(h->last.mem, "Last-Modified: ", 15);
-        h->last.mem += 15;
-        h->last.mem += ngx_http_get_time(h->last.mem,
+        ngx_memcpy(h->last, "Last-Modified: ", 15);
+        h->last += 15;
+        h->last += ngx_http_get_time(h->last,
                                          r->headers_out.last_modified_time);
-        *(h->last.mem++) = CR; *(h->last.mem++) = LF;
+        *(h->last++) = CR; *(h->last++) = LF;
     }
 
     if (r->keepalive == 0) {
-        ngx_memcpy(h->last.mem, "Connection: close" CRLF, 19);
-        h->last.mem += 19;
+        ngx_memcpy(h->last, "Connection: close" CRLF, 19);
+        h->last += 19;
 
     } else {
-        ngx_memcpy(h->last.mem, "Connection: keep-alive" CRLF, 24);
-        h->last.mem += 24;
+        ngx_memcpy(h->last, "Connection: keep-alive" CRLF, 24);
+        h->last += 24;
     }
 
     for (i = 0; i < r->headers_out.headers->nelts; i++) {
@@ -252,22 +267,22 @@ static int ngx_http_header_filter(ngx_ht
             continue;
         }
 
-        ngx_memcpy(h->last.mem, header[i].key.data, header[i].key.len);
-        h->last.mem += header[i].key.len;
-        *(h->last.mem++) = ':' ; *(h->last.mem++) = ' ' ;
+        ngx_memcpy(h->last, header[i].key.data, header[i].key.len);
+        h->last += header[i].key.len;
+        *(h->last++) = ':' ; *(h->last++) = ' ' ;
 
-        ngx_memcpy(h->last.mem, header[i].value.data, header[i].value.len);
-        h->last.mem += header[i].value.len;
-        *(h->last.mem++) = CR; *(h->last.mem++) = LF;
+        ngx_memcpy(h->last, header[i].value.data, header[i].value.len);
+        h->last += header[i].value.len;
+        *(h->last++) = CR; *(h->last++) = LF;
     }
 
     /* STUB */
-    *(h->last.mem) = '\0';
-    ngx_log_debug(r->connection->log, "%s\n" _ h->pos.mem);
+    *(h->last) = '\0';
+    ngx_log_debug(r->connection->log, "%s\n" _ h->pos);
     /**/
 
     /* end of HTTP header */
-    *(h->last.mem++) = CR; *(h->last.mem++) = LF;
+    *(h->last++) = CR; *(h->last++) = LF;
 
     if (r->header_only) {
         h->type |= NGX_HUNK_LAST;
--- a/src/http/ngx_http_output_filter.c
+++ b/src/http/ngx_http_output_filter.c
@@ -26,7 +26,7 @@ static ngx_command_t  ngx_http_output_fi
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_output_filter_conf_t, hunk_size)},
 
-    {ngx_string(""), 0, NULL, 0, 0}
+    {ngx_null_string, 0, NULL, 0, 0}
 };
 
 
@@ -63,8 +63,8 @@ ngx_module_t  ngx_http_output_filter_mod
 #define need_to_copy(r, hunk)                                             \
             (((r->filter & NGX_HTTP_FILTER_NEED_IN_MEMORY)                \
                && (hunk->type & NGX_HUNK_FILE))                           \
-            || ((r->filter & NGX_HTTP_FILTER_NEED_TEMP)                   \
-               && (hunk->type & (NGX_HUNK_MEMORY|NGX_HUNK_MMAP))))
+             || ((r->filter & NGX_HTTP_FILTER_NEED_TEMP)                  \
+                  && (hunk->type & (NGX_HUNK_MEMORY|NGX_HUNK_MMAP))))
 
 
 int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk)
@@ -81,7 +81,7 @@ int ngx_http_output_filter(ngx_http_requ
 
     if (ctx == NULL) {
         ngx_http_create_ctx(r, ctx, ngx_http_output_filter_module,
-                            sizeof(ngx_http_output_filter_ctx_t));
+                            sizeof(ngx_http_output_filter_ctx_t), NGX_ERROR);
     }
 
     /* the short path for the case when the chain ctx->incoming is empty
@@ -124,7 +124,12 @@ int ngx_http_output_filter(ngx_http_requ
                                                 ngx_http_output_filter_module);
 
         if (hunk->type & NGX_HUNK_LAST) {
-            size = hunk->last.mem - hunk->pos.mem;
+            if (hunk->type & NGX_HUNK_IN_MEMORY) {
+                size = hunk->last - hunk->pos;
+            } else {
+                size = hunk->file_last - hunk->file_pos;
+            }
+
             if (size > conf->hunk_size) {
                 size = conf->hunk_size;
             }
@@ -140,7 +145,7 @@ int ngx_http_output_filter(ngx_http_requ
 
 
     /* our hunk is still busy */
-    } else if (ctx->hunk->pos.mem < ctx->hunk->last.mem) {
+    } else if (ctx->hunk->pos < ctx->hunk->last) {
         rc = next_filter(r, NULL);
 
         if (rc == NGX_ERROR || rc == NGX_AGAIN) {
@@ -149,7 +154,7 @@ int ngx_http_output_filter(ngx_http_requ
 
         /* NGX_OK */
         /* set our hunk free */
-        ctx->hunk->pos.mem = ctx->hunk->last.mem = ctx->hunk->start;
+        ctx->hunk->pos = ctx->hunk->last = ctx->hunk->start;
     }
 
 #if (NGX_SUPPRESS_WARN)
@@ -209,11 +214,20 @@ int ngx_http_output_filter(ngx_http_requ
 
             /* NGX_OK */
             /* set our hunk free */
-            ctx->hunk->pos.mem = ctx->hunk->last.mem = ctx->hunk->start;
+            ctx->hunk->pos = ctx->hunk->last = ctx->hunk->start;
+
+            /* repeat until we will have copied the whole first hunk from
+               the chain ctx->incoming */
 
-        /* repeat until we will have copied the whole first hunk from
-           the chain ctx->incoming */
-        } while (ctx->incoming->hunk->pos.mem < ctx->incoming->hunk->last.mem);
+            if (ctx->incoming->hunk->type & NGX_HUNK_IN_MEMORY) {
+                size = ctx->incoming->hunk->last - ctx->incoming->hunk->pos;
+
+            } else {
+                size = ctx->incoming->hunk->file_last
+                                               - ctx->incoming->hunk->file_pos;
+            }
+
+        } while (size);
 
     /* delete the completed hunk from the incoming chain */
     ctx->incoming = ctx->incoming->next;
@@ -229,13 +243,31 @@ static int ngx_http_output_filter_copy_h
 {
     ssize_t  n, size;
 
-    size = src->last.mem - src->pos.mem;
-    if (size > (dst->end - dst->pos.mem)) {
-        size = dst->end - dst->pos.mem;
+    if (src->type & NGX_HUNK_IN_MEMORY) {
+        size = src->last - src->pos;
+    } else {
+        size = src->file_last - src->file_pos;
+    }
+
+    if (size > (dst->end - dst->pos)) {
+        size = dst->end - dst->pos;
     }
 
-    if (src->type & NGX_HUNK_FILE) {
-        n = ngx_read_file(src->file, dst->pos.mem, size, src->pos.file);
+    if (src->type & NGX_HUNK_IN_MEMORY) {
+        ngx_memcpy(src->pos, dst->pos, size);
+        src->pos += size;
+        dst->last += size;
+
+        if (src->type & NGX_HUNK_FILE) {
+            src->file_pos += n;
+        }
+
+        if (src->type & NGX_HUNK_LAST && src->pos == src->last) {
+            dst->type |= NGX_HUNK_LAST;
+        }
+
+    } else {
+        n = ngx_read_file(src->file, dst->pos, size, src->file_pos);
 
         if (n == NGX_ERROR) {
             return n;
@@ -256,17 +288,12 @@ static int ngx_http_output_filter_copy_h
             }
         }
 
-        src->pos.mem += n;
-        dst->last.mem += n;
+        src->file_pos += n;
+        dst->last += n;
 
-    } else {
-        ngx_memcpy(src->pos.mem, dst->pos.mem, size);
-        src->pos.mem += size;
-        dst->last.mem += size;
-    }
-
-    if (src->type & NGX_HUNK_LAST && src->pos.mem == src->last.mem) {
-        dst->type |= NGX_HUNK_LAST;
+        if (src->type & NGX_HUNK_LAST && src->file_pos == src->file_last) {
+            dst->type |= NGX_HUNK_LAST;
+        }
     }
 
     return NGX_OK;
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -25,15 +25,10 @@ int ngx_read_http_request_line(ngx_http_
     } state;
 
     state = r->state;
-    p = r->header_in->pos.mem;
-
-    while (p < r->header_in->last.mem && state < sw_done) {
-        ch = *p++;
+    p = r->header_in->pos;
 
-/*
-printf("\nstate: %d, pos: %x, end: %x, char: '%c' buf: %s",
-       state, p, r->header_in->last, ch, p);
-*/
+    while (p < r->header_in->last && state < sw_done) {
+        ch = *p++;
 
         /* GCC 2.95.2 and VC 6.0 compile this switch as jump table */
 
@@ -45,7 +40,7 @@ printf("\nstate: %d, pos: %x, end: %x, c
 
             switch (ch) {
             case 'G':
-                if (p + 1 >= r->header_in->last.mem) {
+                if (p + 1 >= r->header_in->last) {
                     return NGX_AGAIN;
                 }
 
@@ -58,7 +53,7 @@ printf("\nstate: %d, pos: %x, end: %x, c
                 break;
 
             case 'H':
-                if (p + 2 >= r->header_in->last.mem) {
+                if (p + 2 >= r->header_in->last) {
                     return NGX_AGAIN;
                 }
 
@@ -71,7 +66,7 @@ printf("\nstate: %d, pos: %x, end: %x, c
                 break;
 
             case 'P':
-                if (p + 2 >= r->header_in->last.mem) {
+                if (p + 2 >= r->header_in->last) {
                     return NGX_AGAIN;
                 }
 
@@ -228,9 +223,9 @@ printf("\nstate: %d, pos: %x, end: %x, c
 
         /* "TTP/" */
         case sw_http_version:
-            if (p + 2 >= r->header_in->last.mem) {
+            if (p + 2 >= r->header_in->last) {
                 r->state = sw_http_version;
-                r->header_in->pos.mem = p - 1;
+                r->header_in->pos = p - 1;
                 return NGX_AGAIN;
             }
 
@@ -309,7 +304,7 @@ printf("\nstate: %d, pos: %x, end: %x, c
         }
     }
 
-    r->header_in->pos.mem = p;
+    r->header_in->pos = p;
 
     if (state == sw_done) {
         if (r->request_end == NULL) {
@@ -348,15 +343,10 @@ int ngx_read_http_header_line(ngx_http_r
     } state;
 
     state = r->state;
-    p = h->pos.mem;
-
-    while (p < h->last.mem && state < sw_done) {
-        ch = *p++;
+    p = h->pos;
 
-/*
-printf("\nstate: %d, pos: %x, end: %x, char: '%c' buf: %s",
-       state, p, h->last.mem, ch, p);
-*/
+    while (p < h->last && state < sw_done) {
+        ch = *p++;
 
         switch (state) {
 
@@ -495,7 +485,7 @@ printf("\nstate: %d, pos: %x, end: %x, c
         }
     }
 
-    h->pos.mem = p;
+    h->pos = p;
 
     if (state == sw_done) {
         r->state = sw_start;
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -39,6 +39,14 @@ static char error_404_page[] =
 ;
 
 
+static char error_414_page[] =
+"<html>" CRLF
+"<head><title>414 Request-URI Too Large</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>414 Request-URI Too Large</h1></center>" CRLF
+;
+
+
 static char error_500_page[] =
 "<html>" CRLF
 "<head><title>500 Internal Server Error</title></head>" CRLF
@@ -58,6 +66,16 @@ static ngx_str_t error_pages[] = {
     { 0, NULL},  /* 402 */
     { sizeof(error_403_page) - 1, error_403_page },
     { sizeof(error_404_page) - 1, error_404_page },
+    { 0, NULL},  /* 405 */
+    { 0, NULL},  /* 406 */
+    { 0, NULL},  /* 407 */
+    { 0, NULL},  /* 408 */
+    { 0, NULL},  /* 409 */
+    { 0, NULL},  /* 410 */
+    { 0, NULL},  /* 411 */
+    { 0, NULL},  /* 412 */
+    { 0, NULL},  /* 413 */
+    { sizeof(error_414_page) - 1, error_414_page },
 
     { sizeof(error_500_page) - 1, error_500_page }
 };
@@ -79,7 +97,7 @@ int ngx_http_special_response(ngx_http_r
         err = error - NGX_HTTP_BAD_REQUEST + 4;
 
     else
-        err = error - NGX_HTTP_INTERNAL_SERVER_ERROR + 4 + 5;
+        err = error - NGX_HTTP_INTERNAL_SERVER_ERROR + 4 + 15;
 
     if (error_pages[err].len == 0)
         r->headers_out.content_length = -1;
@@ -97,9 +115,9 @@ int ngx_http_special_response(ngx_http_r
 
     ngx_test_null(message, ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)), NGX_ERROR);
 
-    message->type = NGX_HUNK_MEMORY;
-    message->pos.mem = error_pages[err].data;
-    message->last.mem = error_pages[err].data + error_pages[err].len;
+    message->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
+    message->pos = error_pages[err].data;
+    message->last = error_pages[err].data + error_pages[err].len;
 
     if (ngx_http_output_filter(r, message) == NGX_ERROR) {
         return NGX_ERROR;
@@ -107,9 +125,9 @@ int ngx_http_special_response(ngx_http_r
 
     ngx_test_null(tail, ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)), NGX_ERROR);
 
-    tail->type = NGX_HUNK_MEMORY|NGX_HUNK_LAST;
-    tail->pos.mem = error_tail;
-    tail->last.mem = error_tail + sizeof(error_tail);
+    tail->type = NGX_HUNK_MEMORY|NGX_HUNK_LAST|NGX_HUNK_IN_MEMORY;
+    tail->pos = error_tail;
+    tail->last = error_tail + sizeof(error_tail);
 
     return ngx_http_output_filter(r, tail);
 }
--- a/src/http/ngx_http_write_filter.c
+++ b/src/http/ngx_http_write_filter.c
@@ -70,7 +70,7 @@ int ngx_http_write_filter(ngx_http_reque
                                              ngx_http_write_filter_module);
     if (ctx == NULL) {
         ngx_http_create_ctx(r, ctx, ngx_http_write_filter_module,
-                            sizeof(ngx_http_write_filter_ctx_t));
+                            sizeof(ngx_http_write_filter_ctx_t), NGX_ERROR);
     }
 
     size = flush = 0;
@@ -80,14 +80,12 @@ int ngx_http_write_filter(ngx_http_reque
     /* find the size, the flush point and the last entry of saved chain */
     for (ce = ctx->out; ce; ce = ce->next) {
         le = &ce->next;
-        size += ce->hunk->last.file - ce->hunk->pos.file;
 
-#if (NGX_DEBUG_WRITE_FILTER)
-        ngx_log_debug(r->connection->log, "write filter: old chunk: %x "
-                      QX_FMT " " QD_FMT _
-                      ce->hunk->type _ ce->hunk->pos.file _
-                      ce->hunk->last.file - ce->hunk->pos.file);
-#endif
+        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
+            size += ce->hunk->last - ce->hunk->pos;
+        } else {
+            size += ce->hunk->file_last - ce->hunk->file_pos;
+        }
 
         if (ce->hunk->type & (NGX_HUNK_FLUSH|NGX_HUNK_RECYCLED)) {
             flush = size;
@@ -106,14 +104,12 @@ int ngx_http_write_filter(ngx_http_reque
         ce->next = NULL;
         *le = ce;
         le = &ce->next;
-        size += ce->hunk->last.file - ce->hunk->pos.file;
 
-#if (NGX_DEBUG_WRITE_FILTER)
-        ngx_log_debug(r->connection->log, "write filter: new hunk: %x "
-                      QX_FMT " " QD_FMT _
-                      ce->hunk->type _ ce->hunk->pos.file _
-                      ce->hunk->last.file - ce->hunk->pos.file);
-#endif
+        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
+            size += ce->hunk->last - ce->hunk->pos;
+        } else {
+            size += ce->hunk->file_last - ce->hunk->file_pos;
+        }
 
         if (ce->hunk->type & (NGX_HUNK_FLUSH|NGX_HUNK_RECYCLED)) {
             flush = size;
--- a/src/os/unix/ngx_errno.h
+++ b/src/os/unix/ngx_errno.h
@@ -14,9 +14,11 @@ typedef int               ngx_err_t;
 #define NGX_EAGAIN        EWOULDBLOCK
 #define NGX_EINPROGRESS   EINPROGRESS
 #define NGX_EADDRINUSE    EADDRINUSE
+#define NGX_ECONNRESET    ECONNRESET
 #define NGX_ETIMEDOUT     ETIMEDOUT
 #define NGX_ECANCELED     ECANCELED
 
+
 #define ngx_errno                  errno
 #define ngx_socket_errno           errno
 #define ngx_set_socket_errno(err)  errno = err
--- a/src/os/unix/ngx_freebsd_write_chain.c
+++ b/src/os/unix/ngx_freebsd_write_chain.c
@@ -15,7 +15,7 @@ ngx_chain_t *ngx_freebsd_write_chain(ngx
 {
     int              rc;
     char            *prev;
-    size_t           hsize;
+    size_t           hsize, size;
     off_t            sent;
     struct iovec    *iov;
     struct sf_hdtr   hdtr;
@@ -32,54 +32,54 @@ ngx_chain_t *ngx_freebsd_write_chain(ngx
     ngx_init_array(trailer, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR);
 
     /* create the header iovec */
-    if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
+    if (ngx_hunk_in_memory_only(ce->hunk)) {
         prev = NULL;
         iov = NULL;
 
         /* create the iovec and coalesce the neighbouring chain entries */
-        while (ce && (ce->hunk->type & NGX_HUNK_IN_MEMORY))
-        {
-            if (prev == ce->hunk->pos.mem) {
-                iov->iov_len += ce->hunk->last.mem - ce->hunk->pos.mem;
-                prev = ce->hunk->last.mem;
+        while (ce && ngx_hunk_in_memory_only(ce->hunk)) {
+
+            if (prev == ce->hunk->pos) {
+                iov->iov_len += ce->hunk->last - ce->hunk->pos;
+                prev = ce->hunk->last;
 
             } else {
                 ngx_test_null(iov, ngx_push_array(&header), NGX_CHAIN_ERROR);
-                iov->iov_base = ce->hunk->pos.mem;
-                iov->iov_len = ce->hunk->last.mem - ce->hunk->pos.mem;
-                prev = ce->hunk->last.mem;
+                iov->iov_base = ce->hunk->pos;
+                iov->iov_len = ce->hunk->last - ce->hunk->pos;
+                prev = ce->hunk->last;
             }
 
 #if (HAVE_FREEBSD_SENDFILE_NBYTES_BUG)
-            hsize += ce->hunk->last.mem - ce->hunk->pos.mem;
+            hsize += ce->hunk->last - ce->hunk->pos;
 #endif
             ce = ce->next;
         }
     }
 
-    /* TODO: coalesce the neighbouring shadow file hunks */
+    /* TODO: coalesce the neighbouring file hunks */
     if (ce && (ce->hunk->type & NGX_HUNK_FILE)) {
         file = ce->hunk;
         ce = ce->next;
     }
 
     /* create the trailer iovec */
-    if (ce && ce->hunk->type & NGX_HUNK_IN_MEMORY) {
+    if (ce && ngx_hunk_in_memory_only(ce->hunk)) {
         prev = NULL;
         iov = NULL;
 
         /* create the iovec and coalesce the neighbouring chain entries */
-        while (ce && (ce->hunk->type & NGX_HUNK_IN_MEMORY)) {
+        while (ce && ngx_hunk_in_memory_only(ce->hunk)) {
 
-            if (prev == ce->hunk->pos.mem) {
-                iov->iov_len += ce->hunk->last.mem - ce->hunk->pos.mem;
-                prev = ce->hunk->last.mem;
+            if (prev == ce->hunk->pos) {
+                iov->iov_len += ce->hunk->last - ce->hunk->pos;
+                prev = ce->hunk->last;
 
             } else {
                 ngx_test_null(iov, ngx_push_array(&trailer), NGX_CHAIN_ERROR);
-                iov->iov_base = ce->hunk->pos.mem;
-                iov->iov_len = ce->hunk->last.mem - ce->hunk->pos.mem;
-                prev = ce->hunk->last.mem;
+                iov->iov_base = ce->hunk->pos;
+                iov->iov_len = ce->hunk->last - ce->hunk->pos;
+                prev = ce->hunk->last;
             }
 
             ce = ce->next;
@@ -92,8 +92,8 @@ ngx_chain_t *ngx_freebsd_write_chain(ngx
         hdtr.trailers = (struct iovec *) trailer.elts;
         hdtr.trl_cnt = trailer.nelts;
 
-        rc = sendfile(file->file->fd, c->fd, file->pos.file,
-                      (size_t) (file->last.file - file->pos.file) + hsize,
+        rc = sendfile(file->file->fd, c->fd, file->file_pos,
+                      (size_t) (file->file_last - file->file_pos) + hsize,
                       &hdtr, &sent, 0);
 
         if (rc == -1) {
@@ -110,8 +110,8 @@ ngx_chain_t *ngx_freebsd_write_chain(ngx
 
 #if (NGX_DEBUG_WRITE_CHAIN)
         ngx_log_debug(c->log, "sendfile: %d, @%qd %qd:%d" _
-                      rc _ file->pos.file _ *sent _
-                      (size_t) (file->last.file - file->pos.file) + hsize);
+                      rc _ file->file_pos _ sent _
+                      (size_t) (file->file_last - file->file_pos) + hsize);
 #endif
 
     } else {
@@ -142,31 +142,33 @@ ngx_chain_t *ngx_freebsd_write_chain(ngx
 
     for (ce = in; ce; ce = ce->next) {
 
-#if (NGX_DEBUG_WRITE_CHAIN)
-        ngx_log_debug(c->log, "write chain: %x %qx %qd" _
-                      ce->hunk->type _
-                      ce->hunk->pos.file _
-                      ce->hunk->last.file - ce->hunk->pos.file);
-#endif
+        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
+            size = ce->hunk->last - ce->hunk->pos;
+        } else {
+            size = ce->hunk->file_last - ce->hunk->file_pos;
+        }
+
+        if (sent >= size) {
+            sent -= size;
 
-        if (sent >= ce->hunk->last.file - ce->hunk->pos.file) {
-            sent -= ce->hunk->last.file - ce->hunk->pos.file;
-            ce->hunk->pos.file = ce->hunk->last.file;
+            if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
+                ce->hunk->pos = ce->hunk->last;
+            }
 
-#if (NGX_DEBUG_WRITE_CHAIN)
-            ngx_log_debug(c->log, "write chain done: %qx %qd" _
-                          ce->hunk->pos.file _ sent);
-#endif
+            if (ce->hunk->type & NGX_HUNK_FILE) {
+                ce->hunk->file_pos = ce->hunk->file_last;
+            }
+
             continue;
         }
 
-        ce->hunk->pos.file += sent;
+        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
+            ce->hunk->pos += sent;
+        }
 
-#if (NGX_DEBUG_WRITE_CHAIN)
-        ngx_log_debug(c->log, "write chain rest: %qx %qd" _
-                      ce->hunk->pos.file _
-                      ce->hunk->last.file - ce->hunk->pos.file);
-#endif
+        if (ce->hunk->type & NGX_HUNK_FILE) {
+            ce->hunk->file_pos += sent;
+        }
 
         break;
     }
--- a/src/os/win32/ngx_errno.h
+++ b/src/os/win32/ngx_errno.h
@@ -15,6 +15,7 @@ typedef DWORD             ngx_err_t;
 #define NGX_EAGAIN        WSAEWOULDBLOCK
 #define NGX_EINPROGRESS   WSAEINPROGRESS
 #define NGX_EADDRINUSE    WSAEADDRINUSE
+#define NGX_ECONNRESET    ECONNRESET
 #define NGX_ETIMEDOUT     WSAETIMEDOUT
 
 int ngx_strerror_r(ngx_err_t err, char *errstr, size_t size);