aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd
diff options
context:
space:
mode:
authorng0 <ng0@n0.is>2019-07-24 10:03:37 +0000
committerng0 <ng0@n0.is>2019-07-24 10:03:37 +0000
commitaad43f23f0aa5cf997d9e880e2d78eefd9c6a221 (patch)
treedf91bd1cd474f4f60017ac7bfc3a008c62db0e38 /src/microhttpd
parent1b013c78a695cee32135ec5ec93dff959bf8f8f6 (diff)
downloadlibmicrohttpd-aad43f23f0aa5cf997d9e880e2d78eefd9c6a221.tar.gz
libmicrohttpd-aad43f23f0aa5cf997d9e880e2d78eefd9c6a221.zip
incomplete commit, adding 2 new helper functions and more.
Diffstat (limited to 'src/microhttpd')
-rw-r--r--src/microhttpd/connection.c11
-rw-r--r--src/microhttpd/mhd_send.c325
-rw-r--r--src/microhttpd/mhd_send.h3
3 files changed, 333 insertions, 6 deletions
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 1893de15..626adb42 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -3350,12 +3350,20 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
3350 return; 3350 return;
3351 case MHD_CONNECTION_HEADERS_SENDING: 3351 case MHD_CONNECTION_HEADERS_SENDING:
3352 /* TODO: Maybe use MHD_send_on_connection2_()? */ 3352 /* TODO: Maybe use MHD_send_on_connection2_()? */
3353 /*
3354 ret = MHD_send_on_connection2_ (struct MHD_Connection *connection,
3355 const char *header,
3356 size_t header_size,
3357 const char *buffer,
3358 size_t buffer_size);
3359 */
3353 ret = MHD_send_on_connection_ (connection, 3360 ret = MHD_send_on_connection_ (connection,
3354 &connection->write_buffer 3361 &connection->write_buffer
3355 [connection->write_buffer_send_offset], 3362 [connection->write_buffer_send_offset],
3356 connection->write_buffer_append_offset - 3363 connection->write_buffer_append_offset -
3357 connection->write_buffer_send_offset, 3364 connection->write_buffer_send_offset,
3358 MHD_SSO_MAY_CORK); 3365 MHD_SSO_MAY_CORK);
3366
3359 if (ret < 0) 3367 if (ret < 0)
3360 { 3368 {
3361 if (MHD_ERR_AGAIN_ == ret) 3369 if (MHD_ERR_AGAIN_ == ret)
@@ -3392,7 +3400,8 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
3392#if defined(_MHD_HAVE_SENDFILE) 3400#if defined(_MHD_HAVE_SENDFILE)
3393 if (MHD_resp_sender_sendfile == connection->resp_sender) 3401 if (MHD_resp_sender_sendfile == connection->resp_sender)
3394 { 3402 {
3395 ret = sendfile_adapter (connection); 3403 // ret = sendfile_adapter (connection);
3404 ret = MHD_sendfile_on_connection_ (connection);
3396 } 3405 }
3397 else 3406 else
3398#else /* ! _MHD_HAVE_SENDFILE */ 3407#else /* ! _MHD_HAVE_SENDFILE */
diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c
index 7991830c..ee32a868 100644
--- a/src/microhttpd/mhd_send.c
+++ b/src/microhttpd/mhd_send.c
@@ -33,6 +33,52 @@
33#include "mhd_send.h" 33#include "mhd_send.h"
34 34
35/** 35/**
36 * Set socket to nodelay, on or off.
37 *
38 * @param connection the MHD_Connection structure
39 * @param value the state to set, boolean
40 */
41void
42MHD_send_socket_state_nodelay_ (struct MHD_Connection *connection,
43 bool value)
44{
45#if TCP_NODELAY
46 const MHD_SCKT_OPT_BOOL_ state_val = value ? 1 : 0;
47
48 if (0 == setsockopt (connection->socket_fd,
49 IPPROTO_TCP,
50 TCP_NODELAY,
51 (const void *) &state_val,
52 sizeof (state_val)))
53 {
54 connection->sk_tcp_nodelay_on = value;
55 }
56#endif
57}
58
59void
60MHD_send_socket_state_nopush_ (struct MHD_Connection *connection,
61 bool value,
62 bool state_store)
63{
64#if TCP_NOPUSH
65 const MHD_SCKT_OPT_BOOL_ state_val = value ? 1 : 0;
66
67 if (0 == setsockopt (connection->socket_fd,
68 IPPROTO_TCP,
69 TCP_NOPUSH,
70 (const void *) &state_val,
71 sizeof (state_val)))
72 {
73 /* When TRUE above, this is usually FALSE, but
74 * not always. We can't use the negation of
75 * value for that reason. */
76 connection->sk_tcp_nodelay_on = state_store;
77 }
78#endif
79}
80
81/**
36 * Send buffer on connection, and remember the current state of 82 * Send buffer on connection, and remember the current state of
37 * the socket options; only call setsockopt when absolutely 83 * the socket options; only call setsockopt when absolutely
38 * necessary. 84 * necessary.
@@ -145,6 +191,8 @@ MHD_send_on_connection_ (struct MHD_Connection *connection,
145 * exist and we can disregard TCP_NODELAY unless requested. */ 191 * exist and we can disregard TCP_NODELAY unless requested. */
146 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork)) 192 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork))
147 { 193 {
194 MHD_send_socket_state_nopush_ (connection, true, false);
195 /*
148 if (0 == setsockopt (s, 196 if (0 == setsockopt (s,
149 IPPROTO_TCP, 197 IPPROTO_TCP,
150 TCP_NOPUSH, 198 TCP_NOPUSH,
@@ -153,11 +201,13 @@ MHD_send_on_connection_ (struct MHD_Connection *connection,
153 { 201 {
154 connection->sk_tcp_nodelay_on = false; 202 connection->sk_tcp_nodelay_on = false;
155 } 203 }
204 */
156 } 205 }
157#endif 206#endif
158#if TCP_NODELAY 207#if TCP_NODELAY
159 if ((! using_tls) && (! use_corknopush) && (! have_cork && want_cork)) 208 if ((! using_tls) && (! use_corknopush) && (! have_cork && want_cork))
160 { 209 {
210 /*
161 if (0 == setsockopt (s, 211 if (0 == setsockopt (s,
162 IPPROTO_TCP, 212 IPPROTO_TCP,
163 TCP_NODELAY, 213 TCP_NODELAY,
@@ -166,6 +216,8 @@ MHD_send_on_connection_ (struct MHD_Connection *connection,
166 { 216 {
167 connection->sk_tcp_nodelay_on = false; 217 connection->sk_tcp_nodelay_on = false;
168 } 218 }
219 */
220 MHD_send_socket_state_nodelay_ (connection, false);
169 } 221 }
170#endif 222#endif
171 223
@@ -274,6 +326,8 @@ MHD_send_on_connection_ (struct MHD_Connection *connection,
274 * for the TCP_CORK case here. */ 326 * for the TCP_CORK case here. */
275 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork)) 327 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork))
276 { 328 {
329 MHD_send_socket_state_nopush_ (connection, true, false);
330 /*
277 if (0 == setsockopt (s, 331 if (0 == setsockopt (s,
278 IPPROTO_TCP, 332 IPPROTO_TCP,
279 TCP_NOPUSH, 333 TCP_NOPUSH,
@@ -282,12 +336,15 @@ MHD_send_on_connection_ (struct MHD_Connection *connection,
282 { 336 {
283 connection->sk_tcp_nodelay_on = false; 337 connection->sk_tcp_nodelay_on = false;
284 } 338 }
339 */
285 } 340 }
286#endif 341#endif
287 342
288#if TCP_NODELAY 343#if TCP_NODELAY
289 if ((! using_tls) && (! use_corknopush) && (have_cork && ! want_cork)) 344 if ((! using_tls) && (! use_corknopush) && (have_cork && ! want_cork))
290 { 345 {
346 MHD_send_socket_state_nodelay_ (connection, true);
347 /*
291 if (0 == setsockopt (s, 348 if (0 == setsockopt (s,
292 IPPROTO_TCP, 349 IPPROTO_TCP,
293 TCP_NODELAY, 350 TCP_NODELAY,
@@ -296,6 +353,7 @@ MHD_send_on_connection_ (struct MHD_Connection *connection,
296 { 353 {
297 connection->sk_tcp_nodelay_on = true; 354 connection->sk_tcp_nodelay_on = true;
298 } 355 }
356 */
299 } 357 }
300#endif 358#endif
301 359
@@ -348,6 +406,8 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
348#if TCP_NODELAY 406#if TCP_NODELAY
349 if ((! use_corknopush) && (! have_cork && want_cork)) 407 if ((! use_corknopush) && (! have_cork && want_cork))
350 { 408 {
409 MHD_send_socket_state_nodelay_ (connection, false);
410 /*
351 if (0 == setsockopt (s, 411 if (0 == setsockopt (s,
352 IPPROTO_TCP, 412 IPPROTO_TCP,
353 TCP_NODELAY, 413 TCP_NODELAY,
@@ -356,6 +416,7 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
356 { 416 {
357 connection->sk_tcp_nodelay_on = false; 417 connection->sk_tcp_nodelay_on = false;
358 } 418 }
419 */
359 } 420 }
360#endif 421#endif
361 422
@@ -404,6 +465,8 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
404 if (ret == header_len + buffer_len) 465 if (ret == header_len + buffer_len)
405 { 466 {
406 /* Response complete, set NOPUSH to off */ 467 /* Response complete, set NOPUSH to off */
468 MHD_send_socket_state_nopush_ (connection, false, false);
469 /*
407 if (0 == setsockopt (s, 470 if (0 == setsockopt (s,
408 IPPROTO_TCP, 471 IPPROTO_TCP,
409 TCP_NOPUSH, 472 TCP_NOPUSH,
@@ -412,6 +475,7 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
412 { 475 {
413 connection->sk_tcp_nodelay_on = false; 476 connection->sk_tcp_nodelay_on = false;
414 } 477 }
478 */
415 } 479 }
416 errno = eno; 480 errno = eno;
417 } 481 }
@@ -431,11 +495,262 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
431 * with this seems to be to mmap the file and write(2) as 495 * with this seems to be to mmap the file and write(2) as
432 * large a chunk as possible to the socket. Alternatively, 496 * large a chunk as possible to the socket. Alternatively,
433 * use madvise(..., MADV_SEQUENTIAL). */ 497 * use madvise(..., MADV_SEQUENTIAL). */
498
499#if defined(_MHD_HAVE_SENDFILE)
500/**
501 * Function for sending responses backed by file FD.
502 * A sendfile() wrapper which also performs cork/uncork
503 * operations.
504 *
505 * @param connection the #MHD_Connection structure
506 * @return actual number of bytes sent
507 */
434ssize_t 508ssize_t
435MHD_sendfile_on_connection_ (struct MHD_Connection *connection, 509MHD_sendfile_on_connection_ (struct MHD_Connection *connection)
436 const char *buffer,
437 size_t buffer_size,
438 enum MHD_SendSocketOptions options)
439{ 510{
440 // TODO 511 // I'm looking for a version of sendfile_adapter() that *also* performs
512 // cork/uncork operations. Specifically, we want to make sure that the
513 // buffer is flushed after sendfile() is done (so setsockopt() behavior
514 // equivalent to MHD_SSO_NO_CORK), and _also_ of course update the
515 // setsockopt state, i.e. connection->sk_tcp_nodelay_on = true;
516
517 bool want_cork = false;
518 bool have_cork;
519 bool have_more;
520 bool use_corknopush;
521 bool using_tls = false;
522 ssize_t ret;
523 const int file_fd = connection->response->fd;
524 uint64_t left;
525 uint64_t offsetu64;
526#ifndef HAVE_SENDFILE64
527 const uint64_t max_off_t = (uint64_t)OFF_T_MAX;
528#else /* HAVE_SENDFILE64 */
529 const uint64_t max_off_t = (uint64_t)OFF64_T_MAX;
530#endif /* HAVE_SENDFILE64 */
531#ifdef MHD_LINUX_SOLARIS_SENDFILE
532#ifndef HAVE_SENDFILE64
533 off_t offset;
534#else /* HAVE_SENDFILE64 */
535 off64_t offset;
536#endif /* HAVE_SENDFILE64 */
537#endif /* MHD_LINUX_SOLARIS_SENDFILE */
538#ifdef HAVE_FREEBSD_SENDFILE
539 off_t sent_bytes;
540 int flags = 0;
541#endif
542#ifdef HAVE_DARWIN_SENDFILE
543 off_t len;
544#endif /* HAVE_DARWIN_SENDFILE */
545 const bool used_thr_p_c = (0 != (connection->daemon->options & MHD_USE_THREAD_PER_CONNECTION));
546 const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : MHD_SENFILE_CHUNK_;
547 size_t send_size = 0;
548 mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender);
549
550 offsetu64 = connection->response_write_position + connection->response->fd_off;
551 left = connection->response->total_size - connection->response_write_position;
552 /* Do not allow system to stick sending on single fast connection:
553 * use 128KiB chunks (2MiB for thread-per-connection). */
554 send_size = (left > chunk_size) ? chunk_size : (size_t) left;
555 if (max_off_t < offsetu64)
556 { /* Retry to send with standard 'send()'. */
557 connection->resp_sender = MHD_resp_sender_std;
558 return MHD_ERR_AGAIN_;
559 }
560#ifdef MHD_LINUX_SOLARIS_SENDFILE
561#ifndef HAVE_SENDFILE64
562 offset = (off_t) offsetu64;
563 ret = sendfile (connection->socket_fd,
564 file_fd,
565 &offset,
566 send_size);
567#else /* HAVE_SENDFILE64 */
568 offset = (off64_t) offsetu64;
569 ret = sendfile64 (connection->socket_fd,
570 file_fd,
571 &offset,
572 send_size);
573#endif /* HAVE_SENDFILE64 */
574 if (0 > ret)
575 {
576 const int err = MHD_socket_get_error_();
577 if (MHD_SCKT_ERR_IS_EAGAIN_(err))
578 {
579#ifdef EPOLL_SUPPORT
580 /* EAGAIN --- no longer write-ready */
581 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
582#endif /* EPOLL_SUPPORT */
583 return MHD_ERR_AGAIN_;
584 }
585 if (MHD_SCKT_ERR_IS_EINTR_ (err))
586 return MHD_ERR_AGAIN_;
587#ifdef HAVE_LINUX_SENDFILE
588 if (MHD_SCKT_ERR_IS_(err,
589 MHD_SCKT_EBADF_))
590 return MHD_ERR_BADF_;
591 /* sendfile() failed with EINVAL if mmap()-like operations are not
592 supported for FD or other 'unusual' errors occurred, so we should try
593 to fall back to 'SEND'; see also this thread for info on
594 odd libc/Linux behavior with sendfile:
595 http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */
596 connection->resp_sender = MHD_resp_sender_std;
597 return MHD_ERR_AGAIN_;
598#else /* HAVE_SOLARIS_SENDFILE */
599 if ( (EAFNOSUPPORT == err) ||
600 (EINVAL == err) ||
601 (EOPNOTSUPP == err) )
602 { /* Retry with standard file reader. */
603 connection->resp_sender = MHD_resp_sender_std;
604 return MHD_ERR_AGAIN_;
605 }
606 if ( (ENOTCONN == err) ||
607 (EPIPE == err) )
608 {
609 return MHD_ERR_CONNRESET_;
610 }
611 return MHD_ERR_BADF_; /* Fail hard */
612#endif /* HAVE_SOLARIS_SENDFILE */
613 }
614#ifdef EPOLL_SUPPORT
615 else if (send_size > (size_t)ret)
616 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
617#endif /* EPOLL_SUPPORT */
618#elif defined(HAVE_FREEBSD_SENDFILE)
619#ifdef SF_FLAGS
620 flags = used_thr_p_c ?
621 freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
622#endif /* SF_FLAGS */
623 if (0 != sendfile (file_fd,
624 connection->socket_fd,
625 (off_t) offsetu64,
626 send_size,
627 NULL,
628 &sent_bytes,
629 flags))
630 {
631 const int err = MHD_socket_get_error_();
632 if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
633 MHD_SCKT_ERR_IS_EINTR_(err) ||
634 EBUSY == err)
635 {
636 mhd_assert (SSIZE_MAX >= sent_bytes);
637 if (0 != sent_bytes)
638 return (ssize_t)sent_bytes;
639
640 return MHD_ERR_AGAIN_;
641 }
642 /* Some unrecoverable error. Possibly file FD is not suitable
643 * for sendfile(). Retry with standard send(). */
644 connection->resp_sender = MHD_resp_sender_std;
645 return MHD_ERR_AGAIN_;
646 }
647 mhd_assert (0 < sent_bytes);
648 mhd_assert (SSIZE_MAX >= sent_bytes);
649 ret = (ssize_t)sent_bytes;
650#elif defined(HAVE_DARWIN_SENDFILE)
651 len = (off_t)send_size; /* chunk always fit */
652 if (0 != sendfile (file_fd,
653 connection->socket_fd,
654 (off_t) offsetu64,
655 &len,
656 NULL,
657 0))
658 {
659 const int err = MHD_socket_get_error_();
660 if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
661 MHD_SCKT_ERR_IS_EINTR_(err))
662 {
663 mhd_assert (0 <= len);
664 mhd_assert (SSIZE_MAX >= len);
665 mhd_assert (send_size >= (size_t)len);
666 if (0 != len)
667 return (ssize_t)len;
668
669 return MHD_ERR_AGAIN_;
670 }
671 if (ENOTCONN == err ||
672 EPIPE == err)
673 return MHD_ERR_CONNRESET_;
674 if (ENOTSUP == err ||
675 EOPNOTSUPP == err)
676 { /* This file FD is not suitable for sendfile().
677 * Retry with standard send(). */
678 connection->resp_sender = MHD_resp_sender_std;
679 return MHD_ERR_AGAIN_;
680 }
681 return MHD_ERR_BADF_; /* Return hard error. */
682 }
683 mhd_assert (0 <= len);
684 mhd_assert (SSIZE_MAX >= len);
685 mhd_assert (send_size >= (size_t)len);
686 ret = (ssize_t)len;
687#endif /* HAVE_FREEBSD_SENDFILE */
688
689 /* ! could be avoided by redefining the variable. */
690 have_cork = ! connection->sk_tcp_nodelay_on;
691
692#ifdef MSG_MORE
693 have_more = true;
694#else
695 have_more = false;
696#endif
697
698#if TCP_NODELAY
699 use_corknopush = false;
700#elif TCP_CORK
701 use_corknopush = true;
702#elif TCP_NOPUSH
703 use_corknopush = true;
704#endif
705
706#ifdef HTTPS_SUPPORT
707 using_tls = (0 != (connection->daemon->options & MHD_USE_TLS));
708#endif
709
710#if TCP_CORK
711 /* When we have CORK, we can have NODELAY on the same system,
712 * at least since Linux 2.2 and both can be combined since
713 * Linux 2.5.71. For more details refer to tcp(7) on Linux.
714 * No other system in 2019-06 has TCP_CORK. */
715 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork))
716 {
717 if (0 == setsockopt (connection->socket_fd,
718 IPPROTO_TCP,
719 TCP_CORK,
720 (const void *) &off_val,
721 sizeof (off_val)))
722 {
723 connection->sk_tcp_nodelay_on = true;
724 }
725 else if (0 == setsockopt (connection->socket_fd,
726 IPPROTO_TCP,
727 TCP_NODELAY,
728 (const void *) &on_val,
729 sizeof (on_val)))
730 {
731 connection->sk_tcp_nodelay_on = true;
732 }
733 }
734#elif TCP_NOPUSH
735 /* TCP_NOPUSH on FreeBSD is equal to cork on Linux, with the
736 * exception that we know that TCP_NOPUSH will definitely
737 * exist and we can disregard TCP_NODELAY unless requested. */
738 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork))
739 {
740 MHD_send_socket_state_nopush_ (connection, true, false);
741 /*
742 if (0 == setsockopt (connection->socket_fd,
743 IPPROTO_TCP,
744 TCP_NOPUSH,
745 (const void *) &on_val,
746 sizeof (on_val)))
747 {
748 connection->sk_tcp_nodelay_on = false;
749 }
750 */
751 }
752#endif
753
754 return ret;
441} 755}
756#endif /* _MHD_HAVE_SENDFILE */
diff --git a/src/microhttpd/mhd_send.h b/src/microhttpd/mhd_send.h
index e6ed3268..a6049865 100644
--- a/src/microhttpd/mhd_send.h
+++ b/src/microhttpd/mhd_send.h
@@ -74,4 +74,7 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
74 size_t header_size, 74 size_t header_size,
75 const char *buffer, 75 const char *buffer,
76 size_t buffer_size); 76 size_t buffer_size);
77
78ssize_t
79MHD_sendfile_on_connection_ (struct MHD_Connection *connection);
77#endif /* MHD_SEND_H */ 80#endif /* MHD_SEND_H */