diff options
author | ng0 <ng0@n0.is> | 2019-07-24 10:17:45 +0000 |
---|---|---|
committer | ng0 <ng0@n0.is> | 2019-07-24 10:17:45 +0000 |
commit | 0c40dc481b3ed4149c9bb82118d5c923fb34dcde (patch) | |
tree | 264820d60451da98da40cb54d724103c25d3b268 /src/microhttpd | |
parent | 0b9d7d3bf3a26a512efe404c7ac0044bdd486932 (diff) | |
download | libmicrohttpd-0c40dc481b3ed4149c9bb82118d5c923fb34dcde.tar.gz libmicrohttpd-0c40dc481b3ed4149c9bb82118d5c923fb34dcde.zip |
move sendfile function work to connection.c
Diffstat (limited to 'src/microhttpd')
-rw-r--r-- | src/microhttpd/connection.c | 72 | ||||
-rw-r--r-- | src/microhttpd/mhd_send.c | 262 | ||||
-rw-r--r-- | src/microhttpd/mhd_send.h | 9 |
3 files changed, 82 insertions, 261 deletions
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c index 626adb42..3315ad22 100644 --- a/src/microhttpd/connection.c +++ b/src/microhttpd/connection.c | |||
@@ -288,6 +288,12 @@ send_param_adapter (struct MHD_Connection *connection, | |||
288 | static ssize_t | 288 | static ssize_t |
289 | sendfile_adapter (struct MHD_Connection *connection) | 289 | sendfile_adapter (struct MHD_Connection *connection) |
290 | { | 290 | { |
291 | bool want_cork = false; | ||
292 | bool have_cork; | ||
293 | bool have_more; | ||
294 | bool use_corknopush; | ||
295 | bool using_tls = false; | ||
296 | |||
291 | ssize_t ret; | 297 | ssize_t ret; |
292 | const int file_fd = connection->response->fd; | 298 | const int file_fd = connection->response->fd; |
293 | uint64_t left; | 299 | uint64_t left; |
@@ -454,6 +460,72 @@ sendfile_adapter (struct MHD_Connection *connection) | |||
454 | mhd_assert (send_size >= (size_t)len); | 460 | mhd_assert (send_size >= (size_t)len); |
455 | ret = (ssize_t)len; | 461 | ret = (ssize_t)len; |
456 | #endif /* HAVE_FREEBSD_SENDFILE */ | 462 | #endif /* HAVE_FREEBSD_SENDFILE */ |
463 | |||
464 | /* ! could be avoided by redefining the variable. */ | ||
465 | have_cork = ! connection->sk_tcp_nodelay_on; | ||
466 | |||
467 | #ifdef MSG_MORE | ||
468 | have_more = true; | ||
469 | #else | ||
470 | have_more = false; | ||
471 | #endif | ||
472 | |||
473 | #if TCP_NODELAY | ||
474 | use_corknopush = false; | ||
475 | #elif TCP_CORK | ||
476 | use_corknopush = true; | ||
477 | #elif TCP_NOPUSH | ||
478 | use_corknopush = true; | ||
479 | #endif | ||
480 | |||
481 | #ifdef HTTPS_SUPPORT | ||
482 | using_tls = (0 != (connection->daemon->options & MHD_USE_TLS)); | ||
483 | #endif | ||
484 | |||
485 | #if TCP_CORK | ||
486 | /* When we have CORK, we can have NODELAY on the same system, | ||
487 | * at least since Linux 2.2 and both can be combined since | ||
488 | * Linux 2.5.71. For more details refer to tcp(7) on Linux. | ||
489 | * No other system in 2019-06 has TCP_CORK. */ | ||
490 | if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork)) | ||
491 | { | ||
492 | if (0 == setsockopt (connection->socket_fd, | ||
493 | IPPROTO_TCP, | ||
494 | TCP_CORK, | ||
495 | (const void *) &off_val, | ||
496 | sizeof (off_val))) | ||
497 | { | ||
498 | connection->sk_tcp_nodelay_on = true; | ||
499 | } | ||
500 | else if (0 == setsockopt (connection->socket_fd, | ||
501 | IPPROTO_TCP, | ||
502 | TCP_NODELAY, | ||
503 | (const void *) &on_val, | ||
504 | sizeof (on_val))) | ||
505 | { | ||
506 | connection->sk_tcp_nodelay_on = true; | ||
507 | } | ||
508 | } | ||
509 | #elif TCP_NOPUSH | ||
510 | /* TCP_NOPUSH on FreeBSD is equal to cork on Linux, with the | ||
511 | * exception that we know that TCP_NOPUSH will definitely | ||
512 | * exist and we can disregard TCP_NODELAY unless requested. */ | ||
513 | if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork)) | ||
514 | { | ||
515 | MHD_send_socket_state_nopush_ (connection, true, false); | ||
516 | /* | ||
517 | if (0 == setsockopt (connection->socket_fd, | ||
518 | IPPROTO_TCP, | ||
519 | TCP_NOPUSH, | ||
520 | (const void *) &on_val, | ||
521 | sizeof (on_val))) | ||
522 | { | ||
523 | connection->sk_tcp_nodelay_on = false; | ||
524 | } | ||
525 | */ | ||
526 | } | ||
527 | #endif | ||
528 | |||
457 | return ret; | 529 | return ret; |
458 | } | 530 | } |
459 | #endif /* _MHD_HAVE_SENDFILE */ | 531 | #endif /* _MHD_HAVE_SENDFILE */ |
diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c index 7e312b59..1d3b739a 100644 --- a/src/microhttpd/mhd_send.c +++ b/src/microhttpd/mhd_send.c | |||
@@ -24,14 +24,13 @@ | |||
24 | * @author ng0 <ng0@n0.is> | 24 | * @author ng0 <ng0@n0.is> |
25 | */ | 25 | */ |
26 | 26 | ||
27 | /* TODO: sendfile() wrappers. */ | 27 | /* TODO: sendfile() wrapper, in connection.c */ |
28 | 28 | ||
29 | /* Functions to be used in: send_param_adapter, MHD_send_ | 29 | /* Functions to be used in: send_param_adapter, MHD_send_ |
30 | * and every place where sendfile(), sendfile64(), setsockopt() | 30 | * and every place where sendfile(), sendfile64(), setsockopt() |
31 | * are used. */ | 31 | * are used. */ |
32 | 32 | ||
33 | #include "mhd_send.h" | 33 | #include "mhd_send.h" |
34 | #include "connection.h" | ||
35 | 34 | ||
36 | /** | 35 | /** |
37 | * Set socket to nodelay, on or off. | 36 | * Set socket to nodelay, on or off. |
@@ -496,262 +495,3 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection, | |||
496 | * 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 |
497 | * large a chunk as possible to the socket. Alternatively, | 496 | * large a chunk as possible to the socket. Alternatively, |
498 | * use madvise(..., MADV_SEQUENTIAL). */ | 497 | * use madvise(..., MADV_SEQUENTIAL). */ |
499 | |||
500 | #if defined(_MHD_HAVE_SENDFILE) | ||
501 | /** | ||
502 | * Function for sending responses backed by file FD. | ||
503 | * A sendfile() wrapper which also performs cork/uncork | ||
504 | * operations. | ||
505 | * | ||
506 | * @param connection the #MHD_Connection structure | ||
507 | * @return actual number of bytes sent | ||
508 | */ | ||
509 | ssize_t | ||
510 | MHD_sendfile_on_connection_ (struct MHD_Connection *connection) | ||
511 | { | ||
512 | // I'm looking for a version of sendfile_adapter() that *also* performs | ||
513 | // cork/uncork operations. Specifically, we want to make sure that the | ||
514 | // buffer is flushed after sendfile() is done (so setsockopt() behavior | ||
515 | // equivalent to MHD_SSO_NO_CORK), and _also_ of course update the | ||
516 | // setsockopt state, i.e. connection->sk_tcp_nodelay_on = true; | ||
517 | |||
518 | bool want_cork = false; | ||
519 | bool have_cork; | ||
520 | bool have_more; | ||
521 | bool use_corknopush; | ||
522 | bool using_tls = false; | ||
523 | ssize_t ret; | ||
524 | const int file_fd = connection->response->fd; | ||
525 | uint64_t left; | ||
526 | uint64_t offsetu64; | ||
527 | #ifndef HAVE_SENDFILE64 | ||
528 | const uint64_t max_off_t = (uint64_t)OFF_T_MAX; | ||
529 | #else /* HAVE_SENDFILE64 */ | ||
530 | const uint64_t max_off_t = (uint64_t)OFF64_T_MAX; | ||
531 | #endif /* HAVE_SENDFILE64 */ | ||
532 | #ifdef MHD_LINUX_SOLARIS_SENDFILE | ||
533 | #ifndef HAVE_SENDFILE64 | ||
534 | off_t offset; | ||
535 | #else /* HAVE_SENDFILE64 */ | ||
536 | off64_t offset; | ||
537 | #endif /* HAVE_SENDFILE64 */ | ||
538 | #endif /* MHD_LINUX_SOLARIS_SENDFILE */ | ||
539 | #ifdef HAVE_FREEBSD_SENDFILE | ||
540 | off_t sent_bytes; | ||
541 | int flags = 0; | ||
542 | #endif | ||
543 | #ifdef HAVE_DARWIN_SENDFILE | ||
544 | off_t len; | ||
545 | #endif /* HAVE_DARWIN_SENDFILE */ | ||
546 | const bool used_thr_p_c = (0 != (connection->daemon->options & MHD_USE_THREAD_PER_CONNECTION)); | ||
547 | const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : MHD_SENFILE_CHUNK_; | ||
548 | size_t send_size = 0; | ||
549 | mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender); | ||
550 | |||
551 | offsetu64 = connection->response_write_position + connection->response->fd_off; | ||
552 | left = connection->response->total_size - connection->response_write_position; | ||
553 | /* Do not allow system to stick sending on single fast connection: | ||
554 | * use 128KiB chunks (2MiB for thread-per-connection). */ | ||
555 | send_size = (left > chunk_size) ? chunk_size : (size_t) left; | ||
556 | if (max_off_t < offsetu64) | ||
557 | { /* Retry to send with standard 'send()'. */ | ||
558 | connection->resp_sender = MHD_resp_sender_std; | ||
559 | return MHD_ERR_AGAIN_; | ||
560 | } | ||
561 | #ifdef MHD_LINUX_SOLARIS_SENDFILE | ||
562 | #ifndef HAVE_SENDFILE64 | ||
563 | offset = (off_t) offsetu64; | ||
564 | ret = sendfile (connection->socket_fd, | ||
565 | file_fd, | ||
566 | &offset, | ||
567 | send_size); | ||
568 | #else /* HAVE_SENDFILE64 */ | ||
569 | offset = (off64_t) offsetu64; | ||
570 | ret = sendfile64 (connection->socket_fd, | ||
571 | file_fd, | ||
572 | &offset, | ||
573 | send_size); | ||
574 | #endif /* HAVE_SENDFILE64 */ | ||
575 | if (0 > ret) | ||
576 | { | ||
577 | const int err = MHD_socket_get_error_(); | ||
578 | if (MHD_SCKT_ERR_IS_EAGAIN_(err)) | ||
579 | { | ||
580 | #ifdef EPOLL_SUPPORT | ||
581 | /* EAGAIN --- no longer write-ready */ | ||
582 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
583 | #endif /* EPOLL_SUPPORT */ | ||
584 | return MHD_ERR_AGAIN_; | ||
585 | } | ||
586 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
587 | return MHD_ERR_AGAIN_; | ||
588 | #ifdef HAVE_LINUX_SENDFILE | ||
589 | if (MHD_SCKT_ERR_IS_(err, | ||
590 | MHD_SCKT_EBADF_)) | ||
591 | return MHD_ERR_BADF_; | ||
592 | /* sendfile() failed with EINVAL if mmap()-like operations are not | ||
593 | supported for FD or other 'unusual' errors occurred, so we should try | ||
594 | to fall back to 'SEND'; see also this thread for info on | ||
595 | odd libc/Linux behavior with sendfile: | ||
596 | http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */ | ||
597 | connection->resp_sender = MHD_resp_sender_std; | ||
598 | return MHD_ERR_AGAIN_; | ||
599 | #else /* HAVE_SOLARIS_SENDFILE */ | ||
600 | if ( (EAFNOSUPPORT == err) || | ||
601 | (EINVAL == err) || | ||
602 | (EOPNOTSUPP == err) ) | ||
603 | { /* Retry with standard file reader. */ | ||
604 | connection->resp_sender = MHD_resp_sender_std; | ||
605 | return MHD_ERR_AGAIN_; | ||
606 | } | ||
607 | if ( (ENOTCONN == err) || | ||
608 | (EPIPE == err) ) | ||
609 | { | ||
610 | return MHD_ERR_CONNRESET_; | ||
611 | } | ||
612 | return MHD_ERR_BADF_; /* Fail hard */ | ||
613 | #endif /* HAVE_SOLARIS_SENDFILE */ | ||
614 | } | ||
615 | #ifdef EPOLL_SUPPORT | ||
616 | else if (send_size > (size_t)ret) | ||
617 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
618 | #endif /* EPOLL_SUPPORT */ | ||
619 | #elif defined(HAVE_FREEBSD_SENDFILE) | ||
620 | #ifdef SF_FLAGS | ||
621 | flags = used_thr_p_c ? | ||
622 | freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_; | ||
623 | #endif /* SF_FLAGS */ | ||
624 | if (0 != sendfile (file_fd, | ||
625 | connection->socket_fd, | ||
626 | (off_t) offsetu64, | ||
627 | send_size, | ||
628 | NULL, | ||
629 | &sent_bytes, | ||
630 | flags)) | ||
631 | { | ||
632 | const int err = MHD_socket_get_error_(); | ||
633 | if (MHD_SCKT_ERR_IS_EAGAIN_(err) || | ||
634 | MHD_SCKT_ERR_IS_EINTR_(err) || | ||
635 | EBUSY == err) | ||
636 | { | ||
637 | mhd_assert (SSIZE_MAX >= sent_bytes); | ||
638 | if (0 != sent_bytes) | ||
639 | return (ssize_t)sent_bytes; | ||
640 | |||
641 | return MHD_ERR_AGAIN_; | ||
642 | } | ||
643 | /* Some unrecoverable error. Possibly file FD is not suitable | ||
644 | * for sendfile(). Retry with standard send(). */ | ||
645 | connection->resp_sender = MHD_resp_sender_std; | ||
646 | return MHD_ERR_AGAIN_; | ||
647 | } | ||
648 | mhd_assert (0 < sent_bytes); | ||
649 | mhd_assert (SSIZE_MAX >= sent_bytes); | ||
650 | ret = (ssize_t)sent_bytes; | ||
651 | #elif defined(HAVE_DARWIN_SENDFILE) | ||
652 | len = (off_t)send_size; /* chunk always fit */ | ||
653 | if (0 != sendfile (file_fd, | ||
654 | connection->socket_fd, | ||
655 | (off_t) offsetu64, | ||
656 | &len, | ||
657 | NULL, | ||
658 | 0)) | ||
659 | { | ||
660 | const int err = MHD_socket_get_error_(); | ||
661 | if (MHD_SCKT_ERR_IS_EAGAIN_(err) || | ||
662 | MHD_SCKT_ERR_IS_EINTR_(err)) | ||
663 | { | ||
664 | mhd_assert (0 <= len); | ||
665 | mhd_assert (SSIZE_MAX >= len); | ||
666 | mhd_assert (send_size >= (size_t)len); | ||
667 | if (0 != len) | ||
668 | return (ssize_t)len; | ||
669 | |||
670 | return MHD_ERR_AGAIN_; | ||
671 | } | ||
672 | if (ENOTCONN == err || | ||
673 | EPIPE == err) | ||
674 | return MHD_ERR_CONNRESET_; | ||
675 | if (ENOTSUP == err || | ||
676 | EOPNOTSUPP == err) | ||
677 | { /* This file FD is not suitable for sendfile(). | ||
678 | * Retry with standard send(). */ | ||
679 | connection->resp_sender = MHD_resp_sender_std; | ||
680 | return MHD_ERR_AGAIN_; | ||
681 | } | ||
682 | return MHD_ERR_BADF_; /* Return hard error. */ | ||
683 | } | ||
684 | mhd_assert (0 <= len); | ||
685 | mhd_assert (SSIZE_MAX >= len); | ||
686 | mhd_assert (send_size >= (size_t)len); | ||
687 | ret = (ssize_t)len; | ||
688 | #endif /* HAVE_FREEBSD_SENDFILE */ | ||
689 | |||
690 | /* ! could be avoided by redefining the variable. */ | ||
691 | have_cork = ! connection->sk_tcp_nodelay_on; | ||
692 | |||
693 | #ifdef MSG_MORE | ||
694 | have_more = true; | ||
695 | #else | ||
696 | have_more = false; | ||
697 | #endif | ||
698 | |||
699 | #if TCP_NODELAY | ||
700 | use_corknopush = false; | ||
701 | #elif TCP_CORK | ||
702 | use_corknopush = true; | ||
703 | #elif TCP_NOPUSH | ||
704 | use_corknopush = true; | ||
705 | #endif | ||
706 | |||
707 | #ifdef HTTPS_SUPPORT | ||
708 | using_tls = (0 != (connection->daemon->options & MHD_USE_TLS)); | ||
709 | #endif | ||
710 | |||
711 | #if TCP_CORK | ||
712 | /* When we have CORK, we can have NODELAY on the same system, | ||
713 | * at least since Linux 2.2 and both can be combined since | ||
714 | * Linux 2.5.71. For more details refer to tcp(7) on Linux. | ||
715 | * No other system in 2019-06 has TCP_CORK. */ | ||
716 | if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork)) | ||
717 | { | ||
718 | if (0 == setsockopt (connection->socket_fd, | ||
719 | IPPROTO_TCP, | ||
720 | TCP_CORK, | ||
721 | (const void *) &off_val, | ||
722 | sizeof (off_val))) | ||
723 | { | ||
724 | connection->sk_tcp_nodelay_on = true; | ||
725 | } | ||
726 | else if (0 == setsockopt (connection->socket_fd, | ||
727 | IPPROTO_TCP, | ||
728 | TCP_NODELAY, | ||
729 | (const void *) &on_val, | ||
730 | sizeof (on_val))) | ||
731 | { | ||
732 | connection->sk_tcp_nodelay_on = true; | ||
733 | } | ||
734 | } | ||
735 | #elif TCP_NOPUSH | ||
736 | /* TCP_NOPUSH on FreeBSD is equal to cork on Linux, with the | ||
737 | * exception that we know that TCP_NOPUSH will definitely | ||
738 | * exist and we can disregard TCP_NODELAY unless requested. */ | ||
739 | if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork)) | ||
740 | { | ||
741 | MHD_send_socket_state_nopush_ (connection, true, false); | ||
742 | /* | ||
743 | if (0 == setsockopt (connection->socket_fd, | ||
744 | IPPROTO_TCP, | ||
745 | TCP_NOPUSH, | ||
746 | (const void *) &on_val, | ||
747 | sizeof (on_val))) | ||
748 | { | ||
749 | connection->sk_tcp_nodelay_on = false; | ||
750 | } | ||
751 | */ | ||
752 | } | ||
753 | #endif | ||
754 | |||
755 | return ret; | ||
756 | } | ||
757 | #endif /* _MHD_HAVE_SENDFILE */ | ||
diff --git a/src/microhttpd/mhd_send.h b/src/microhttpd/mhd_send.h index a6049865..c4a6d705 100644 --- a/src/microhttpd/mhd_send.h +++ b/src/microhttpd/mhd_send.h | |||
@@ -77,4 +77,13 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection, | |||
77 | 77 | ||
78 | ssize_t | 78 | ssize_t |
79 | MHD_sendfile_on_connection_ (struct MHD_Connection *connection); | 79 | MHD_sendfile_on_connection_ (struct MHD_Connection *connection); |
80 | |||
81 | void | ||
82 | MHD_send_socket_state_nopush_ (struct MHD_Connection *connection, | ||
83 | bool value, | ||
84 | bool state_store); | ||
85 | |||
86 | void | ||
87 | MHD_send_socket_state_nodelay_ (struct MHD_Connection *connection, | ||
88 | bool value); | ||
80 | #endif /* MHD_SEND_H */ | 89 | #endif /* MHD_SEND_H */ |