changeset 164:84036764e215

nginx-0.0.1-2003-10-29-11:30:44 import
author Igor Sysoev <igor@sysoev.ru>
date Wed, 29 Oct 2003 08:30:44 +0000
parents fb61ba77beba
children 894a01c6aea3
files src/core/ngx_output_chain.c src/event/modules/ngx_aio_module.c src/event/modules/ngx_kqueue_module.c src/event/ngx_event.h src/event/ngx_event_accept.c src/event/ngx_event_connect.c src/event/ngx_event_pipe.c src/event/ngx_event_pipe.h src/http/modules/ngx_http_gzip_filter.c src/http/modules/ngx_http_range_filter.c src/http/modules/proxy/ngx_http_proxy_handler.c src/http/modules/proxy/ngx_http_proxy_handler.h src/http/ngx_http.h src/http/ngx_http_core_module.c src/http/ngx_http_header_filter.c src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_request_body.c src/http/ngx_http_special_response.c src/os/unix/ngx_aio.h src/os/unix/ngx_aio_read.c src/os/unix/ngx_aio_read_chain.c src/os/unix/ngx_aio_write.c src/os/unix/ngx_aio_write_chain.c src/os/unix/ngx_readv_chain.c
diffstat 25 files changed, 531 insertions(+), 333 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -47,6 +47,7 @@ int ngx_output_chain(ngx_output_chain_ct
     }
 
     last = NGX_NONE;
+    out = NULL;
     last_out = &out;
 
     for ( ;; ) {
@@ -167,7 +168,7 @@ ngx_inline static int ngx_output_chain_n
         return 0;
     }
 
-    if (!ctx->sendfile) {
+    if (!ctx->sendfile && (!(hunk->type & NGX_HUNK_IN_MEMORY))) {
         return 1;
     }
 
--- a/src/event/modules/ngx_aio_module.c
+++ b/src/event/modules/ngx_aio_module.c
@@ -19,7 +19,7 @@ static int ngx_aio_process_events(ngx_lo
 
 ngx_os_io_t ngx_os_aio = {
     ngx_aio_read,
-    NULL,
+    ngx_aio_read_chain,
     ngx_aio_write,
     ngx_aio_write_chain,
     NGX_HAVE_ZEROCOPY
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -437,7 +437,7 @@ static int ngx_kqueue_process_events(ngx
             break;
 
         case EVFILT_AIO:
-            ev->ready = 1;
+            ev->aio_complete = 1;
             ev->active = 0;
 
             ev->event_handler(ev);
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -60,13 +60,19 @@ struct ngx_event_s {
     unsigned         instance:1;
 
     /*
-     * event was passed or would be passed to a kernel;
-     * the posted aio operation.
+     * the event was passed or would be passed to a kernel;
+     * aio mode: 1 - the posted aio operation,
+     *           0 - the complete aio operation or no aio operation.
      */
     unsigned         active:1;
 
-    /* ready event; the complete aio operation */
+    /*
+     * the ready event;
+     * in aio mode "ready" is always set - it makes things simple
+     * to learn whether the aio operation complete use aio_complete flag
+     */
     unsigned         ready:1;
+    unsigned         aio_complete:1;
 
     unsigned         eof:1;
     unsigned         error:1;
@@ -338,15 +344,8 @@ int ngx_event_post_acceptex(ngx_listenin
 
 
 
-ngx_inline static int ngx_handle_read_event(ngx_event_t *rev)
+ngx_inline static int ngx_handle_read_event(ngx_event_t *rev, int close)
 {
-    if (ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_EDGE_EVENT)) {
-
-        /* aio, iocp, epoll */
-
-        return NGX_OK;
-    }
-
     if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
 
         /* kqueue */
@@ -359,26 +358,31 @@ ngx_inline static int ngx_handle_read_ev
         }
 
         return NGX_OK;
-    }
 
-    /* select, poll, /dev/poll */
+    } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {
+
+        /* select, poll, /dev/poll */
 
-    if (!rev->active && !rev->ready) {
-        if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT) == NGX_ERROR) {
-            return NGX_ERROR;
+        if (!rev->active && !rev->ready) {
+            if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT)
+                                                                == NGX_ERROR) {
+                return NGX_ERROR;
+            }
+
+            return NGX_OK;
         }
 
-        return NGX_OK;
+        if (rev->active && (rev->ready || close)) {
+            if (ngx_del_event(rev, NGX_READ_EVENT, close ? NGX_CLOSE_EVENT : 0)
+                                                                == NGX_ERROR) {
+                return NGX_ERROR;
+            }
+
+            return NGX_OK;
+        }
     }
 
-    if (rev->active && (rev->ready || rev->eof)) {
-        if (ngx_del_event(rev, NGX_READ_EVENT, rev->eof ? NGX_CLOSE_EVENT : 0)
-                                                                == NGX_ERROR) {
-            return NGX_ERROR;
-        }
-
-        return NGX_OK;
-    }
+    /* aio, iocp, epoll, rt signals */
 
     return NGX_OK;
 }
@@ -411,13 +415,6 @@ ngx_inline static int ngx_handle_level_r
 
 ngx_inline static int ngx_handle_write_event(ngx_event_t *wev, int lowat)
 {
-    if (ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_EDGE_EVENT)) {
-
-        /* aio, iocp, epoll */
-
-        return NGX_OK;
-    }
-
     if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
 
         /* kqueue */
@@ -437,25 +434,30 @@ ngx_inline static int ngx_handle_write_e
         }
 
         return NGX_OK;
-    }
 
-    /* select, poll, /dev/poll */
+    } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {
+
+        /* select, poll, /dev/poll */
 
-    if (!wev->active && !wev->ready) {
-        if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT) == NGX_ERROR) {
-            return NGX_ERROR;
+        if (!wev->active && !wev->ready) {
+            if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT)
+                                                                == NGX_ERROR) {
+                return NGX_ERROR;
+            }
+
+            return NGX_OK;
         }
 
-        return NGX_OK;
+        if (wev->active && wev->ready) {
+            if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
+                return NGX_ERROR;
+            }
+
+            return NGX_OK;
+        }
     }
 
-    if (wev->active && wev->ready) {
-        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
-            return NGX_ERROR;
-        }
-
-        return NGX_OK;
-    }
+    /* aio, iocp, epoll, rt signals */
 
     return NGX_OK;
 }
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -172,9 +172,11 @@ ngx_log_debug(ev->log, "ADDR %s" _ ls->l
         c->fd = s;
         c->unexpected_eof = 1;
         wev->write = 1;
+        wev->ready = 1;
 
-        if ((ngx_event_flags & NGX_USE_AIO_EVENT) == 0) {
-            wev->ready = 1;
+        if (ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_EDGE_EVENT)) {
+            /* aio, iocp, epoll */
+            rev->ready = 1;
         }
 
         c->ctx = ls->ctx;
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -202,7 +202,19 @@ ngx_log_debug(pc->log, "CONNECT: %s" _ p
         }
     }
 
-    /* TODO: epoll, aio, iocp */
+    if (ngx_event_flags & NGX_USE_AIO_EVENT) {
+        /* aio, iocp */
+        rev->ready = 1;
+ 
+#if 1
+        /* TODO: NGX_EINPROGRESS */
+
+        wev->ready = 1;
+        return NGX_OK;
+#endif
+    }
+
+    /* TODO: epoll */
 
     if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {     /* kqueue */
         event = NGX_CLEAR_EVENT;
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -19,6 +19,8 @@ static int ngx_event_pipe_drain_chains(n
 
 int ngx_event_pipe(ngx_event_pipe_t *p, int do_write)
 {
+    ngx_event_t  *rev, *wev;
+
     for ( ;; ) {
         if (do_write) {
             if (ngx_event_pipe_write_to_downstream(p) == NGX_ABORT) {
@@ -40,15 +42,26 @@ int ngx_event_pipe(ngx_event_pipe_t *p, 
         do_write = 1;
     }
 
-    if (ngx_handle_read_event(p->upstream->read) == NGX_ERROR) {
+    rev = p->upstream->read;
+
+    if (ngx_handle_read_event(rev, (rev->eof || rev->error)) == NGX_ERROR) {
         return NGX_ABORT;
     }
 
-    if (ngx_handle_write_event(p->downstream->write,
-                                           /* TODO: lowat */ 0) == NGX_ERROR) {
+    if (rev->active) {
+        ngx_add_timer(rev, p->read_timeout);
+    }
+
+    wev = p->downstream->write;
+
+    if (ngx_handle_write_event(wev, p->send_lowat) == NGX_ERROR) {
         return NGX_ABORT;
     }
 
+    if (wev->active) {
+        ngx_add_timer(wev, p->send_timeout);
+    }
+
     return NGX_OK;
 }
 
@@ -112,7 +125,6 @@ int ngx_event_pipe_read_upstream(ngx_eve
 
                             ngx_log_error(NGX_LOG_ERR, p->log,
                                           p->upstream->read->kq_errno,
-                                          /* TODO: ngx_readv_chain_n */
                                           "readv() failed");
                         }
 
@@ -213,8 +225,6 @@ int ngx_event_pipe_read_upstream(ngx_eve
                 break;
             }
 
-            ngx_log_debug(p->log, "HUNK_FREE: %d" _ chain->hunk->num);
-
             n = ngx_recv_chain(p->upstream, chain);
 
             ngx_log_debug(p->log, "recv_chain: %d" _ n);
@@ -343,8 +353,6 @@ int ngx_event_pipe_write_to_downstream(n
                 ngx_event_pipe_free_shadow_raw_hunk(&p->free_raw_hunks,
                                                     cl->hunk);
 
-ngx_log_debug(p->log, "HUNK OUT: %d %x" _ cl->hunk->num _ cl->hunk->type);
-
             } else if (!p->cachable && p->in) {
                 cl = p->in;
 
@@ -356,8 +364,6 @@ ngx_log_debug(p->log, "HUNK OUT: %d %x" 
 
                 p->in = p->in->next;
 
-ngx_log_debug(p->log, "HUNK IN: %d" _ cl->hunk->num);
-
             } else {
                 break;
             }
--- a/src/event/ngx_event_pipe.h
+++ b/src/event/ngx_event_pipe.h
@@ -59,6 +59,10 @@ struct ngx_event_pipe_s {
     ngx_connection_t  *upstream;
     ngx_connection_t  *downstream;
 
+    ngx_msec_t         read_timeout;
+    ngx_msec_t         send_timeout;
+    ssize_t            send_lowat;
+
     ngx_pool_t        *pool;
     ngx_log_t         *log;
 
--- a/src/http/modules/ngx_http_gzip_filter.c
+++ b/src/http/modules/ngx_http_gzip_filter.c
@@ -190,18 +190,23 @@ static int ngx_http_gzip_header_filter(n
                         sizeof(ngx_http_gzip_ctx_t), NGX_ERROR);
     ctx->request = r;
 
-    ngx_test_null(r->headers_out.content_encoding,
-                  ngx_push_table(r->headers_out.headers),
-                  NGX_ERROR);
+    if (!(r->headers_out.content_encoding =
+                   ngx_http_add_header(&r->headers_out, ngx_http_headers_out)))
+    {
+        return NGX_ERROR;
+    }
 
     r->headers_out.content_encoding->key.len = 0;
     r->headers_out.content_encoding->key.data = NULL;
-    r->headers_out.content_encoding->value.len = 4;
+    r->headers_out.content_encoding->value.len = sizeof("gzip") - 1;
     r->headers_out.content_encoding->value.data = "gzip";
 
     ctx->length = r->headers_out.content_length_n;
     r->headers_out.content_length_n = -1;
-    r->headers_out.content_length = NULL;
+    if (r->headers_out.content_length) {
+        r->headers_out.content_length->key.len = 0;
+        r->headers_out.content_length = NULL;
+    }
     r->filter |= NGX_HTTP_FILTER_NEED_IN_MEMORY;
 
     return ngx_http_next_header_filter(r);
--- a/src/http/modules/ngx_http_range_filter.c
+++ b/src/http/modules/ngx_http_range_filter.c
@@ -96,9 +96,12 @@ static int ngx_http_range_header_filter(
         || r->headers_in.range->value.len < 7
         || ngx_strncasecmp(r->headers_in.range->value.data, "bytes=", 6) != 0)
     {
-        ngx_test_null(r->headers_out.accept_ranges,
-                      ngx_push_table(r->headers_out.headers),
-                      NGX_ERROR);
+
+        if (!(r->headers_out.accept_ranges =
+                   ngx_http_add_header(&r->headers_out, ngx_http_headers_out)))
+        {
+            return NGX_ERROR;
+        }
 
         r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1;
         r->headers_out.accept_ranges->key.data = "Accept-Ranges";
--- a/src/http/modules/proxy/ngx_http_proxy_handler.c
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.c
@@ -475,6 +475,11 @@ static void ngx_http_proxy_send_request(
 
                     /* rc == NGX_OK */
 
+                    if (c->read->ready) {
+                        /* post aio operation */
+                        ngx_http_proxy_process_upstream_status_line(c->read);
+                    }
+
                     if (ngx_handle_level_write_event(c->write) == NGX_ERROR) {
                         ngx_http_proxy_finalize_request(p,
                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
@@ -742,8 +747,8 @@ static void ngx_http_proxy_process_upstr
                 }
             }
 
-            ngx_log_debug(c->log, "HTTP proxy header: '%s: %s'" _
-                          h->key.data _ h->value.data);
+            ngx_log_debug(c->log, "HTTP proxy header: %08X '%s: %s'" _
+                          h _ h->key.data _ h->value.data);
 
             continue;
 
@@ -791,10 +796,6 @@ static ssize_t ngx_http_proxy_read_upstr
     n = p->header_in->last - p->header_in->pos;
 
     if (n > 0) {
-#if 0
-        /* TODO THINK */
-        rev->ready = 0;
-#endif
         return n;
     }
 
@@ -804,7 +805,7 @@ static ssize_t ngx_http_proxy_read_upstr
     if (n == NGX_AGAIN) {
         ngx_add_timer(rev, p->lcf->read_timeout);
 
-        if (ngx_handle_read_event(rev) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
             ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
             return NGX_ERROR;
         }
@@ -829,10 +830,11 @@ static ssize_t ngx_http_proxy_read_upstr
 
 static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p)
 {
-    int                  rc, i;
-    ngx_table_elt_t     *ch, *ph;
-    ngx_event_pipe_t    *ep;
-    ngx_http_request_t  *r;
+    int                        rc, i;
+    ngx_table_elt_t           *ch, *h;
+    ngx_event_pipe_t          *ep;
+    ngx_http_request_t        *r;
+    ngx_http_core_loc_conf_t  *clcf;
 
     r = p->request;
 
@@ -843,26 +845,26 @@ static void ngx_http_proxy_send_response
 
     /* copy an upstream header to r->headers_out */
 
-    ph = (ngx_table_elt_t *) p->headers_in.headers->elts;
+    h = p->headers_in.headers->elts;
     for (i = 0; i < p->headers_in.headers->nelts; i++) {
 
-        if (&ph[i] == p->headers_in.connection) {
+        if (&h[i] == p->headers_in.connection) {
             continue;
         }
 
         if (p->accel) {
-            if (&ph[i] == p->headers_in.date
-                || &ph[i] == p->headers_in.accept_ranges) {
+            if (&h[i] == p->headers_in.date
+                || &h[i] == p->headers_in.accept_ranges) {
                 continue;
             }
 
-            if (&ph[i] == p->headers_in.server && !p->lcf->pass_server) {
+            if (&h[i] == p->headers_in.server && !p->lcf->pass_server) {
                 continue;
             }
         }
 
-        if (&ph[i] == p->headers_in.content_type) {
-            r->headers_out.content_type = &ph[i];
+        if (&h[i] == p->headers_in.content_type) {
+            r->headers_out.content_type = &h[i];
             r->headers_out.content_type->key.len = 0;
             continue;
         }
@@ -873,7 +875,7 @@ static void ngx_http_proxy_send_response
             return;
         }
 
-        *ch = ph[i];
+        *ch = h[i];
 
         /*
          * ngx_http_header_filter() output the following headers
@@ -883,17 +885,17 @@ static void ngx_http_proxy_send_response
          *     r->headers_out.content_length
          */
 
-        if (&ph[i] == p->headers_in.server) {
+        if (&h[i] == p->headers_in.server) {
             r->headers_out.server = ch;
             continue;
         }
 
-        if (&ph[i] == p->headers_in.date) {
+        if (&h[i] == p->headers_in.date) {
             r->headers_out.date = ch;
             continue;
         }
 
-        if (&ph[i] == p->headers_in.content_length) {
+        if (&h[i] == p->headers_in.content_length) {
 
             r->headers_out.content_length_n =
                              ngx_atoi(p->headers_in.content_length->value.data,
@@ -977,6 +979,12 @@ static void ngx_http_proxy_send_response
         r->sendfile = 1;
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    ep->read_timeout = p->lcf->read_timeout;
+    ep->send_timeout = clcf->send_timeout;
+    ep->send_lowat = clcf->send_lowat;
+
     p->event_pipe = ep;
 
     p->upstream.connection->read->event_handler = ngx_http_proxy_process_body;
--- a/src/http/modules/proxy/ngx_http_proxy_handler.h
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.h
@@ -44,6 +44,8 @@ typedef struct {
 
 
 typedef struct {
+    ngx_table_t      *headers;   /* it must be first field */
+
     ngx_table_elt_t  *date;
     ngx_table_elt_t  *server;
     ngx_table_elt_t  *connection;
@@ -53,8 +55,6 @@ typedef struct {
     ngx_table_elt_t  *accept_ranges;
 
     off_t             content_length_n;
-
-    ngx_table_t      *headers;
 } ngx_http_proxy_headers_in_t;
 
 
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -60,6 +60,9 @@ int ngx_http_special_response_handler(ng
 
 time_t ngx_http_parse_time(char *value, size_t len);
 size_t ngx_http_get_time(char *buf, time_t t);
+ngx_table_elt_t *ngx_http_add_header(void *header,
+                                     ngx_http_header_t *http_headers);
+
 
 
 int ngx_http_discard_body(ngx_http_request_t *r);
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -4,7 +4,9 @@
 #include <ngx_event.h>
 #include <ngx_http.h>
 #include <nginx.h>
-
+#if __FreeBSD__
+#include <ngx_freebsd_init.h>
+#endif
 
 
 static void ngx_http_phase_event_handler(ngx_event_t *rev);
@@ -31,6 +33,10 @@ static char *ngx_set_server_name(ngx_con
                                  void *conf);
 static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 
+static char *ngx_http_lowat_check(ngx_conf_t *cf, void *post, void *data);
+
+static ngx_conf_post_t  ngx_http_lowat_post = { ngx_http_lowat_check } ;
+
 
 static ngx_command_t  ngx_http_core_commands[] = {
 
@@ -151,6 +157,13 @@ static ngx_command_t  ngx_http_core_comm
      offsetof(ngx_http_core_loc_conf_t, send_timeout),
      NULL},
 
+    {ngx_string("send_lowat"),
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+     ngx_conf_set_size_slot,
+     NGX_HTTP_LOC_CONF_OFFSET,
+     offsetof(ngx_http_core_loc_conf_t, send_lowat),
+     &ngx_http_lowat_post},
+
     {ngx_string("keepalive_timeout"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_msec_slot,
@@ -506,6 +519,49 @@ int ngx_http_delay_handler(ngx_http_requ
 #endif
 
 
+ngx_table_elt_t *ngx_http_add_header(void *header,
+                                     ngx_http_header_t *http_headers)
+{
+    int               i, j;
+    char             *prev;
+    ngx_table_t      *headers;
+    ngx_table_elt_t  *h, *new;
+
+    headers = *(ngx_table_t **) header;
+
+    prev = headers->elts;
+
+    if (!(new = ngx_push_table(headers))) {
+        return NULL;
+    }
+
+    if (prev == headers->elts) {
+        return new;
+    }
+
+    h = headers->elts;
+    for (i = 0; i < headers->nelts; i++) {
+        if (h[i].key.len == 0) {
+            continue;
+        }
+
+        for (j = 0; http_headers[j].name.len != 0; j++) {
+            if (http_headers[j].name.len != h[i].key.len) {
+                continue;
+            }
+
+            if (ngx_strcasecmp(http_headers[j].name.data, h[i].key.data) == 0) {
+                *((ngx_table_elt_t **)
+                      ((char *) &header + http_headers[j].offset)) = &h[i];
+                break;
+            }
+        }
+    }
+
+    return new;
+}
+
+
 static int ngx_http_core_init(ngx_cycle_t *cycle)
 {
 #if 0
@@ -852,6 +908,7 @@ static void *ngx_http_core_create_loc_co
     lcf->client_body_timeout = NGX_CONF_UNSET;
     lcf->sendfile = NGX_CONF_UNSET;
     lcf->send_timeout = NGX_CONF_UNSET;
+    lcf->send_lowat = NGX_CONF_UNSET;
     lcf->discarded_buffer_size = NGX_CONF_UNSET;
     lcf->keepalive_timeout = NGX_CONF_UNSET;
     lcf->lingering_time = NGX_CONF_UNSET;
@@ -925,6 +982,7 @@ static char *ngx_http_core_merge_loc_con
                               prev->client_body_timeout, 10000);
     ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
     ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 10000);
+    ngx_conf_merge_size_value(conf->send_lowat, prev->send_lowat, 0);
     ngx_conf_merge_size_value(conf->discarded_buffer_size,
                               prev->discarded_buffer_size, 1500);
     ngx_conf_merge_msec_value(conf->keepalive_timeout,
@@ -1063,3 +1121,29 @@ static char *ngx_set_error_log(ngx_conf_
 
     return NGX_CONF_OK;
 }
+
+
+static char *ngx_http_lowat_check(ngx_conf_t *cf, void *post, void *data)
+{
+    int *np = data;
+
+#if (HAVE_LOWAT_EVENT)
+
+    if (*np >= ngx_freebsd_net_inet_tcp_sendspace) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"send_lowat\" must be less than %d "
+                           "(sysctl net.inet.tcp.sendspace)",
+                           ngx_freebsd_net_inet_tcp_sendspace);
+
+        return NGX_CONF_ERROR;
+    }
+
+#else
+
+    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                       "\"send_lowat\" is not supported, ignored");
+
+#endif
+
+    return NGX_CONF_OK;
+}
--- a/src/http/ngx_http_header_filter.c
+++ b/src/http/ngx_http_header_filter.c
@@ -102,9 +102,8 @@ static int ngx_http_header_filter(ngx_ht
         r->header_only = 1;
     }
 
-    /* 9 is for "HTTP/1.x ", 2 is for trailing "\r\n"
-       and 2 is for end of header */
-    len = 9 + 2 + 2;
+    /* 2 is for trailing "\r\n" and 2 is for "\r\n" in the end of header */
+    len = sizeof("HTTP/1.x ") - 1 + 2 + 2;
 
     /* status line */
     if (r->headers_out.status_line.len) {
@@ -150,29 +149,29 @@ static int ngx_http_header_filter(ngx_ht
         len += r->headers_out.date->key.len
                + r->headers_out.date->value.len + 2;
     } else {
-        /* "Date: ... \r\n" */
-        len += 37;
+        len += sizeof("Date: Mon, 28 Sep 1970 00:00:00 GMT" CRLF) - 1;
     }
 
     if (r->headers_out.content_range && r->headers_out.content_range->value.len)
     {
-        len += 15 + r->headers_out.content_range->value.len + 2;
+        len += sizeof("Content-Range: ") - 1
+               + r->headers_out.content_range->value.len + 2;
     }
 
     if (r->headers_out.content_length == NULL) {
         if (r->headers_out.content_length_n >= 0) {
-            /* "Content-Length: ... \r\n", 2^64 is 20 characters */
-            len += 48;
+                                            /* 2^64 */
+            len += sizeof("Content-Length: 18446744073709551616" CRLF) - 1;
         }
     }
 
     if (r->headers_out.content_type && r->headers_out.content_type->value.len) {
         r->headers_out.content_type->key.len = 0;
-        len += 14 + r->headers_out.content_type->value.len + 2;
+        len += sizeof("Content-Type: ") - 1
+               + r->headers_out.content_type->value.len + 2;
 
         if (r->headers_out.charset.len) {
-            /* "; charset= ... " */
-            len += 10 + r->headers_out.charset.len;
+            len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
         }
     }
 
@@ -187,9 +186,8 @@ static int ngx_http_header_filter(ngx_ht
         && r->headers_out.location->value.data[0] == '/')
     {
         r->headers_out.location->key.len = 0;
-        /* "Location: http:// ... \r\n" */
-        len += 17 + r->server_name->len
-               + r->headers_out.location->value.len + 2;
+        len += sizeof("Location: http://") - 1,
+               + r->server_name->len + r->headers_out.location->value.len + 2;
 
         if (r->port != 80) {
             len += r->port_name->len;
@@ -201,24 +199,20 @@ static int ngx_http_header_filter(ngx_ht
                + r->headers_out.last_modified->value.len + 2;
 
     } else if (r->headers_out.last_modified_time != -1) {
-        /* "Last-Modified: ... \r\n" */
-        len += 46;
+        len += sizeof("Last-Modified: Mon, 28 Sep 1970 00:00:00 GMT" CRLF) - 1;
     }
 
     if (r->chunked) {
-        /* "Transfer-Encoding: chunked\r\n" */
-        len += 28;
+        len += sizeof("Transfer-Encoding: chunked" CRLF) - 1;
     }
 
     if (r->keepalive) {
-        /* "Connection: keep-alive\r\n" */
-        len += 24;
+        len += sizeof("Connection: keep-alive" CRLF) - 1;
     } else {
-        /* "Connection: close\r\n" */
-        len += 19;
+        len += sizeof("Connection: closed" CRLF) - 1;
     }
 
-    header = (ngx_table_elt_t *) r->headers_out.headers->elts;
+    header = r->headers_out.headers->elts;
     for (i = 0; i < r->headers_out.headers->nelts; i++) {
         if (header[i].key.len == 0) {
             continue;
@@ -230,7 +224,7 @@ 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 " */
-    h->last = ngx_cpymem(h->last, "HTTP/1.1 ", 9);
+    h->last = ngx_cpymem(h->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1);
 
     /* status line */
     if (r->headers_out.status_line.len) {
@@ -248,7 +242,7 @@ static int ngx_http_header_filter(ngx_ht
     }
 
     if (!(r->headers_out.date && r->headers_out.date->key.len)) {
-        h->last = ngx_cpymem(h->last, "Date: ", 6);
+        h->last = ngx_cpymem(h->last, "Date: ", sizeof("Date: ") - 1);
         h->last += ngx_http_get_time(h->last, time(NULL));
         *(h->last++) = CR; *(h->last++) = LF;
     }
@@ -256,7 +250,8 @@ static int ngx_http_header_filter(ngx_ht
 
     if (r->headers_out.content_range && r->headers_out.content_range->value.len)
     {
-        h->last = ngx_cpymem(h->last, "Content-Range: ", 15);
+        h->last = ngx_cpymem(h->last, "Content-Range: ",
+                             sizeof("Content-Range: ") - 1);
         h->last = ngx_cpymem(h->last, r->headers_out.content_range->value.data,
                              r->headers_out.content_range->value.len);
         *(h->last++) = CR; *(h->last++) = LF;
@@ -265,19 +260,22 @@ static int ngx_http_header_filter(ngx_ht
     if (r->headers_out.content_length == NULL) {
         /* 2^64 is 20 characters  */
         if (r->headers_out.content_length_n >= 0) {
-            h->last += ngx_snprintf(h->last, 49,
-                                    "Content-Length: " OFF_FMT CRLF,
-                                    r->headers_out.content_length_n);
+            h->last += ngx_snprintf(h->last,
+                            sizeof("Content-Length: 18446744073709551616" CRLF),
+                            "Content-Length: " OFF_FMT CRLF,
+                            r->headers_out.content_length_n);
         }
     }
 
     if (r->headers_out.content_type && r->headers_out.content_type->value.len) {
-        h->last = ngx_cpymem(h->last, "Content-Type: ", 14);
+        h->last = ngx_cpymem(h->last, "Content-Type: ",
+                             sizeof("Content-Type: ") - 1);
         h->last = ngx_cpymem(h->last, r->headers_out.content_type->value.data,
                              r->headers_out.content_type->value.len);
 
         if (r->headers_out.charset.len) {
-            h->last = ngx_cpymem(h->last, "; charset=", 10);
+            h->last = ngx_cpymem(h->last, "; charset=",
+                                 sizeof("; charset=") - 1);
             h->last = ngx_cpymem(h->last, r->headers_out.charset.data,
                                  r->headers_out.charset.len);
         }
@@ -288,7 +286,8 @@ static int ngx_http_header_filter(ngx_ht
     if (r->headers_out.content_encoding
         && r->headers_out.content_encoding->value.len)
     {
-        h->last = ngx_cpymem(h->last, "Content-Encoding: ", 18);
+        h->last = ngx_cpymem(h->last, "Content-Encoding: ",
+                             sizeof("Content-Encoding: ") - 1);
         h->last = ngx_cpymem(h->last,
                              r->headers_out.content_encoding->value.data,
                              r->headers_out.content_encoding->value.len);
@@ -300,7 +299,8 @@ static int ngx_http_header_filter(ngx_ht
         && r->headers_out.location->value.len
         && r->headers_out.location->value.data[0] == '/')
     {
-        h->last = ngx_cpymem(h->last, "Location: http://", 17);
+        h->last = ngx_cpymem(h->last, "Location: http://",
+                             sizeof("Location: http://") - 1);
         h->last = ngx_cpymem(h->last, r->server_name->data,
                              r->server_name->len);
         if (r->port != 80) {
@@ -317,21 +317,25 @@ static int ngx_http_header_filter(ngx_ht
     if (!(r->headers_out.last_modified && r->headers_out.last_modified->key.len)
         && r->headers_out.last_modified_time != -1)
     {
-        h->last = ngx_cpymem(h->last, "Last-Modified: ", 15);
+        h->last = ngx_cpymem(h->last, "Last-Modified: ",
+                             sizeof("Last-Modified: ") - 1);
         h->last += ngx_http_get_time(h->last,
-                                         r->headers_out.last_modified_time);
+                                            r->headers_out.last_modified_time);
         *(h->last++) = CR; *(h->last++) = LF;
     }
 
     if (r->chunked) {
-        h->last = ngx_cpymem(h->last, "Transfer-Encoding: chunked" CRLF, 28);
+        h->last = ngx_cpymem(h->last, "Transfer-Encoding: chunked" CRLF,
+                             sizeof("Transfer-Encoding: chunked" CRLF) - 1);
     }
 
     if (r->keepalive) {
-        h->last = ngx_cpymem(h->last, "Connection: keep-alive" CRLF, 24);
+        h->last = ngx_cpymem(h->last, "Connection: keep-alive" CRLF,
+                             sizeof("Connection: keep-alive" CRLF) - 1);
 
     } else {
-        h->last = ngx_cpymem(h->last, "Connection: close" CRLF, 19);
+        h->last = ngx_cpymem(h->last, "Connection: close" CRLF,
+                             sizeof("Connection: close" CRLF) - 1);
     }
 
     for (i = 0; i < r->headers_out.headers->nelts; i++) {
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -44,7 +44,7 @@ static char *client_header_errors[] = {
 
 
 
-static ngx_http_header_t headers_in[] = {
+ngx_http_header_t ngx_http_headers_in[] = {
     { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host) },
     { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection) },
     { ngx_string("If-Modified-Since"),
@@ -66,6 +66,27 @@ static ngx_http_header_t headers_in[] = 
 };
 
 
+ngx_http_header_t ngx_http_headers_out[] = {
+    { ngx_string("Server"), offsetof(ngx_http_headers_out_t, server) },
+    { ngx_string("Date"), offsetof(ngx_http_headers_out_t, date) },
+    { ngx_string("Content-Type"),
+                             offsetof(ngx_http_headers_out_t, content_type) },
+    { ngx_string("Content-Length"),
+                           offsetof(ngx_http_headers_out_t, content_length) },
+    { ngx_string("Content-Encoding"),
+                         offsetof(ngx_http_headers_out_t, content_encoding) },
+
+    /* Location */
+
+    { ngx_string("Last-Modified"),
+                            offsetof(ngx_http_headers_out_t, last_modified) },
+    { ngx_string("Accept-Ranges"),
+                            offsetof(ngx_http_headers_out_t, accept_ranges) },
+
+    { ngx_null_string, 0 }
+};
+
+
 static void ngx_http_dummy(ngx_event_t *wev)
 {
     return;
@@ -106,20 +127,14 @@ void ngx_http_init_connection(ngx_connec
     rev->event_handler = ngx_http_init_request;
 
     if (rev->ready) {
-        /* deferred accept */
+        /* deferred accept, aio, iocp, epoll */
         ngx_http_init_request(rev);
         return;
     }
 
     ngx_add_timer(rev, c->listening->post_accept_timeout);
 
-    if (ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_EDGE_EVENT)) {
-        /* aio, iocp, epoll */
-        ngx_http_init_request(rev);
-        return;
-    }
-
-    if (ngx_handle_read_event(rev) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
         ngx_http_close_connection(c);
         return;
     }
@@ -256,7 +271,7 @@ ngx_log_debug(rev->log, "IN: %08x" _ in_
         return;
     }
 
-    r->headers_out.headers = ngx_create_table(r->pool, 10);
+    r->headers_out.headers = ngx_create_table(r->pool, 1);
     if (r->headers_out.headers == NULL) {
         ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         ngx_http_close_connection(c);
@@ -451,7 +466,7 @@ static void ngx_http_process_request_lin
         lctx = c->log->data;
         lctx->action = "reading client request headers";
         lctx->url = r->unparsed_uri.data;
-        r->headers_in.headers = ngx_create_table(r->pool, 10);
+        r->headers_in.headers = ngx_create_table(r->pool, 1);
 
         if (cscf->large_client_header
             && r->header_in->pos == r->header_in->last)
@@ -560,8 +575,8 @@ static void ngx_http_process_request_hea
 
             /* a header line has been parsed successfully */
 
-            h = ngx_push_table(r->headers_in.headers);
-            if (h == NULL) {
+            if (!(h = ngx_http_add_header(&r->headers_in, ngx_http_headers_in)))
+            {
                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                 ngx_http_close_connection(c);
                 return;
@@ -593,14 +608,16 @@ static void ngx_http_process_request_hea
                 h->value.data[h->value.len] = '\0';
             }
 
-            for (i = 0; headers_in[i].name.len != 0; i++) {
-                if (headers_in[i].name.len != h->key.len) {
+            for (i = 0; ngx_http_headers_in[i].name.len != 0; i++) {
+                if (ngx_http_headers_in[i].name.len != h->key.len) {
                     continue;
                 }
 
-                if (ngx_strcasecmp(headers_in[i].name.data, h->key.data) == 0) {
-                    *((ngx_table_elt_t **)
-                        ((char *) &r->headers_in + headers_in[i].offset)) = h;
+                if (ngx_strcasecmp(ngx_http_headers_in[i].name.data,
+                                   h->key.data) == 0)
+                {
+                    *((ngx_table_elt_t **) ((char *) &r->headers_in
+                                         + ngx_http_headers_in[i].offset)) = h;
                     break;
                 }
             }
@@ -692,10 +709,6 @@ static ssize_t ngx_http_read_request_hea
     n = r->header_in->last - r->header_in->pos;
 
     if (n > 0) {
-#if 0
-        /* TODO: THINK - AIO ??? */
-        rev->ready = 0;
-#endif
         return n;
     }
 
@@ -709,7 +722,7 @@ static ssize_t ngx_http_read_request_hea
             r->header_timeout_set = 1;
         }
 
-        if (ngx_handle_read_event(rev) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
             ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
             ngx_http_close_connection(r->connection);
             return NGX_ERROR;
@@ -1157,7 +1170,7 @@ static void ngx_http_set_keepalive(ngx_h
         c->tcp_nopush = 0;
     }
 
-    if (rev->ready || (ngx_event_flags & NGX_USE_AIO_EVENT)) {
+    if (rev->ready) {
         ngx_http_keepalive_handler(rev);
     }
 }
@@ -1256,7 +1269,7 @@ static void ngx_http_set_lingering_close
         return;
     }
 
-    if (rev->ready || (ngx_event_flags & NGX_USE_AIO_EVENT)) {
+    if (rev->ready) {
         ngx_http_lingering_close_handler(rev);
     }
 }
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -68,6 +68,8 @@ typedef struct {
 
 
 typedef struct {
+    ngx_table_t      *headers;   /* it must be first field */
+
     ngx_table_elt_t  *host;
     ngx_table_elt_t  *connection;
     ngx_table_elt_t  *if_modified_since;
@@ -83,8 +85,6 @@ typedef struct {
     ssize_t           content_length_n;
     size_t            connection_type;
     ssize_t           keep_alive_n;
-
-    ngx_table_t      *headers;
 } ngx_http_headers_in_t;
 
 
@@ -108,6 +108,8 @@ typedef struct {
 
 
 typedef struct {
+    ngx_table_t      *headers;   /* it must be first field */
+
     int               status;
     ngx_str_t         status_line;
 
@@ -124,8 +126,6 @@ typedef struct {
     ngx_str_t         charset;
     ngx_array_t       ranges;
 
-    ngx_table_t      *headers;
-
     off_t             content_length_n;
     char             *etag;
     time_t            date_time;
@@ -231,4 +231,9 @@ struct ngx_http_request_s {
 };
 
 
+extern ngx_http_header_t ngx_http_headers_in[];
+extern ngx_http_header_t ngx_http_headers_out[];
+
+
+
 #endif /* _NGX_HTTP_REQUEST_H_INCLUDED_ */
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -112,7 +112,7 @@ static void ngx_http_read_client_request
         clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
         ngx_add_timer(rev, clcf->client_body_timeout);
 
-        if (ngx_handle_read_event(rev) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
             ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         }
 
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -218,9 +218,11 @@ int ngx_http_special_response_handler(ng
                                           + sizeof(error_tail) - 1
                                           + sizeof(msie_stub) - 1;
 
-        ngx_test_null(r->headers_out.content_type,
-                      ngx_push_table(r->headers_out.headers),
-                      NGX_HTTP_INTERNAL_SERVER_ERROR);
+        if (!(r->headers_out.content_type =
+                   ngx_http_add_header(&r->headers_out, ngx_http_headers_out)))
+        {
+            return NGX_ERROR;
+        }
 
         r->headers_out.content_type->key.len = 12;
         r->headers_out.content_type->key.data = "Content-Type";
@@ -245,7 +247,9 @@ int ngx_http_special_response_handler(ng
     out = NULL;
     ll = NULL;
 
-    ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
+    if (!(h = ngx_calloc_hunk(r->pool))) {
+        return NGX_ERROR;
+    }
     h->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
     h->pos = error_pages[err].data;
     h->last = error_pages[err].data + error_pages[err].len;
@@ -254,7 +258,9 @@ int ngx_http_special_response_handler(ng
     ngx_chain_add_link(out, ll, cl);
 
 
-    ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
+    if (!(h = ngx_calloc_hunk(r->pool))) {
+        return NGX_ERROR;
+    }
     h->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
     h->pos = error_tail;
     h->last = error_tail + sizeof(error_tail) - 1;
@@ -270,7 +276,9 @@ int ngx_http_special_response_handler(ng
         && error != NGX_HTTP_REQUEST_URI_TOO_LARGE
        )
     {
-        ngx_test_null(h, ngx_calloc_hunk(r->pool), NGX_ERROR);
+        if (!(h = ngx_calloc_hunk(r->pool))) {
+            return NGX_ERROR;
+        }
         h->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
         h->pos = msie_stub;
         h->last = msie_stub + sizeof(msie_stub) - 1;
--- a/src/os/unix/ngx_aio.h
+++ b/src/os/unix/ngx_aio.h
@@ -6,6 +6,7 @@
 
 
 ssize_t ngx_aio_read(ngx_connection_t *c, char *buf, size_t size);
+ssize_t ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl);
 ssize_t ngx_aio_write(ngx_connection_t *c, char *buf, size_t size);
 ngx_chain_t *ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in);
 
--- a/src/os/unix/ngx_aio_read.c
+++ b/src/os/unix/ngx_aio_read.c
@@ -10,102 +10,89 @@
 
 
 /*
-    The data is ready - 3 syscalls:
-        aio_read(), aio_error(), aio_return()
-    The data is not ready - 4 (kqueue) or 5 syscalls:
-        aio_read(), aio_error(), notifiction,
-                                             aio_error(), aio_return()
-                                             aio_cancel(), aio_error()
-*/
-
+ * the ready data requires 3 syscalls:
+ *     aio_write(), aio_error(), aio_return()
+ * the non-ready data requires 4 (kqueue) or 5 syscalls:
+ *     aio_write(), aio_error(), notifiction, aio_error(), aio_return()
+ *                               timeout, aio_cancel(), aio_error()
+ */
 
 ssize_t ngx_aio_read(ngx_connection_t *c, char *buf, size_t size)
 {
-    int           rc, first, canceled;
-    ngx_event_t  *ev;
+    int           n;
+    ngx_event_t  *rev;
+
+    rev = c->read;
 
-    ev = c->read;
+    if (rev->active) {
+        ngx_log_error(NGX_LOG_ALERT, rev->log, 0, "SECOND AIO POST");
+        return NGX_AGAIN;
+    }
 
-    canceled = 0;
+    if (!rev->aio_complete) {
+        ngx_memzero(&rev->aiocb, sizeof(struct aiocb));
 
-    if (ev->timedout) {
-        ngx_set_socket_errno(NGX_ETIMEDOUT);
-        ngx_log_error(NGX_LOG_ERR, ev->log, 0, "aio_read() timed out");
+        rev->aiocb.aio_fildes = c->fd;
+        rev->aiocb.aio_buf = buf;
+        rev->aiocb.aio_nbytes = size;
 
-        rc = aio_cancel(c->fd, &ev->aiocb);
-        if (rc == -1) {
-            ngx_log_error(NGX_LOG_CRIT, ev->log, ngx_errno,
-                          "aio_cancel() failed");
+#if (HAVE_KQUEUE)
+        rev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
+        rev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+        rev->aiocb.aio_sigevent.sigev_value.sigval_ptr = rev;
+#endif
+
+        if (aio_read(&rev->aiocb) == -1) {
+            ngx_log_error(NGX_LOG_CRIT, rev->log, ngx_errno,
+                          "aio_read() failed");
+            rev->error = 1;
             return NGX_ERROR;
         }
 
-        ngx_log_debug(ev->log, "aio_cancel: %d" _ rc);
+        ngx_log_debug(rev->log, "aio_read: OK");
 
-        canceled = 1;
-
-        ev->ready = 1;
+        rev->active = 1;
     }
 
-    first = 0;
-
-    if (!ev->ready) {
-        ngx_memzero(&ev->aiocb, sizeof(struct aiocb));
-
-        ev->aiocb.aio_fildes = c->fd;
-        ev->aiocb.aio_buf = buf;
-        ev->aiocb.aio_nbytes = size;
-
-#if (HAVE_KQUEUE)
-        ev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
-        ev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
-        ev->aiocb.aio_sigevent.sigev_value.sigval_ptr = ev;
-#endif
+    rev->aio_complete = 0;
 
-        if (aio_read(&ev->aiocb) == -1) {
-            ngx_log_error(NGX_LOG_CRIT, ev->log, ngx_errno,
-                          "aio_read() failed");
-            return NGX_ERROR;
-        }
-
-        ngx_log_debug(ev->log, "aio_read: OK");
-
-        ev->active = 1;
-        first = 1;
-    }
-
-    ev->ready = 0;
-
-    rc = aio_error(&ev->aiocb);
-    if (rc == -1) {
-        ngx_log_error(NGX_LOG_CRIT, ev->log, ngx_errno, "aio_error() failed");
+    n = aio_error(&rev->aiocb);
+    if (n == -1) {
+        ngx_log_error(NGX_LOG_ALERT, rev->log, ngx_errno, "aio_error() failed");
+        rev->error = 1;
         return NGX_ERROR;
     }
 
-    if (rc != 0) {
-        if (rc == NGX_EINPROGRESS) {
-            if (!first) {
-                ngx_log_error(NGX_LOG_CRIT, ev->log, rc,
+    if (n != 0) {
+        if (n == NGX_EINPROGRESS) {
+            if (!rev->active) {
+                ngx_log_error(NGX_LOG_ALERT, rev->log, n,
                               "aio_read() still in progress");
             }
             return NGX_AGAIN;
         }
 
-        if (rc == NGX_ECANCELED && canceled) {
-            return NGX_ERROR;
-        }
-
-        ngx_log_error(NGX_LOG_CRIT, ev->log, rc, "aio_read() failed");
+        ngx_log_error(NGX_LOG_CRIT, rev->log, n, "aio_read() failed");
+        rev->error = 1;
         return NGX_ERROR;
     }
 
-    rc = aio_return(&ev->aiocb);
-    if (rc == -1) {
-        ngx_log_error(NGX_LOG_CRIT, ev->log, ngx_errno, "aio_return() failed");
+    n = aio_return(&rev->aiocb);
+    if (n == -1) {
+        ngx_log_error(NGX_LOG_ALERT, rev->log, ngx_errno,
+                      "aio_return() failed");
 
+        rev->error = 1;
         return NGX_ERROR;
     }
 
-    ngx_log_debug(ev->log, "aio_read: %d" _ rc);
+    rev->active = 0;
+
+    ngx_log_debug(rev->log, "aio_read: %d" _ n);
 
-    return rc;
+    if (n == 0) {
+        rev->eof = 1;
+    }
+
+    return n;
 }
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_aio_read_chain.c
@@ -0,0 +1,57 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_aio.h>
+
+
+ssize_t ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl)
+{
+    int           n;
+    char         *buf, *prev;
+    size_t        size, total;
+    ngx_err_t     err;
+
+    total = 0;
+
+    while (cl) {
+
+        /* we can post the single aio operation only */
+
+        if (c->read->active) {
+            return total ? total : NGX_AGAIN;
+        }
+
+        buf = cl->hunk->pos;
+        prev = buf;
+        size = 0;
+
+        /* coalesce the neighbouring hunks */
+
+        while (cl && prev == cl->hunk->pos) {
+            size += cl->hunk->last - cl->hunk->pos;
+            prev = cl->hunk->last;
+            cl = cl->next;
+        }
+
+        n = ngx_aio_read(c, buf, size);
+
+        ngx_log_debug(c->log, "aio_read: %d" _ n);
+
+        if (n == NGX_AGAIN) {
+            return total ? total : NGX_AGAIN;
+        }
+
+        if (n == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        if (n > 0) {
+            total += n;
+        }
+
+        ngx_log_debug(c->log, "aio_read total: %d" _ total);
+    }
+
+    return total ? total : NGX_AGAIN;
+}
--- a/src/os/unix/ngx_aio_write.c
+++ b/src/os/unix/ngx_aio_write.c
@@ -10,107 +10,89 @@
 
 
 /*
-    The data is ready - 3 syscalls:
-        aio_write(), aio_error(), aio_return()
-    The data is not ready - 4 (kqueue) or 5 syscalls:
-        aio_write(), aio_error(), notifiction,
-                                             aio_error(), aio_return()
-                                             aio_cancel(), aio_error()
-*/
+ * the ready data requires 3 syscalls:
+ *     aio_write(), aio_error(), aio_return()
+ * the non-ready data requires 4 (kqueue) or 5 syscalls:
+ *     aio_write(), aio_error(), notifiction, aio_error(), aio_return()
+ *                               timeout, aio_cancel(), aio_error()
+ */
 
 ssize_t ngx_aio_write(ngx_connection_t *c, char *buf, size_t size)
 {
-    int           rc, first, canceled;
-    ngx_event_t  *ev;
+    int           n;
+    ngx_event_t  *wev;
 
-    ev = c->write;
+    wev = c->write;
 
-    canceled = 0;
+    if (wev->active) {
+        return NGX_AGAIN;
+    }
 
-ngx_log_debug(ev->log, "aio: ev->ready: %d" _ ev->ready);
-ngx_log_debug(ev->log, "aio: aiocb: %08x" _ &ev->aiocb);
+ngx_log_debug(wev->log, "aio: wev->aio_complete: %d" _ wev->aio_complete);
 
-#if 0
-    if (ev->timedout) {
-        ngx_set_socket_errno(NGX_ETIMEDOUT);
-        ngx_log_error(NGX_LOG_ERR, ev->log, 0, "aio_write() timed out");
+    if (!wev->aio_complete) {
+        ngx_memzero(&wev->aiocb, sizeof(struct aiocb));
 
-        rc = aio_cancel(c->fd, &ev->aiocb);
-        if (rc == -1) {
-            ngx_log_error(NGX_LOG_CRIT, ev->log, ngx_errno,
-                          "aio_cancel() failed");
-            return NGX_ERROR;
-        }
+        wev->aiocb.aio_fildes = c->fd;
+        wev->aiocb.aio_buf = buf;
+        wev->aiocb.aio_nbytes = size;
 
-        ngx_log_debug(ev->log, "aio_cancel: %d" _ rc);
-
-        canceled = 1;
-
-        ev->ready = 1;
-    }
+#if (HAVE_KQUEUE)
+        wev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
+        wev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+        wev->aiocb.aio_sigevent.sigev_value.sigval_ptr = wev;
 #endif
 
-    first = 0;
-
-    if (!ev->ready) {
-        ngx_memzero(&ev->aiocb, sizeof(struct aiocb));
-
-        ev->aiocb.aio_fildes = c->fd;
-        ev->aiocb.aio_buf = buf;
-        ev->aiocb.aio_nbytes = size;
-
-#if (HAVE_KQUEUE)
-        ev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
-        ev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
-        ev->aiocb.aio_sigevent.sigev_value.sigval_ptr = ev;
-#endif
-
-        if (aio_write(&ev->aiocb) == -1) {
-            ngx_log_error(NGX_LOG_CRIT, ev->log, ngx_errno,
+        if (aio_write(&wev->aiocb) == -1) {
+            ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno,
                           "aio_write() failed");
             return NGX_ERROR;
         }
 
-        ngx_log_debug(ev->log, "aio_write: OK");
+        ngx_log_debug(wev->log, "aio_write: OK");
 
-        ev->active = 1;
-        first = 1;
+        wev->active = 1;
     }
 
-    ev->ready = 0;
+    wev->aio_complete = 0;
 
-    rc = aio_error(&ev->aiocb);
-    if (rc == -1) {
-        ngx_log_error(NGX_LOG_CRIT, ev->log, ngx_errno, "aio_error() failed");
+    n = aio_error(&wev->aiocb);
+    if (n == -1) {
+        ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno, "aio_error() failed");
+        wev->error = 1;
         return NGX_ERROR;
     }
 
-    if (rc != 0) {
-        if (rc == NGX_EINPROGRESS) {
-            if (!first) {
-                ngx_log_error(NGX_LOG_CRIT, ev->log, rc,
+    if (n != 0) {
+        if (n == NGX_EINPROGRESS) {
+            if (!wev->active) {
+                ngx_log_error(NGX_LOG_ALERT, wev->log, n,
                               "aio_write() still in progress");
             }
             return NGX_AGAIN;
         }
 
-        if (rc == NGX_ECANCELED && canceled) {
-            return NGX_ERROR;
-        }
-
-        ngx_log_error(NGX_LOG_CRIT, ev->log, rc, "aio_write() failed");
+        ngx_log_error(NGX_LOG_CRIT, wev->log, n, "aio_write() failed");
+        wev->error = 1;
         return NGX_ERROR;
     }
 
-    rc = aio_return(&ev->aiocb);
-    if (rc == -1) {
-        ngx_log_error(NGX_LOG_CRIT, ev->log, ngx_errno, "aio_return() failed");
+    n = aio_return(&wev->aiocb);
+    if (n == -1) {
+        ngx_log_error(NGX_LOG_ALERT, wev->log, ngx_errno,
+                      "aio_return() failed");
 
+        wev->error = 1;
         return NGX_ERROR;
     }
 
-    ev->active = 0;
-    ngx_log_debug(ev->log, "aio_write: %d" _ rc);
+    wev->active = 0;
+
+    ngx_log_debug(wev->log, "aio_write: %d" _ n);
 
-    return rc;
+    if (n == 0) {
+        wev->eof = 1;
+    }
+
+    return n;
 }
--- a/src/os/unix/ngx_aio_write_chain.c
+++ b/src/os/unix/ngx_aio_write_chain.c
@@ -7,68 +7,74 @@
 
 ngx_chain_t *ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in)
 {
-    int              rc;
-    char            *buf, *prev;
-    off_t            sent;
-    size_t           size;
-    ngx_err_t        err;
-    ngx_chain_t     *ce;
+    int           n;
+    char         *buf, *prev;
+    off_t         sent;
+    size_t        size;
+    ngx_err_t     err;
+    ngx_chain_t  *cl;
 
     sent = 0;
-    ce = in;
+    cl = in;
+
+    while (cl) {
 
-    while (ce) {
+        if (cl->hunk->last - cl->hunk->pos == 0) {
+            cl = cl->next;
+            continue;
+        }
 
         /* we can post the single aio operation only */
 
         if (c->write->active) {
-            return ce;
+            return cl;
         }
 
-        buf = prev = ce->hunk->pos;
+        buf = cl->hunk->pos;
+        prev = buf;
         size = 0;
 
-        /* coalesce the neighbouring chain entries */
+        /* coalesce the neighbouring hunks */
 
-        while (ce && prev == ce->hunk->pos) {
-            size += ce->hunk->last - ce->hunk->pos;
-            prev = ce->hunk->last;
-            ce = ce->next;
+        while (cl && prev == cl->hunk->pos) {
+            size += cl->hunk->last - cl->hunk->pos;
+            prev = cl->hunk->last;
+            cl = cl->next;
         }
 
-        rc = ngx_aio_write(c, buf, size);
+        n = ngx_aio_write(c, buf, size);
 
 #if (NGX_DEBUG_WRITE_CHAIN)
-        ngx_log_debug(c->log, "aio_write rc: %d" _ rc);
+        ngx_log_debug(c->log, "aio_write: %d" _ n);
 #endif
 
-        if (rc == NGX_ERROR) {
+        if (n == NGX_ERROR) {
             return NGX_CHAIN_ERROR;
         }
 
-        if (rc > 0) {
-            sent += rc;
-            c->sent += rc;
+        if (n > 0) {
+            sent += n;
+            c->sent += n;
         }
 
 #if (NGX_DEBUG_WRITE_CHAIN)
         ngx_log_debug(c->log, "aio_write sent: " OFF_FMT _ c->sent);
 #endif
 
-        for (ce = in; ce; ce = ce->next) {
+        for (cl = in; cl; cl = cl->next) {
 
-            if (sent >= ce->hunk->last - ce->hunk->pos) {
-                sent -= ce->hunk->last - ce->hunk->pos;
-                ce->hunk->pos = ce->hunk->last;
+            if (sent >= cl->hunk->last - cl->hunk->pos) {
+                sent -= cl->hunk->last - cl->hunk->pos;
+                cl->hunk->pos = cl->hunk->last;
 
                 continue;
             }
 
-            ce->hunk->pos += sent;
+            cl->hunk->pos += sent;
 
             break;
         }
     }
 
-    return ce;
+    return cl;
 }
--- a/src/os/unix/ngx_readv_chain.c
+++ b/src/os/unix/ngx_readv_chain.c
@@ -7,13 +7,14 @@
 ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain)
 {
     char          *prev;
-    ssize_t        n;
+    ssize_t        n, size;
     struct iovec  *iov;
     ngx_err_t      err;
     ngx_array_t    io;
 
     prev = NULL;
     iov = NULL;
+    size = 0;
 
     ngx_init_array(io, c->pool, 10, sizeof(struct iovec), NGX_ERROR);
 
@@ -29,6 +30,7 @@ ssize_t ngx_readv_chain(ngx_connection_t
             iov->iov_len = chain->hunk->end - chain->hunk->last;
         }
 
+        size += chain->hunk->end - chain->hunk->last;
         prev = chain->hunk->end;
         chain = chain->next;
     }
@@ -42,7 +44,6 @@ ngx_log_debug(c->log, "recv: %d:%d" _ io
 
     } else if (n == -1) {
         c->read->ready = 0;
-        c->read->error = 1;
 
         err = ngx_errno;
         if (err == NGX_EAGAIN) {
@@ -50,8 +51,12 @@ ngx_log_debug(c->log, "recv: %d:%d" _ io
             return NGX_AGAIN;
         }
 
+        c->read->error = 1;
         ngx_log_error(NGX_LOG_ERR, c->log, err, "readv() failed");
         return NGX_ERROR;
+
+    } else if (n < size) {
+        c->read->ready = 0;
     }
 
     return n;