changeset 63:36d2c25cc9bb

nginx-0.0.1-2003-02-26-23:21:43 import
author Igor Sysoev <igor@sysoev.ru>
date Wed, 26 Feb 2003 20:21:43 +0000
parents 8ccba41a678e
children 34d647deb1da
files src/core/ngx_connection.h src/core/ngx_write_chain.c src/event/modules/ngx_aio_module.c src/event/modules/ngx_aio_module.h src/event/modules/ngx_kqueue_module.c src/event/ngx_event.c src/event/ngx_event.h src/event/ngx_event_accept.c src/event/ngx_event_aio_write.c src/http/modules/ngx_http_static_handler.c src/http/ngx_http_header_filter.c src/http/ngx_http_write_filter.c src/os/unix/ngx_aio_write_chain.c src/os/unix/ngx_freebsd_write_chain.c
diffstat 14 files changed, 382 insertions(+), 180 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -79,4 +79,19 @@ EV_VNODE        should notify by some si
                 or stat if aged >= N seconds (big enough)
 #endif
 
+
+extern ngx_chain_t *(*ngx_write_chain_proc)
+                                        (ngx_connection_t *c, ngx_chain_t *in);
+
+
+ngx_chain_t *ngx_write_chain(ngx_connection_t *c, ngx_chain_t *in, off_t flush);
+
+
+/* TODO: move it to OS specific file */
+#if (__FreeBSD__)
+ngx_chain_t *ngx_freebsd_write_chain(ngx_connection_t *c, ngx_chain_t *in);
+ngx_chain_t *ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in);
+#endif
+
+
 #endif /* _NGX_CONNECTION_H_INCLUDED_ */
--- a/src/core/ngx_write_chain.c
+++ b/src/core/ngx_write_chain.c
@@ -7,197 +7,44 @@
 #include <ngx_array.h>
 #include <ngx_hunk.h>
 #include <ngx_connection.h>
-#include <ngx_sendv.h>
-#include <ngx_sendfile.h>
+
 
-#include <ngx_write_chain.h>
+ngx_chain_t *(*ngx_write_chain_proc)(ngx_connection_t *c, ngx_chain_t *in);
 
 
 ngx_chain_t *ngx_write_chain(ngx_connection_t *c, ngx_chain_t *in, off_t flush)
 {
-    int           rc, i, last;
-    u_int         flags;
-    char         *prev;
+#if (NGX_EVENT)
+
+    return (*ngx_write_chain_proc)(c, in);
+
+#elif (NGX_EVENT_THREAD)
+
     off_t         sent;
-    ngx_iovec_t  *iov;
-    ngx_array_t   header, trailer;
-    ngx_hunk_t   *file;
-    ngx_chain_t  *ce;
+    ngx_chain_t  *rc;
 
-    ch = in;
-    file = NULL;
-    last = 0;
-
-    ngx_init_array(header, c->pool, 10, sizeof(ngx_iovec_t), NGX_CHAIN_ERROR);
-    ngx_init_array(trailer, c->pool, 10, sizeof(ngx_iovec_t), NGX_CHAIN_ERROR);
+    sent = flush - c->sent;
 
     do {
-        header.nelts = 0;
-        trailer.nelts = 0;
-
-        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
-            prev = NULL;
-            iov = NULL;
-
-            /* create iovec and coalesce the neighbouring chain entries */
-            while (ce && (ce->hunk->type & NGX_HUNK_IN_MEMORY))
-            {
-                if (prev == ce->hunk->pos.mem) {
-                    iov->ngx_iov_len += ce->hunk->last.mem - ce->hunk->pos.mem;
+        rc = (*ngx_write_chain_proc)(c, in);
 
-                } else {
-                    ngx_test_null(iov, ngx_push_array(&header),
-                                  NGX_CHAIN_ERROR);
-                    iov->ngx_iov_base = ce->hunk->pos.mem;
-                    iov->ngx_iov_len = ce->hunk->last.mem - ce->hunk->pos.mem;
-                    prev = ce->hunk->last.mem;
-                }
-
-                if (ce->hunk->type & NGX_HUNK_LAST) {
-                    last = 1;
-                }
-
-                ce = ce->next;
-            }
-        }
-
-        if (ce && (ce->hunk->type & NGX_HUNK_FILE)) {
-            file = ce->hunk;
-            ce = ce->next;
-
-            if (ce->hunk->type & NGX_HUNK_LAST) {
-                last = 1;
-            }
+        if (rc == NGX_CHAIN_ERROR && rc == NULL) {
+            return rc;
         }
 
-#if (HAVE_MAX_SENDFILE_IOVEC)
-        if (file && header->nelts > HAVE_MAX_SENDFILE_IOVEC) {
-            rc = ngx_sendv(c->fd, (ngx_iovec_t *) header->elts, header->nelts,
-                           &sent);
-        } else {
-#endif
-            if (ch && ch->hunk->type & NGX_HUNK_IN_MEMORY) {
-                prev = NULL;
-                iov = NULL;
-
-                while (ch && (ch->hunk->type & NGX_HUNK_IN_MEMORY)) {
+    } while (c->thread && flush > c->sent - sent);
 
-                    if (prev == ch->hunk->pos.mem) {
-                        iov->ngx_iov_len +=
-                                        ch->hunk->last.mem - ch->hunk->pos.mem;
-
-                    } else {
-                        ngx_test_null(iov, ngx_push_array(trailer),
-                                      NGX_CHAIN_ERROR);
-                        iov->ngx_iov_base = ch->hunk->pos.mem;
-                        iov->ngx_iov_len =
-                                        ch->hunk->last.mem - ch->hunk->pos.mem;
-                        prev = ch->hunk->last.mem;
-                    }
-
-                    if (ch->hunk->type & NGX_HUNK_LAST) {
-                        last = 1;
-                    }
+#else
 
-                    ch = ch->next;
-                }
-            }
+    ngx_chain_t  *rc;
 
-            if (file) {
-                flags = ngx_sendfile_flags;
-#if (HAVE_SENDFILE_DISCONNECT)
-                if (last && c->close) {
-                    flags |= NGX_SENDFILE_DISCONNECT;
-                }
-#endif
-                rc = ngx_sendfile(c,
-                                  (ngx_iovec_t *) header->elts, header->nelts,
-                                  file->file->fd, file->pos.file,
-                                  (size_t) (file->last.file - file->pos.file),
-                                  (ngx_iovec_t *) trailer->elts, trailer->nelts,
-                                  &sent, flags);
-
-#if (HAVE_AIO_EVENT) && !(HAVE_IOCP_EVENT)
-            } else if (ngx_event_flags & NGX_HAVE_AIO_EVENT) {
-
-                sent = 0;
-                rc = NGX_AGAIN;
-                iov = (ngx_iovec_t *) header->elts;
-                for (i = 0; i < header->nelts; i++) {
-                    rc = ngx_event_aio_write(c, iov[i].ngx_iov_base,
-                                             iov[i].ngx_iov_len);
-
-                    if (rc > 0) {
-                        sent += rc;
-                    } else {
-                        break;
-                    }
+    do {
 
-                    if (rc < (int) iov->ngx_iov_len) {
-                        break;
-                    }
-                }
-#endif
-            } else {
-                rc = ngx_sendv(c, (ngx_iovec_t *) header->elts, header->nelts);
-
-                sent = rc > 0 ? rc: 0;
+        rc = (*ngx_write_chain_proc)(c, in);
 
-#if (NGX_DEBUG_EVENT_WRITE)
-                ngx_log_debug(c->log, "sendv: " OFF_FMT _ sent);
-#endif
-            }
-#if (HAVE_MAX_SENDFILE_IOVEC)
-        }
-#endif
-        if (rc == NGX_ERROR)
-            return (ngx_chain_t *) NGX_ERROR;
-
-        c->sent += sent;
-        flush -= sent;
-
-        for (ch = in; ch; ch = ch->next) {
-
-#if (NGX_DEBUG_EVENT_WRITE)
-            ngx_log_debug(c->log, "event write: %x " QX_FMT " " OFF_FMT _
-                          ch->hunk->type _
-                          ch->hunk->pos.file _
-                          ch->hunk->last.file - ch->hunk->pos.file);
-#endif
+    } while (rc != NGX_CHAIN_ERROR && rc != NULL);
 
-            if (sent >= ch->hunk->last.file - ch->hunk->pos.file) {
-                sent -= ch->hunk->last.file - ch->hunk->pos.file;
-                ch->hunk->pos.file = ch->hunk->last.file;
-
-#if (NGX_DEBUG_EVENT_WRITE)
-                ngx_log_debug(c->log, "event write: " QX_FMT " 0 " OFF_FMT _
-                              ch->hunk->pos.file _ sent);
-#endif
-
-/*
-                if (ch->hunk->type & NGX_HUNK_LAST)
-                   break;
-*/
-
-                continue;
-            }
+    return rc;
 
-            ch->hunk->pos.file += sent;
-
-#if (NGX_DEBUG_EVENT_WRITE)
-            ngx_log_debug(c->log, "event write: " QX_FMT " " OFF_FMT _
-                          ch->hunk->pos.file _
-                          ch->hunk->last.file - ch->hunk->pos.file);
 #endif
-
-            break;
-        }
-
-    /* flush hunks if threaded state */
-    } while (c->write->context && flush > 0);
-
-    ngx_destroy_array(&trailer);
-    ngx_destroy_array(&header);
-
-    return ch;
 }
--- a/src/event/modules/ngx_aio_module.c
+++ b/src/event/modules/ngx_aio_module.c
@@ -1,4 +1,39 @@
 
+#include <ngx_config.h>
+
+#include <ngx_core.h>
+#include <ngx_types.h>
+#include <ngx_log.h>
+#include <ngx_connection.h>
+#include <ngx_event.h>
+#include <ngx_event_timer.h>
+
+#if (HAVE_KQUEUE)
+#include <ngx_kqueue_module.h>
+#endif
+
+
+int ngx_aio_init(int max_connections, ngx_log_t *log)
+{
+#if (HAVE_KQUEUE)
+
+    int  rc;
+
+    rc = ngx_kqueue_init(max_connections, log);
+
+    ngx_event_flags = NGX_HAVE_AIO_EVENT;
+    ngx_write_chain_proc = ngx_aio_write_chain;
+
+    return rc;
+
+#endif
+}
+
+
+
+
+
+#if 0
 /* 1 */
 int ngx_posix_aio_process_events(ngx_log_t *log)
 {
@@ -47,4 +82,4 @@ void aio_sig_handler(int signo, siginfo_
 {
     push siginfo->si_value.sival_ptr
 }
-
+#endif
new file mode 100644
--- /dev/null
+++ b/src/event/modules/ngx_aio_module.h
@@ -0,0 +1,14 @@
+#ifndef _NGX_AIO_MODULE_H_INCLUDED_
+#define _NGX_AIO_MODULE_H_INCLUDED_
+
+
+#include <ngx_types.h>
+#include <ngx_log.h>
+#include <ngx_event.h>
+
+
+int ngx_aio_init(int max_connections, ngx_log_t *log);
+int ngx_aio_process_events(ngx_log_t *log);
+
+
+#endif /* _NGX_AIO_MODULE_H_INCLUDED_ */
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -79,6 +79,8 @@ int ngx_kqueue_init(int max_connections,
 #endif
                      |NGX_HAVE_KQUEUE_EVENT;
 
+    ngx_write_chain_proc = ngx_freebsd_write_chain;
+
 #endif
 
 #endif
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -25,6 +25,10 @@
 #include <ngx_kqueue_module.h>
 #endif
 
+#if (HAVE_AIO)
+#include <ngx_aio_module.h>
+#endif
+
 #if (HAVE_IOCP)
 #include <ngx_event_acceptex.h>
 #include <ngx_iocp_module.h>
@@ -54,6 +58,9 @@ static int (*ngx_event_init[]) (int max_
 #if (HAVE_KQUEUE)
     ngx_kqueue_init,
 #endif
+#if (HAVE_AIO)
+    ngx_aio_init,
+#endif
 #if (HAVE_IOCP)
     ngx_iocp_init
 #endif
@@ -83,6 +90,9 @@ void ngx_pre_thread(ngx_array_t *ls, ngx
     ngx_event_type = NGX_DEVPOLL_EVENT_N;
 #endif
 #if 0
+    ngx_event_type = NGX_AIO_EVENT_N;
+#endif
+#if 0
     ngx_event_type = NGX_IOCP_EVENT_N;
 #endif
 
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -116,6 +116,9 @@ typedef enum {
 #if (HAVE_KQUEUE)
     NGX_KQUEUE_EVENT_N,
 #endif
+#if (HAVE_AIO)
+    NGX_AIO_EVENT_N,
+#endif
 #if (HAVE_IOCP)
     NGX_IOCP_EVENT_N,
 #endif
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -156,6 +156,9 @@ int ngx_event_accept(ngx_event_t *ev)
 
 #elif (HAVE_KQUEUE)
 
+/*
+        if (ngx_event_type == NGX_HAVE_AIO_EVENT  or NGX_HAVE_KQUEUE_EVENT) {
+*/
         if (ngx_event_type == NGX_HAVE_KQUEUE_EVENT) {
             ev->available--;
         }
--- a/src/event/ngx_event_aio_write.c
+++ b/src/event/ngx_event_aio_write.c
@@ -5,6 +5,7 @@
 #include <ngx_log.h>
 #include <ngx_recv.h>
 #include <ngx_connection.h>
+#include <ngx_event.h>
 
 #if (HAVE_KQUEUE)
 #include <ngx_kqueue_module.h>
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -141,7 +141,7 @@ int ngx_http_static_handler(ngx_http_req
     if (r->header_only)
         return rc;
 
-#if 0
+#if 1
 
     h->type = NGX_HUNK_FILE|NGX_HUNK_LAST;
     h->pos.file = 0;
--- a/src/http/ngx_http_header_filter.c
+++ b/src/http/ngx_http_header_filter.c
@@ -95,7 +95,8 @@ static int ngx_http_header_filter(ngx_ht
             ngx_log_debug(r->connection->log, "%d %d" _
                           ims _ r->headers_out.last_modified_time);
 
-            if (ims != NGX_ERROR && ims >= r->headers_out.last_modified_time) {
+            /* I think that the date equality is correcter */
+            if (ims != NGX_ERROR && ims == r->headers_out.last_modified_time) {
                 r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
                 r->headers_out.content_length = -1;
                 r->headers_out.content_type->key.len = 0;
--- a/src/http/ngx_http_write_filter.c
+++ b/src/http/ngx_http_write_filter.c
@@ -4,6 +4,7 @@
 #include <ngx_core.h>
 #include <ngx_hunk.h>
 #include <ngx_conf_file.h>
+#include <ngx_connection.h>
 
 #include <ngx_event_write.h>
 
@@ -81,7 +82,7 @@ int ngx_http_write_filter(ngx_http_reque
         le = &ce->next;
         size += ce->hunk->last.file - ce->hunk->pos.file;
 
-#if (NGX_DEBUG_WRITE_FILTER0)
+#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 _
@@ -107,7 +108,7 @@ int ngx_http_write_filter(ngx_http_reque
         le = &ce->next;
         size += ce->hunk->last.file - ce->hunk->pos.file;
 
-#if (NGX_DEBUG_WRITE_FILTER0)
+#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 _
@@ -127,7 +128,7 @@ int ngx_http_write_filter(ngx_http_reque
                 ngx_http_get_module_loc_conf(r->main ? r->main : r,
                                              ngx_http_write_filter_module);
 
-#if (NGX_DEBUG_WRITE_FILTER0)
+#if (NGX_DEBUG_WRITE_FILTER)
     ngx_log_debug(r->connection->log, "write filter: last:%d flush:%d" _
                   last _ flush);
 #endif
@@ -138,7 +139,7 @@ int ngx_http_write_filter(ngx_http_reque
         return NGX_OK;
     }
 
-    chain = ngx_event_write(r->connection, ctx->out, flush);
+    chain = ngx_write_chain(r->connection, ctx->out, flush);
 
 #if (NGX_DEBUG_WRITE_FILTER)
     ngx_log_debug(r->connection->log, "write filter %x" _ chain);
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_aio_write_chain.c
@@ -0,0 +1,92 @@
+
+#include <ngx_config.h>
+
+#include <ngx_core.h>
+#include <ngx_types.h>
+#include <ngx_alloc.h>
+#include <ngx_array.h>
+#include <ngx_hunk.h>
+#include <ngx_connection.h>
+#include <ngx_sendv.h>
+#include <ngx_sendfile.h>
+
+
+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;
+
+    sent = 0;
+    ce = in;
+
+    while (ce) {
+
+ngx_log_debug(c->log, "aio_write ce: %x" _ ce->hunk->pos.mem);
+
+        buf = prev = ce->hunk->pos.mem;
+        size = 0;
+
+        /* coalesce the neighbouring chain entries */
+        while (ce && prev == ce->hunk->pos.mem) {
+            size += ce->hunk->last.mem - ce->hunk->pos.mem;
+            prev = ce->hunk->last.mem;
+            ce = ce->next;
+        }
+
+        rc = ngx_event_aio_write(c, buf, size);
+
+ngx_log_debug(c->log, "aio_write rc: %d" _ rc);
+
+        if (rc > 0) {
+            sent += rc;
+            c->sent += rc;
+
+        } else if (rc == NGX_ERROR) {
+            return NGX_CHAIN_ERROR;
+
+        } else if (rc == NGX_AGAIN) {
+            break;
+        }
+    }
+
+#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) {
+
+#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 (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 (NGX_DEBUG_WRITE_CHAIN)
+            ngx_log_debug(c->log, "write chain done: %qx %qd" _
+                          ce->hunk->pos.file _ sent);
+#endif
+            continue;
+        }
+
+        ce->hunk->pos.file += 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
+
+        break;
+    }
+
+    return ce;
+}
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_freebsd_write_chain.c
@@ -0,0 +1,178 @@
+
+#include <ngx_config.h>
+
+#include <ngx_core.h>
+#include <ngx_types.h>
+#include <ngx_alloc.h>
+#include <ngx_array.h>
+#include <ngx_hunk.h>
+#include <ngx_connection.h>
+#include <ngx_sendv.h>
+#include <ngx_sendfile.h>
+
+
+ngx_chain_t *ngx_freebsd_write_chain(ngx_connection_t *c, ngx_chain_t *in)
+{
+    int              rc;
+    char            *prev;
+    size_t           hsize;
+    off_t            sent;
+    struct iovec    *iov;
+    struct sf_hdtr   hdtr;
+    ngx_err_t        err;
+    ngx_array_t      header, trailer;
+    ngx_hunk_t      *file;
+    ngx_chain_t     *ce;
+
+    ce = in;
+    file = NULL;
+    hsize = 0;
+
+    ngx_init_array(header, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR);
+    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) {
+        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;
+
+            } 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;
+            }
+
+#if (HAVE_FREEBSD_SENDFILE_NBYTES_BUG)
+            hsize += ce->hunk->last.mem - ce->hunk->pos.mem;
+#endif
+            ce = ce->next;
+        }
+    }
+
+    /* TODO: coalesce the neighbouring shadow 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) {
+        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;
+
+            } 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;
+            }
+
+            ce = ce->next;
+        }
+    }
+
+    if (file) {
+        hdtr.headers = (struct iovec *) header.elts;
+        hdtr.hdr_cnt = header.nelts;
+        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,
+                      &hdtr, &sent, 0);
+
+        if (rc == -1) {
+            err = ngx_errno;
+            if (err == NGX_EAGAIN || err == NGX_EINTR) {
+                ngx_log_error(NGX_LOG_INFO, c->log, err,
+                              "sendfile() sent only %qd bytes", sent);
+
+            } else {
+                ngx_log_error(NGX_LOG_CRIT, c->log, err, "sendfile() failed");
+                return NGX_CHAIN_ERROR;
+            }
+        }
+
+#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);
+#endif
+
+    } else {
+        rc = writev(c->fd, (struct iovec *) header.elts, header.nelts);
+
+        if (rc == -1) {
+            err = ngx_errno;
+            if (err == NGX_EAGAIN) {
+                ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EAGAIN");
+
+            } else if (err == NGX_EINTR) {
+                ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EINTR");
+
+            } else {
+                ngx_log_error(NGX_LOG_CRIT, c->log, err, "writev() failed");
+                return NGX_CHAIN_ERROR;
+            }
+        }
+
+        sent = rc > 0 ? rc : 0;
+    }
+
+#if (NGX_DEBUG_WRITE_CHAIN)
+    ngx_log_debug(c->log, "sendv: %qd" _ sent);
+#endif
+
+    c->sent += sent;
+
+    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 (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 (NGX_DEBUG_WRITE_CHAIN)
+            ngx_log_debug(c->log, "write chain done: %qx %qd" _
+                          ce->hunk->pos.file _ sent);
+#endif
+            continue;
+        }
+
+        ce->hunk->pos.file += 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
+
+        break;
+    }
+
+    ngx_destroy_array(&trailer);
+    ngx_destroy_array(&header);
+
+    return ce;
+}