diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | configure.ac | 22 | ||||
-rw-r--r-- | src/microhttpd/Makefile.am | 1 | ||||
-rw-r--r-- | src/microhttpd/connection.c | 266 | ||||
-rw-r--r-- | src/microhttpd/connection_https.c | 2 | ||||
-rw-r--r-- | src/microhttpd/connection_https.h | 14 | ||||
-rw-r--r-- | src/microhttpd/internal.h | 14 | ||||
-rw-r--r-- | src/microhttpd/mhd_send.c | 586 | ||||
-rw-r--r-- | src/microhttpd/mhd_send.h | 100 | ||||
-rw-r--r-- | src/microhttpd/mhd_sockets.c | 4 | ||||
-rw-r--r-- | src/microhttpd/response.c | 6 |
11 files changed, 794 insertions, 222 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 |
diff --git a/configure.ac b/configure.ac index 5292798a..1a0870b7 100644 --- a/configure.ac +++ b/configure.ac | |||
@@ -642,6 +642,28 @@ AM_CONDITIONAL(USE_MS_LIB_TOOL, [test "x$ac_cv_use_ms_lib_tool" = "xyes"]) | |||
642 | MHD_CHECK_SOCKET_SHUTDOWN_TRIGGER([AC_DEFINE([HAVE_LISTEN_SHUTDOWN],[1],[can use shutdown on listen sockets])]) | 642 | MHD_CHECK_SOCKET_SHUTDOWN_TRIGGER([AC_DEFINE([HAVE_LISTEN_SHUTDOWN],[1],[can use shutdown on listen sockets])]) |
643 | AM_CONDITIONAL([HAVE_LISTEN_SHUTDOWN], [test "x$mhd_cv_host_shtdwn_trgr_select" = "xyes"]) | 643 | AM_CONDITIONAL([HAVE_LISTEN_SHUTDOWN], [test "x$mhd_cv_host_shtdwn_trgr_select" = "xyes"]) |
644 | 644 | ||
645 | # SENDMSG. Should we check for SCM_RIGHTS instead? | ||
646 | # https://lists.x.org/archives/xorg-devel/2013-November/038687.html | ||
647 | AC_MSG_CHECKING([whether sendmsg is available]) | ||
648 | AC_SEARCH_LIBS(sendmsg, socket, AC_DEFINE([HAVE_SENDMSG],1,[Define if your platform supports sendmsg])) | ||
649 | AC_MSG_CHECKING([whether writev is available]) | ||
650 | AC_CHECK_FUNCS([writev]) | ||
651 | |||
652 | # check MSG_MORE defined | ||
653 | AC_MSG_CHECKING([whether MSG_MORE is defined]) | ||
654 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ | ||
655 | #include <sys/types.h> | ||
656 | #include <sys/socket.h> | ||
657 | ]],[[return MSG_MORE;]] | ||
658 | )], | ||
659 | [ | ||
660 | AC_MSG_RESULT(yes) | ||
661 | AC_DEFINE(HAVE_MSG_MORE, [1], [have MSG_MORE]) | ||
662 | ], | ||
663 | [ | ||
664 | AC_MSG_RESULT(no) | ||
665 | ]) | ||
666 | |||
645 | # set GCC options | 667 | # set GCC options |
646 | # use '-fno-strict-aliasing', but only if the compiler | 668 | # use '-fno-strict-aliasing', but only if the compiler |
647 | # and linker can take it | 669 | # and linker can take it |
diff --git a/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..dab28878 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,6 +320,7 @@ _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); |
323 | #if OLD_SOCKOPT | ||
504 | #if defined(TCP_NODELAY) | 324 | #if defined(TCP_NODELAY) |
505 | if (connection->sk_tcp_nodelay_on) | 325 | if (connection->sk_tcp_nodelay_on) |
506 | { | 326 | { |
@@ -517,7 +337,9 @@ socket_start_extra_buffering (struct MHD_Connection *connection) | |||
517 | } | 337 | } |
518 | } | 338 | } |
519 | #endif /* TCP_NODELAY */ | 339 | #endif /* TCP_NODELAY */ |
340 | #endif /* OLD_SOCKOPT */ | ||
520 | 341 | ||
342 | #if OLD_SOCKOPT | ||
521 | #if defined(MHD_TCP_CORK_NOPUSH) | 343 | #if defined(MHD_TCP_CORK_NOPUSH) |
522 | if (!connection->sk_tcp_cork_nopush_on) | 344 | if (!connection->sk_tcp_cork_nopush_on) |
523 | { | 345 | { |
@@ -534,12 +356,15 @@ socket_start_extra_buffering (struct MHD_Connection *connection) | |||
534 | } | 356 | } |
535 | } | 357 | } |
536 | #endif /* MHD_TCP_CORK_NOPUSH */ | 358 | #endif /* MHD_TCP_CORK_NOPUSH */ |
359 | #endif /* OLD_SOCKOPT */ | ||
537 | 360 | ||
361 | #if OLD_SOCKOPT | ||
538 | #if defined(TCP_NODELAY) | 362 | #if defined(TCP_NODELAY) |
539 | return connection->sk_tcp_cork_nopush_on && !connection->sk_tcp_nodelay_on; | 363 | return connection->sk_tcp_cork_nopush_on && !connection->sk_tcp_nodelay_on; |
540 | #else /* ! TCP_NODELAY */ | 364 | #else /* ! TCP_NODELAY */ |
541 | return connection->sk_tcp_cork_nopush_on; | 365 | return connection->sk_tcp_cork_nopush_on; |
542 | #endif /* ! TCP_NODELAY */ | 366 | #endif /* ! TCP_NODELAY */ |
367 | #endif /* OLD_SOCKOPT */ | ||
543 | } | 368 | } |
544 | 369 | ||
545 | 370 | ||
@@ -552,6 +377,7 @@ socket_start_extra_buffering (struct MHD_Connection *connection) | |||
552 | _MHD_static_inline bool | 377 | _MHD_static_inline bool |
553 | socket_start_no_buffering (struct MHD_Connection *connection) | 378 | socket_start_no_buffering (struct MHD_Connection *connection) |
554 | { | 379 | { |
380 | #if OLD_SOCKOPT | ||
555 | #if defined(MHD_TCP_CORK_NOPUSH) | 381 | #if defined(MHD_TCP_CORK_NOPUSH) |
556 | if (connection->sk_tcp_cork_nopush_on) | 382 | if (connection->sk_tcp_cork_nopush_on) |
557 | { | 383 | { |
@@ -584,6 +410,7 @@ socket_start_no_buffering (struct MHD_Connection *connection) | |||
584 | } | 410 | } |
585 | #endif /* TCP_NODELAY */ | 411 | #endif /* TCP_NODELAY */ |
586 | return connection->sk_tcp_nodelay_on && !connection->sk_tcp_cork_nopush_on; | 412 | return connection->sk_tcp_nodelay_on && !connection->sk_tcp_cork_nopush_on; |
413 | #endif /* OLD_SOCKOPT */ | ||
587 | } | 414 | } |
588 | 415 | ||
589 | 416 | ||
@@ -601,6 +428,7 @@ socket_start_no_buffering_flush (struct MHD_Connection *connection) | |||
601 | #if defined(TCP_NOPUSH) && !defined(TCP_CORK) | 428 | #if defined(TCP_NOPUSH) && !defined(TCP_CORK) |
602 | const int dummy = 0; | 429 | const int dummy = 0; |
603 | #endif /* !TCP_CORK */ | 430 | #endif /* !TCP_CORK */ |
431 | #if OLD_SOCKOPT | ||
604 | #if defined(TCP_CORK) || (defined(__FreeBSD__) && __FreeBSD__+0 >= 9) | 432 | #if defined(TCP_CORK) || (defined(__FreeBSD__) && __FreeBSD__+0 >= 9) |
605 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | 433 | const MHD_SCKT_OPT_BOOL_ off_val = 0; |
606 | /* Switching off TCP_CORK flush buffer even | 434 | /* Switching off TCP_CORK flush buffer even |
@@ -614,7 +442,7 @@ socket_start_no_buffering_flush (struct MHD_Connection *connection) | |||
614 | connection->sk_tcp_cork_nopush_on = false; | 442 | connection->sk_tcp_cork_nopush_on = false; |
615 | } | 443 | } |
616 | #endif /* MHD_TCP_CORK_NOPUSH */ | 444 | #endif /* MHD_TCP_CORK_NOPUSH */ |
617 | 445 | #endif /* OLD_SOCKOPT */ | |
618 | res = socket_start_no_buffering (connection); | 446 | res = socket_start_no_buffering (connection); |
619 | #if defined(__FreeBSD__) && __FreeBSD__+0 >= 9 | 447 | #if defined(__FreeBSD__) && __FreeBSD__+0 >= 9 |
620 | /* FreeBSD do not need zero-send for flushing starting from version 9 */ | 448 | /* FreeBSD do not need zero-send for flushing starting from version 9 */ |
@@ -640,6 +468,7 @@ _MHD_static_inline bool | |||
640 | socket_start_normal_buffering (struct MHD_Connection *connection) | 468 | socket_start_normal_buffering (struct MHD_Connection *connection) |
641 | { | 469 | { |
642 | mhd_assert(NULL != connection); | 470 | mhd_assert(NULL != connection); |
471 | #if OLD_SOCKOPT | ||
643 | #if defined(MHD_TCP_CORK_NOPUSH) | 472 | #if defined(MHD_TCP_CORK_NOPUSH) |
644 | if (connection->sk_tcp_cork_nopush_on) | 473 | if (connection->sk_tcp_cork_nopush_on) |
645 | { | 474 | { |
@@ -672,6 +501,7 @@ socket_start_normal_buffering (struct MHD_Connection *connection) | |||
672 | } | 501 | } |
673 | #endif /* TCP_NODELAY */ | 502 | #endif /* TCP_NODELAY */ |
674 | return !connection->sk_tcp_nodelay_on && !connection->sk_tcp_cork_nopush_on; | 503 | return !connection->sk_tcp_nodelay_on && !connection->sk_tcp_cork_nopush_on; |
504 | #endif | ||
675 | } | 505 | } |
676 | 506 | ||
677 | 507 | ||
@@ -3315,11 +3145,12 @@ MHD_connection_handle_write (struct MHD_Connection *connection) | |||
3315 | case MHD_CONNECTION_HEADERS_PROCESSED: | 3145 | case MHD_CONNECTION_HEADERS_PROCESSED: |
3316 | return; | 3146 | return; |
3317 | case MHD_CONNECTION_CONTINUE_SENDING: | 3147 | case MHD_CONNECTION_CONTINUE_SENDING: |
3318 | ret = connection->send_cls (connection, | 3148 | ret = MHD_send_on_connection_ (connection, |
3319 | &HTTP_100_CONTINUE | 3149 | &HTTP_100_CONTINUE |
3320 | [connection->continue_message_write_offset], | 3150 | [connection->continue_message_write_offset], |
3321 | MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE) - | 3151 | MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE) - |
3322 | connection->continue_message_write_offset); | 3152 | connection->continue_message_write_offset, |
3153 | MHD_SSO_NO_CORK); | ||
3323 | if (ret < 0) | 3154 | if (ret < 0) |
3324 | { | 3155 | { |
3325 | if (MHD_ERR_AGAIN_ == ret) | 3156 | if (MHD_ERR_AGAIN_ == ret) |
@@ -3349,11 +3180,27 @@ MHD_connection_handle_write (struct MHD_Connection *connection) | |||
3349 | mhd_assert (0); | 3180 | mhd_assert (0); |
3350 | return; | 3181 | return; |
3351 | case MHD_CONNECTION_HEADERS_SENDING: | 3182 | case MHD_CONNECTION_HEADERS_SENDING: |
3352 | ret = connection->send_cls (connection, | 3183 | /* if the response body is not available, we use MHD_send_on_connection_() */ |
3353 | &connection->write_buffer | 3184 | if (NULL != connection->response->crc) |
3354 | [connection->write_buffer_send_offset], | 3185 | { |
3355 | connection->write_buffer_append_offset - | 3186 | ret = MHD_send_on_connection_ (connection, |
3356 | connection->write_buffer_send_offset); | 3187 | &connection->write_buffer |
3188 | [connection->write_buffer_send_offset], | ||
3189 | connection->write_buffer_append_offset - | ||
3190 | connection->write_buffer_send_offset, | ||
3191 | MHD_SSO_MAY_CORK); | ||
3192 | } | ||
3193 | else | ||
3194 | { | ||
3195 | ret = MHD_send_on_connection2_ (connection, | ||
3196 | &connection->write_buffer | ||
3197 | [connection->write_buffer_send_offset], | ||
3198 | connection->write_buffer_append_offset - | ||
3199 | connection->write_buffer_send_offset, | ||
3200 | connection->response->data, | ||
3201 | connection->response->data_buffer_size); | ||
3202 | } | ||
3203 | |||
3357 | if (ret < 0) | 3204 | if (ret < 0) |
3358 | { | 3205 | { |
3359 | if (MHD_ERR_AGAIN_ == ret) | 3206 | if (MHD_ERR_AGAIN_ == ret) |
@@ -3401,11 +3248,12 @@ MHD_connection_handle_write (struct MHD_Connection *connection) | |||
3401 | - response->data_start; | 3248 | - response->data_start; |
3402 | if (data_write_offset > (uint64_t)SIZE_MAX) | 3249 | if (data_write_offset > (uint64_t)SIZE_MAX) |
3403 | MHD_PANIC (_("Data offset exceeds limit")); | 3250 | MHD_PANIC (_("Data offset exceeds limit")); |
3404 | ret = connection->send_cls (connection, | 3251 | ret = MHD_send_on_connection_ (connection, |
3405 | &response->data | 3252 | &response->data |
3406 | [(size_t)data_write_offset], | 3253 | [(size_t)data_write_offset], |
3407 | response->data_size - | 3254 | response->data_size - |
3408 | (size_t)data_write_offset); | 3255 | (size_t)data_write_offset, |
3256 | MHD_SSO_NO_CORK); | ||
3409 | #if DEBUG_SEND_DATA | 3257 | #if DEBUG_SEND_DATA |
3410 | if (ret > 0) | 3258 | if (ret > 0) |
3411 | fprintf (stderr, | 3259 | fprintf (stderr, |
@@ -3444,11 +3292,12 @@ MHD_connection_handle_write (struct MHD_Connection *connection) | |||
3444 | mhd_assert (0); | 3292 | mhd_assert (0); |
3445 | return; | 3293 | return; |
3446 | case MHD_CONNECTION_CHUNKED_BODY_READY: | 3294 | case MHD_CONNECTION_CHUNKED_BODY_READY: |
3447 | ret = connection->send_cls (connection, | 3295 | ret = MHD_send_on_connection_ (connection, |
3448 | &connection->write_buffer | 3296 | &connection->write_buffer |
3449 | [connection->write_buffer_send_offset], | 3297 | [connection->write_buffer_send_offset], |
3450 | connection->write_buffer_append_offset - | 3298 | connection->write_buffer_append_offset - |
3451 | connection->write_buffer_send_offset); | 3299 | connection->write_buffer_send_offset, |
3300 | MHD_SSO_NO_CORK); | ||
3452 | if (ret < 0) | 3301 | if (ret < 0) |
3453 | { | 3302 | { |
3454 | if (MHD_ERR_AGAIN_ == ret) | 3303 | if (MHD_ERR_AGAIN_ == ret) |
@@ -3472,11 +3321,12 @@ MHD_connection_handle_write (struct MHD_Connection *connection) | |||
3472 | mhd_assert (0); | 3321 | mhd_assert (0); |
3473 | return; | 3322 | return; |
3474 | case MHD_CONNECTION_FOOTERS_SENDING: | 3323 | case MHD_CONNECTION_FOOTERS_SENDING: |
3475 | ret = connection->send_cls (connection, | 3324 | ret = MHD_send_on_connection_ (connection, |
3476 | &connection->write_buffer | 3325 | &connection->write_buffer |
3477 | [connection->write_buffer_send_offset], | 3326 | [connection->write_buffer_send_offset], |
3478 | connection->write_buffer_append_offset - | 3327 | connection->write_buffer_append_offset - |
3479 | connection->write_buffer_send_offset); | 3328 | connection->write_buffer_send_offset, |
3329 | MHD_SSO_HDR_CORK); | ||
3480 | if (ret < 0) | 3330 | if (ret < 0) |
3481 | { | 3331 | { |
3482 | if (MHD_ERR_AGAIN_ == ret) | 3332 | if (MHD_ERR_AGAIN_ == ret) |
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/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..a41586d9 --- /dev/null +++ b/src/microhttpd/mhd_send.c | |||
@@ -0,0 +1,586 @@ | |||
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 | #else | ||
53 | int ret; | ||
54 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | ||
55 | const MHD_SCKT_OPT_BOOL_ on_val = 1; | ||
56 | |||
57 | /* If sk_cork_on is already what we pass in, return. */ | ||
58 | if (connection->sk_cork_on == want_cork) | ||
59 | { | ||
60 | /* nothing to do, success! */ | ||
61 | return; | ||
62 | } | ||
63 | |||
64 | ret = -1; | ||
65 | #if TCP_CORK | ||
66 | if (want_cork) | ||
67 | ret = setsockopt (connection->socket_fd, | ||
68 | IPPROTO_TCP, | ||
69 | TCP_CORK, | ||
70 | (const void *) &on_val, | ||
71 | sizeof (on_val)); | ||
72 | #elif TCP_NODELAY | ||
73 | ret = setsockopt (connection->socket_fd, | ||
74 | IPPROTO_TCP, | ||
75 | TCP_NODELAY, | ||
76 | (const void *) want_cork ? &off_val : &on_val, | ||
77 | sizeof (on_val)); | ||
78 | #elif TCP_NOPUSH | ||
79 | ret = setsockopt (connection->socket_fd, | ||
80 | IPPROTO_TCP, | ||
81 | TCP_NOPUSH, | ||
82 | (const void *) &on_val, | ||
83 | sizeof (on_val)); | ||
84 | #endif | ||
85 | |||
86 | if (0 == ret) | ||
87 | { | ||
88 | connection->sk_cork_on = want_cork; | ||
89 | } | ||
90 | return; | ||
91 | #endif /* MSG_MORE */ | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Handle setsockopt calls. | ||
96 | * | ||
97 | * @param connection the MHD_Connection structure | ||
98 | * @param want_cork cork state, boolean | ||
99 | */ | ||
100 | static void | ||
101 | post_cork_setsockopt (struct MHD_Connection *connection, | ||
102 | bool want_cork) | ||
103 | { | ||
104 | #if HAVE_MSG_MORE | ||
105 | #else | ||
106 | int ret; | ||
107 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | ||
108 | const MHD_SCKT_OPT_BOOL_ on_val = 1; | ||
109 | |||
110 | /* If sk_cork_on is already what we pass in, return. */ | ||
111 | if (connection->sk_cork_on == want_cork) | ||
112 | { | ||
113 | /* nothing to do, success! */ | ||
114 | return; | ||
115 | } | ||
116 | ret = -1; | ||
117 | #if TCP_CORK | ||
118 | if (! want_cork) | ||
119 | ret = setsockopt (connection->socket_fd, | ||
120 | IPPROTO_TCP, | ||
121 | TCP_CORK, | ||
122 | &off_val, | ||
123 | sizeof (off_val)); | ||
124 | #elif TCP_NODELAY | ||
125 | /* nothing to do */ | ||
126 | #elif TCP_NOPUSH | ||
127 | ret = setsockopt (connection->socket_fd, | ||
128 | IPPROTO_TCP, | ||
129 | TCP_NOPUSH, | ||
130 | (const void *) &off_val, | ||
131 | sizeof (off_val)); | ||
132 | #endif | ||
133 | if (0 == ret) | ||
134 | { | ||
135 | connection->sk_cork_on = want_cork; | ||
136 | } | ||
137 | return; | ||
138 | #endif /* MSG_MORE */ | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * Send buffer on connection, and remember the current state of | ||
143 | * the socket options; only call setsockopt when absolutely | ||
144 | * necessary. | ||
145 | * | ||
146 | * @param connection the MHD_Connection structure | ||
147 | * @param buffer content of the buffer to send | ||
148 | * @param buffer_size the size of the buffer (in bytes) | ||
149 | * @param options the #MHD_SendSocketOptions enum, | ||
150 | * #MHD_SSO_NO_CORK: definitely no corking (use NODELAY, or explicitly disable cork), | ||
151 | * #MHD_SSO_MAY_CORK: should enable corking (use MSG_MORE, or explicitly enable cork), | ||
152 | * #MHD_SSO_HDR_CORK: consider tcpi_snd_mss and consider not corking for the header | ||
153 | * part if the size of the header is close to the MSS. | ||
154 | * Only used if we are NOT doing 100 Continue and are still sending the | ||
155 | * header (provided in full as the buffer to #MHD_send_on_connection_ or as | ||
156 | * the header to #MHD_send_on_connection2_). | ||
157 | * @return sum of the number of bytes sent from both buffers or | ||
158 | * -1 on error | ||
159 | */ | ||
160 | ssize_t | ||
161 | MHD_send_on_connection_ (struct MHD_Connection *connection, | ||
162 | const char *buffer, | ||
163 | size_t buffer_size, | ||
164 | enum MHD_SendSocketOptions options) | ||
165 | { | ||
166 | bool want_cork; | ||
167 | bool using_tls = false; | ||
168 | MHD_socket s = connection->socket_fd; | ||
169 | ssize_t ret; | ||
170 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | ||
171 | const MHD_SCKT_OPT_BOOL_ on_val = 1; | ||
172 | |||
173 | /* error handling from send_param_adapter() */ | ||
174 | if ((MHD_INVALID_SOCKET == s) || (MHD_CONNECTION_CLOSED == connection->state)) | ||
175 | { | ||
176 | return MHD_ERR_NOTCONN_; | ||
177 | } | ||
178 | |||
179 | /* from send_param_adapter() */ | ||
180 | if (buffer_size > MHD_SCKT_SEND_MAX_SIZE_) | ||
181 | buffer_size = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */ | ||
182 | |||
183 | /* Get socket options, change/set options if necessary. */ | ||
184 | switch (options) | ||
185 | { | ||
186 | /* No corking */ | ||
187 | case MHD_SSO_NO_CORK: | ||
188 | want_cork = false; | ||
189 | break; | ||
190 | /* Do corking, consider MSG_MORE instead if available. */ | ||
191 | case MHD_SSO_MAY_CORK: | ||
192 | want_cork = true; | ||
193 | break; | ||
194 | /* Cork the header. */ | ||
195 | case MHD_SSO_HDR_CORK: | ||
196 | want_cork = (buffer_size <= 1024); | ||
197 | break; | ||
198 | } | ||
199 | |||
200 | /* ! could be avoided by redefining the variable. */ | ||
201 | // bool have_cork = ! connection->sk_tcp_nodelay_on; | ||
202 | bool have_cork = ! connection->sk_cork_on; | ||
203 | |||
204 | #ifdef HTTPS_SUPPORT | ||
205 | using_tls = (0 != (connection->daemon->options & MHD_USE_TLS)); | ||
206 | #endif | ||
207 | |||
208 | #ifdef HTTPS_SUPPORT | ||
209 | if (using_tls) | ||
210 | { | ||
211 | if (want_cork && ! have_cork) | ||
212 | { | ||
213 | gnutls_record_cork (connection->tls_session); | ||
214 | connection->sk_cork_on = false; | ||
215 | } | ||
216 | if (buffer_size > SSIZE_MAX) | ||
217 | buffer_size = SSIZE_MAX; | ||
218 | ret = gnutls_record_send (connection->tls_session, | ||
219 | buffer, | ||
220 | buffer_size); | ||
221 | if ( (GNUTLS_E_AGAIN == ret) || | ||
222 | (GNUTLS_E_INTERRUPTED == ret) ) | ||
223 | { | ||
224 | #ifdef EPOLL_SUPPORT | ||
225 | if (GNUTLS_E_AGAIN == ret) | ||
226 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
227 | #endif | ||
228 | return MHD_ERR_AGAIN_; | ||
229 | } | ||
230 | if (ret < 0) | ||
231 | { | ||
232 | /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication | ||
233 | disrupted); interpret as a hard error */ | ||
234 | return MHD_ERR_NOTCONN_; | ||
235 | } | ||
236 | #ifdef EPOLL_SUPPORT | ||
237 | /* Unlike non-TLS connections, do not reset "write-ready" if | ||
238 | * sent amount smaller than provided amount, as TLS | ||
239 | * connections may break data into smaller parts for sending. */ | ||
240 | #endif /* EPOLL_SUPPORT */ | ||
241 | |||
242 | if (! want_cork && have_cork) | ||
243 | { | ||
244 | (void) gnutls_record_uncork (connection->tls_session, 0); | ||
245 | connection->sk_cork_on = true; | ||
246 | } | ||
247 | } | ||
248 | else | ||
249 | #endif /* HTTPS_SUPPORT */ | ||
250 | { | ||
251 | /* plaintext transmission */ | ||
252 | pre_cork_setsockopt (connection, want_cork); | ||
253 | #if HAVE_MSG_MORE | ||
254 | ret = send (s, | ||
255 | buffer, | ||
256 | buffer_size, | ||
257 | MAYBE_MSG_NOSIGNAL | (want_cork ? MSG_MORE : 0)); | ||
258 | #else | ||
259 | ret = send (connection->socket_fd, | ||
260 | buffer, | ||
261 | buffer_size, | ||
262 | MAYBE_MSG_NOSIGNAL); | ||
263 | #endif | ||
264 | |||
265 | if (0 > ret) | ||
266 | { | ||
267 | const int err = MHD_socket_get_error_ (); | ||
268 | |||
269 | if (MHD_SCKT_ERR_IS_EAGAIN_ (err)) | ||
270 | { | ||
271 | #if EPOLL_SUPPORT | ||
272 | /* EAGAIN, no longer write-ready */ | ||
273 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
274 | #endif /* EPOLL_SUPPORT */ | ||
275 | return MHD_ERR_AGAIN_; | ||
276 | } | ||
277 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
278 | return MHD_ERR_AGAIN_; | ||
279 | if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ECONNRESET_)) | ||
280 | return MHD_ERR_CONNRESET_; | ||
281 | /* Treat any other error as hard error. */ | ||
282 | return MHD_ERR_NOTCONN_; | ||
283 | } | ||
284 | #if EPOLL_SUPPORT | ||
285 | else if (buffer_size > (size_t) ret) | ||
286 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
287 | #endif /* EPOLL_SUPPORT */ | ||
288 | |||
289 | post_cork_setsockopt (connection, want_cork); | ||
290 | } | ||
291 | |||
292 | return ret; | ||
293 | } | ||
294 | |||
295 | |||
296 | /** | ||
297 | * Send header followed by buffer on connection. | ||
298 | * Uses writev if possible to send both at once | ||
299 | * and returns the sum of the number of bytes sent from | ||
300 | * both buffers, or -1 on error; | ||
301 | * if writev is unavailable, this call MUST only send from 'header' | ||
302 | * (as we cannot handle the case that the first write | ||
303 | * succeeds and the 2nd fails!). | ||
304 | * | ||
305 | * @param connection the MHD_Connection structure | ||
306 | * @param header content of header to send | ||
307 | * @param header_size the size of the header (in bytes) | ||
308 | * @param buffer content of the buffer to send | ||
309 | * @param buffer_size the size of the buffer (in bytes) | ||
310 | * @return sum of the number of bytes sent from both buffers or | ||
311 | * -1 on error | ||
312 | */ | ||
313 | ssize_t | ||
314 | MHD_send_on_connection2_ (struct MHD_Connection *connection, | ||
315 | const char *header, | ||
316 | size_t header_size, | ||
317 | const char *buffer, | ||
318 | size_t buffer_size) | ||
319 | { | ||
320 | #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) | ||
321 | MHD_socket s = connection->socket_fd; | ||
322 | bool want_cork; | ||
323 | int iovcnt; | ||
324 | int eno; | ||
325 | ssize_t ret; | ||
326 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | ||
327 | struct iovec vector[2]; | ||
328 | |||
329 | want_cork = ! connection->sk_cork_on; | ||
330 | |||
331 | pre_cork_setsockopt (connection, want_cork); | ||
332 | |||
333 | vector[0].iov_base = (void *) header; | ||
334 | vector[0].iov_len = strlen (header); | ||
335 | vector[1].iov_base = (void *) buffer; | ||
336 | vector[1].iov_len = strlen (buffer); | ||
337 | |||
338 | #if HAVE_SENDMSG | ||
339 | struct msghdr msg; | ||
340 | memset(&msg, 0, sizeof(struct msghdr)); | ||
341 | |||
342 | msg.msg_iov = vector; | ||
343 | msg.msg_iovlen = 2; | ||
344 | |||
345 | /* | ||
346 | * questionable for this case, bus maybe worth considering for now: | ||
347 | * On at least NetBSD (and FreeBSD?) we need to set both msg_control and | ||
348 | * mgs_controllen to 0. | ||
349 | * If you set msg_control to nonnull, NetBSD expects you to have | ||
350 | * msg_controllen > 0. (sys/kern/uipc_syscalls.c in do_sys_sendmsg_so) | ||
351 | * for reference. Thanks to pDNS (for FreeBSD), Riastradh for NetBSD. | ||
352 | */ | ||
353 | /* | ||
354 | msg.msg_control = 0; | ||
355 | msg.msg_controllen = 0; | ||
356 | */ | ||
357 | ret = sendmsg (s, &msg, MAYBE_MSG_NOSIGNAL); | ||
358 | #elif HAVE_WRITEV | ||
359 | iovcnt = sizeof (vector) / sizeof (struct iovec); | ||
360 | ret = writev (s, vector, iovcnt); | ||
361 | #endif | ||
362 | |||
363 | if (ret == header_size + buffer_size) | ||
364 | want_cork = false; | ||
365 | |||
366 | post_cork_setsockopt (connection, want_cork); | ||
367 | |||
368 | return ret; | ||
369 | |||
370 | #else | ||
371 | return MHD_send_on_connection_ (connection, | ||
372 | header, | ||
373 | header_size, | ||
374 | MHD_SSO_HDR_CORK); | ||
375 | #endif | ||
376 | } | ||
377 | |||
378 | /** | ||
379 | * sendfile() chuck size | ||
380 | */ | ||
381 | #define MHD_SENFILE_CHUNK_ (0x20000) | ||
382 | |||
383 | /** | ||
384 | * sendfile() chuck size for thread-per-connection | ||
385 | */ | ||
386 | #define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000) | ||
387 | |||
388 | #ifdef HAVE_FREEBSD_SENDFILE | ||
389 | #ifdef SF_FLAGS | ||
390 | /** | ||
391 | * FreeBSD sendfile() flags | ||
392 | */ | ||
393 | static int freebsd_sendfile_flags_; | ||
394 | |||
395 | /** | ||
396 | * FreeBSD sendfile() flags for thread-per-connection | ||
397 | */ | ||
398 | static int freebsd_sendfile_flags_thd_p_c_; | ||
399 | #endif /* SF_FLAGS */ | ||
400 | |||
401 | #endif /* HAVE_FREEBSD_SENDFILE */ | ||
402 | |||
403 | #if defined(_MHD_HAVE_SENDFILE) | ||
404 | /** | ||
405 | * Function for sending responses backed by file FD. | ||
406 | * | ||
407 | * @param connection the MHD connection structure | ||
408 | * @return actual number of bytes sent | ||
409 | */ | ||
410 | ssize_t | ||
411 | sendfile_adapter (struct MHD_Connection *connection) | ||
412 | { | ||
413 | ssize_t ret; | ||
414 | const int file_fd = connection->response->fd; | ||
415 | uint64_t left; | ||
416 | uint64_t offsetu64; | ||
417 | #ifndef HAVE_SENDFILE64 | ||
418 | const uint64_t max_off_t = (uint64_t)OFF_T_MAX; | ||
419 | #else /* HAVE_SENDFILE64 */ | ||
420 | const uint64_t max_off_t = (uint64_t)OFF64_T_MAX; | ||
421 | #endif /* HAVE_SENDFILE64 */ | ||
422 | #ifdef MHD_LINUX_SOLARIS_SENDFILE | ||
423 | #ifndef HAVE_SENDFILE64 | ||
424 | off_t offset; | ||
425 | #else /* HAVE_SENDFILE64 */ | ||
426 | off64_t offset; | ||
427 | #endif /* HAVE_SENDFILE64 */ | ||
428 | #endif /* MHD_LINUX_SOLARIS_SENDFILE */ | ||
429 | #ifdef HAVE_FREEBSD_SENDFILE | ||
430 | off_t sent_bytes; | ||
431 | int flags = 0; | ||
432 | #endif | ||
433 | #ifdef HAVE_DARWIN_SENDFILE | ||
434 | off_t len; | ||
435 | #endif /* HAVE_DARWIN_SENDFILE */ | ||
436 | const bool used_thr_p_c = (0 != (connection->daemon->options & MHD_USE_THREAD_PER_CONNECTION)); | ||
437 | const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : MHD_SENFILE_CHUNK_; | ||
438 | size_t send_size = 0; | ||
439 | mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender); | ||
440 | |||
441 | pre_cork_setsockopt (connection, false); | ||
442 | |||
443 | offsetu64 = connection->response_write_position + connection->response->fd_off; | ||
444 | left = connection->response->total_size - connection->response_write_position; | ||
445 | /* Do not allow system to stick sending on single fast connection: | ||
446 | * use 128KiB chunks (2MiB for thread-per-connection). */ | ||
447 | send_size = (left > chunk_size) ? chunk_size : (size_t) left; | ||
448 | if (max_off_t < offsetu64) | ||
449 | { /* Retry to send with standard 'send()'. */ | ||
450 | connection->resp_sender = MHD_resp_sender_std; | ||
451 | return MHD_ERR_AGAIN_; | ||
452 | } | ||
453 | #ifdef MHD_LINUX_SOLARIS_SENDFILE | ||
454 | #ifndef HAVE_SENDFILE64 | ||
455 | offset = (off_t) offsetu64; | ||
456 | ret = sendfile (connection->socket_fd, | ||
457 | file_fd, | ||
458 | &offset, | ||
459 | send_size); | ||
460 | #else /* HAVE_SENDFILE64 */ | ||
461 | offset = (off64_t) offsetu64; | ||
462 | ret = sendfile64 (connection->socket_fd, | ||
463 | file_fd, | ||
464 | &offset, | ||
465 | send_size); | ||
466 | #endif /* HAVE_SENDFILE64 */ | ||
467 | if (0 > ret) | ||
468 | { | ||
469 | const int err = MHD_socket_get_error_(); | ||
470 | if (MHD_SCKT_ERR_IS_EAGAIN_(err)) | ||
471 | { | ||
472 | #ifdef EPOLL_SUPPORT | ||
473 | /* EAGAIN --- no longer write-ready */ | ||
474 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
475 | #endif /* EPOLL_SUPPORT */ | ||
476 | return MHD_ERR_AGAIN_; | ||
477 | } | ||
478 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
479 | return MHD_ERR_AGAIN_; | ||
480 | #ifdef HAVE_LINUX_SENDFILE | ||
481 | if (MHD_SCKT_ERR_IS_(err, | ||
482 | MHD_SCKT_EBADF_)) | ||
483 | return MHD_ERR_BADF_; | ||
484 | /* sendfile() failed with EINVAL if mmap()-like operations are not | ||
485 | supported for FD or other 'unusual' errors occurred, so we should try | ||
486 | to fall back to 'SEND'; see also this thread for info on | ||
487 | odd libc/Linux behavior with sendfile: | ||
488 | http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */ | ||
489 | connection->resp_sender = MHD_resp_sender_std; | ||
490 | return MHD_ERR_AGAIN_; | ||
491 | #else /* HAVE_SOLARIS_SENDFILE */ | ||
492 | if ( (EAFNOSUPPORT == err) || | ||
493 | (EINVAL == err) || | ||
494 | (EOPNOTSUPP == err) ) | ||
495 | { /* Retry with standard file reader. */ | ||
496 | connection->resp_sender = MHD_resp_sender_std; | ||
497 | return MHD_ERR_AGAIN_; | ||
498 | } | ||
499 | if ( (ENOTCONN == err) || | ||
500 | (EPIPE == err) ) | ||
501 | { | ||
502 | return MHD_ERR_CONNRESET_; | ||
503 | } | ||
504 | return MHD_ERR_BADF_; /* Fail hard */ | ||
505 | #endif /* HAVE_SOLARIS_SENDFILE */ | ||
506 | } | ||
507 | #ifdef EPOLL_SUPPORT | ||
508 | else if (send_size > (size_t)ret) | ||
509 | connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; | ||
510 | #endif /* EPOLL_SUPPORT */ | ||
511 | #elif defined(HAVE_FREEBSD_SENDFILE) | ||
512 | #ifdef SF_FLAGS | ||
513 | flags = used_thr_p_c ? | ||
514 | freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_; | ||
515 | #endif /* SF_FLAGS */ | ||
516 | if (0 != sendfile (file_fd, | ||
517 | connection->socket_fd, | ||
518 | (off_t) offsetu64, | ||
519 | send_size, | ||
520 | NULL, | ||
521 | &sent_bytes, | ||
522 | flags)) | ||
523 | { | ||
524 | const int err = MHD_socket_get_error_(); | ||
525 | if (MHD_SCKT_ERR_IS_EAGAIN_(err) || | ||
526 | MHD_SCKT_ERR_IS_EINTR_(err) || | ||
527 | EBUSY == err) | ||
528 | { | ||
529 | mhd_assert (SSIZE_MAX >= sent_bytes); | ||
530 | if (0 != sent_bytes) | ||
531 | return (ssize_t)sent_bytes; | ||
532 | |||
533 | return MHD_ERR_AGAIN_; | ||
534 | } | ||
535 | /* Some unrecoverable error. Possibly file FD is not suitable | ||
536 | * for sendfile(). Retry with standard send(). */ | ||
537 | connection->resp_sender = MHD_resp_sender_std; | ||
538 | return MHD_ERR_AGAIN_; | ||
539 | } | ||
540 | mhd_assert (0 < sent_bytes); | ||
541 | mhd_assert (SSIZE_MAX >= sent_bytes); | ||
542 | ret = (ssize_t)sent_bytes; | ||
543 | #elif defined(HAVE_DARWIN_SENDFILE) | ||
544 | len = (off_t)send_size; /* chunk always fit */ | ||
545 | if (0 != sendfile (file_fd, | ||
546 | connection->socket_fd, | ||
547 | (off_t) offsetu64, | ||
548 | &len, | ||
549 | NULL, | ||
550 | 0)) | ||
551 | { | ||
552 | const int err = MHD_socket_get_error_(); | ||
553 | if (MHD_SCKT_ERR_IS_EAGAIN_(err) || | ||
554 | MHD_SCKT_ERR_IS_EINTR_(err)) | ||
555 | { | ||
556 | mhd_assert (0 <= len); | ||
557 | mhd_assert (SSIZE_MAX >= len); | ||
558 | mhd_assert (send_size >= (size_t)len); | ||
559 | if (0 != len) | ||
560 | return (ssize_t)len; | ||
561 | |||
562 | return MHD_ERR_AGAIN_; | ||
563 | } | ||
564 | if (ENOTCONN == err || | ||
565 | EPIPE == err) | ||
566 | return MHD_ERR_CONNRESET_; | ||
567 | if (ENOTSUP == err || | ||
568 | EOPNOTSUPP == err) | ||
569 | { /* This file FD is not suitable for sendfile(). | ||
570 | * Retry with standard send(). */ | ||
571 | connection->resp_sender = MHD_resp_sender_std; | ||
572 | return MHD_ERR_AGAIN_; | ||
573 | } | ||
574 | return MHD_ERR_BADF_; /* Return hard error. */ | ||
575 | } | ||
576 | mhd_assert (0 <= len); | ||
577 | mhd_assert (SSIZE_MAX >= len); | ||
578 | mhd_assert (send_size >= (size_t)len); | ||
579 | ret = (ssize_t)len; | ||
580 | #endif /* HAVE_FREEBSD_SENDFILE */ | ||
581 | |||
582 | post_cork_setsockopt (connection, false); | ||
583 | |||
584 | return ret; | ||
585 | } | ||
586 | #endif /* _MHD_HAVE_SENDFILE */ | ||
diff --git a/src/microhttpd/mhd_send.h b/src/microhttpd/mhd_send.h new file mode 100644 index 00000000..5f0f8155 --- /dev/null +++ b/src/microhttpd/mhd_send.h | |||
@@ -0,0 +1,100 @@ | |||
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 | ssize_t | ||
93 | MHD_sendfile_on_connection_ (struct MHD_Connection *connection); | ||
94 | |||
95 | #if defined(_MHD_HAVE_SENDFILE) | ||
96 | ssize_t | ||
97 | sendfile_adapter (struct MHD_Connection *connection); | ||
98 | #endif | ||
99 | |||
100 | #endif /* MHD_SEND_H */ | ||
diff --git a/src/microhttpd/mhd_sockets.c b/src/microhttpd/mhd_sockets.c index 04405945..0bfe2517 100644 --- a/src/microhttpd/mhd_sockets.c +++ b/src/microhttpd/mhd_sockets.c | |||
@@ -474,20 +474,24 @@ MHD_socket_buffering_reset_ (MHD_socket sock) | |||
474 | #if defined(TCP_NODELAY) || defined(MHD_TCP_CORK_NOPUSH) | 474 | #if defined(TCP_NODELAY) || defined(MHD_TCP_CORK_NOPUSH) |
475 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | 475 | const MHD_SCKT_OPT_BOOL_ off_val = 0; |
476 | #if defined(MHD_TCP_CORK_NOPUSH) | 476 | #if defined(MHD_TCP_CORK_NOPUSH) |
477 | //#if OLD_SOCKOPT | ||
477 | /* Disable extra buffering */ | 478 | /* Disable extra buffering */ |
478 | res = (0 == setsockopt (sock, | 479 | res = (0 == setsockopt (sock, |
479 | IPPROTO_TCP, | 480 | IPPROTO_TCP, |
480 | MHD_TCP_CORK_NOPUSH, | 481 | MHD_TCP_CORK_NOPUSH, |
481 | (const void *) &off_val, | 482 | (const void *) &off_val, |
482 | sizeof (off_val))) && res; | 483 | sizeof (off_val))) && res; |
484 | //#endif | ||
483 | #endif /* MHD_TCP_CORK_NOPUSH */ | 485 | #endif /* MHD_TCP_CORK_NOPUSH */ |
484 | #if defined(TCP_NODELAY) | 486 | #if defined(TCP_NODELAY) |
487 | //#if OLD_SOCKOPT | ||
485 | /* Enable Nagle's algorithm for normal buffering */ | 488 | /* Enable Nagle's algorithm for normal buffering */ |
486 | res = (0 == setsockopt (sock, | 489 | res = (0 == setsockopt (sock, |
487 | IPPROTO_TCP, | 490 | IPPROTO_TCP, |
488 | TCP_NODELAY, | 491 | TCP_NODELAY, |
489 | (const void *) &off_val, | 492 | (const void *) &off_val, |
490 | sizeof (off_val))) && res; | 493 | sizeof (off_val))) && res; |
494 | //#endif | ||
491 | #endif /* TCP_NODELAY */ | 495 | #endif /* TCP_NODELAY */ |
492 | #else /* !TCP_NODELAY && !MHD_TCP_CORK_NOPUSH */ | 496 | #else /* !TCP_NODELAY && !MHD_TCP_CORK_NOPUSH */ |
493 | (void) sock; | 497 | (void) sock; |
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c index 3e9fb053..7b98a45c 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; |