Line data Source code
1 : /*
2 : * lwan - web server
3 : * Copyright (c) 2013 L. A. F. Pereira <l@tia.mat.br>
4 : *
5 : * This program is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU General Public License
7 : * as published by the Free Software Foundation; either version 2
8 : * of the License, or any later version.
9 : *
10 : * This program is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : * GNU General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU General Public License
16 : * along with this program; if not, write to the Free Software
17 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 : * USA.
19 : */
20 :
21 : #include <assert.h>
22 : #include <errno.h>
23 : #include <fcntl.h>
24 : #include <sys/sendfile.h>
25 : #include <sys/socket.h>
26 : #include <sys/types.h>
27 : #include <unistd.h>
28 :
29 : #include "lwan-io-wrappers.h"
30 : #include "lwan-private.h"
31 :
32 : static const int MAX_FAILED_TRIES = 5;
33 :
34 : ssize_t
35 18 : lwan_writev(struct lwan_request *request, struct iovec *iov, int iov_count)
36 : {
37 18 : ssize_t total_written = 0;
38 18 : int curr_iov = 0;
39 18 : int flags = (request->conn->flags & CONN_CORK) ? MSG_MORE : 0;
40 :
41 18 : for (int tries = MAX_FAILED_TRIES; tries;) {
42 18 : const int remaining_len = (int)(iov_count - curr_iov);
43 : ssize_t written;
44 :
45 18 : if (remaining_len == 1) {
46 0 : const struct iovec *vec = &iov[curr_iov];
47 18 : return lwan_send(request, vec->iov_base, vec->iov_len, flags);
48 : }
49 :
50 18 : struct msghdr hdr = {
51 18 : .msg_iov = iov + curr_iov,
52 18 : .msg_iovlen = (size_t)remaining_len,
53 : };
54 18 : written = sendmsg(request->fd, &hdr, flags);
55 :
56 18 : if (UNLIKELY(written < 0)) {
57 : /* FIXME: Consider short writes as another try as well? */
58 0 : tries--;
59 :
60 0 : switch (errno) {
61 0 : case EAGAIN:
62 : case EINTR:
63 0 : goto try_again;
64 0 : default:
65 0 : goto out;
66 : }
67 : }
68 :
69 18 : total_written += written;
70 :
71 68 : while (curr_iov < iov_count &&
72 50 : written >= (ssize_t)iov[curr_iov].iov_len) {
73 50 : written -= (ssize_t)iov[curr_iov].iov_len;
74 50 : curr_iov++;
75 : }
76 :
77 18 : if (curr_iov == iov_count)
78 18 : return total_written;
79 :
80 0 : iov[curr_iov].iov_base = (char *)iov[curr_iov].iov_base + written;
81 0 : iov[curr_iov].iov_len -= (size_t)written;
82 :
83 0 : try_again:
84 0 : coro_yield(request->conn->coro, CONN_CORO_WANT_WRITE);
85 : }
86 :
87 0 : out:
88 0 : coro_yield(request->conn->coro, CONN_CORO_ABORT);
89 0 : __builtin_unreachable();
90 : }
91 :
92 : ssize_t
93 0 : lwan_readv(struct lwan_request *request, struct iovec *iov, int iov_count)
94 : {
95 0 : ssize_t total_bytes_read = 0;
96 0 : int curr_iov = 0;
97 :
98 0 : for (int tries = MAX_FAILED_TRIES; tries;) {
99 : ssize_t bytes_read =
100 0 : readv(request->fd, iov + curr_iov, iov_count - curr_iov);
101 0 : if (UNLIKELY(bytes_read < 0)) {
102 : /* FIXME: Consider short reads as another try as well? */
103 0 : tries--;
104 :
105 0 : switch (errno) {
106 0 : case EAGAIN:
107 : case EINTR:
108 0 : goto try_again;
109 0 : default:
110 0 : goto out;
111 : }
112 : }
113 :
114 0 : total_bytes_read += bytes_read;
115 :
116 0 : while (curr_iov < iov_count &&
117 0 : bytes_read >= (ssize_t)iov[curr_iov].iov_len) {
118 0 : bytes_read -= (ssize_t)iov[curr_iov].iov_len;
119 0 : curr_iov++;
120 : }
121 :
122 0 : if (curr_iov == iov_count)
123 0 : return total_bytes_read;
124 :
125 0 : iov[curr_iov].iov_base = (char *)iov[curr_iov].iov_base + bytes_read;
126 0 : iov[curr_iov].iov_len -= (size_t)bytes_read;
127 :
128 0 : try_again:
129 0 : coro_yield(request->conn->coro, CONN_CORO_WANT_READ);
130 : }
131 :
132 0 : out:
133 0 : coro_yield(request->conn->coro, CONN_CORO_ABORT);
134 0 : __builtin_unreachable();
135 : }
136 :
137 506 : ssize_t lwan_send(struct lwan_request *request,
138 : const void *buf,
139 : size_t count,
140 : int flags)
141 : {
142 506 : ssize_t total_sent = 0;
143 :
144 506 : if (request->conn->flags & CONN_CORK)
145 254 : flags |= MSG_MORE;
146 :
147 506 : for (int tries = MAX_FAILED_TRIES; tries;) {
148 506 : ssize_t written = send(request->fd, buf, count, flags);
149 503 : if (UNLIKELY(written < 0)) {
150 0 : tries--;
151 :
152 0 : switch (errno) {
153 0 : case EAGAIN:
154 : case EINTR:
155 0 : goto try_again;
156 0 : default:
157 0 : goto out;
158 : }
159 : }
160 :
161 503 : total_sent += written;
162 503 : if ((size_t)total_sent == count)
163 503 : return total_sent;
164 0 : if ((size_t)total_sent < count)
165 0 : buf = (char *)buf + written;
166 :
167 0 : try_again:
168 0 : coro_yield(request->conn->coro, CONN_CORO_WANT_WRITE);
169 : }
170 :
171 0 : out:
172 0 : coro_yield(request->conn->coro, CONN_CORO_ABORT);
173 0 : __builtin_unreachable();
174 : }
175 :
176 : ssize_t
177 0 : lwan_recv(struct lwan_request *request, void *buf, size_t count, int flags)
178 : {
179 0 : ssize_t total_recv = 0;
180 :
181 0 : for (int tries = MAX_FAILED_TRIES; tries;) {
182 0 : ssize_t recvd = recv(request->fd, buf, count, flags);
183 0 : if (UNLIKELY(recvd < 0)) {
184 0 : tries--;
185 :
186 0 : switch (errno) {
187 0 : case EAGAIN:
188 0 : if (flags & MSG_DONTWAIT)
189 0 : return total_recv;
190 : /* Fallthrough */
191 : case EINTR:
192 0 : goto try_again;
193 0 : default:
194 0 : goto out;
195 : }
196 : }
197 :
198 0 : total_recv += recvd;
199 0 : if ((size_t)total_recv == count)
200 0 : return total_recv;
201 0 : if ((size_t)total_recv < count)
202 0 : buf = (char *)buf + recvd;
203 :
204 0 : try_again:
205 0 : coro_yield(request->conn->coro, CONN_CORO_WANT_READ);
206 : }
207 :
208 0 : out:
209 0 : coro_yield(request->conn->coro, CONN_CORO_ABORT);
210 0 : __builtin_unreachable();
211 : }
212 :
213 : #if defined(__linux__)
214 5 : void lwan_sendfile(struct lwan_request *request,
215 : int in_fd,
216 : off_t offset,
217 : size_t count,
218 : const char *header,
219 : size_t header_len)
220 : {
221 : /* Clamp each chunk to 2^21 bytes[1] to balance throughput and
222 : * scalability. This used to be capped to 2^14 bytes, as that's the
223 : * maximum TLS record size[2], but was found to be hurtful for
224 : * performance[2], so use the same default value that Nginx uses.
225 : *
226 : * First chunk is clamped to 2^21 - header_len, because the header is
227 : * sent using MSG_MORE. Subsequent chunks are sized 2^21 bytes. (Do
228 : * this regardless of this connection being TLS or not for simplicity.)
229 : *
230 : * [1] https://www.kernel.org/doc/html/v5.12/networking/tls.html#sending-tls-application-data
231 : * [2] https://github.com/lpereira/lwan/issues/334
232 : */
233 5 : size_t chunk_size = LWAN_MIN(count, (1ul << 21) - header_len);
234 5 : size_t to_be_written = count;
235 :
236 5 : assert(header_len < (1ul << 21));
237 :
238 5 : lwan_send(request, header, header_len, MSG_MORE);
239 :
240 0 : while (true) {
241 5 : ssize_t written = sendfile(request->fd, in_fd, &offset, chunk_size);
242 5 : if (written < 0) {
243 0 : switch (errno) {
244 0 : case EAGAIN:
245 : case EINTR:
246 0 : goto try_again;
247 0 : default:
248 0 : coro_yield(request->conn->coro, CONN_CORO_ABORT);
249 0 : __builtin_unreachable();
250 : }
251 : }
252 :
253 5 : to_be_written -= (size_t)written;
254 5 : if (!to_be_written)
255 4 : break;
256 :
257 1 : chunk_size = LWAN_MIN(to_be_written, 1ul << 21);
258 1 : lwan_readahead_queue(in_fd, offset, chunk_size);
259 :
260 1 : try_again:
261 1 : coro_yield(request->conn->coro, CONN_CORO_WANT_WRITE);
262 : }
263 4 : }
264 : #elif defined(__FreeBSD__) || defined(__APPLE__)
265 : void lwan_sendfile(struct lwan_request *request,
266 : int in_fd,
267 : off_t offset,
268 : size_t count,
269 : const char *header,
270 : size_t header_len)
271 : {
272 : struct sf_hdtr headers = {.headers =
273 : (struct iovec[]){{.iov_base = (void *)header,
274 : .iov_len = header_len}},
275 : .hdr_cnt = 1};
276 : off_t sbytes = (off_t)count;
277 :
278 : if (!count) {
279 : /* FreeBSD's sendfile() won't send the headers when count is 0. Why? */
280 : return (void)lwan_writev(request, headers.headers, headers.hdr_cnt);
281 : }
282 :
283 : while (true) {
284 : int r;
285 :
286 : #ifdef __APPLE__
287 : r = sendfile(in_fd, request->fd, offset, &sbytes, &headers, 0);
288 : #else
289 : r = sendfile(in_fd, request->fd, offset, count, &headers, &sbytes,
290 : SF_MNOWAIT);
291 : #endif
292 :
293 : if (UNLIKELY(r < 0)) {
294 : switch (errno) {
295 : case EAGAIN:
296 : case EBUSY:
297 : case EINTR:
298 : goto try_again;
299 : default:
300 : coro_yield(request->conn->coro, CONN_CORO_ABORT);
301 : __builtin_unreachable();
302 : }
303 : }
304 :
305 : count -= (size_t)sbytes;
306 : if (!count)
307 : break;
308 :
309 : try_again:
310 : coro_yield(request->conn->coro, CONN_CORO_WANT_WRITE);
311 : }
312 : }
313 : #else
314 : static size_t try_pread_file(struct lwan_request *request,
315 : int fd,
316 : void *buffer,
317 : size_t len,
318 : off_t offset)
319 : {
320 : size_t total_read = 0;
321 :
322 : for (int tries = MAX_FAILED_TRIES; tries;) {
323 : ssize_t r = pread(fd, buffer, len, offset);
324 :
325 : if (UNLIKELY(r < 0)) {
326 : tries--;
327 :
328 : switch (errno) {
329 : case EAGAIN:
330 : case EINTR:
331 : /* fd is a file, re-read -- but give other coros some time, too */
332 : coro_yield(request->conn->coro, CONN_CORO_YIELD);
333 : continue;
334 : default:
335 : break;
336 : }
337 : }
338 :
339 : total_read += (size_t)r;
340 :
341 : if (r == 0 || total_read == len)
342 : return total_read;
343 :
344 : offset += r;
345 : }
346 :
347 : coro_yield(request->conn->coro, CONN_CORO_ABORT);
348 : __builtin_unreachable();
349 : }
350 :
351 : void lwan_sendfile(struct lwan_request *request,
352 : int in_fd,
353 : off_t offset,
354 : size_t count,
355 : const char *header,
356 : size_t header_len)
357 : {
358 : unsigned char buffer[512];
359 :
360 : lwan_send(request, header, header_len, MSG_MORE);
361 :
362 : while (count) {
363 : size_t bytes_read = try_pread_file(
364 : request, in_fd, buffer, LWAN_MIN(count, sizeof(buffer)), offset);
365 : lwan_send(request, buffer, bytes_read, 0);
366 : count -= bytes_read;
367 : offset += bytes_read;
368 : }
369 : }
370 : #endif
|