aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2018-12-10 22:14:59 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2018-12-10 22:14:59 +0300
commite13c79ee3b5208c8b94538144abe58eab099b3f8 (patch)
treedccd90d3b8b5d5c8a94d6ac8572250e36536bb2e
parent4b5e8e0b45cde5f80f160d1abd42e604503b3544 (diff)
downloadlibmicrohttpd-e13c79ee3b5208c8b94538144abe58eab099b3f8.tar.gz
libmicrohttpd-e13c79ee3b5208c8b94538144abe58eab099b3f8.zip
Track socket CORK/NODELAY states to avoid extra syscalls
-rw-r--r--src/microhttpd/connection.c215
-rw-r--r--src/microhttpd/daemon.c9
-rw-r--r--src/microhttpd/internal.h13
-rw-r--r--src/microhttpd/mhd_sockets.c35
-rw-r--r--src/microhttpd/mhd_sockets.h23
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
497socket_start_extra_buffering (struct MHD_Connection *connection) 501socket_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
554socket_start_no_buffering (struct MHD_Connection *connection) 553socket_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
634socket_start_normal_buffering (struct MHD_Connection *connection) 640socket_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 &param_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 */
470int
471MHD_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
736MHD_socket_noninheritable_ (MHD_socket sock); 749MHD_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 */
758int
759MHD_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/**