diff options
Diffstat (limited to 'src/microhttpd/mhd_send.c')
-rw-r--r-- | src/microhttpd/mhd_send.c | 637 |
1 files changed, 637 insertions, 0 deletions
diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c new file mode 100644 index 00000000..928e92cb --- /dev/null +++ b/src/microhttpd/mhd_send.c | |||
@@ -0,0 +1,637 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2019 ng0 <ng0@n0.is> | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library 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 GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | |||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file microhttpd/mhd_send.c | ||
23 | * @brief Implementation of send() wrappers. | ||
24 | * @author ng0 (N. Gillmann) | ||
25 | * @author Christian Grothoff | ||
26 | * @author Evgeny Grin | ||
27 | */ | ||
28 | |||
29 | /* Worth considering for future improvements and additions: | ||
30 | * NetBSD has no sendfile or sendfile64. The way to work | ||
31 | * with this seems to be to mmap the file and write(2) as | ||
32 | * large a chunk as possible to the socket. Alternatively, | ||
33 | * use madvise(..., MADV_SEQUENTIAL). */ | ||
34 | |||
35 | /* Functions to be used in: send_param_adapter, MHD_send_ | ||
36 | * and every place where sendfile(), sendfile64(), setsockopt() | ||
37 | * are used. */ | ||
38 | |||
39 | #include "mhd_send.h" | ||
40 | |||
41 | /** | ||
42 | * Handle setsockopt calls. | ||
43 | * | ||
44 | * @param connection the MHD_Connection structure | ||
45 | * @param want_cork cork state, boolean | ||
46 | */ | ||
47 | static void | ||
48 | pre_cork_setsockopt (struct MHD_Connection *connection, | ||
49 | bool want_cork) | ||
50 | { | ||
51 | #if HAVE_MSG_MORE | ||
52 | /* We use the MSG_MORE option for corking, no need for extra syscalls! */ | ||
53 | #elif defined(MHD_TCP_CORK_NOPUSH) | ||
54 | int ret; | ||
55 | |||
56 | /* If sk_cork_on is already what we pass in, return. */ | ||
57 | if (connection->sk_cork_on == want_cork) | ||
58 | { | ||
59 | /* nothing to do, success! */ | ||
60 | return; | ||
61 | } | ||
62 | if (! want_cork) | ||
63 | return; /* nothing to do *pre* syscall! */ | ||
64 | ret = MHD_socket_cork_ (connection->socket_fd, | ||
65 | true); | ||
66 | if (0 == ret) | ||
67 | { | ||
68 | connection->sk_cork_on = true; | ||
69 | return; | ||
70 | } | ||
71 | switch (errno) | ||
72 | { | ||
73 | case ENOTSOCK: | ||
74 | /* FIXME: Could be we are talking to a pipe, maybe remember this | ||
75 | and avoid all setsockopt() in the future? */ | ||
76 | break; | ||
77 | case EBADF: | ||
78 | /* FIXME: should we die hard here? */ | ||
79 | break; | ||
80 | case EINVAL: | ||
81 | /* FIXME: optlen invalid, should at least log this, maybe die */ | ||
82 | break; | ||
83 | case EFAULT: | ||
84 | /* wopsie, should at leats log this, FIXME: maybe die */ | ||
85 | #ifdef HAVE_MESSAGES | ||
86 | MHD_DLOG (daemon, | ||
87 | _("The addresss pointed to by optval is not a valid part of the process address space: %s\n"), | ||
88 | MHD_socket_last_strerr_()); | ||
89 | #endif | ||
90 | break; | ||
91 | case ENOPROTOOPT: | ||
92 | /* optlen unknown, should at least log this */ | ||
93 | #ifdef HAVE_MESSAGES | ||
94 | MHD_DLOG (daemon, | ||
95 | _("The option is unknown: %s\n"), | ||
96 | MHD_socket_last_strerr_()); | ||
97 | #endif | ||
98 | break; | ||
99 | default: | ||
100 | /* any others? man page does not list more... */ | ||
101 | break; | ||
102 | } | ||
103 | #else | ||
104 | /* CORK/NOPUSH/MSG_MORE do not exist on this platform, | ||
105 | so we must toggle Naggle's algorithm on/off instead | ||
106 | (otherwise we keep it always off) */ | ||
107 | if (connection->sk_cork_on == want_cork) | ||
108 | { | ||
109 | /* nothing to do, success! */ | ||
110 | return; | ||
111 | } | ||
112 | if ( (want_cork) && | ||
113 | (0 == MHD_socket_set_nodelay_ (connection->socket_fd, | ||
114 | false)) ) | ||
115 | connection->sk_cork_on = true; | ||
116 | #endif | ||
117 | } | ||
118 | |||
119 | |||
120 | /** | ||
121 | * Handle setsockopt calls. | ||
122 | * | ||
123 | * @param connection the MHD_Connection structure | ||
124 | * @param want_cork cork state, boolean | ||
125 | */ | ||
126 | static void | ||
127 | post_cork_setsockopt (struct MHD_Connection *connection, | ||
128 | bool want_cork) | ||
129 | { | ||
130 | #if HAVE_MSG_MORE | ||
131 | /* We use the MSG_MORE option for corking, no need for extra syscalls! */ | ||
132 | #elif defined(MHD_TCP_CORK_NOPUSH) | ||
133 | int ret; | ||
134 | |||
135 | /* If sk_cork_on is already what we pass in, return. */ | ||
136 | if (connection->sk_cork_on == want_cork) | ||
137 | { | ||
138 | /* nothing to do, success! */ | ||
139 | return; | ||
140 | } | ||
141 | if (want_cork) | ||
142 | return; /* nothing to do *post* syscall (in fact, we should never | ||
143 | get here, as sk_cork_on should have succeeded in the | ||
144 | pre-syscall) */ | ||
145 | ret = MHD_socket_cork_ (connection->socket_fd, | ||
146 | false); | ||
147 | if (0 == ret) | ||
148 | { | ||
149 | connection->sk_cork_on = false; | ||
150 | return; | ||
151 | } | ||
152 | switch (errno) | ||
153 | { | ||
154 | case ENOTSOCK: | ||
155 | /* FIXME: Could be we are talking to a pipe, maybe remember this | ||
156 | and avoid all setsockopt() in the future? */ | ||
157 | break; | ||
158 | case EBADF: | ||
159 | /* FIXME: should we die hard here? */ | ||
160 | break; | ||
161 | case EINVAL: | ||
162 | /* FIXME: optlen invalid, should at least log this, maybe die */ | ||
163 | break; | ||
164 | case EFAULT: | ||
165 | /* wopsie, should at leats log this, FIXME: maybe die */ | ||
166 | #ifdef HAVE_MESSAGES | ||
167 | MHD_DLOG (daemon, | ||
168 | _("The addresss pointed to by optval is not a valid part of the process address space: %s\n"), | ||
169 | MHD_socket_last_strerr_()); | ||
170 | #endif | ||
171 | break; | ||
172 | case ENOPROTOOPT: | ||
173 | /* optlen unknown, should at least log this */ | ||
174 | #ifdef HAVE_MESSAGES | ||
175 | MHD_DLOG (daemon, | ||
176 | _("The option is unknown: %s\n"), | ||
177 | MHD_socket_last_strerr_()); | ||
178 | #endif | ||
179 | break; | ||
180 | default: | ||
181 | /* any others? man page does not list more... */ | ||
182 | break; | ||
183 | } | ||
184 | #else | ||
185 | /* CORK/NOPUSH/MSG_MORE do not exist on this platform, | ||
186 | so we must toggle Naggle's algorithm on/off instead | ||
187 | (otherwise we keep it always off) */ | ||
188 | if (connection->sk_cork_on == want_cork) | ||
189 | { | ||
190 | /* nothing to do, success! */ | ||
191 | return; | ||
192 | } | ||
193 | if ( (! want_cork) && | ||
194 | (0 == MHD_socket_set_nodelay_ (connection->socket_fd, | ||
195 | true)) ) | ||
196 | connection->sk_cork_on = false; | ||
197 | #endif | ||
198 | } | ||
199 | |||
200 | |||
201 | /** | ||
202 | * Send buffer on connection, and remember the current state of | ||
203 | * the socket options; only call setsockopt when absolutely | ||
204 | * necessary. | ||
205 | * | ||
206 | * @param connection the MHD_Connection structure | ||
207 | * @param buffer content of the buffer to send | ||
208 | * @param buffer_size the size of the buffer (in bytes) | ||
209 | * @param options the #MHD_SendSocketOptions enum, | ||
210 | * #MHD_SSO_NO_CORK: definitely no corking (use NODELAY, or explicitly disable cork), | ||
211 | * #MHD_SSO_MAY_CORK: should enable corking (use MSG_MORE, or explicitly enable cork), | ||
212 | * #MHD_SSO_HDR_CORK: consider tcpi_snd_mss and consider not corking for the header | ||
213 | * part if the size of the header is close to the MSS. | ||
214 | * Only used if we are NOT doing 100 Continue and are still sending the | ||
215 | * header (provided in full as the buffer to #MHD_send_on_connection_ or as | ||
216 | * the header to #MHD_send_on_connection2_). | ||
217 | * @return sum of the number of bytes sent from both buffers or | ||
218 | * -1 on error | ||
219 | */ | ||
220 | ssize_t | ||
221 | MHD_send_on_connection_ (struct MHD_Connection *connection, | ||
222 | const char *buffer, | ||
223 | size_t buffer_size, | ||
224 | enum MHD_SendSocketOptions options) | ||
225 | { | ||
226 | bool want_cork; | ||
227 | MHD_socket s = connection->socket_fd; | ||
228 | ssize_t ret; | ||
229 | |||
230 | /* error handling from send_param_adapter() */ | ||
231 | if ((MHD_INVALID_SOCKET == s) || (MHD_CONNECTION_CLOSED == connection->state)) | ||
232 | { | ||
233 | return MHD_ERR_NOTCONN_; | ||
234 | } | ||
235 | |||
236 | /* from send_param_adapter() */ | ||
237 | if (buffer_size > MHD_SCKT_SEND_MAX_SIZE_) | ||
238 | buffer_size = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */ | ||
239 | |||
240 | /* Get socket options, change/set options if necessary. */ | ||
241 | switch (options) | ||
242 | { | ||
243 | /* No corking */ | ||
244 | case MHD_SSO_NO_CORK: | ||
245 | want_cork = false; | ||
246 | break; | ||
247 | /* Do corking, consider MSG_MORE instead if available. */ | ||
248 | case MHD_SSO_MAY_CORK: | ||
249 | want_cork = true; | ||
250 | break; | ||
251 | /* Cork the header. */ | ||
252 | case MHD_SSO_HDR_CORK: | ||
253 | want_cork = (buffer_size <= 1024); | ||
254 | break; | ||
255 | } | ||
256 | |||
257 | #ifdef HTTPS_SUPPORT | ||
258 | if (0 != (connection->daemon->options & MHD_USE_TLS)) | ||
259 | { | ||
260 | bool have_cork = connection->sk_cork_on; | ||
261 | |||
262 | if (want_cork && ! have_cork) | ||
263 | { | ||
264 | gnutls_record_cork (connection->tls_session); | ||
265 | connection->sk_cork_on = true; | ||
266 | } | ||
267 | if (buffer_size > SSIZE_MAX) | ||
268 | buffer_size = SSIZE_MAX; | ||
269 | ret = gnutls_record_send (connection->tls_session, | ||
270 | buffer, | ||
271 | buffer_size); | ||
272 | if ( (GNUTLS_E_AGAIN == ret) || | ||
273 | (GNUTLS_E_INTERRUPTED == ret) ) | ||
274 | { | ||
275 | #ifdef EPOLL_SUPPORT | ||
276 | if (GNUTLS_E_AGAIN == ret) | ||
277 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
278 | #endif | ||
279 | return MHD_ERR_AGAIN_; | ||
280 | } | ||
281 | if (ret < 0) | ||
282 | { | ||
283 | /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication | ||
284 | disrupted); interpret as a hard error */ | ||
285 | return MHD_ERR_NOTCONN_; | ||
286 | } | ||
287 | #ifdef EPOLL_SUPPORT | ||
288 | /* Unlike non-TLS connections, do not reset "write-ready" if | ||
289 | * sent amount smaller than provided amount, as TLS | ||
290 | * connections may break data into smaller parts for sending. */ | ||
291 | #endif /* EPOLL_SUPPORT */ | ||
292 | |||
293 | if (! want_cork && have_cork) | ||
294 | { | ||
295 | (void) gnutls_record_uncork (connection->tls_session, 0); | ||
296 | connection->sk_cork_on = false; | ||
297 | } | ||
298 | } | ||
299 | else | ||
300 | #endif /* HTTPS_SUPPORT */ | ||
301 | { | ||
302 | /* plaintext transmission */ | ||
303 | pre_cork_setsockopt (connection, want_cork); | ||
304 | #if HAVE_MSG_MORE | ||
305 | ret = send (s, | ||
306 | buffer, | ||
307 | buffer_size, | ||
308 | MAYBE_MSG_NOSIGNAL | (want_cork ? MSG_MORE : 0)); | ||
309 | #else | ||
310 | ret = send (connection->socket_fd, | ||
311 | buffer, | ||
312 | buffer_size, | ||
313 | MAYBE_MSG_NOSIGNAL); | ||
314 | #endif | ||
315 | |||
316 | if (0 > ret) | ||
317 | { | ||
318 | const int err = MHD_socket_get_error_ (); | ||
319 | |||
320 | if (MHD_SCKT_ERR_IS_EAGAIN_ (err)) | ||
321 | { | ||
322 | #if EPOLL_SUPPORT | ||
323 | /* EAGAIN, no longer write-ready */ | ||
324 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
325 | #endif /* EPOLL_SUPPORT */ | ||
326 | return MHD_ERR_AGAIN_; | ||
327 | } | ||
328 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
329 | return MHD_ERR_AGAIN_; | ||
330 | if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ECONNRESET_)) | ||
331 | return MHD_ERR_CONNRESET_; | ||
332 | /* Treat any other error as hard error. */ | ||
333 | return MHD_ERR_NOTCONN_; | ||
334 | } | ||
335 | #if EPOLL_SUPPORT | ||
336 | else if (buffer_size > (size_t) ret) | ||
337 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
338 | #endif /* EPOLL_SUPPORT */ | ||
339 | if (ret == buffer_size) | ||
340 | post_cork_setsockopt (connection, want_cork); | ||
341 | } | ||
342 | |||
343 | return ret; | ||
344 | } | ||
345 | |||
346 | |||
347 | /** | ||
348 | * Send header followed by buffer on connection. | ||
349 | * Uses writev if possible to send both at once | ||
350 | * and returns the sum of the number of bytes sent from | ||
351 | * both buffers, or -1 on error; | ||
352 | * if writev is unavailable, this call MUST only send from 'header' | ||
353 | * (as we cannot handle the case that the first write | ||
354 | * succeeds and the 2nd fails!). | ||
355 | * | ||
356 | * @param connection the MHD_Connection structure | ||
357 | * @param header content of header to send | ||
358 | * @param header_size the size of the header (in bytes) | ||
359 | * @param buffer content of the buffer to send | ||
360 | * @param buffer_size the size of the buffer (in bytes) | ||
361 | * @return sum of the number of bytes sent from both buffers or | ||
362 | * -1 on error | ||
363 | */ | ||
364 | ssize_t | ||
365 | MHD_send_on_connection2_ (struct MHD_Connection *connection, | ||
366 | const char *header, | ||
367 | size_t header_size, | ||
368 | const char *buffer, | ||
369 | size_t buffer_size) | ||
370 | { | ||
371 | #ifdef HTTPS_SUPPORT | ||
372 | if (0 != (connection->daemon->options & MHD_USE_TLS)) | ||
373 | return MHD_send_on_connection_ (connection, | ||
374 | header, | ||
375 | header_size, | ||
376 | MHD_SSO_HDR_CORK); | ||
377 | #endif | ||
378 | #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) | ||
379 | MHD_socket s = connection->socket_fd; | ||
380 | ssize_t ret; | ||
381 | struct iovec vector[2]; | ||
382 | |||
383 | /* Since we generally give the fully answer, we do not want | ||
384 | corking to happen */ | ||
385 | pre_cork_setsockopt (connection, false); | ||
386 | |||
387 | vector[0].iov_base = (void *) header; | ||
388 | vector[0].iov_len = header_size; | ||
389 | vector[1].iov_base = (void *) buffer; | ||
390 | vector[1].iov_len = buffer_size; | ||
391 | |||
392 | #if HAVE_SENDMSG | ||
393 | { | ||
394 | struct msghdr msg; | ||
395 | |||
396 | memset(&msg, 0, sizeof(struct msghdr)); | ||
397 | msg.msg_iov = vector; | ||
398 | msg.msg_iovlen = 2; | ||
399 | |||
400 | ret = sendmsg (s, &msg, MAYBE_MSG_NOSIGNAL); | ||
401 | } | ||
402 | #elif HAVE_WRITEV | ||
403 | { | ||
404 | int iovcnt; | ||
405 | |||
406 | iovcnt = sizeof (vector) / sizeof (struct iovec); | ||
407 | ret = writev (s, vector, iovcnt); | ||
408 | } | ||
409 | #endif | ||
410 | |||
411 | /* Only if we succeeded sending the full buffer, we need to make sure that | ||
412 | the OS flushes at the end */ | ||
413 | if (ret == header_size + buffer_size) | ||
414 | post_cork_setsockopt (connection, false); | ||
415 | |||
416 | return ret; | ||
417 | |||
418 | #else | ||
419 | return MHD_send_on_connection_ (connection, | ||
420 | header, | ||
421 | header_size, | ||
422 | MHD_SSO_HDR_CORK); | ||
423 | #endif | ||
424 | } | ||
425 | |||
426 | /** | ||
427 | * sendfile() chuck size | ||
428 | */ | ||
429 | #define MHD_SENFILE_CHUNK_ (0x20000) | ||
430 | |||
431 | /** | ||
432 | * sendfile() chuck size for thread-per-connection | ||
433 | */ | ||
434 | #define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000) | ||
435 | |||
436 | #ifdef HAVE_FREEBSD_SENDFILE | ||
437 | #ifdef SF_FLAGS | ||
438 | /** | ||
439 | * FreeBSD sendfile() flags | ||
440 | */ | ||
441 | static int freebsd_sendfile_flags_; | ||
442 | |||
443 | /** | ||
444 | * FreeBSD sendfile() flags for thread-per-connection | ||
445 | */ | ||
446 | static int freebsd_sendfile_flags_thd_p_c_; | ||
447 | #endif /* SF_FLAGS */ | ||
448 | |||
449 | #endif /* HAVE_FREEBSD_SENDFILE */ | ||
450 | |||
451 | #if defined(_MHD_HAVE_SENDFILE) | ||
452 | /** | ||
453 | * Function for sending responses backed by file FD. | ||
454 | * | ||
455 | * @param connection the MHD connection structure | ||
456 | * @return actual number of bytes sent | ||
457 | */ | ||
458 | ssize_t | ||
459 | MHD_send_sendfile_ (struct MHD_Connection *connection) | ||
460 | { | ||
461 | ssize_t ret; | ||
462 | const int file_fd = connection->response->fd; | ||
463 | uint64_t left; | ||
464 | uint64_t offsetu64; | ||
465 | #ifndef HAVE_SENDFILE64 | ||
466 | const uint64_t max_off_t = (uint64_t)OFF_T_MAX; | ||
467 | #else /* HAVE_SENDFILE64 */ | ||
468 | const uint64_t max_off_t = (uint64_t)OFF64_T_MAX; | ||
469 | #endif /* HAVE_SENDFILE64 */ | ||
470 | #ifdef MHD_LINUX_SOLARIS_SENDFILE | ||
471 | #ifndef HAVE_SENDFILE64 | ||
472 | off_t offset; | ||
473 | #else /* HAVE_SENDFILE64 */ | ||
474 | off64_t offset; | ||
475 | #endif /* HAVE_SENDFILE64 */ | ||
476 | #endif /* MHD_LINUX_SOLARIS_SENDFILE */ | ||
477 | #ifdef HAVE_FREEBSD_SENDFILE | ||
478 | off_t sent_bytes; | ||
479 | int flags = 0; | ||
480 | #endif | ||
481 | #ifdef HAVE_DARWIN_SENDFILE | ||
482 | off_t len; | ||
483 | #endif /* HAVE_DARWIN_SENDFILE */ | ||
484 | const bool used_thr_p_c = (0 != (connection->daemon->options & MHD_USE_THREAD_PER_CONNECTION)); | ||
485 | const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : MHD_SENFILE_CHUNK_; | ||
486 | size_t send_size = 0; | ||
487 | mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender); | ||
488 | |||
489 | pre_cork_setsockopt (connection, false); | ||
490 | |||
491 | offsetu64 = connection->response_write_position + connection->response->fd_off; | ||
492 | left = connection->response->total_size - connection->response_write_position; | ||
493 | /* Do not allow system to stick sending on single fast connection: | ||
494 | * use 128KiB chunks (2MiB for thread-per-connection). */ | ||
495 | send_size = (left > chunk_size) ? chunk_size : (size_t) left; | ||
496 | if (max_off_t < offsetu64) | ||
497 | { /* Retry to send with standard 'send()'. */ | ||
498 | connection->resp_sender = MHD_resp_sender_std; | ||
499 | return MHD_ERR_AGAIN_; | ||
500 | } | ||
501 | #ifdef MHD_LINUX_SOLARIS_SENDFILE | ||
502 | #ifndef HAVE_SENDFILE64 | ||
503 | offset = (off_t) offsetu64; | ||
504 | ret = sendfile (connection->socket_fd, | ||
505 | file_fd, | ||
506 | &offset, | ||
507 | send_size); | ||
508 | #else /* HAVE_SENDFILE64 */ | ||
509 | offset = (off64_t) offsetu64; | ||
510 | ret = sendfile64 (connection->socket_fd, | ||
511 | file_fd, | ||
512 | &offset, | ||
513 | send_size); | ||
514 | #endif /* HAVE_SENDFILE64 */ | ||
515 | if (0 > ret) | ||
516 | { | ||
517 | const int err = MHD_socket_get_error_(); | ||
518 | if (MHD_SCKT_ERR_IS_EAGAIN_(err)) | ||
519 | { | ||
520 | #ifdef EPOLL_SUPPORT | ||
521 | /* EAGAIN --- no longer write-ready */ | ||
522 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
523 | #endif /* EPOLL_SUPPORT */ | ||
524 | return MHD_ERR_AGAIN_; | ||
525 | } | ||
526 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
527 | return MHD_ERR_AGAIN_; | ||
528 | #ifdef HAVE_LINUX_SENDFILE | ||
529 | if (MHD_SCKT_ERR_IS_(err, | ||
530 | MHD_SCKT_EBADF_)) | ||
531 | return MHD_ERR_BADF_; | ||
532 | /* sendfile() failed with EINVAL if mmap()-like operations are not | ||
533 | supported for FD or other 'unusual' errors occurred, so we should try | ||
534 | to fall back to 'SEND'; see also this thread for info on | ||
535 | odd libc/Linux behavior with sendfile: | ||
536 | http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */ | ||
537 | connection->resp_sender = MHD_resp_sender_std; | ||
538 | return MHD_ERR_AGAIN_; | ||
539 | #else /* HAVE_SOLARIS_SENDFILE */ | ||
540 | if ( (EAFNOSUPPORT == err) || | ||
541 | (EINVAL == err) || | ||
542 | (EOPNOTSUPP == err) ) | ||
543 | { /* Retry with standard file reader. */ | ||
544 | connection->resp_sender = MHD_resp_sender_std; | ||
545 | return MHD_ERR_AGAIN_; | ||
546 | } | ||
547 | if ( (ENOTCONN == err) || | ||
548 | (EPIPE == err) ) | ||
549 | { | ||
550 | return MHD_ERR_CONNRESET_; | ||
551 | } | ||
552 | return MHD_ERR_BADF_; /* Fail hard */ | ||
553 | #endif /* HAVE_SOLARIS_SENDFILE */ | ||
554 | } | ||
555 | #ifdef EPOLL_SUPPORT | ||
556 | else if (send_size > (size_t)ret) | ||
557 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
558 | #endif /* EPOLL_SUPPORT */ | ||
559 | #elif defined(HAVE_FREEBSD_SENDFILE) | ||
560 | #ifdef SF_FLAGS | ||
561 | flags = used_thr_p_c ? | ||
562 | freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_; | ||
563 | #endif /* SF_FLAGS */ | ||
564 | if (0 != sendfile (file_fd, | ||
565 | connection->socket_fd, | ||
566 | (off_t) offsetu64, | ||
567 | send_size, | ||
568 | NULL, | ||
569 | &sent_bytes, | ||
570 | flags)) | ||
571 | { | ||
572 | const int err = MHD_socket_get_error_(); | ||
573 | if (MHD_SCKT_ERR_IS_EAGAIN_(err) || | ||
574 | MHD_SCKT_ERR_IS_EINTR_(err) || | ||
575 | EBUSY == err) | ||
576 | { | ||
577 | mhd_assert (SSIZE_MAX >= sent_bytes); | ||
578 | if (0 != sent_bytes) | ||
579 | return (ssize_t)sent_bytes; | ||
580 | |||
581 | return MHD_ERR_AGAIN_; | ||
582 | } | ||
583 | /* Some unrecoverable error. Possibly file FD is not suitable | ||
584 | * for sendfile(). Retry with standard send(). */ | ||
585 | connection->resp_sender = MHD_resp_sender_std; | ||
586 | return MHD_ERR_AGAIN_; | ||
587 | } | ||
588 | mhd_assert (0 < sent_bytes); | ||
589 | mhd_assert (SSIZE_MAX >= sent_bytes); | ||
590 | ret = (ssize_t)sent_bytes; | ||
591 | #elif defined(HAVE_DARWIN_SENDFILE) | ||
592 | len = (off_t)send_size; /* chunk always fit */ | ||
593 | if (0 != sendfile (file_fd, | ||
594 | connection->socket_fd, | ||
595 | (off_t) offsetu64, | ||
596 | &len, | ||
597 | NULL, | ||
598 | 0)) | ||
599 | { | ||
600 | const int err = MHD_socket_get_error_(); | ||
601 | if (MHD_SCKT_ERR_IS_EAGAIN_(err) || | ||
602 | MHD_SCKT_ERR_IS_EINTR_(err)) | ||
603 | { | ||
604 | mhd_assert (0 <= len); | ||
605 | mhd_assert (SSIZE_MAX >= len); | ||
606 | mhd_assert (send_size >= (size_t)len); | ||
607 | if (0 != len) | ||
608 | return (ssize_t)len; | ||
609 | |||
610 | return MHD_ERR_AGAIN_; | ||
611 | } | ||
612 | if (ENOTCONN == err || | ||
613 | EPIPE == err) | ||
614 | return MHD_ERR_CONNRESET_; | ||
615 | if (ENOTSUP == err || | ||
616 | EOPNOTSUPP == err) | ||
617 | { /* This file FD is not suitable for sendfile(). | ||
618 | * Retry with standard send(). */ | ||
619 | connection->resp_sender = MHD_resp_sender_std; | ||
620 | return MHD_ERR_AGAIN_; | ||
621 | } | ||
622 | return MHD_ERR_BADF_; /* Return hard error. */ | ||
623 | } | ||
624 | mhd_assert (0 <= len); | ||
625 | mhd_assert (SSIZE_MAX >= len); | ||
626 | mhd_assert (send_size >= (size_t)len); | ||
627 | ret = (ssize_t)len; | ||
628 | #endif /* HAVE_FREEBSD_SENDFILE */ | ||
629 | |||
630 | /* Make sure we send the data without delay ONLY if we | ||
631 | provided the complete response (not on partial write) */ | ||
632 | if (ret == left) | ||
633 | post_cork_setsockopt (connection, false); | ||
634 | |||
635 | return ret; | ||
636 | } | ||
637 | #endif /* _MHD_HAVE_SENDFILE */ | ||