aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--ChangeLog11
-rw-r--r--configure.ac22
-rw-r--r--doc/libmicrohttpd.texi4
-rw-r--r--src/include/microhttpd.h12
-rw-r--r--src/microhttpd/Makefile.am1
-rw-r--r--src/microhttpd/connection.c468
-rw-r--r--src/microhttpd/connection_https.c2
-rw-r--r--src/microhttpd/connection_https.h14
-rw-r--r--src/microhttpd/daemon.c14
-rw-r--r--src/microhttpd/internal.h14
-rw-r--r--src/microhttpd/mhd_send.c637
-rw-r--r--src/microhttpd/mhd_send.h97
-rw-r--r--src/microhttpd/mhd_sockets.c88
-rw-r--r--src/microhttpd/mhd_sockets.h38
-rw-r--r--src/microhttpd/response.c44
-rw-r--r--src/microhttpd/test_upgrade.c2
-rw-r--r--src/microhttpd/test_upgrade_large.c2
18 files changed, 1045 insertions, 426 deletions
diff --git a/.gitignore b/.gitignore
index ff6afe00..88f8ab32 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ config.rpath
14/config.log 14/config.log
15/app.info 15/app.info
16/debug 16/debug
17/build-aux
17/exclude 18/exclude
18/autom4te.cache 19/autom4te.cache
19/scripts 20/scripts
diff --git a/ChangeLog b/ChangeLog
index 92161c6f..afedf50c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
1Fri 02 Aug 2019 00:00:00 PM UTC
2 Fixes and optimizations for the setsockopt handling:
3 * Added: MHD_UPGRADE_ACTION_CORK_ON and MHD_UPGRADE_ACTION_CORK_OFF
4 to enum MHD_UpgradeAction (turn corking on/off on the underlying
5 socket).
6 * Use calls and flags native to the system for corking and
7 other operations, tested with performance improvements on
8 FreeBSD, Debian Linux, NetBSD, and cygwin. In particular,
9 this adds selective usage of MSG_MORE, NODELAY, TCP_NOPUSH,
10 TCP_CORK. -ng0
11
1Thu 01 Aug 2019 01:23:36 PM CEST 12Thu 01 Aug 2019 01:23:36 PM CEST
2 Releasing libmicrohttpd 0.9.66. -CG 13 Releasing libmicrohttpd 0.9.66. -CG
3 14
diff --git a/configure.ac b/configure.ac
index 5292798a..1a0870b7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -642,6 +642,28 @@ AM_CONDITIONAL(USE_MS_LIB_TOOL, [test "x$ac_cv_use_ms_lib_tool" = "xyes"])
642MHD_CHECK_SOCKET_SHUTDOWN_TRIGGER([AC_DEFINE([HAVE_LISTEN_SHUTDOWN],[1],[can use shutdown on listen sockets])]) 642MHD_CHECK_SOCKET_SHUTDOWN_TRIGGER([AC_DEFINE([HAVE_LISTEN_SHUTDOWN],[1],[can use shutdown on listen sockets])])
643AM_CONDITIONAL([HAVE_LISTEN_SHUTDOWN], [test "x$mhd_cv_host_shtdwn_trgr_select" = "xyes"]) 643AM_CONDITIONAL([HAVE_LISTEN_SHUTDOWN], [test "x$mhd_cv_host_shtdwn_trgr_select" = "xyes"])
644 644
645# SENDMSG. Should we check for SCM_RIGHTS instead?
646# https://lists.x.org/archives/xorg-devel/2013-November/038687.html
647AC_MSG_CHECKING([whether sendmsg is available])
648AC_SEARCH_LIBS(sendmsg, socket, AC_DEFINE([HAVE_SENDMSG],1,[Define if your platform supports sendmsg]))
649AC_MSG_CHECKING([whether writev is available])
650AC_CHECK_FUNCS([writev])
651
652# check MSG_MORE defined
653AC_MSG_CHECKING([whether MSG_MORE is defined])
654AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
655#include <sys/types.h>
656#include <sys/socket.h>
657]],[[return MSG_MORE;]]
658 )],
659 [
660 AC_MSG_RESULT(yes)
661 AC_DEFINE(HAVE_MSG_MORE, [1], [have MSG_MORE])
662 ],
663 [
664 AC_MSG_RESULT(no)
665 ])
666
645# set GCC options 667# set GCC options
646# use '-fno-strict-aliasing', but only if the compiler 668# use '-fno-strict-aliasing', but only if the compiler
647# and linker can take it 669# and linker can take it
diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi
index 4424337f..d9d53dd7 100644
--- a/doc/libmicrohttpd.texi
+++ b/doc/libmicrohttpd.texi
@@ -2329,6 +2329,10 @@ Set of actions to be performed on upgraded connections. Passed as an argument t
2329@table @code 2329@table @code
2330@item MHD_UPGRADE_ACTION_CLOSE 2330@item MHD_UPGRADE_ACTION_CLOSE
2331Closes the connection. Must be called once the application is done with the client. Takes no additional arguments. 2331Closes the connection. Must be called once the application is done with the client. Takes no additional arguments.
2332@item MHD_UPGRADE_ACTION_CORK_ON
2333Enable corking on the underlying socket.
2334@item MHD_UPGRADE_ACTION_CORK_OFF
2335Disable corking on the underlying socket.
2332 2336
2333@end table 2337@end table
2334@end deftp 2338@end deftp
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index c2fc90a4..00288696 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -3132,7 +3132,17 @@ enum MHD_UpgradeAction
3132 * 3132 *
3133 * Takes no extra arguments. 3133 * Takes no extra arguments.
3134 */ 3134 */
3135 MHD_UPGRADE_ACTION_CLOSE = 0 3135 MHD_UPGRADE_ACTION_CLOSE = 0,
3136
3137 /**
3138 * Enable CORKing on the underlying socket.
3139 */
3140 MHD_UPGRADE_ACTION_CORK_ON = 1,
3141
3142 /**
3143 * Disable CORKing on the underlying socket.
3144 */
3145 MHD_UPGRADE_ACTION_CORK_OFF = 2
3136 3146
3137}; 3147};
3138 3148
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index 8bc60879..597a2d56 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -62,6 +62,7 @@ libmicrohttpd_la_SOURCES = \
62 mhd_limits.h mhd_byteorder.h \ 62 mhd_limits.h mhd_byteorder.h \
63 sysfdsetsize.c sysfdsetsize.h \ 63 sysfdsetsize.c sysfdsetsize.h \
64 mhd_str.c mhd_str.h \ 64 mhd_str.c mhd_str.h \
65 mhd_send.h mhd_send.c \
65 mhd_assert.h \ 66 mhd_assert.h \
66 mhd_sockets.c mhd_sockets.h \ 67 mhd_sockets.c mhd_sockets.h \
67 mhd_itc.c mhd_itc.h mhd_itc_types.h \ 68 mhd_itc.c mhd_itc.h mhd_itc_types.h \
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 6f33dbc1..7b630b77 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -53,7 +53,7 @@
53/* For FreeBSD version identification */ 53/* For FreeBSD version identification */
54#include <sys/param.h> 54#include <sys/param.h>
55#endif /* HAVE_SYS_PARAM_H */ 55#endif /* HAVE_SYS_PARAM_H */
56 56#include "mhd_send.h"
57 57
58/** 58/**
59 * Message to transmit when http 1.1 request is received 59 * Message to transmit when http 1.1 request is received
@@ -278,187 +278,6 @@ send_param_adapter (struct MHD_Connection *connection,
278} 278}
279 279
280 280
281#if defined(_MHD_HAVE_SENDFILE)
282/**
283 * Function for sending responses backed by file FD.
284 *
285 * @param connection the MHD connection structure
286 * @return actual number of bytes sent
287 */
288static ssize_t
289sendfile_adapter (struct MHD_Connection *connection)
290{
291 ssize_t ret;
292 const int file_fd = connection->response->fd;
293 uint64_t left;
294 uint64_t offsetu64;
295#ifndef HAVE_SENDFILE64
296 const uint64_t max_off_t = (uint64_t)OFF_T_MAX;
297#else /* HAVE_SENDFILE64 */
298 const uint64_t max_off_t = (uint64_t)OFF64_T_MAX;
299#endif /* HAVE_SENDFILE64 */
300#ifdef MHD_LINUX_SOLARIS_SENDFILE
301#ifndef HAVE_SENDFILE64
302 off_t offset;
303#else /* HAVE_SENDFILE64 */
304 off64_t offset;
305#endif /* HAVE_SENDFILE64 */
306#endif /* MHD_LINUX_SOLARIS_SENDFILE */
307#ifdef HAVE_FREEBSD_SENDFILE
308 off_t sent_bytes;
309 int flags = 0;
310#endif
311#ifdef HAVE_DARWIN_SENDFILE
312 off_t len;
313#endif /* HAVE_DARWIN_SENDFILE */
314 const bool used_thr_p_c = (0 != (connection->daemon->options & MHD_USE_THREAD_PER_CONNECTION));
315 const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : MHD_SENFILE_CHUNK_;
316 size_t send_size = 0;
317 mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender);
318
319 offsetu64 = connection->response_write_position + connection->response->fd_off;
320 left = connection->response->total_size - connection->response_write_position;
321 /* Do not allow system to stick sending on single fast connection:
322 * use 128KiB chunks (2MiB for thread-per-connection). */
323 send_size = (left > chunk_size) ? chunk_size : (size_t) left;
324 if (max_off_t < offsetu64)
325 { /* Retry to send with standard 'send()'. */
326 connection->resp_sender = MHD_resp_sender_std;
327 return MHD_ERR_AGAIN_;
328 }
329#ifdef MHD_LINUX_SOLARIS_SENDFILE
330#ifndef HAVE_SENDFILE64
331 offset = (off_t) offsetu64;
332 ret = sendfile (connection->socket_fd,
333 file_fd,
334 &offset,
335 send_size);
336#else /* HAVE_SENDFILE64 */
337 offset = (off64_t) offsetu64;
338 ret = sendfile64 (connection->socket_fd,
339 file_fd,
340 &offset,
341 send_size);
342#endif /* HAVE_SENDFILE64 */
343 if (0 > ret)
344 {
345 const int err = MHD_socket_get_error_();
346 if (MHD_SCKT_ERR_IS_EAGAIN_(err))
347 {
348#ifdef EPOLL_SUPPORT
349 /* EAGAIN --- no longer write-ready */
350 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
351#endif /* EPOLL_SUPPORT */
352 return MHD_ERR_AGAIN_;
353 }
354 if (MHD_SCKT_ERR_IS_EINTR_ (err))
355 return MHD_ERR_AGAIN_;
356#ifdef HAVE_LINUX_SENDFILE
357 if (MHD_SCKT_ERR_IS_(err,
358 MHD_SCKT_EBADF_))
359 return MHD_ERR_BADF_;
360 /* sendfile() failed with EINVAL if mmap()-like operations are not
361 supported for FD or other 'unusual' errors occurred, so we should try
362 to fall back to 'SEND'; see also this thread for info on
363 odd libc/Linux behavior with sendfile:
364 http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */
365 connection->resp_sender = MHD_resp_sender_std;
366 return MHD_ERR_AGAIN_;
367#else /* HAVE_SOLARIS_SENDFILE */
368 if ( (EAFNOSUPPORT == err) ||
369 (EINVAL == err) ||
370 (EOPNOTSUPP == err) )
371 { /* Retry with standard file reader. */
372 connection->resp_sender = MHD_resp_sender_std;
373 return MHD_ERR_AGAIN_;
374 }
375 if ( (ENOTCONN == err) ||
376 (EPIPE == err) )
377 {
378 return MHD_ERR_CONNRESET_;
379 }
380 return MHD_ERR_BADF_; /* Fail hard */
381#endif /* HAVE_SOLARIS_SENDFILE */
382 }
383#ifdef EPOLL_SUPPORT
384 else if (send_size > (size_t)ret)
385 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
386#endif /* EPOLL_SUPPORT */
387#elif defined(HAVE_FREEBSD_SENDFILE)
388#ifdef SF_FLAGS
389 flags = used_thr_p_c ?
390 freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
391#endif /* SF_FLAGS */
392 if (0 != sendfile (file_fd,
393 connection->socket_fd,
394 (off_t) offsetu64,
395 send_size,
396 NULL,
397 &sent_bytes,
398 flags))
399 {
400 const int err = MHD_socket_get_error_();
401 if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
402 MHD_SCKT_ERR_IS_EINTR_(err) ||
403 EBUSY == err)
404 {
405 mhd_assert (SSIZE_MAX >= sent_bytes);
406 if (0 != sent_bytes)
407 return (ssize_t)sent_bytes;
408
409 return MHD_ERR_AGAIN_;
410 }
411 /* Some unrecoverable error. Possibly file FD is not suitable
412 * for sendfile(). Retry with standard send(). */
413 connection->resp_sender = MHD_resp_sender_std;
414 return MHD_ERR_AGAIN_;
415 }
416 mhd_assert (0 < sent_bytes);
417 mhd_assert (SSIZE_MAX >= sent_bytes);
418 ret = (ssize_t)sent_bytes;
419#elif defined(HAVE_DARWIN_SENDFILE)
420 len = (off_t)send_size; /* chunk always fit */
421 if (0 != sendfile (file_fd,
422 connection->socket_fd,
423 (off_t) offsetu64,
424 &len,
425 NULL,
426 0))
427 {
428 const int err = MHD_socket_get_error_();
429 if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
430 MHD_SCKT_ERR_IS_EINTR_(err))
431 {
432 mhd_assert (0 <= len);
433 mhd_assert (SSIZE_MAX >= len);
434 mhd_assert (send_size >= (size_t)len);
435 if (0 != len)
436 return (ssize_t)len;
437
438 return MHD_ERR_AGAIN_;
439 }
440 if (ENOTCONN == err ||
441 EPIPE == err)
442 return MHD_ERR_CONNRESET_;
443 if (ENOTSUP == err ||
444 EOPNOTSUPP == err)
445 { /* This file FD is not suitable for sendfile().
446 * Retry with standard send(). */
447 connection->resp_sender = MHD_resp_sender_std;
448 return MHD_ERR_AGAIN_;
449 }
450 return MHD_ERR_BADF_; /* Return hard error. */
451 }
452 mhd_assert (0 <= len);
453 mhd_assert (SSIZE_MAX >= len);
454 mhd_assert (send_size >= (size_t)len);
455 ret = (ssize_t)len;
456#endif /* HAVE_FREEBSD_SENDFILE */
457 return ret;
458}
459#endif /* _MHD_HAVE_SENDFILE */
460
461
462/** 281/**
463 * Check whether is possible to force push socket buffer content as 282 * Check whether is possible to force push socket buffer content as
464 * partial packet. 283 * partial packet.
@@ -501,89 +320,6 @@ _MHD_static_inline bool
501socket_start_extra_buffering (struct MHD_Connection *connection) 320socket_start_extra_buffering (struct MHD_Connection *connection)
502{ 321{
503 mhd_assert(NULL != connection); 322 mhd_assert(NULL != connection);
504#if defined(TCP_NODELAY)
505 if (connection->sk_tcp_nodelay_on)
506 {
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,
511 IPPROTO_TCP,
512 TCP_NODELAY,
513 (const void *) &off_val,
514 sizeof (off_val)))
515 {
516 connection->sk_tcp_nodelay_on = false;
517 }
518 }
519#endif /* TCP_NODELAY */
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
538#if defined(TCP_NODELAY)
539 return connection->sk_tcp_cork_nopush_on && !connection->sk_tcp_nodelay_on;
540#else /* ! TCP_NODELAY */
541 return connection->sk_tcp_cork_nopush_on;
542#endif /* ! TCP_NODELAY */
543}
544
545
546/**
547 * Activate no buffering mode (no delay sending) on connection socket.
548 *
549 * @param connection connection to be processed
550 * @return true on success, false otherwise
551 */
552_MHD_static_inline bool
553socket_start_no_buffering (struct MHD_Connection *connection)
554{
555#if defined(MHD_TCP_CORK_NOPUSH)
556 if (connection->sk_tcp_cork_nopush_on)
557 {
558 const MHD_SCKT_OPT_BOOL_ off_val = 0;
559 /* Disable extra buffering */
560 if (0 == setsockopt (connection->socket_fd,
561 IPPROTO_TCP,
562 MHD_TCP_CORK_NOPUSH,
563 (const void *) &off_val,
564 sizeof (off_val)))
565 {
566 connection->sk_tcp_cork_nopush_on = false;
567 }
568 }
569#endif /* MHD_TCP_CORK_NOPUSH */
570
571#if defined(TCP_NODELAY)
572 if (!connection->sk_tcp_nodelay_on)
573 {
574 const MHD_SCKT_OPT_BOOL_ on_val = 1;
575 /* Enable sending without delay */
576 if (0 == setsockopt (connection->socket_fd,
577 IPPROTO_TCP,
578 TCP_NODELAY,
579 (const void *) &on_val,
580 sizeof (on_val)))
581 {
582 connection->sk_tcp_nodelay_on = true;
583 }
584 }
585#endif /* TCP_NODELAY */
586 return connection->sk_tcp_nodelay_on && !connection->sk_tcp_cork_nopush_on;
587} 323}
588 324
589 325
@@ -601,21 +337,7 @@ socket_start_no_buffering_flush (struct MHD_Connection *connection)
601#if defined(TCP_NOPUSH) && !defined(TCP_CORK) 337#if defined(TCP_NOPUSH) && !defined(TCP_CORK)
602 const int dummy = 0; 338 const int dummy = 0;
603#endif /* !TCP_CORK */ 339#endif /* !TCP_CORK */
604#if defined(TCP_CORK) || (defined(__FreeBSD__) && __FreeBSD__+0 >= 9)
605 const MHD_SCKT_OPT_BOOL_ off_val = 0;
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 */
617 340
618 res = socket_start_no_buffering (connection);
619#if defined(__FreeBSD__) && __FreeBSD__+0 >= 9 341#if defined(__FreeBSD__) && __FreeBSD__+0 >= 9
620 /* FreeBSD do not need zero-send for flushing starting from version 9 */ 342 /* FreeBSD do not need zero-send for flushing starting from version 9 */
621#elif defined(TCP_NOPUSH) && !defined(TCP_CORK) 343#elif defined(TCP_NOPUSH) && !defined(TCP_CORK)
@@ -631,51 +353,6 @@ socket_start_no_buffering_flush (struct MHD_Connection *connection)
631 353
632 354
633/** 355/**
634 * Activate normal buffering mode on connection socket.
635 *
636 * @param connection connection to be processed
637 * @return true on success, false otherwise
638 */
639_MHD_static_inline bool
640socket_start_normal_buffering (struct MHD_Connection *connection)
641{
642 mhd_assert(NULL != connection);
643#if defined(MHD_TCP_CORK_NOPUSH)
644 if (connection->sk_tcp_cork_nopush_on)
645 {
646 const MHD_SCKT_OPT_BOOL_ off_val = 0;
647 /* Disable extra buffering */
648 if (0 == setsockopt (connection->socket_fd,
649 IPPROTO_TCP,
650 MHD_TCP_CORK_NOPUSH,
651 (const void *) &off_val,
652 sizeof (off_val)))
653 {
654 connection->sk_tcp_cork_nopush_on = false;
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,
665 IPPROTO_TCP,
666 TCP_NODELAY,
667 (const void *) &off_val,
668 sizeof (off_val)))
669 {
670 connection->sk_tcp_nodelay_on = false;
671 }
672 }
673#endif /* TCP_NODELAY */
674 return !connection->sk_tcp_nodelay_on && !connection->sk_tcp_cork_nopush_on;
675}
676
677
678/**
679 * Get all of the headers from the request. 356 * Get all of the headers from the request.
680 * 357 *
681 * @param connection connection to get values from 358 * @param connection connection to get values from
@@ -3315,11 +2992,12 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
3315 case MHD_CONNECTION_HEADERS_PROCESSED: 2992 case MHD_CONNECTION_HEADERS_PROCESSED:
3316 return; 2993 return;
3317 case MHD_CONNECTION_CONTINUE_SENDING: 2994 case MHD_CONNECTION_CONTINUE_SENDING:
3318 ret = connection->send_cls (connection, 2995 ret = MHD_send_on_connection_ (connection,
3319 &HTTP_100_CONTINUE 2996 &HTTP_100_CONTINUE
3320 [connection->continue_message_write_offset], 2997 [connection->continue_message_write_offset],
3321 MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE) - 2998 MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE) -
3322 connection->continue_message_write_offset); 2999 connection->continue_message_write_offset,
3000 MHD_SSO_NO_CORK);
3323 if (ret < 0) 3001 if (ret < 0)
3324 { 3002 {
3325 if (MHD_ERR_AGAIN_ == ret) 3003 if (MHD_ERR_AGAIN_ == ret)
@@ -3349,26 +3027,55 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
3349 mhd_assert (0); 3027 mhd_assert (0);
3350 return; 3028 return;
3351 case MHD_CONNECTION_HEADERS_SENDING: 3029 case MHD_CONNECTION_HEADERS_SENDING:
3352 ret = connection->send_cls (connection, 3030 {
3353 &connection->write_buffer 3031 const size_t wb_ready = connection->write_buffer_append_offset -
3354 [connection->write_buffer_send_offset], 3032 connection->write_buffer_send_offset;
3355 connection->write_buffer_append_offset - 3033
3356 connection->write_buffer_send_offset); 3034 /* if the response body is not available, we use MHD_send_on_connection_() */
3357 if (ret < 0) 3035 if (NULL != connection->response->crc)
3358 { 3036 {
3359 if (MHD_ERR_AGAIN_ == ret) 3037 ret = MHD_send_on_connection_ (connection,
3038 &connection->write_buffer
3039 [connection->write_buffer_send_offset],
3040 wb_ready,
3041 MHD_SSO_MAY_CORK);
3042 }
3043 else
3044 {
3045 ret = MHD_send_on_connection2_ (connection,
3046 &connection->write_buffer
3047 [connection->write_buffer_send_offset],
3048 wb_ready,
3049 connection->response->data,
3050 connection->response->data_buffer_size);
3051 }
3052
3053 if (ret < 0)
3054 {
3055 if (MHD_ERR_AGAIN_ == ret)
3056 return;
3057 CONNECTION_CLOSE_ERROR (connection,
3058 _("Connection was closed while sending response headers.\n"));
3360 return; 3059 return;
3361 CONNECTION_CLOSE_ERROR (connection, 3060 }
3362 _("Connection was closed while sending response headers.\n")); 3061 if (ret > wb_ready)
3062 {
3063 mhd_assert (NULL == connection->repsonse->crc);
3064 /* We sent not just header data but also some response data,
3065 update both offsets! */
3066 connection->write_buffer_send_offset += wb_ready;
3067 ret -= wb_ready;
3068 connection->response_write_position += ret;
3069 }
3070 else
3071 connection->write_buffer_send_offset += ret;
3072 MHD_update_last_activity_ (connection);
3073 if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
3363 return; 3074 return;
3364 } 3075 check_write_done (connection,
3365 connection->write_buffer_send_offset += ret; 3076 MHD_CONNECTION_HEADERS_SENT);
3366 MHD_update_last_activity_ (connection);
3367 if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
3368 return; 3077 return;
3369 check_write_done (connection, 3078 }
3370 MHD_CONNECTION_HEADERS_SENT);
3371 return;
3372 case MHD_CONNECTION_HEADERS_SENT: 3079 case MHD_CONNECTION_HEADERS_SENT:
3373 return; 3080 return;
3374 case MHD_CONNECTION_NORMAL_BODY_READY: 3081 case MHD_CONNECTION_NORMAL_BODY_READY:
@@ -3390,7 +3097,7 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
3390#if defined(_MHD_HAVE_SENDFILE) 3097#if defined(_MHD_HAVE_SENDFILE)
3391 if (MHD_resp_sender_sendfile == connection->resp_sender) 3098 if (MHD_resp_sender_sendfile == connection->resp_sender)
3392 { 3099 {
3393 ret = sendfile_adapter (connection); 3100 ret = MHD_send_sendfile_ (connection);
3394 } 3101 }
3395 else 3102 else
3396#else /* ! _MHD_HAVE_SENDFILE */ 3103#else /* ! _MHD_HAVE_SENDFILE */
@@ -3401,11 +3108,12 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
3401 - response->data_start; 3108 - response->data_start;
3402 if (data_write_offset > (uint64_t)SIZE_MAX) 3109 if (data_write_offset > (uint64_t)SIZE_MAX)
3403 MHD_PANIC (_("Data offset exceeds limit")); 3110 MHD_PANIC (_("Data offset exceeds limit"));
3404 ret = connection->send_cls (connection, 3111 ret = MHD_send_on_connection_ (connection,
3405 &response->data 3112 &response->data
3406 [(size_t)data_write_offset], 3113 [(size_t)data_write_offset],
3407 response->data_size - 3114 response->data_size -
3408 (size_t)data_write_offset); 3115 (size_t)data_write_offset,
3116 MHD_SSO_NO_CORK);
3409#if DEBUG_SEND_DATA 3117#if DEBUG_SEND_DATA
3410 if (ret > 0) 3118 if (ret > 0)
3411 fprintf (stderr, 3119 fprintf (stderr,
@@ -3444,11 +3152,12 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
3444 mhd_assert (0); 3152 mhd_assert (0);
3445 return; 3153 return;
3446 case MHD_CONNECTION_CHUNKED_BODY_READY: 3154 case MHD_CONNECTION_CHUNKED_BODY_READY:
3447 ret = connection->send_cls (connection, 3155 ret = MHD_send_on_connection_ (connection,
3448 &connection->write_buffer 3156 &connection->write_buffer
3449 [connection->write_buffer_send_offset], 3157 [connection->write_buffer_send_offset],
3450 connection->write_buffer_append_offset - 3158 connection->write_buffer_append_offset -
3451 connection->write_buffer_send_offset); 3159 connection->write_buffer_send_offset,
3160 MHD_SSO_NO_CORK);
3452 if (ret < 0) 3161 if (ret < 0)
3453 { 3162 {
3454 if (MHD_ERR_AGAIN_ == ret) 3163 if (MHD_ERR_AGAIN_ == ret)
@@ -3472,11 +3181,12 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
3472 mhd_assert (0); 3181 mhd_assert (0);
3473 return; 3182 return;
3474 case MHD_CONNECTION_FOOTERS_SENDING: 3183 case MHD_CONNECTION_FOOTERS_SENDING:
3475 ret = connection->send_cls (connection, 3184 ret = MHD_send_on_connection_ (connection,
3476 &connection->write_buffer 3185 &connection->write_buffer
3477 [connection->write_buffer_send_offset], 3186 [connection->write_buffer_send_offset],
3478 connection->write_buffer_append_offset - 3187 connection->write_buffer_append_offset -
3479 connection->write_buffer_send_offset); 3188 connection->write_buffer_send_offset,
3189 MHD_SSO_HDR_CORK);
3480 if (ret < 0) 3190 if (ret < 0)
3481 { 3191 {
3482 if (MHD_ERR_AGAIN_ == ret) 3192 if (MHD_ERR_AGAIN_ == ret)
@@ -3723,11 +3433,6 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
3723 if (need_100_continue (connection)) 3433 if (need_100_continue (connection))
3724 { 3434 {
3725 connection->state = MHD_CONNECTION_CONTINUE_SENDING; 3435 connection->state = MHD_CONNECTION_CONTINUE_SENDING;
3726 if (socket_flush_possible (connection))
3727 socket_start_extra_buffering (connection);
3728 else
3729 socket_start_no_buffering (connection);
3730
3731 break; 3436 break;
3732 } 3437 }
3733 if ( (NULL != connection->response) && 3438 if ( (NULL != connection->response) &&
@@ -3752,9 +3457,7 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
3752 { 3457 {
3753 connection->state = MHD_CONNECTION_CONTINUE_SENT; 3458 connection->state = MHD_CONNECTION_CONTINUE_SENT;
3754 if (socket_flush_possible (connection)) 3459 if (socket_flush_possible (connection))
3755 socket_start_no_buffering_flush (connection); 3460 socket_start_no_buffering_flush (connection); /* REMOVE: Dead */
3756 else
3757 socket_start_normal_buffering (connection);
3758 3461
3759 continue; 3462 continue;
3760 } 3463 }
@@ -3855,11 +3558,6 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
3855 continue; 3558 continue;
3856 } 3559 }
3857 connection->state = MHD_CONNECTION_HEADERS_SENDING; 3560 connection->state = MHD_CONNECTION_HEADERS_SENDING;
3858 if (socket_flush_possible (connection))
3859 socket_start_extra_buffering (connection);
3860 else
3861 socket_start_no_buffering (connection);
3862
3863 break; 3561 break;
3864 case MHD_CONNECTION_HEADERS_SENDING: 3562 case MHD_CONNECTION_HEADERS_SENDING:
3865 /* no default action */ 3563 /* no default action */
@@ -3867,12 +3565,11 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
3867 case MHD_CONNECTION_HEADERS_SENT: 3565 case MHD_CONNECTION_HEADERS_SENT:
3868 /* Some clients may take some actions right after header receive */ 3566 /* Some clients may take some actions right after header receive */
3869 if (socket_flush_possible (connection)) 3567 if (socket_flush_possible (connection))
3870 socket_start_no_buffering_flush (connection); 3568 socket_start_no_buffering_flush (connection); /* REMOVE: Dead */
3871 3569
3872#ifdef UPGRADE_SUPPORT 3570#ifdef UPGRADE_SUPPORT
3873 if (NULL != connection->response->upgrade_handler) 3571 if (NULL != connection->response->upgrade_handler)
3874 { 3572 {
3875 socket_start_normal_buffering (connection);
3876 connection->state = MHD_CONNECTION_UPGRADE; 3573 connection->state = MHD_CONNECTION_UPGRADE;
3877 /* This connection is "upgraded". Pass socket to application. */ 3574 /* This connection is "upgraded". Pass socket to application. */
3878 if (MHD_YES != 3575 if (MHD_YES !=
@@ -3894,10 +3591,6 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
3894 continue; 3591 continue;
3895 } 3592 }
3896#endif /* UPGRADE_SUPPORT */ 3593#endif /* UPGRADE_SUPPORT */
3897 if (socket_flush_possible (connection))
3898 socket_start_extra_buffering (connection);
3899 else
3900 socket_start_normal_buffering (connection);
3901 3594
3902 if (connection->have_chunked_upload) 3595 if (connection->have_chunked_upload)
3903 connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY; 3596 connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
@@ -3929,8 +3622,7 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
3929#endif 3622#endif
3930 connection->state = MHD_CONNECTION_NORMAL_BODY_READY; 3623 connection->state = MHD_CONNECTION_NORMAL_BODY_READY;
3931 /* Buffering for flushable socket was already enabled*/ 3624 /* Buffering for flushable socket was already enabled*/
3932 if (socket_flush_possible (connection)) 3625
3933 socket_start_no_buffering (connection);
3934 break; 3626 break;
3935 } 3627 }
3936 /* mutex was already unlocked by "try_ready_normal_body */ 3628 /* mutex was already unlocked by "try_ready_normal_body */
@@ -3963,8 +3655,7 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
3963#endif 3655#endif
3964 connection->state = MHD_CONNECTION_CHUNKED_BODY_READY; 3656 connection->state = MHD_CONNECTION_CHUNKED_BODY_READY;
3965 /* Buffering for flushable socket was already enabled */ 3657 /* Buffering for flushable socket was already enabled */
3966 if (socket_flush_possible (connection)) 3658
3967 socket_start_no_buffering (connection);
3968 continue; 3659 continue;
3969 } 3660 }
3970 /* mutex was already unlocked by try_ready_chunked_body */ 3661 /* mutex was already unlocked by try_ready_chunked_body */
@@ -3998,9 +3689,7 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
3998 continue; 3689 continue;
3999 } 3690 }
4000 if (socket_flush_possible (connection)) 3691 if (socket_flush_possible (connection))
4001 socket_start_no_buffering_flush (connection); 3692 socket_start_no_buffering_flush (connection); /* REMOVE: Dead */
4002 else
4003 socket_start_normal_buffering (connection);
4004 3693
4005 MHD_destroy_response (connection->response); 3694 MHD_destroy_response (connection->response);
4006 connection->response = NULL; 3695 connection->response = NULL;
@@ -4028,8 +3717,7 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
4028 else 3717 else
4029 { 3718 {
4030 /* can try to keep-alive */ 3719 /* can try to keep-alive */
4031 if (socket_flush_possible (connection)) 3720
4032 socket_start_normal_buffering (connection);
4033 connection->version = NULL; 3721 connection->version = NULL;
4034 connection->state = MHD_CONNECTION_INIT; 3722 connection->state = MHD_CONNECTION_INIT;
4035 connection->last = NULL; 3723 connection->last = NULL;
diff --git a/src/microhttpd/connection_https.c b/src/microhttpd/connection_https.c
index 5efced33..8202329b 100644
--- a/src/microhttpd/connection_https.c
+++ b/src/microhttpd/connection_https.c
@@ -98,7 +98,7 @@ recv_tls_adapter (struct MHD_Connection *connection,
98 * @return positive value for number of bytes actually sent or 98 * @return positive value for number of bytes actually sent or
99 * negative value for error number MHD_ERR_xxx_ 99 * negative value for error number MHD_ERR_xxx_
100 */ 100 */
101static ssize_t 101ssize_t
102send_tls_adapter (struct MHD_Connection *connection, 102send_tls_adapter (struct MHD_Connection *connection,
103 const void *other, 103 const void *other,
104 size_t i) 104 size_t i)
diff --git a/src/microhttpd/connection_https.h b/src/microhttpd/connection_https.h
index 1c12ea9f..e91a84d3 100644
--- a/src/microhttpd/connection_https.h
+++ b/src/microhttpd/connection_https.h
@@ -60,6 +60,20 @@ MHD_run_tls_handshake_ (struct MHD_Connection *connection);
60 */ 60 */
61bool 61bool
62MHD_tls_connection_shutdown (struct MHD_Connection *connection); 62MHD_tls_connection_shutdown (struct MHD_Connection *connection);
63
64/**
65 * Callback for writing data to the socket.
66 *
67 * @param connection the MHD connection structure
68 * @param other data to write
69 * @param i number of bytes to write
70 * @return positive value for number of bytes actually sent or
71 * negative value for error number MHD_ERR_xxx_
72 */
73ssize_t
74send_tls_adapter (struct MHD_Connection *connection,
75 const void *other,
76 size_t i);
63#endif /* HTTPS_SUPPORT */ 77#endif /* HTTPS_SUPPORT */
64 78
65#endif 79#endif
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index a8fc98c6..fb4abd5d 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -3229,6 +3229,19 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
3229 } 3229 }
3230 return MHD_NO; 3230 return MHD_NO;
3231 } 3231 }
3232#if defined(MHD_TCP_CORK_NOPUSH) || defined(HAVE_MSG_MORE)
3233 /* We will use TCP_CORK or TCP_NOPUSH or MSG_MORE to control
3234 transmission, disable Nagle's algorithm (always) */
3235 if (0 != MHD_socket_set_nodelay_ (s,
3236 true))
3237 {
3238#ifdef HAVE_MESSAGES
3239 MHD_DLOG (daemon,
3240 _("Failed to disable TCP Nagle on socket: %s\n"),
3241 MHD_socket_last_strerr_());
3242 }
3243#endif
3244#endif
3232#if !defined(USE_ACCEPT4) || !defined(HAVE_SOCK_NONBLOCK) 3245#if !defined(USE_ACCEPT4) || !defined(HAVE_SOCK_NONBLOCK)
3233 if (! MHD_socket_nonblocking_ (s)) 3246 if (! MHD_socket_nonblocking_ (s))
3234 { 3247 {
@@ -6228,7 +6241,6 @@ MHD_start_daemon_va (unsigned int flags,
6228 } 6241 }
6229 } 6242 }
6230#endif /* HAVE_GETSOCKNAME */ 6243#endif /* HAVE_GETSOCKNAME */
6231
6232 if ( (MHD_INVALID_SOCKET != listen_fd) && 6244 if ( (MHD_INVALID_SOCKET != listen_fd) &&
6233 (! MHD_socket_nonblocking_ (listen_fd)) ) 6245 (! MHD_socket_nonblocking_ (listen_fd)) )
6234 { 6246 {
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index 1f5aeaf3..ac43d819 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -871,19 +871,13 @@ struct MHD_Connection
871 /** 871 /**
872 * true if #socket_fd is non-blocking, false otherwise. 872 * true if #socket_fd is non-blocking, false otherwise.
873 */ 873 */
874 bool sk_nonblck; 874 bool sk_nonblck; // FIXME: hopefully dead?
875 875
876 /** 876 /**
877 * Indicate whether connection socket has TCP_NODELAY turned on / Nagle’s algorithm turned off. 877 * Indicate whether connection socket has TCP_CORK / Nagle’s algorithm turned on/off
878 * TCP_NODELAY should not be turned on when TCP_CORK/TCP_NOPUSH is turned off. 878 * on this socket.
879 */ 879 */
880 bool sk_tcp_nodelay_on; 880 bool sk_cork_on;
881
882 /**
883 * Indicate whether connection socket has TCP_CORK/TCP_NOPUSH turned on.
884 * TCP_CORK/TCP_NOPUSH should not be turned on when TCP_NODELAY is turned off.
885 */
886 bool sk_tcp_cork_nopush_on;
887 881
888 /** 882 /**
889 * Has this socket been closed for reading (i.e. other side closed 883 * Has this socket been closed for reading (i.e. other side closed
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 */
47static void
48pre_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 */
126static void
127post_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 */
220ssize_t
221MHD_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 */
364ssize_t
365MHD_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 */
441static int freebsd_sendfile_flags_;
442
443/**
444 * FreeBSD sendfile() flags for thread-per-connection
445 */
446static 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 */
458ssize_t
459MHD_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 */
diff --git a/src/microhttpd/mhd_send.h b/src/microhttpd/mhd_send.h
new file mode 100644
index 00000000..a766c8c0
--- /dev/null
+++ b/src/microhttpd/mhd_send.h
@@ -0,0 +1,97 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2019 ng0
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 mhd_send.h
23 * @brief Implementation of send() wrappers.
24 * @author ng0
25 */
26
27#ifndef MHD_SEND_H
28#define MHD_SEND_H
29
30#include "platform.h"
31#include "internal.h"
32#if defined(HAVE_STDBOOL_H)
33#include <stdbool.h>
34#endif /* HAVE_STDBOOL_H */
35#include <errno.h>
36#include "mhd_sockets.h"
37#include "connection.h"
38#include "connection_https.h"
39
40#ifdef MHD_LINUX_SOLARIS_SENDFILE
41#include <sys/sendfile.h>
42#endif /* MHD_LINUX_SOLARIS_SENDFILE */
43#if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE)
44#include <sys/types.h>
45#include <sys/socket.h>
46#include <sys/uio.h>
47#endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */
48
49#ifdef HAVE_SYS_PARAM_H
50/* For FreeBSD version identification */
51#include <sys/param.h>
52#endif /* HAVE_SYS_PARAM_H */
53
54/**
55 * The enumeration of send socket options.
56 */
57enum MHD_SendSocketOptions
58{
59 /**
60 * definitely no corking (use NODELAY, or explicitly disable cork)
61 */
62 MHD_SSO_NO_CORK = 0,
63 /**
64 * should enable corking (use MSG_MORE, or explicitly enable cork)
65 */
66 MHD_SSO_MAY_CORK = 1,
67 /**
68 * consider tcpi_snd_mss and consider not corking for the header
69 * part if the size of the header is close to the MSS.
70 * Only used if we are NOT doing 100 Continue and are still
71 * sending the header (provided in full as the buffer to
72 * MHD_send_on_connection_ or as the header to
73 * MHD_send_on_connection2_).
74 */
75 MHD_SSO_HDR_CORK = 2
76};
77
78
79ssize_t
80MHD_send_on_connection_ (struct MHD_Connection *connection,
81 const char *buffer,
82 size_t buffer_size,
83 enum MHD_SendSocketOptions options);
84
85ssize_t
86MHD_send_on_connection2_ (struct MHD_Connection *connection,
87 const char *header,
88 size_t header_size,
89 const char *buffer,
90 size_t buffer_size);
91
92#if defined(_MHD_HAVE_SENDFILE)
93ssize_t
94MHD_send_sendfile_ (struct MHD_Connection *connection);
95#endif
96
97#endif /* MHD_SEND_H */
diff --git a/src/microhttpd/mhd_sockets.c b/src/microhttpd/mhd_sockets.c
index 04405945..b4e73480 100644
--- a/src/microhttpd/mhd_sockets.c
+++ b/src/microhttpd/mhd_sockets.c
@@ -462,6 +462,64 @@ MHD_socket_noninheritable_ (MHD_socket sock)
462 462
463 463
464/** 464/**
465 * Disable Nagle's algorithm on @a sock. This is what we do by default for
466 * all TCP sockets in MHD, unless the platform does not support the MSG_MORE
467 * or MSG_CORK or MSG_NOPUSH options.
468 *
469 * @param sock socket to manipulate
470 * @param on value to use
471 * @return 0 on success
472 */
473int
474MHD_socket_set_nodelay_ (MHD_socket sock,
475 bool on)
476{
477#ifdef TCP_NODELAY
478 {
479 const MHD_SCKT_OPT_BOOL_ off_val = 0;
480 const MHD_SCKT_OPT_BOOL_ on_val = 1;
481 /* Disable Nagle's algorithm for normal buffering */
482 return setsockopt (sock,
483 IPPROTO_TCP,
484 TCP_NODELAY,
485 (const void *) (on) ? &on_val : &off_val,
486 sizeof (on_val));
487 }
488#else
489 (void) sock;
490 return 0;
491#endif /* TCP_NODELAY */
492}
493
494
495/**
496 * Enable/disable the cork option.
497 *
498 * @param sock socket to manipulate
499 * @param on set to true to enable CORK, false to disable
500 * @return non-zero if succeeded, zero otherwise
501 */
502int
503MHD_socket_cork_ (MHD_socket sock,
504 bool on)
505{
506#if defined(MHD_TCP_CORK_NOPUSH)
507 const MHD_SCKT_OPT_BOOL_ off_val = 0;
508 const MHD_SCKT_OPT_BOOL_ on_val = 1;
509
510 /* Disable extra buffering */
511 return (0 == setsockopt (sock,
512 IPPROTO_TCP,
513 MHD_TCP_CORK_NOPUSH,
514 (const void *) (on ? &on_val : &off_val),
515 sizeof (off_val)));
516#else
517 return 0;
518#endif /* MHD_TCP_CORK_NOPUSH */
519}
520
521
522/**
465 * Change socket buffering mode to default. 523 * Change socket buffering mode to default.
466 * 524 *
467 * @param sock socket to manipulate 525 * @param sock socket to manipulate
@@ -470,29 +528,19 @@ MHD_socket_noninheritable_ (MHD_socket sock)
470int 528int
471MHD_socket_buffering_reset_ (MHD_socket sock) 529MHD_socket_buffering_reset_ (MHD_socket sock)
472{ 530{
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) 531#if defined(MHD_TCP_CORK_NOPUSH)
532 int res = ! MHD_socket_set_nodelay_ (sock,
533 true);
477 /* Disable extra buffering */ 534 /* Disable extra buffering */
478 res = (0 == setsockopt (sock, 535 return MHD_socket_cork_ (sock,
479 IPPROTO_TCP, 536 false) && res;
480 MHD_TCP_CORK_NOPUSH, 537#elif defined(HAVE_MSG_MORE)
481 (const void *) &off_val, 538 return ! MHD_socket_set_nodelay_ (sock,
482 sizeof (off_val))) && res; 539 true);
540#else
541 return ! MHD_socket_set_nodelay_ (sock,
542 false);
483#endif /* MHD_TCP_CORK_NOPUSH */ 543#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} 544}
497 545
498 546
diff --git a/src/microhttpd/mhd_sockets.h b/src/microhttpd/mhd_sockets.h
index a684d71d..08b01c20 100644
--- a/src/microhttpd/mhd_sockets.h
+++ b/src/microhttpd/mhd_sockets.h
@@ -35,6 +35,7 @@
35#include "mhd_options.h" 35#include "mhd_options.h"
36 36
37#include <errno.h> 37#include <errno.h>
38#include <stdbool.h>
38 39
39#if !defined(MHD_POSIX_SOCKETS) && !defined(MHD_WINSOCK_SOCKETS) 40#if !defined(MHD_POSIX_SOCKETS) && !defined(MHD_WINSOCK_SOCKETS)
40# if !defined(_WIN32) || defined(__CYGWIN__) 41# if !defined(_WIN32) || defined(__CYGWIN__)
@@ -745,6 +746,19 @@ MHD_socket_nonblocking_ (MHD_socket sock);
745 746
746 747
747/** 748/**
749 * Disable Nagle's algorithm on @a sock. This is what we do by default for
750 * all TCP sockets in MHD, unless the platform does not support the MSG_MORE
751 * or MSG_CORK or MSG_NOPUSH options.
752 *
753 * @param sock socket to manipulate
754 * @param on value to use
755 * @return 0 on success
756 */
757int
758MHD_socket_set_nodelay_ (MHD_socket sock,
759 bool on);
760
761/**
748 * Change socket options to be non-inheritable. 762 * Change socket options to be non-inheritable.
749 * 763 *
750 * @param sock socket to manipulate 764 * @param sock socket to manipulate
@@ -756,6 +770,30 @@ MHD_socket_noninheritable_ (MHD_socket sock);
756 770
757 771
758/** 772/**
773 * Enable/disable the cork option.
774 *
775 * TCP_NOPUSH has the same logic as MSG_MSG_MORE.
776 * The two are more or less equivalent by a source
777 * transformation (ie
778 * send(MSG_MORE) => "set TCP_NOPUSH + send() + clear TCP_NOPUSH".
779 * Both of them are really fairly "local", but TCP_NOPUSH has a
780 * _notion_ of persistency that is entirely lacking in MSG_MORE.
781 * ... with TCP_NOPUSH you basically have to know what your last
782 * write is, and clear the bit _before_ that write if you want
783 * to avoid bad latencies.
784 *
785 * See also: https://yarchive.net/comp/linux/sendfile.html
786 *
787 * @param sock socket to manipulate
788 * @param on set to true to enable CORK, false to disable
789 * @return non-zero if succeeded, zero otherwise
790 */
791int
792MHD_socket_cork_ (MHD_socket sock,
793 bool on);
794
795
796/**
759 * Change socket buffering mode to default. 797 * Change socket buffering mode to default.
760 * 798 *
761 * @param sock socket to manipulate 799 * @param sock socket to manipulate
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
index 3e9fb053..65ea7b09 100644
--- a/src/microhttpd/response.c
+++ b/src/microhttpd/response.c
@@ -604,9 +604,9 @@ MHD_create_response_from_fd_at_offset64 (uint64_t size,
604 604
605 response = MHD_create_response_from_callback (size, 605 response = MHD_create_response_from_callback (size,
606 MHD_FILE_READ_BLOCK_SIZE, 606 MHD_FILE_READ_BLOCK_SIZE,
607 &file_reader, 607 &file_reader,
608 NULL, 608 NULL,
609 &free_callback); 609 &free_callback);
610 if (NULL == response) 610 if (NULL == response)
611 return NULL; 611 return NULL;
612 response->fd = fd; 612 response->fd = fd;
@@ -825,6 +825,44 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh,
825 * be moved to cleanup list by MHD_resume_connection(). */ 825 * be moved to cleanup list by MHD_resume_connection(). */
826 MHD_resume_connection (connection); 826 MHD_resume_connection (connection);
827 return MHD_YES; 827 return MHD_YES;
828 case MHD_UPGRADE_ACTION_CORK_ON:
829 if (connection->sk_cork_on)
830 return MHD_YES;
831#ifdef HTTPS_SUPPORT
832 if (0 != (daemon->options & MHD_USE_TLS) )
833 {
834 gnutls_record_cork (connection->tls_session);
835 connection->sk_cork_on = true;
836 return MHD_YES;
837 }
838 else
839#else
840 {
841 if (0 ==
842 MHD_socket_cork_ (connection->socket_fd,
843 true))
844 connection->sk_cork_on = true;
845 }
846#endif
847 case MHD_UPGRADE_ACTION_CORK_OFF:
848 if (! connection->sk_cork_on)
849 return MHD_YES;
850#ifdef HTTPS_SUPPORT
851 if (0 != (daemon->options & MHD_USE_TLS) )
852 {
853 gnutls_record_uncork (connection->tls_session, 0);
854 connection->sk_cork_on = false;
855 return MHD_YES;
856 }
857 else
858#else
859 {
860 if (0 ==
861 MHD_socket_cork_ (connection->socket_fd,
862 false))
863 connection->sk_cork_on = false;
864 }
865#endif
828 default: 866 default:
829 /* we don't understand this one */ 867 /* we don't understand this one */
830 return MHD_NO; 868 return MHD_NO;
diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c
index caf12e61..9135187c 100644
--- a/src/microhttpd/test_upgrade.c
+++ b/src/microhttpd/test_upgrade.c
@@ -685,6 +685,8 @@ run_usock (void *cls)
685{ 685{
686 struct MHD_UpgradeResponseHandle *urh = cls; 686 struct MHD_UpgradeResponseHandle *urh = cls;
687 687
688 MHD_upgrade_action (urh,
689 MHD_UPGRADE_ACTION_CORK_OFF);
688 send_all (usock, 690 send_all (usock,
689 "Hello"); 691 "Hello");
690 recv_all (usock, 692 recv_all (usock,
diff --git a/src/microhttpd/test_upgrade_large.c b/src/microhttpd/test_upgrade_large.c
index 7eabf82c..3131fdf4 100644
--- a/src/microhttpd/test_upgrade_large.c
+++ b/src/microhttpd/test_upgrade_large.c
@@ -704,6 +704,8 @@ run_usock (void *cls)
704{ 704{
705 struct MHD_UpgradeResponseHandle *urh = cls; 705 struct MHD_UpgradeResponseHandle *urh = cls;
706 706
707 MHD_upgrade_action (urh,
708 MHD_UPGRADE_ACTION_CORK_OFF);
707 send_all (usock, 709 send_all (usock,
708 LARGE_STRING); 710 LARGE_STRING);
709 recv_all (usock, 711 recv_all (usock,