changeset 163:fb61ba77beba

nginx-0.0.1-2003-10-28-18:45:41 import
author Igor Sysoev <igor@sysoev.ru>
date Tue, 28 Oct 2003 15:45:41 +0000
parents 96993d4d5067
children 84036764e215
files src/core/ngx_alloc.h src/event/modules/ngx_kqueue_module.c src/event/ngx_event.h src/event/ngx_event_pipe.c src/http/modules/proxy/ngx_http_proxy_handler.c src/http/modules/proxy/ngx_http_proxy_handler.h src/http/ngx_http_core_module.c src/os/unix/ngx_freebsd_sendfile_chain.c src/os/unix/ngx_readv_chain.c src/os/unix/ngx_recv.c
diffstat 10 files changed, 192 insertions(+), 185 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_alloc.h
+++ b/src/core/ngx_alloc.h
@@ -17,8 +17,6 @@
 
 #define ngx_test_null(p, alloc, rc)  if ((p = alloc) == NULL) { return rc; }
 
-#define ngx_is_null(p, alloc)   if ((p = alloc) == NULL)
-
 
 typedef struct ngx_pool_large_s  ngx_pool_large_t;
 
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -422,8 +422,8 @@ static int ngx_kqueue_process_events(ngx
             ev->available = event_list[i].data;
 
             if (event_list[i].flags & EV_EOF) {
-                ev->eof = 1;
-                ev->error = event_list[i].fflags;
+                ev->kq_eof = 1;
+                ev->kq_errno = event_list[i].fflags;
             }
 
             if (ev->oneshot && ev->timer_set) {
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -29,8 +29,9 @@ struct ngx_event_s {
 
     unsigned int     index;
 
-    ngx_event_t     *prev;     /* queue in mutex(), aio_read(), aio_write()  */
-    ngx_event_t     *next;     /*                                            */
+    /* queue in mutex(), aio_read(), aio_write()  */
+    ngx_event_t     *prev;
+    ngx_event_t     *next;
 
     ngx_event_t     *timer_prev;
     ngx_event_t     *timer_next;
@@ -40,22 +41,22 @@ struct ngx_event_s {
 
     ngx_log_t       *log;
 
-    int              available; /* kqueue only:                              */
-                                /*   accept: number of sockets that wait     */
-                                /*           to be accepted                  */
-                                /*   read:   bytes to read                   */
-                                /*   write:  available space in buffer       */
-                                /* otherwise:                                */
-                                /*   accept: 1 if accept many, 0 otherwise   */
+    /*
+     * kqueue only:
+     *   accept:     number of sockets that wait to be accepted
+     *   read:       bytes to read
+     *   write:      available space in buffer
+     *
+     * otherwise:
+     *   accept:     1 if accept many, 0 otherwise
+     */
+    int              available;
 
     unsigned         oneshot:1;
 
-#if 0
-    unsigned         listening:1;
-#endif
     unsigned         write:1;
 
-    /* used to detect stale events in kqueue, rt signals and epoll */
+    /* used to detect the stale events in kqueue, rt signals and epoll */
     unsigned         instance:1;
 
     /*
@@ -67,12 +68,12 @@ struct ngx_event_s {
     /* ready event; the complete aio operation */
     unsigned         ready:1;
 
+    unsigned         eof:1;
+    unsigned         error:1;
+
     unsigned         timedout:1;
     unsigned         timer_set:1;
 
-#if 1
-    unsigned         blocked:1;
-#endif
     unsigned         delayed:1;
 
     unsigned         read_discarded:1;
@@ -87,8 +88,8 @@ struct ngx_event_s {
 #endif
 
 #if (HAVE_KQUEUE)
-    unsigned         eof:1;
-    int              error;
+    unsigned         kq_eof:1;
+    int              kq_errno;
 #endif
 
 #if (HAVE_LOWAT_EVENT) /* kqueue's NOTE_LOWAT */
@@ -108,41 +109,27 @@ struct ngx_event_s {
 
 
 #if 0
-    void            *thr_ctx;   /* event thread context if $(CC) doesn't
-                                   understand __thread declaration
-                                   and pthread_getspecific() is too costly */
+
+    /* the threads support */
+
+    /*
+     * the event thread context, we store it here
+     * if $(CC) does not understand __thread declaration
+     * and pthread_getspecific() is too costly
+     */
+
+    void            *thr_ctx;
 
 #if (NGX_EVENT_T_PADDING)
-    int              padding[NGX_EVENT_T_PADDING];  /* event should not cross
-                                                       cache line in SMP */
+
+    /* event should not cross cache line in SMP */
+
+    int              padding[NGX_EVENT_T_PADDING];
 #endif
 #endif
 };
 
 
-#if 1
-typedef enum {
-    NGX_SELECT_EVENT_N = 0,
-#if (HAVE_POLL)
-    NGX_POLL_EVENT_N,
-#endif
-#if (HAVE_DEVPOLL)
-    NGX_DEVPOLL_EVENT_N,
-#endif
-#if (HAVE_KQUEUE)
-    NGX_KQUEUE_EVENT_N,
-#endif
-#if (HAVE_AIO)
-    NGX_AIO_EVENT_N,
-#endif
-#if (HAVE_IOCP)
-    NGX_IOCP_EVENT_N,
-#endif
-    NGX_DUMMY_EVENT_N    /* avoid comma at end of enumerator list */
-} ngx_event_type_e ;
-#endif
-
-
 typedef struct {
     int   (*add)(ngx_event_t *ev, int event, u_int flags);
     int   (*del)(ngx_event_t *ev, int event, u_int flags);
@@ -159,6 +146,9 @@ typedef struct {
 } ngx_event_actions_t;
 
 
+extern ngx_event_actions_t   ngx_event_actions;
+
+
 /*
  * The event filter requires to read/write the whole data -
  * select, poll, /dev/poll, kqueue.
@@ -243,12 +233,8 @@ typedef struct {
 #define NGX_ONESHOT_EVENT  EV_ONESHOT
 #define NGX_CLEAR_EVENT    EV_CLEAR
 
-#ifndef HAVE_CLEAR_EVENT
-#define HAVE_CLEAR_EVENT   1
-#endif
 
-
-#elif (HAVE_POLL) || (HAVE_DEVPOLL)
+#elif (HAVE_POLL)
 
 #define NGX_READ_EVENT     POLLIN
 #define NGX_WRITE_EVENT    POLLOUT
@@ -256,7 +242,16 @@ typedef struct {
 #define NGX_LEVEL_EVENT    0
 #define NGX_ONESHOT_EVENT  1
 
-#else
+
+#elif (HAVE_DEVPOLL)
+
+#define NGX_READ_EVENT     POLLIN
+#define NGX_WRITE_EVENT    POLLOUT
+
+#define NGX_LEVEL_EVENT    0
+
+
+#else /* select */
 
 #define NGX_READ_EVENT     0
 #define NGX_WRITE_EVENT    1
@@ -266,84 +261,35 @@ typedef struct {
 
 #endif /* HAVE_KQUEUE */
 
-#ifndef NGX_CLEAR_EVENT
-#define NGX_CLEAR_EVENT    0    /* dummy */
+
+#if (HAVE_IOCP_EVENT)
+#define NGX_IOCP_ACCEPT      0
+#define NGX_IOCP_IO          1
 #endif
 
-#if (USE_KQUEUE)
 
-#define ngx_init_events      ngx_kqueue_init
-#define ngx_process_events   ngx_kqueue_process_events
-#define ngx_add_event        ngx_kqueue_add_event
-#define ngx_del_event        ngx_kqueue_del_event
-#if 0
-#define ngx_add_timer        ngx_kqueue_add_timer
-#else
-#define ngx_add_timer        ngx_event_add_timer
+#ifndef NGX_CLEAR_EVENT
+#define NGX_CLEAR_EVENT    0    /* dummy declaration */
 #endif
-#define ngx_event_recv       ngx_event_recv_core
 
-#else
 
-#define ngx_init_events     (ngx_event_init[ngx_event_type])
 #define ngx_process_events   ngx_event_actions.process
 #define ngx_add_event        ngx_event_actions.add
 #define ngx_del_event        ngx_event_actions.del
 #define ngx_add_conn         ngx_event_actions.add_conn
 #define ngx_del_conn         ngx_event_actions.del_conn
 
-#if 0
-#if (HAVE_IOCP_EVENT)
-#define ngx_event_recv       ngx_event_wsarecv
-#elif (HAVE_AIO_EVENT)
-#define ngx_event_recv       ngx_event_aio_read
-#else
-#define ngx_event_recv       ngx_io.recv
-#define ngx_write_chain      ngx_io.send_chain
-#endif
-#endif
+#define ngx_add_timer        ngx_event_add_timer
+#define ngx_del_timer        ngx_event_del_timer
 
-#endif
-
-
-
-
-
-/* ***************************** */
 
 #define ngx_recv             ngx_io.recv
 #define ngx_recv_chain       ngx_io.recv_chain
 #define ngx_write_chain      ngx_io.send_chain
 
 
-#define ngx_add_timer        ngx_event_add_timer
-#define ngx_del_timer        ngx_event_del_timer
-
-
-#if (HAVE_IOCP_EVENT)
-#define NGX_IOCP_ACCEPT      0
-#define NGX_IOCP_IO          1
-#endif
-
-/* ***************************** */
-
-
-
-
-
-
-#if !(USE_KQUEUE)
-extern ngx_event_actions_t   ngx_event_actions;
-extern ngx_event_type_e      ngx_event_type;
-extern int                   ngx_event_flags;
-#endif
-
-
-
-/* ***************************** */
 
 #define NGX_EVENT_MODULE      0x544E5645  /* "EVNT" */
-
 #define NGX_EVENT_CONF        0x00200000
 
 
@@ -364,8 +310,10 @@ typedef struct {
 } ngx_event_module_t;
 
 
-extern ngx_module_t        ngx_events_module;
-extern ngx_module_t        ngx_event_core_module;
+
+extern int                   ngx_event_flags;
+extern ngx_module_t          ngx_events_module;
+extern ngx_module_t          ngx_event_core_module;
 
 
 #define ngx_event_get_conf(conf_ctx, module)                                  \
@@ -380,24 +328,10 @@ void ngx_event_acceptex(ngx_event_t *ev)
 int ngx_event_post_acceptex(ngx_listening_t *ls, int n);
 #endif
 
-/* ***************************** */
-
-
-
-
-
-ssize_t ngx_event_recv_core(ngx_connection_t *c, char *buf, size_t size);
-int ngx_event_close_connection(ngx_event_t *ev);
-
-
-int  ngx_pre_thread(ngx_array_t *ls, ngx_pool_t *pool, ngx_log_t *log);
-void ngx_worker(ngx_cycle_t *cycle);
-
-
-/* ***************************** */
 
 
 #include <ngx_event_timer.h>
+
 #if (WIN32)
 #include <ngx_iocp_module.h>
 #endif
@@ -437,8 +371,9 @@ ngx_inline static int ngx_handle_read_ev
         return NGX_OK;
     }
 
-    if (rev->active && rev->ready) {
-        if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+    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;
         }
 
@@ -551,7 +486,4 @@ ngx_inline static int ngx_handle_level_w
 }
 
 
-/* ***************************** */
-
-
 #endif /* _NGX_EVENT_H_INCLUDED_ */
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -98,20 +98,46 @@ int ngx_event_pipe_read_upstream(ngx_eve
 
             if (ngx_event_flags == NGX_HAVE_KQUEUE_EVENT) {
 
-                if (p->upstream->read->error) {
-                    ngx_log_error(NGX_LOG_ERR, p->log, p->upstream->read->error,
+                if (p->upstream->read->available == 0) {
+                    if (p->upstream->read->kq_eof) {
+                        p->upstream->read->ready = 0;
+                        p->upstream->read->eof = 0;
+                        p->upstream_eof = 1;
+                        p->read = 1;
+
+                        if (p->upstream->read->kq_errno) {
+                            p->upstream->read->error = 1;
+                            p->upstream_error = 1;
+                            p->upstream_eof = 0;
+
+                            ngx_log_error(NGX_LOG_ERR, p->log,
+                                          p->upstream->read->kq_errno,
+                                          /* TODO: ngx_readv_chain_n */
+                                          "readv() failed");
+                        }
+
+                        break;
+                    }
+                }
+
+#if 0
+                if (p->upstream->read->kq_errno) {
+                    ngx_log_error(NGX_LOG_ERR, p->log,
+                                  p->upstream->read->kq_errno,
                                   "readv() failed");
                     p->upstream_error = 1;
 
                     break;
 
-                } else if (p->upstream->read->eof
+                } else if (p->upstream->read->kq_eof
                            && p->upstream->read->available == 0) {
                     p->upstream_eof = 1;
                     p->read = 1;
 
                     break;
                 }
+#endif
+
             }
 #endif
 
--- a/src/http/modules/proxy/ngx_http_proxy_handler.c
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.c
@@ -110,6 +110,13 @@ static ngx_command_t ngx_http_proxy_comm
      offsetof(ngx_http_proxy_loc_conf_t, temp_file_write_size),
      NULL},
 
+    {ngx_string("proxy_pass_server"),
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+     ngx_conf_set_flag_slot,
+     NGX_HTTP_LOC_CONF_OFFSET,
+     offsetof(ngx_http_proxy_loc_conf_t, pass_server),
+     NULL},
+
     ngx_null_command
 };
 
@@ -237,19 +244,11 @@ ngx_log_debug(r->connection->log, "timer
         ngx_del_timer(r->connection->read);
     }
 
-    ngx_is_null(cl, ngx_http_proxy_create_request(p)) {
+    if (!(cl = ngx_http_proxy_create_request(p))) {
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         return;
     }
 
-#if 0
-    cl = ngx_http_proxy_create_request(p);
-    if (cl == NULL) {
-        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-        return;
-    }
-#endif
-
     if (r->request_hunks) {
         cl->next = r->request_hunks;
     }
@@ -266,6 +265,7 @@ ngx_log_debug(r->connection->log, "timer
     out_ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t));
     if (out_ctx == NULL) {
         ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
     }
 
     p->output_chain_ctx = out_ctx;
@@ -288,6 +288,7 @@ ngx_log_debug(r->connection->log, "timer
     write_ctx = ngx_pcalloc(r->pool, sizeof(ngx_chain_write_ctx_t));
     if (write_ctx == NULL) {
         ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
     }
 
     out_ctx->output_ctx = write_ctx;
@@ -767,7 +768,7 @@ static void ngx_http_proxy_process_upstr
             return;
         }
 
-        /* NGX_AGAIN: a header line parsing is still not complete */
+        /* rc == NGX_AGAIN: a header line parsing is still not complete */
 
         if (p->header_in->last == p->header_in->end) {
             ngx_log_error(NGX_LOG_ERR, rev->log, 0,
@@ -835,6 +836,8 @@ static void ngx_http_proxy_send_response
 
     r = p->request;
 
+    r->headers_out.status = p->status;
+
     r->headers_out.content_length_n = -1;
     r->headers_out.content_length = NULL;
 
@@ -852,6 +855,16 @@ static void ngx_http_proxy_send_response
                 || &ph[i] == p->headers_in.accept_ranges) {
                 continue;
             }
+
+            if (&ph[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];
+            r->headers_out.content_type->key.len = 0;
+            continue;
         }
 
         ch = ngx_push_table(r->headers_out.headers);
@@ -862,13 +875,26 @@ static void ngx_http_proxy_send_response
 
         *ch = ph[i];
 
-        if (&ph[i] == p->headers_in.content_type) {
-            r->headers_out.content_type = ch;
-            r->headers_out.content_type->key.len = 0;
+        /*
+         * ngx_http_header_filter() output the following headers
+         * from r->headers_out.headers if they are set:
+         *     r->headers_out.server,
+         *     r->headers_out.date,
+         *     r->headers_out.content_length
+         */
+
+        if (&ph[i] == p->headers_in.server) {
+            r->headers_out.server = ch;
+            continue;
+        }
+
+        if (&ph[i] == p->headers_in.date) {
+            r->headers_out.date = ch;
             continue;
         }
 
         if (&ph[i] == p->headers_in.content_length) {
+
             r->headers_out.content_length_n =
                              ngx_atoi(p->headers_in.content_length->value.data,
                                       p->headers_in.content_length->value.len);
@@ -877,23 +903,9 @@ static void ngx_http_proxy_send_response
         }
     }
 
-    /* STUB */
-
-    if (p->headers_in.server) {
-        r->headers_out.server = p->headers_in.server;
-    }
-
-    if (!p->accel && p->headers_in.date) {
-        r->headers_out.date = p->headers_in.date;
-    }
-
-    /* */
-
 
     /* TODO: preallocate event_pipe hunks, look "Content-Length" */
 
-    r->headers_out.status = p->status;
-
     rc = ngx_http_send_header(r);
 
     p->header_sent = 1;
@@ -1413,6 +1425,8 @@ static void *ngx_http_proxy_create_loc_c
 
     conf->next_upstream = NGX_CONF_UNSET;
 
+    conf->pass_server = NGX_CONF_UNSET;
+
     return conf;
 }
 
@@ -1451,6 +1465,8 @@ static char *ngx_http_proxy_merge_loc_co
     ngx_conf_merge_path_value(conf->temp_path, prev->temp_path,
                               "temp", 1, 2, 0, cf->pool);
 
+    ngx_conf_merge_value(conf->pass_server, prev->pass_server, 0);
+
     return NULL;
 }
 
--- a/src/http/modules/proxy/ngx_http_proxy_handler.h
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.h
@@ -32,6 +32,8 @@ typedef struct {
     ssize_t                     temp_file_write_size;
     int                         cyclic_temp_file;
 
+    int                         pass_server;
+
     int                         next_upstream;
 
     ngx_path_t                 *temp_path;
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -138,7 +138,7 @@ static ngx_command_t  ngx_http_core_comm
      NULL},
 
     {ngx_string("sendfile"),
-     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_core_loc_conf_t, sendfile),
@@ -173,7 +173,7 @@ static ngx_command_t  ngx_http_core_comm
      NULL},
 
     {ngx_string("msie_padding"),
-     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_core_loc_conf_t, msie_padding),
--- a/src/os/unix/ngx_freebsd_sendfile_chain.c
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c
@@ -25,8 +25,8 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(
 {
     int              rc, eintr, eagain;
     char            *prev;
+    off_t            sent, fprev;
     ssize_t          hsize, fsize, size;
-    off_t            sent, fprev;
     struct iovec    *iov;
     struct sf_hdtr   hdtr;
     ngx_err_t        err;
@@ -67,15 +67,14 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(
 
             if (prev == cl->hunk->pos) {
                 iov->iov_len += cl->hunk->last - cl->hunk->pos;
-                prev = cl->hunk->last;
 
             } else {
                 ngx_test_null(iov, ngx_push_array(&header), NGX_CHAIN_ERROR);
                 iov->iov_base = cl->hunk->pos;
                 iov->iov_len = cl->hunk->last - cl->hunk->pos;
-                prev = cl->hunk->last;
             }
 
+            prev = cl->hunk->last;
             hsize += cl->hunk->last - cl->hunk->pos;
         }
 
@@ -118,14 +117,14 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(
 
             if (prev == cl->hunk->pos) {
                 iov->iov_len += cl->hunk->last - cl->hunk->pos;
-                prev = cl->hunk->last;
 
             } else {
                 ngx_test_null(iov, ngx_push_array(&trailer), NGX_CHAIN_ERROR);
                 iov->iov_base = cl->hunk->pos;
                 iov->iov_len = cl->hunk->last - cl->hunk->pos;
-                prev = cl->hunk->last;
             }
+
+            prev = cl->hunk->last;
         }
 
         /*
@@ -182,6 +181,7 @@ ngx_log_debug(c->log, "NOPUSH");
                                   "sendfile() sent only %qd bytes", sent);
 
                 } else {
+                    c->write->error = 1;
                     ngx_log_error(NGX_LOG_CRIT, c->log, err,
                                   "sendfile() failed");
                     return NGX_CHAIN_ERROR;
@@ -206,6 +206,7 @@ ngx_log_debug(c->log, "NOPUSH");
                     ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EINTR");
 
                 } else {
+                    c->write->error = 1;
                     ngx_log_error(NGX_LOG_CRIT, c->log, err, "writev() failed");
                     return NGX_CHAIN_ERROR;
                 }
--- a/src/os/unix/ngx_readv_chain.c
+++ b/src/os/unix/ngx_readv_chain.c
@@ -6,23 +6,30 @@
 
 ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain)
 {
+    char          *prev;
     ssize_t        n;
     struct iovec  *iov;
     ngx_err_t      err;
     ngx_array_t    io;
 
-#if (NGX_SUPPRESS_WARN)
+    prev = NULL;
     iov = NULL;
-#endif
 
     ngx_init_array(io, c->pool, 10, sizeof(struct iovec), NGX_ERROR);
 
-    /* TODO: coalesce the neighbouring chain entries */
+    /* coalesce the neighbouring hunks */
 
     while (chain) {
-        ngx_test_null(iov, ngx_push_array(&io), NGX_ERROR);
-        iov->iov_base = chain->hunk->last;
-        iov->iov_len = chain->hunk->end - chain->hunk->last;
+        if (prev == chain->hunk->last) {
+            iov->iov_len += chain->hunk->end - chain->hunk->last;
+
+        } else {
+            ngx_test_null(iov, ngx_push_array(&io), NGX_ERROR);
+            iov->iov_base = chain->hunk->last;
+            iov->iov_len = chain->hunk->end - chain->hunk->last;
+        }
+
+        prev = chain->hunk->end;
         chain = chain->next;
     }
 
@@ -30,8 +37,12 @@ ngx_log_debug(c->log, "recv: %d:%d" _ io
 
     n = readv(c->fd, (struct iovec *) io.elts, io.nelts);
 
-    if (n == -1) {
+    if (n == 0) {
+        c->read->eof = 1;
+
+    } else if (n == -1) {
         c->read->ready = 0;
+        c->read->error = 1;
 
         err = ngx_errno;
         if (err == NGX_EAGAIN) {
--- a/src/os/unix/ngx_recv.c
+++ b/src/os/unix/ngx_recv.c
@@ -18,15 +18,19 @@ ssize_t ngx_unix_recv(ngx_connection_t *
 
     if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
         ngx_log_debug(c->log, "recv: eof:%d, avail:%d, err:%d" _
-                      rev->eof _ rev->available _ rev->error);
+                      rev->kq_eof _ rev->available _ rev->kq_errno);
 
         if (rev->available == 0) {
-            if (rev->eof) {
+            if (rev->kq_eof) {
                 rev->ready = 0;
-                if (rev->error) {
-                    ngx_set_socket_errno(rev->error);
-                    return ngx_unix_recv_error(rev, rev->error);
+                rev->eof = 1;
+
+                if (rev->kq_errno) {
+                    rev->error = 1;
+                    ngx_set_socket_errno(rev->kq_errno);
+                    return ngx_unix_recv_error(rev, rev->kq_errno);
                 }
+
                 return 0;
 
             } else {
@@ -43,8 +47,14 @@ ssize_t ngx_unix_recv(ngx_connection_t *
         if (n >= 0) {
             if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
                 rev->available -= n;
+
+                /*
+                 * rev->available can be negative here because some additional
+                 * bytes can be received between kevent() and recv()
+                 */
+
                 if (rev->available <= 0) {
-                    if (!rev->eof) {
+                    if (!rev->kq_eof) {
                         rev->ready = 0;
                     }
 
@@ -60,10 +70,15 @@ ssize_t ngx_unix_recv(ngx_connection_t *
                 rev->ready = 0;
             }
 
+            if (n == 0) {
+                rev->eof = 1;
+            }
+
             return n;
         }
 
         rev->ready = 0;
+        rev->error = 1;
         n = ngx_unix_recv_error(rev, ngx_socket_errno);
 
     } while (n == NGX_EINTR);
@@ -89,10 +104,16 @@ ssize_t ngx_unix_recv(ngx_connection_t *
             if ((size_t) n < size) {
                 rev->ready = 0;
             }
+
+            if (n == 0) {
+                rev->eof = 1;
+            }
+
             return n;
         }
 
         rev->ready = 0;
+        rev->error = 1;
         n = ngx_unix_recv_error(rev, ngx_socket_errno);
 
     } while (n == NGX_EINTR);