diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | ChangeLog | 11 | ||||
-rw-r--r-- | configure.ac | 22 | ||||
-rw-r--r-- | doc/libmicrohttpd.texi | 4 | ||||
-rw-r--r-- | src/include/microhttpd.h | 12 | ||||
-rw-r--r-- | src/microhttpd/Makefile.am | 1 | ||||
-rw-r--r-- | src/microhttpd/connection.c | 468 | ||||
-rw-r--r-- | src/microhttpd/connection_https.c | 2 | ||||
-rw-r--r-- | src/microhttpd/connection_https.h | 14 | ||||
-rw-r--r-- | src/microhttpd/daemon.c | 14 | ||||
-rw-r--r-- | src/microhttpd/internal.h | 14 | ||||
-rw-r--r-- | src/microhttpd/mhd_send.c | 637 | ||||
-rw-r--r-- | src/microhttpd/mhd_send.h | 97 | ||||
-rw-r--r-- | src/microhttpd/mhd_sockets.c | 88 | ||||
-rw-r--r-- | src/microhttpd/mhd_sockets.h | 38 | ||||
-rw-r--r-- | src/microhttpd/response.c | 44 | ||||
-rw-r--r-- | src/microhttpd/test_upgrade.c | 2 | ||||
-rw-r--r-- | src/microhttpd/test_upgrade_large.c | 2 |
18 files changed, 1045 insertions, 426 deletions
@@ -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 |
@@ -1,3 +1,14 @@ | |||
1 | Fri 18 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 | |||
1 | Fri 09 Aug 2019 10:07:27 AM CEST | 12 | Fri 09 Aug 2019 10:07:27 AM CEST |
2 | Copy compiler and linker hardening flags from GNUnet (updating | 13 | Copy compiler and linker hardening flags from GNUnet (updating |
3 | configure.ac). -CG | 14 | configure.ac). -CG |
diff --git a/configure.ac b/configure.ac index 086481a2..37ae0fe6 100644 --- a/configure.ac +++ b/configure.ac | |||
@@ -670,6 +670,28 @@ AM_CONDITIONAL(USE_MS_LIB_TOOL, [test "x$ac_cv_use_ms_lib_tool" = "xyes"]) | |||
670 | MHD_CHECK_SOCKET_SHUTDOWN_TRIGGER([AC_DEFINE([HAVE_LISTEN_SHUTDOWN],[1],[can use shutdown on listen sockets])]) | 670 | MHD_CHECK_SOCKET_SHUTDOWN_TRIGGER([AC_DEFINE([HAVE_LISTEN_SHUTDOWN],[1],[can use shutdown on listen sockets])]) |
671 | AM_CONDITIONAL([HAVE_LISTEN_SHUTDOWN], [test "x$mhd_cv_host_shtdwn_trgr_select" = "xyes"]) | 671 | AM_CONDITIONAL([HAVE_LISTEN_SHUTDOWN], [test "x$mhd_cv_host_shtdwn_trgr_select" = "xyes"]) |
672 | 672 | ||
673 | # SENDMSG. Should we check for SCM_RIGHTS instead? | ||
674 | # https://lists.x.org/archives/xorg-devel/2013-November/038687.html | ||
675 | AC_MSG_CHECKING([whether sendmsg is available]) | ||
676 | AC_SEARCH_LIBS(sendmsg, socket, AC_DEFINE([HAVE_SENDMSG],1,[Define if your platform supports sendmsg])) | ||
677 | AC_MSG_CHECKING([whether writev is available]) | ||
678 | AC_CHECK_FUNCS([writev]) | ||
679 | |||
680 | # check MSG_MORE defined | ||
681 | AC_MSG_CHECKING([whether MSG_MORE is defined]) | ||
682 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | ||
683 | #include <sys/types.h> | ||
684 | #include <sys/socket.h> | ||
685 | ]],[[return MSG_MORE;]] | ||
686 | )], | ||
687 | [ | ||
688 | AC_MSG_RESULT(yes) | ||
689 | AC_DEFINE(HAVE_MSG_MORE, [1], [have MSG_MORE]) | ||
690 | ], | ||
691 | [ | ||
692 | AC_MSG_RESULT(no) | ||
693 | ]) | ||
694 | |||
673 | # set GCC options | 695 | # set GCC options |
674 | # use '-fno-strict-aliasing', but only if the compiler | 696 | # use '-fno-strict-aliasing', but only if the compiler |
675 | # and linker can take it | 697 | # 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 |
2331 | Closes the connection. Must be called once the application is done with the client. Takes no additional arguments. | 2331 | Closes the connection. Must be called once the application is done with the client. Takes no additional arguments. |
2332 | @item MHD_UPGRADE_ACTION_CORK_ON | ||
2333 | Enable corking on the underlying socket. | ||
2334 | @item MHD_UPGRADE_ACTION_CORK_OFF | ||
2335 | Disable 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 | */ | ||
288 | static ssize_t | ||
289 | sendfile_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 | |||
501 | socket_start_extra_buffering (struct MHD_Connection *connection) | 320 | socket_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 | ||
553 | socket_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 | ||
640 | socket_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 | */ |
101 | static ssize_t | 101 | ssize_t |
102 | send_tls_adapter (struct MHD_Connection *connection, | 102 | send_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 | */ |
61 | bool | 61 | bool |
62 | MHD_tls_connection_shutdown (struct MHD_Connection *connection); | 62 | MHD_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 | */ | ||
73 | ssize_t | ||
74 | send_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 | */ | ||
47 | static void | ||
48 | pre_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 | */ | ||
126 | static void | ||
127 | post_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 | */ | ||
220 | ssize_t | ||
221 | MHD_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 | */ | ||
364 | ssize_t | ||
365 | MHD_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 | */ | ||
441 | static int freebsd_sendfile_flags_; | ||
442 | |||
443 | /** | ||
444 | * FreeBSD sendfile() flags for thread-per-connection | ||
445 | */ | ||
446 | static 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 | */ | ||
458 | ssize_t | ||
459 | MHD_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 | */ | ||
57 | enum 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 | |||
79 | ssize_t | ||
80 | MHD_send_on_connection_ (struct MHD_Connection *connection, | ||
81 | const char *buffer, | ||
82 | size_t buffer_size, | ||
83 | enum MHD_SendSocketOptions options); | ||
84 | |||
85 | ssize_t | ||
86 | MHD_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) | ||
93 | ssize_t | ||
94 | MHD_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 | */ | ||
473 | int | ||
474 | MHD_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 | */ | ||
502 | int | ||
503 | MHD_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) | |||
470 | int | 528 | int |
471 | MHD_socket_buffering_reset_ (MHD_socket sock) | 529 | MHD_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 | */ | ||
757 | int | ||
758 | MHD_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 | */ | ||
791 | int | ||
792 | MHD_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, |