1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_event.h>
11
12
13ssize_t
14ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit)
15{
16    u_char        *prev;
17    ssize_t        n, size;
18    ngx_err_t      err;
19    ngx_array_t    vec;
20    ngx_event_t   *rev;
21    struct iovec  *iov, iovs[NGX_IOVS_PREALLOCATE];
22
23    rev = c->read;
24
25#if (NGX_HAVE_KQUEUE)
26
27    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
28        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
29                       "readv: eof:%d, avail:%d, err:%d",
30                       rev->pending_eof, rev->available, rev->kq_errno);
31
32        if (rev->available == 0) {
33            if (rev->pending_eof) {
34                rev->ready = 0;
35                rev->eof = 1;
36
37                ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,
38                              "kevent() reported about an closed connection");
39
40                if (rev->kq_errno) {
41                    rev->error = 1;
42                    ngx_set_socket_errno(rev->kq_errno);
43                    return NGX_ERROR;
44                }
45
46                return 0;
47
48            } else {
49                return NGX_AGAIN;
50            }
51        }
52    }
53
54#endif
55
56#if (NGX_HAVE_EPOLLRDHUP)
57
58    if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {
59        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
60                       "readv: eof:%d, avail:%d",
61                       rev->pending_eof, rev->available);
62
63        if (!rev->available && !rev->pending_eof) {
64            return NGX_AGAIN;
65        }
66    }
67
68#endif
69
70    prev = NULL;
71    iov = NULL;
72    size = 0;
73
74    vec.elts = iovs;
75    vec.nelts = 0;
76    vec.size = sizeof(struct iovec);
77    vec.nalloc = NGX_IOVS_PREALLOCATE;
78    vec.pool = c->pool;
79
80    /* coalesce the neighbouring bufs */
81
82    while (chain) {
83        n = chain->buf->end - chain->buf->last;
84
85        if (limit) {
86            if (size >= limit) {
87                break;
88            }
89
90            if (size + n > limit) {
91                n = (ssize_t) (limit - size);
92            }
93        }
94
95        if (prev == chain->buf->last) {
96            iov->iov_len += n;
97
98        } else {
99            if (vec.nelts >= IOV_MAX) {
100                break;
101            }
102
103            iov = ngx_array_push(&vec);
104            if (iov == NULL) {
105                return NGX_ERROR;
106            }
107
108            iov->iov_base = (void *) chain->buf->last;
109            iov->iov_len = n;
110        }
111
112        size += n;
113        prev = chain->buf->end;
114        chain = chain->next;
115    }
116
117    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
118                   "readv: %ui, last:%uz", vec.nelts, iov->iov_len);
119
120    do {
121        n = readv(c->fd, (struct iovec *) vec.elts, vec.nelts);
122
123        if (n == 0) {
124            rev->ready = 0;
125            rev->eof = 1;
126
127#if (NGX_HAVE_KQUEUE)
128
129            /*
130             * on FreeBSD readv() may return 0 on closed socket
131             * even if kqueue reported about available data
132             */
133
134            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
135                rev->available = 0;
136            }
137
138#endif
139
140            return 0;
141        }
142
143        if (n > 0) {
144
145#if (NGX_HAVE_KQUEUE)
146
147            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
148                rev->available -= n;
149
150                /*
151                 * rev->available may be negative here because some additional
152                 * bytes may be received between kevent() and readv()
153                 */
154
155                if (rev->available <= 0) {
156                    if (!rev->pending_eof) {
157                        rev->ready = 0;
158                    }
159
160                    rev->available = 0;
161                }
162
163                return n;
164            }
165
166#endif
167
168#if (NGX_HAVE_EPOLLRDHUP)
169
170            if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)
171                && ngx_use_epoll_rdhup)
172            {
173                if (n < size) {
174                    if (!rev->pending_eof) {
175                        rev->ready = 0;
176                    }
177
178                    rev->available = 0;
179                }
180
181                return n;
182            }
183
184#endif
185
186            if (n < size && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) {
187                rev->ready = 0;
188            }
189
190            return n;
191        }
192
193        err = ngx_socket_errno;
194
195        if (err == NGX_EAGAIN || err == NGX_EINTR) {
196            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
197                           "readv() not ready");
198            n = NGX_AGAIN;
199
200        } else {
201            n = ngx_connection_error(c, err, "readv() failed");
202            break;
203        }
204
205    } while (err == NGX_EINTR);
206
207    rev->ready = 0;
208
209    if (n == NGX_ERROR) {
210        c->read->error = 1;
211    }
212
213    return n;
214}
215