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 
13 ssize_t
ngx_readv_chain(ngx_connection_t * c,ngx_chain_t * chain,off_t limit)14 ngx_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) || (NGX_HAVE_FSTACK)
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) || (NGX_HAVE_FSTACK)
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) || (NGX_HAVE_FSTACK)
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