text: 
  /*
   * Copyright (C) Igor Sysoev
   * Copyright (C) Nginx, Inc.
   */
  
  
  #include <ngx_config.h>
  #include <ngx_core.h>
  #include <ngx_event.h>
  
  
  ssize_t
  ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit)
  {
      u_char        *prev;
      ssize_t        n, size;
      ngx_err_t      err;
      ngx_array_t    vec;
      ngx_event_t   *rev;
      struct iovec  *iov, iovs[NGX_IOVS_PREALLOCATE];
  
      rev = c->read;
  
  #if (NGX_HAVE_KQUEUE)
  
      if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
          ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
                         "readv: eof:%d, avail:%d, err:%d",
                         rev->pending_eof, rev->available, rev->kq_errno);
  
          if (rev->available == 0) {
              if (rev->pending_eof) {
                  rev->ready = 0;
                  rev->eof = 1;
  
                  ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,
                                "kevent() reported about an closed connection");
  
                  if (rev->kq_errno) {
                      rev->error = 1;
                      ngx_set_socket_errno(rev->kq_errno);
                      return NGX_ERROR;
                  }
  
                  return 0;
  
              } else {
                  return NGX_AGAIN;
              }
          }
      }
  
  #endif
  
      prev = NULL;
      iov = NULL;
      size = 0;
  
      vec.elts = iovs;
      vec.nelts = 0;
      vec.size = sizeof(struct iovec);
      vec.nalloc = NGX_IOVS_PREALLOCATE;
      vec.pool = c->pool;
  
      /* coalesce the neighbouring bufs */
  
      while (chain) {
          n = chain->buf->end - chain->buf->last;
  
          if (limit) {
              if (size >= limit) {
                  break;
              }
  
              if (size + n > limit) {
                  n = (ssize_t) (limit - size);
              }
          }
  
          if (prev == chain->buf->last) {
              iov->iov_len += n;
  
          } else {
              if (vec.nelts >= IOV_MAX) {
                  break;
              }
  
              iov = ngx_array_push(&vec);
              if (iov == NULL) {
                  return NGX_ERROR;
              }
  
              iov->iov_base = (void *) chain->buf->last;
              iov->iov_len = n;
          }
  
          size += n;
          prev = chain->buf->end;
          chain = chain->next;
      }
  
      ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                     "readv: %d, last:%d", vec.nelts, iov->iov_len);
  
      do {
          n = readv(c->fd, (struct iovec *) vec.elts, vec.nelts);
  
          if (n >= 0) {
  
  #if (NGX_HAVE_KQUEUE)
  
              if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
                  rev->available -= n;
  
                  /*
                   * rev->available may be negative here because some additional
                   * bytes may be received between kevent() and recv()
                   */
  
                  if (rev->available <= 0) {
                      if (!rev->pending_eof) {
                          rev->ready = 0;
                      }
  
                      if (rev->available < 0) {
                          rev->available = 0;
                      }
                  }
  
                  if (n == 0) {
  
                      /*
                       * on FreeBSD recv() may return 0 on closed socket
                       * even if kqueue reported about available data
                       */
  
  #if 0
                      ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                                    "readv() returned 0 while kevent() reported "
                                    "%d available bytes", rev->available);
  #endif
  
                      rev->ready = 0;
                      rev->eof = 1;
                      rev->available = 0;
                  }
  
                  return n;
              }
  
  #endif /* NGX_HAVE_KQUEUE */
  
              if (n < size && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) {
                  rev->ready = 0;
              }
  
              if (n == 0) {
                  rev->eof = 1;
              }
  
              return n;
          }
  
          err = ngx_socket_errno;
  
          if (err == NGX_EAGAIN || err == NGX_EINTR) {
              ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                             "readv() not ready");
              n = NGX_AGAIN;
  
          } else {
              n = ngx_connection_error(c, err, "readv() failed");
              break;
          }
  
      } while (err == NGX_EINTR);
  
      rev->ready = 0;
  
      if (n == NGX_ERROR) {
          c->read->error = 1;
      }
  
      return n;
  }
  