aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd')
-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
13 files changed, 996 insertions, 425 deletions
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,