diff options
author | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2018-12-10 22:14:59 +0300 |
---|---|---|
committer | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2018-12-10 22:14:59 +0300 |
commit | e13c79ee3b5208c8b94538144abe58eab099b3f8 (patch) | |
tree | dccd90d3b8b5d5c8a94d6ac8572250e36536bb2e | |
parent | 4b5e8e0b45cde5f80f160d1abd42e604503b3544 (diff) | |
download | libmicrohttpd-e13c79ee3b5208c8b94538144abe58eab099b3f8.tar.gz libmicrohttpd-e13c79ee3b5208c8b94538144abe58eab099b3f8.zip |
Track socket CORK/NODELAY states to avoid extra syscalls
-rw-r--r-- | src/microhttpd/connection.c | 215 | ||||
-rw-r--r-- | src/microhttpd/daemon.c | 9 | ||||
-rw-r--r-- | src/microhttpd/internal.h | 13 | ||||
-rw-r--r-- | src/microhttpd/mhd_sockets.c | 35 | ||||
-rw-r--r-- | src/microhttpd/mhd_sockets.h | 23 |
5 files changed, 185 insertions, 110 deletions
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c index 4d2f49b5..d521c036 100644 --- a/src/microhttpd/connection.c +++ b/src/microhttpd/connection.c | |||
@@ -49,6 +49,10 @@ | |||
49 | #ifdef HTTPS_SUPPORT | 49 | #ifdef HTTPS_SUPPORT |
50 | #include "connection_https.h" | 50 | #include "connection_https.h" |
51 | #endif /* HTTPS_SUPPORT */ | 51 | #endif /* HTTPS_SUPPORT */ |
52 | #ifdef HAVE_SYS_PARAM_H | ||
53 | /* For FreeBSD version identification */ | ||
54 | #include <sys/param.h> | ||
55 | #endif /* HAVE_SYS_PARAM_H */ | ||
52 | 56 | ||
53 | 57 | ||
54 | /** | 58 | /** |
@@ -496,51 +500,46 @@ socket_flush_possible(struct MHD_Connection *connection) | |||
496 | _MHD_static_inline bool | 500 | _MHD_static_inline bool |
497 | socket_start_extra_buffering (struct MHD_Connection *connection) | 501 | socket_start_extra_buffering (struct MHD_Connection *connection) |
498 | { | 502 | { |
499 | bool res = false; | ||
500 | #if defined(TCP_CORK) || defined(TCP_NOPUSH) | ||
501 | const MHD_SCKT_OPT_BOOL_ on_val = 1; | ||
502 | #if defined(TCP_NODELAY) | ||
503 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | ||
504 | #endif /* TCP_NODELAY */ | ||
505 | (void) connection; /* mute compiler warning, assertion below | ||
506 | may be compiled out! */ | ||
507 | mhd_assert(NULL != connection); | 503 | mhd_assert(NULL != connection); |
508 | #if defined(TCP_NOPUSH) && !defined(TCP_CORK) | ||
509 | /* Buffer data before sending */ | ||
510 | res = (0 == setsockopt (connection->socket_fd, | ||
511 | IPPROTO_TCP, | ||
512 | TCP_NOPUSH, | ||
513 | (const void *) &on_val, | ||
514 | sizeof (on_val))); | ||
515 | #if defined(TCP_NODELAY) | 504 | #if defined(TCP_NODELAY) |
516 | /* Enable Nagle's algorithm */ | 505 | if (connection->sk_tcp_nodelay_on) |
517 | /* TCP_NODELAY may interfere with TCP_NOPUSH */ | 506 | { |
518 | res = (0 == setsockopt (connection->socket_fd, | 507 | const MHD_SCKT_OPT_BOOL_ off_val = 0; |
508 | /* Enable Nagle's algorithm */ | ||
509 | /* TCP_NODELAY may interfere with TCP_NOPUSH */ | ||
510 | if (0 == setsockopt (connection->socket_fd, | ||
519 | IPPROTO_TCP, | 511 | IPPROTO_TCP, |
520 | TCP_NODELAY, | 512 | TCP_NODELAY, |
521 | (const void *) &off_val, | 513 | (const void *) &off_val, |
522 | sizeof (off_val))) && res; | 514 | sizeof (off_val))) |
515 | { | ||
516 | connection->sk_tcp_nodelay_on = false; | ||
517 | } | ||
518 | } | ||
523 | #endif /* TCP_NODELAY */ | 519 | #endif /* TCP_NODELAY */ |
524 | #else /* TCP_CORK */ | 520 | |
521 | #if defined(MHD_TCP_CORK_NOPUSH) | ||
522 | if (!connection->sk_tcp_cork_nopush_on) | ||
523 | { | ||
524 | const MHD_SCKT_OPT_BOOL_ on_val = 1; | ||
525 | /* Buffer data before sending (TCP_CORK) or | ||
526 | * Send only full frames (TCP_NOPUSH) */ | ||
527 | if (0 == setsockopt (connection->socket_fd, | ||
528 | IPPROTO_TCP, | ||
529 | MHD_TCP_CORK_NOPUSH, | ||
530 | (const void *) &on_val, | ||
531 | sizeof (on_val))) | ||
532 | { | ||
533 | connection->sk_tcp_cork_nopush_on = true; | ||
534 | } | ||
535 | } | ||
536 | #endif /* MHD_TCP_CORK_NOPUSH */ | ||
537 | |||
525 | #if defined(TCP_NODELAY) | 538 | #if defined(TCP_NODELAY) |
526 | /* Enable Nagle's algorithm */ | 539 | return connection->sk_tcp_cork_nopush_on && !connection->sk_tcp_nodelay_on; |
527 | /* TCP_NODELAY may prevent enabling TCP_CORK. Resulting buffering mode depends | 540 | #else /* ! TCP_NODELAY */ |
528 | solely on TCP_CORK result, so ignoring return code here. */ | 541 | return connection->sk_tcp_cork_nopush_on; |
529 | (void) setsockopt (connection->socket_fd, | 542 | #endif /* ! TCP_NODELAY */ |
530 | IPPROTO_TCP, | ||
531 | TCP_NODELAY, | ||
532 | (const void *) &off_val, | ||
533 | sizeof (off_val)); | ||
534 | #endif /* TCP_NODELAY */ | ||
535 | /* Send only full packets */ | ||
536 | res = (0 == setsockopt (connection->socket_fd, | ||
537 | IPPROTO_TCP, | ||
538 | TCP_CORK, | ||
539 | (const void *) &on_val, | ||
540 | sizeof (on_val))); | ||
541 | #endif /* TCP_CORK */ | ||
542 | #endif /* TCP_CORK || TCP_NOPUSH */ | ||
543 | return res; | ||
544 | } | 543 | } |
545 | 544 | ||
546 | 545 | ||
@@ -553,43 +552,38 @@ socket_start_extra_buffering (struct MHD_Connection *connection) | |||
553 | _MHD_static_inline bool | 552 | _MHD_static_inline bool |
554 | socket_start_no_buffering (struct MHD_Connection *connection) | 553 | socket_start_no_buffering (struct MHD_Connection *connection) |
555 | { | 554 | { |
556 | #if defined(TCP_NODELAY) | 555 | #if defined(MHD_TCP_CORK_NOPUSH) |
557 | bool res = true; | 556 | if (connection->sk_tcp_cork_nopush_on) |
558 | const MHD_SCKT_OPT_BOOL_ on_val = 1; | 557 | { |
559 | #if defined(TCP_CORK) || defined(TCP_NOPUSH) | 558 | const MHD_SCKT_OPT_BOOL_ off_val = 0; |
560 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | 559 | /* Disable extra buffering */ |
561 | #endif /* TCP_CORK || TCP_NOPUSH */ | 560 | if (0 == setsockopt (connection->socket_fd, |
562 | |||
563 | (void)connection; /* Mute compiler warning. */ | ||
564 | mhd_assert(NULL != connection); | ||
565 | #if defined(TCP_CORK) | ||
566 | /* Allow partial packets */ | ||
567 | res = (0 == setsockopt (connection->socket_fd, | ||
568 | IPPROTO_TCP, | 561 | IPPROTO_TCP, |
569 | TCP_CORK, | 562 | MHD_TCP_CORK_NOPUSH, |
570 | (const void *) &off_val, | 563 | (const void *) &off_val, |
571 | sizeof (off_val))) && res; | 564 | sizeof (off_val))) |
572 | #endif /* TCP_CORK */ | 565 | { |
566 | connection->sk_tcp_cork_nopush_on = false; | ||
567 | } | ||
568 | } | ||
569 | #endif /* MHD_TCP_CORK_NOPUSH */ | ||
570 | |||
573 | #if defined(TCP_NODELAY) | 571 | #if defined(TCP_NODELAY) |
574 | /* Disable Nagle's algorithm for sending packets without delay */ | 572 | if (!connection->sk_tcp_nodelay_on) |
575 | res = (0 == setsockopt (connection->socket_fd, | 573 | { |
574 | const MHD_SCKT_OPT_BOOL_ on_val = 1; | ||
575 | /* Enable sending without delay */ | ||
576 | if (0 == setsockopt (connection->socket_fd, | ||
576 | IPPROTO_TCP, | 577 | IPPROTO_TCP, |
577 | TCP_NODELAY, | 578 | TCP_NODELAY, |
578 | (const void *) &on_val, | 579 | (const void *) &on_val, |
579 | sizeof (on_val))) && res; | 580 | sizeof (on_val))) |
581 | { | ||
582 | connection->sk_tcp_nodelay_on = true; | ||
583 | } | ||
584 | } | ||
580 | #endif /* TCP_NODELAY */ | 585 | #endif /* TCP_NODELAY */ |
581 | #if defined(TCP_NOPUSH) && !defined(TCP_CORK) | 586 | return connection->sk_tcp_nodelay_on && !connection->sk_tcp_cork_nopush_on; |
582 | /* Disable extra buffering */ | ||
583 | res = (0 == setsockopt (connection->socket_fd, | ||
584 | IPPROTO_TCP, | ||
585 | TCP_NOPUSH, | ||
586 | (const void *) &off_val, | ||
587 | sizeof (off_val))) && res; | ||
588 | #endif /* TCP_NOPUSH && !TCP_CORK */ | ||
589 | return res; | ||
590 | #else /* !TCP_NODELAY */ | ||
591 | return false; | ||
592 | #endif /* !TCP_NODELAY */ | ||
593 | } | 587 | } |
594 | 588 | ||
595 | 589 | ||
@@ -607,12 +601,24 @@ socket_start_no_buffering_flush (struct MHD_Connection *connection) | |||
607 | #if defined(TCP_NOPUSH) && !defined(TCP_CORK) | 601 | #if defined(TCP_NOPUSH) && !defined(TCP_CORK) |
608 | const int dummy = 0; | 602 | const int dummy = 0; |
609 | #endif /* !TCP_CORK */ | 603 | #endif /* !TCP_CORK */ |
610 | 604 | #if defined(TCP_CORK) || (defined(__FreeBSD__) && __FreeBSD__+0 >= 9) | |
611 | (void)connection; /* Mute compiler warning. */ | 605 | const MHD_SCKT_OPT_BOOL_ off_val = 0; |
612 | mhd_assert(NULL != connection); | 606 | /* Switching off TCP_CORK flush buffer even |
607 | * if TCP_CORK was not enabled */ | ||
608 | if (0 == setsockopt (connection->socket_fd, | ||
609 | IPPROTO_TCP, | ||
610 | MHD_TCP_CORK_NOPUSH, | ||
611 | (const void *) &off_val, | ||
612 | sizeof (off_val))) | ||
613 | { | ||
614 | connection->sk_tcp_cork_nopush_on = false; | ||
615 | } | ||
616 | #endif /* MHD_TCP_CORK_NOPUSH */ | ||
613 | 617 | ||
614 | res = socket_start_no_buffering (connection); | 618 | res = socket_start_no_buffering (connection); |
615 | #if defined(TCP_NOPUSH) && !defined(TCP_CORK) | 619 | #if defined(__FreeBSD__) && __FreeBSD__+0 >= 9 |
620 | /* FreeBSD do not need zero-send for flushing starting from version 9 */ | ||
621 | #elif defined(TCP_NOPUSH) && !defined(TCP_CORK) | ||
616 | /* Force flush data with zero send otherwise Darwin and some BSD systems | 622 | /* Force flush data with zero send otherwise Darwin and some BSD systems |
617 | will add 5 seconds delay. Not required with TCP_CORK as switching off | 623 | will add 5 seconds delay. Not required with TCP_CORK as switching off |
618 | TCP_CORK always flushes socket buffer. */ | 624 | TCP_CORK always flushes socket buffer. */ |
@@ -633,50 +639,39 @@ socket_start_no_buffering_flush (struct MHD_Connection *connection) | |||
633 | _MHD_static_inline bool | 639 | _MHD_static_inline bool |
634 | socket_start_normal_buffering (struct MHD_Connection *connection) | 640 | socket_start_normal_buffering (struct MHD_Connection *connection) |
635 | { | 641 | { |
636 | #if defined(TCP_NODELAY) | ||
637 | bool res = true; | ||
638 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | ||
639 | #if defined(TCP_CORK) | ||
640 | MHD_SCKT_OPT_BOOL_ cork_val = 0; | ||
641 | socklen_t param_size = sizeof (cork_val); | ||
642 | #endif /* TCP_CORK */ | ||
643 | mhd_assert(NULL != connection); | 642 | mhd_assert(NULL != connection); |
644 | #if defined(TCP_CORK) | 643 | #if defined(MHD_TCP_CORK_NOPUSH) |
645 | /* Allow partial packets */ | 644 | if (connection->sk_tcp_cork_nopush_on) |
646 | /* Disabling TCP_CORK will flush partial packet even if TCP_CORK wasn't enabled before | 645 | { |
647 | so try to check current value of TCP_CORK to prevent unrequested flushing */ | 646 | const MHD_SCKT_OPT_BOOL_ off_val = 0; |
648 | if ( (0 != getsockopt (connection->socket_fd, | 647 | /* Disable extra buffering */ |
649 | IPPROTO_TCP, | 648 | if (0 == setsockopt (connection->socket_fd, |
650 | TCP_CORK, | ||
651 | (void*)&cork_val, | ||
652 | ¶m_size)) || | ||
653 | (0 != cork_val)) | ||
654 | res = (0 == setsockopt (connection->socket_fd, | ||
655 | IPPROTO_TCP, | ||
656 | TCP_CORK, | ||
657 | (const void *) &off_val, | ||
658 | sizeof (off_val))) && res; | ||
659 | #elif defined(TCP_NOPUSH) | ||
660 | /* Disable extra buffering */ | ||
661 | /* No need to check current value as disabling TCP_NOPUSH will not flush partial | ||
662 | packet if TCP_NOPUSH wasn't enabled before */ | ||
663 | res = (0 == setsockopt (connection->socket_fd, | ||
664 | IPPROTO_TCP, | 649 | IPPROTO_TCP, |
665 | TCP_NOPUSH, | 650 | MHD_TCP_CORK_NOPUSH, |
666 | (const void *) &off_val, | 651 | (const void *) &off_val, |
667 | sizeof (off_val))) && res; | 652 | sizeof (off_val))) |
668 | #endif /* TCP_NOPUSH && !TCP_CORK */ | 653 | { |
669 | /* Enable Nagle's algorithm for normal buffering */ | 654 | connection->sk_tcp_cork_nopush_on = false; |
670 | res = (0 == setsockopt (connection->socket_fd, | 655 | } |
656 | } | ||
657 | #endif /* MHD_TCP_CORK_NOPUSH */ | ||
658 | |||
659 | #if defined(TCP_NODELAY) | ||
660 | if (connection->sk_tcp_nodelay_on) | ||
661 | { | ||
662 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | ||
663 | /* Enable Nagle's algorithm */ | ||
664 | if (0 == setsockopt (connection->socket_fd, | ||
671 | IPPROTO_TCP, | 665 | IPPROTO_TCP, |
672 | TCP_NODELAY, | 666 | TCP_NODELAY, |
673 | (const void *) &off_val, | 667 | (const void *) &off_val, |
674 | sizeof (off_val))) && res; | 668 | sizeof (off_val))) |
675 | return res; | 669 | { |
676 | #else /* !TCP_NODELAY */ | 670 | connection->sk_tcp_nodelay_on = false; |
677 | (void) connection; | 671 | } |
678 | return false; | 672 | } |
679 | #endif /* !TCP_NODELAY */ | 673 | #endif /* TCP_NODELAY */ |
674 | return !connection->sk_tcp_nodelay_on && !connection->sk_tcp_cork_nopush_on; | ||
680 | } | 675 | } |
681 | 676 | ||
682 | 677 | ||
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c index 2c162def..ea12a532 100644 --- a/src/microhttpd/daemon.c +++ b/src/microhttpd/daemon.c | |||
@@ -3020,6 +3020,15 @@ MHD_add_connection (struct MHD_Daemon *daemon, | |||
3020 | _("Failed to set noninheritable mode on new client socket.\n")); | 3020 | _("Failed to set noninheritable mode on new client socket.\n")); |
3021 | #endif | 3021 | #endif |
3022 | } | 3022 | } |
3023 | |||
3024 | if ( (0 == (daemon->options & MHD_USE_TURBO)) && | ||
3025 | (! MHD_socket_buffering_reset_ (client_socket)) ) | ||
3026 | { | ||
3027 | #ifdef HAVE_MESSAGES | ||
3028 | MHD_DLOG (daemon, | ||
3029 | _("Failed to reset buffering mode on new client socket.\n")); | ||
3030 | #endif | ||
3031 | } | ||
3023 | return internal_add_connection (daemon, | 3032 | return internal_add_connection (daemon, |
3024 | client_socket, | 3033 | client_socket, |
3025 | addr, | 3034 | addr, |
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h index aa6af5e8..97db942f 100644 --- a/src/microhttpd/internal.h +++ b/src/microhttpd/internal.h | |||
@@ -601,6 +601,7 @@ enum MHD_ConnKeepAlive | |||
601 | MHD_CONN_USE_KEEPALIVE = 1 | 601 | MHD_CONN_USE_KEEPALIVE = 1 |
602 | }; | 602 | }; |
603 | 603 | ||
604 | |||
604 | /** | 605 | /** |
605 | * State kept for each HTTP request. | 606 | * State kept for each HTTP request. |
606 | */ | 607 | */ |
@@ -863,6 +864,18 @@ struct MHD_Connection | |||
863 | bool sk_nonblck; | 864 | bool sk_nonblck; |
864 | 865 | ||
865 | /** | 866 | /** |
867 | * Indicate whether connection socket has TCP_NODELAY turned on / Nagle’s algorithm turned off. | ||
868 | * TCP_NODELAY should not be turned on when TCP_CORK/TCP_NOPUSH is turned off. | ||
869 | */ | ||
870 | bool sk_tcp_nodelay_on; | ||
871 | |||
872 | /** | ||
873 | * Indicate whether connection socket has TCP_CORK/TCP_NOPUSH turned on. | ||
874 | * TCP_CORK/TCP_NOPUSH should not be turned on when TCP_NODELAY is turned off. | ||
875 | */ | ||
876 | bool sk_tcp_cork_nopush_on; | ||
877 | |||
878 | /** | ||
866 | * Has this socket been closed for reading (i.e. other side closed | 879 | * Has this socket been closed for reading (i.e. other side closed |
867 | * the connection)? If so, we must completely close the connection | 880 | * the connection)? If so, we must completely close the connection |
868 | * once we are done sending our response (and stop trying to read | 881 | * once we are done sending our response (and stop trying to read |
diff --git a/src/microhttpd/mhd_sockets.c b/src/microhttpd/mhd_sockets.c index 56a8a852..3504dc26 100644 --- a/src/microhttpd/mhd_sockets.c +++ b/src/microhttpd/mhd_sockets.c | |||
@@ -462,6 +462,41 @@ MHD_socket_noninheritable_ (MHD_socket sock) | |||
462 | 462 | ||
463 | 463 | ||
464 | /** | 464 | /** |
465 | * Change socket buffering mode to default. | ||
466 | * | ||
467 | * @param sock socket to manipulate | ||
468 | * @return non-zero if succeeded, zero otherwise | ||
469 | */ | ||
470 | int | ||
471 | MHD_socket_buffering_reset_ (MHD_socket sock) | ||
472 | { | ||
473 | int res = !0; | ||
474 | #if defined(TCP_NODELAY) || defined(MHD_TCP_CORK_NOPUSH) | ||
475 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | ||
476 | #if defined(MHD_TCP_CORK_NOPUSH) | ||
477 | /* Disable extra buffering */ | ||
478 | res = (0 == setsockopt (sock, | ||
479 | IPPROTO_TCP, | ||
480 | MHD_TCP_CORK_NOPUSH, | ||
481 | (const void *) &off_val, | ||
482 | sizeof (off_val))) && res; | ||
483 | #endif /* MHD_TCP_CORK_NOPUSH */ | ||
484 | #if defined(TCP_NODELAY) | ||
485 | /* Enable Nagle's algorithm for normal buffering */ | ||
486 | res = (0 == setsockopt (sock, | ||
487 | IPPROTO_TCP, | ||
488 | TCP_NODELAY, | ||
489 | (const void *) &off_val, | ||
490 | sizeof (off_val))) && res; | ||
491 | #endif /* TCP_NODELAY */ | ||
492 | #else /* !TCP_NODELAY && !MHD_TCP_CORK_NOPUSH */ | ||
493 | (void) sock; | ||
494 | #endif /* !TCP_NODELAY && !MHD_TCP_CORK_NOPUSH */ | ||
495 | return res; | ||
496 | } | ||
497 | |||
498 | |||
499 | /** | ||
465 | * Create a listen socket, with noninheritable flag if possible. | 500 | * Create a listen socket, with noninheritable flag if possible. |
466 | * | 501 | * |
467 | * @param pf protocol family to use | 502 | * @param pf protocol family to use |
diff --git a/src/microhttpd/mhd_sockets.h b/src/microhttpd/mhd_sockets.h index 9be34144..8663edd3 100644 --- a/src/microhttpd/mhd_sockets.h +++ b/src/microhttpd/mhd_sockets.h | |||
@@ -194,6 +194,19 @@ | |||
194 | #endif | 194 | #endif |
195 | 195 | ||
196 | 196 | ||
197 | #if defined(TCP_CORK) | ||
198 | /** | ||
199 | * Value of TCP_CORK or TCP_NOPUSH | ||
200 | */ | ||
201 | #define MHD_TCP_CORK_NOPUSH TCP_CORK | ||
202 | #elif defined(TCP_NOPUSH) | ||
203 | /** | ||
204 | * Value of TCP_CORK or TCP_NOPUSH | ||
205 | */ | ||
206 | #define MHD_TCP_CORK_NOPUSH TCP_NOPUSH | ||
207 | #endif /* TCP_NOPUSH */ | ||
208 | |||
209 | |||
197 | /** | 210 | /** |
198 | * MHD_SCKT_OPT_BOOL_ is type for bool parameters for setsockopt()/getsockopt() | 211 | * MHD_SCKT_OPT_BOOL_ is type for bool parameters for setsockopt()/getsockopt() |
199 | */ | 212 | */ |
@@ -736,6 +749,16 @@ int | |||
736 | MHD_socket_noninheritable_ (MHD_socket sock); | 749 | MHD_socket_noninheritable_ (MHD_socket sock); |
737 | 750 | ||
738 | 751 | ||
752 | /** | ||
753 | * Change socket buffering mode to default. | ||
754 | * | ||
755 | * @param sock socket to manipulate | ||
756 | * @return non-zero if succeeded, zero otherwise | ||
757 | */ | ||
758 | int | ||
759 | MHD_socket_buffering_reset_ (MHD_socket sock); | ||
760 | |||
761 | |||
739 | #if defined(SOL_SOCKET) && defined(SO_NOSIGPIPE) | 762 | #if defined(SOL_SOCKET) && defined(SO_NOSIGPIPE) |
740 | static const int _MHD_socket_int_one = 1; | 763 | static const int _MHD_socket_int_one = 1; |
741 | /** | 764 | /** |