libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 43c21d59f2f7671e1888f3f2bd6169b99b755317
parent 0d394e2aaee3fbbebd148c373630a6ecbc3b3592
Author: ng0 <ng0@n0.is>
Date:   Thu, 20 Jun 2019 19:16:55 +0000

mhd_send: Restructure.

Diffstat:
Msrc/microhttpd/mhd_send.c | 352+++++++++++++++++++++++++++++++++----------------------------------------------
1 file changed, 146 insertions(+), 206 deletions(-)

diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c @@ -20,13 +20,14 @@ /** * @file microhttpd/mhd_send.c - * @brief Implementation of send() and sendfile() wrappers. + * @brief Implementation of send() wrappers. * @author ng0 <ng0@n0.is> */ // to be used in: send_param_adapter, MHD_send_ // and every place where sendfile(), sendfile64(), setsockopt() // are used. +// TODO: sendfile() wrappers. #include "platform.h" @@ -35,6 +36,9 @@ // TCP_CORK/TCP_NOPUSH: don't send out partial frames. // TCP_NODELAY: disable Nagle (aggregate data based on // buffer pressur). +// TODO: It is possible that Solaris/SunOS depending on +// the linked library needs a different setsockopt usage: +// https://stackoverflow.com/questions/48670299/setsockopt-usage-in-linux-and-solaris-invalid-argument-in-solaris enum MHD_SendSocketOptions { @@ -61,11 +65,12 @@ enum MHD_SendSocketOptions * (so far) only provide a FreeBSD compatibility here, for example). * Since we only deal with IPPROTO_TCP flags in this file and nowhere * else, we don't have to move this elsewhere for now. - */ +*/ +/* #if ! defined(TCP_CORK) && defined(TCP_NOPUSH) #define TCP_CORK TCP_NOPUSH #endif - +*/ /* * -- OBJECTIVE: * connection: use member 'socket', and remember the @@ -87,187 +92,133 @@ MHD_send_on_connection_ (struct MHD_Connection *connection, size_t buffer_size, enum MHD_SendSocketOptions options) { - size_t length, opt1, opt2; - ssize_t num_bytes; - int errno = 0; - /* s: the socket. */ + //size_t length, opt1, opt2; + // ssize_t num_bytes; + //int errno = 0; + bool want_cork, have_cork, have_more; + /* The socket. */ MHD_socket s = connection->socket_fd; - bool want_cork; - bool have_cork; - bool have_more; - + // new code... + /* Get socket options, change/set options if necessary. */ switch (options) { + /* No corking */ case MHD_SSO_NO_CORK: want_cork = false; break; + /* Do corking, consider MSG_MORE instead if available */ case MHD_SSO_MAY_CORK: want_cork = true; break; + /* Cork the header */ case MHD_SSO_HDR_CORK: want_cork = (buffer_size >= 1024) && (buffer_size <= 1220); break; } + + // ! could be avoided by redefining the variable have_cork = ! connection->sk_tcp_nodelay_on; + #ifdef MSG_MORE have_more = true; #else have_more = false; #endif + bool use_corknopush; +#if HAVE_NODELAY + use_corknopush = false; +#elif HAVE_CORK + use_corknopush = true; +#elif HAVE_NOPUSH + use_corknopush = true; +#endif - /* Get socket options, change/set options if necessary. */ - switch (options) +#if HAVE_CORK + if (use_corknopush) { - /* No corking */ - case MHD_SSO_NO_CORK: - if (! connection->sk_tcp_nodelay_on) + if (have_cork && ! want_cork) { - opt1 = 1; - opt2 = sizeof (int); - /* - * TODO: It is possible that Solaris/SunOS depending on - * the linked library needs a different setsockopt usage: - * https://stackoverflow.com/questions/48670299/setsockopt-usage-in-linux-and-solaris-invalid-argument-in-solaris - */ - if (0 == setsockopt (s, IPPROTO_TCP, TCP_NODELAY, &opt1, opt2)) - { - connection->sk_tcp_nodelay_on = true; - } - else - { - /* - * TODO: use last return from setsockopt - * here, which for error is -1 when its - * implementation is POSIX conform. - */ - errno = -1; - } + setsockopt (s, IPPROTO_TCP, TCP_CORK, 1, sizeof (int)) || + (setsockopt (s, IPPROTO_TCP, TCP_NODELAY, 1, sizeof (int)) &&connection + ->sk_tcp_nodelay = true); + //setsockopt (cork-on); // or nodelay on // + update connection->sk_tcp_nodelay_on + // When we have CORK, we can have NODELAY on the same system, + // at least since Linux 2.2 and both can be combined since + // Linux 2.5.71. See tcp(7). No other system in 2019-06 has TCP_CORK. } - /* Do corking, do MSG_MORE instead if available */ - case MHD_SSO_MAY_CORK: -#if defined(TCP_CORK) && ! defined(MSG_MORE) - /* - * We have TCP_CORK and we don't have MSG_MORE. - * This means we want to enable corking. - * Check if our corking boolean is not already set. - */ - if (! connection->sk_tcp_cork_nopush_on) + } +#elif HAVE_NOPUSH + /* + * TCP_NOPUSH on FreeBSD is equal to cork on Linux, with the + * exception that we know that TCP_NOPUSH will definitely + * exist and we can disregard TCP_NODELAY unless requested. + */ + if (use_corknopush) + { + if (have_cork && ! want_cork) { - /* - * corking boolean is false. We want to enable - * Corking then. - */ - opt1 = 1; - opt2 = sizeof (int); - /* - * If we succesfully set TCP_CORK, set the corking - * boolean to true. - */ - if (0 == setsockopt (s, IPPROTO_TCP, TCP_CORK, &opt1, opt2)) - { - connection->sk_tcp_cork_nopush_on = true; - } - /* And if we don't, set errno to -1. */ - else - { - errno = -1; - } + setsockopt (s, IPPROTO_TCP, TCP_NOPUSH, 1, sizeof (int)); + // TODO: set corknopush to true here? + // connection->sk_tcp_cork_nopush_on = true; } + } #endif -#ifdef MSG_MORE - /* - * We have MSG_MORE. This means we want to use MSG_MORE - * for send() and keep the socket on NODELAY. - * Check if our nodelay boolean is false. - */ - if (! connection->sk_tcp_nodelay_on) +#if HAVE_NODELAY + if (! use_corknopush) + { + if (! have_cork && want_cork) { - /* - * If we have MSG_MORE, keep the - * socket on NO_DELAY / NO_CORK. - * Since MSG_MORE is an argument to - * send(), in some cases we will be - * using send() with MSG_MORE. - */ - opt1 = 1; - opt2 = sizeof (int); - /* - * If we successfully set TCP_NODELAY, set the nodelay - * boolean to true. - */ - if (0 == setsockopt (s, IPPROTO_TCP, TCP_NODELAY, &opt1, opt2)) - { - connection->sk_tcp_nodelay_on = true; - } - else - { - errno = -1; - } + // setsockopt (nodelay-off); + setsockopt (s, IPPROTO_TCP, TCP_NODELAY, 0, sizeof (int)); + connection->sk_tcp_nodelay = false; } + // ... + } #endif - /* Cork the header */ - case MHD_SSO_HDR_CORK: - if (something_with_snd_mss > (sizeof (buffer - 10))) - { // magic guessing? - if ((! 100_Continue) && (sending_header)) - { - // uncork - if (connection->sk_tcp_cork_nopush_on) - { - opt1 = 0; - opt2 = sizeof (int); - if (0 == setsockopt (s, IPPROTO_TCP, TCP_CORK, &opt1, opt2)) - connection->sk_tcp_cork_nopush_on = false; - } - // setsockopt() uncork flag - opt1 = 1; - opt2 = sizeof (int); - if (0 == setsockopt (s, IPPROTO_TCP, TCP_NODELAY, &opt1, opt2)) - connection->sk_tcp_nodelay_on = true; - // -> if we now cork again, would that - // be too much for this case? If we - // want to cork, we use case 1. - } + + + ret = send (s, buffer, buffer_size, want_cork ? MSG_MORE : 0); + eno = errno; +#if HAVE_CORK + if (use_corknopush) + { + if (! have_cork && want_cork && ! have_more) + { + //setsockopt (cork-off); // or nodelay off // + update connection->sk_tcp_nodelay_on + setsockopt (s, IPPROTO_TCP, TCP_CORK, 0, sizeof (int)) || + (setsockopt (s, IPPROTO_TCP, TCP_NODELAY, 0, sizeof (int)) &&connection + ->sk_tcp_nodelay_on = false); } } -} -if (MHD_SendSocketOptions == 1) -{ -#ifdef MSG_MORE - num_bytes = send (s, buffer, buffer_size, MSG_MORE); -#else - num_bytes = send (s, buffer, buffer_size); +#elif HAVE_NOPUSH + // We don't have MSG_MORE. + if (use_corknopush) + { + // ... + } #endif -} -else -{ - num_bytes = send (s, buffer, buffer_size); -} -// -- pseudo Start: -// set socket := connect->MHD_socket -// get stateof(socket) -// in case 0,1,2 where case from MHD_SendSocketOptions do -// $case: -// setsockopt PLATFORM_ACCORDINGLY -// "update socket state" -// send(socket, buffer, buffer_size) -// if socketError: -// return -1 -// return numBytes -// -- pseudo End -// error -/* - * send() returns -1 on error, we might as well return num_bytes, - * but we need to catch the errors before send(). - */ -if (0 != errno) - return -1; -if (0 == errno) - return num_bytes; -} + +#if HAVE_NODELAY + if (! use_corknopush) + { + if (have_cork && ! want_cork) + { + // setsockopt (nodelay - on); + setsockopt (s, + IPPROTO_TCP, + TCP_NODELAY, + 1, + sizeof (int)) &&connection->sk_tcp_nodelay_on = true; + } + // ... + } +#endif + errno = eno; + return ret; + // * Send header followed by buffer on connection; // * uses writev if possible to send both at once @@ -285,55 +236,46 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection, size_t buffer_size, enum MHD_SendSocketOptions options) { - int errno = 0; MHD_socket s = connection->socket_fd; - // -- <pseudo> - // set socket := connect->MHD_socket - // in case 0,1,2 where case from MHD_SendSocketOptions do - // $case: - // setsockopt - // update boolean - // nbuffer = header + buffer - // #if defined(WRITEV) - // struct iovec vector[2]; - // vector[0].iov_base = header; - // vector[0].iov_len = header_size; - // vector[1].iov_base = buffer; - // vector[1].iov_len = buffer_size; - // i = writev(s, &vector[0], 2); - // num_bytes = send(socket, i, WHATSIZE?) - // #else - // //not available, send a combination of header + buffer. - // size_t nbuffersize = buffer_size + header_size - // num_bytes = send(socket, nbuffer, nbuffersize) - // #endif - // if socketError: - // return -1 - // return numBytes - // -- </pseudo> - struct tcp_info tcp_; - size_t opt1, opt2, length; - - if (0 == - getsockopt (connection->socket, - TCP_INFO, - IPPROTO_TCP, - &tcp_, - sizeof (tcp_))) - { - // mss = tcp_.tcpi_snd_mss; - } + bool want_cork, have_cork, have_more; switch (options) { + /* No corking */ case MHD_SSO_NO_CORK: - /* No corking */ + want_cork = false; + break; + /* Do corking, do MSG_MORE instead if available */ case MHD_SSO_MAY_CORK: + want_cork = true; + break; + /* Cork the header */ case MHD_SSO_HDR_CORK: + want_cork = (buffer_size >= 1024) && (buffer_size <= 1220); + break; } - if (MHD_SendSocketOptions == 1) + // ! could be avoided by redefining the variable + have_cork = ! connection->sk_tcp_nodelay_on; + +#ifdef MSG_MORE + have_more = true; +#else + have_more = false; +#endif + + bool use_corknopush; + +#if HAVE_NODELAY + use_corknopush = false; +#elif HAVE_CORK + use_corknopush = true; +#elif HAVE_NOPUSH + use_corknopush = true; +#endif + +#if HAVE_WRITEV + if ((options == MHD_SSO_HDR_CORK) && want_cork) { -#ifdef WRITEV int iovcnt; struct iovec vector[2]; vector[0].iov_base = header; @@ -341,22 +283,20 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection, vector[1].iov_base = buffer; vector[1].iov_len = strlen (buffer); iovcnt = sizeof (vector) / sizeof (struct iovec); - int i = writev (s, vector, iovcnt); - fprintf (stdout, "i=%d, errno=%d\n", i, errno); -#else - // not available, send a combination of header + buffer. - //size_t concatsize = header_size + buffer_size; - //const char *concatbuffer; - //concatbuffer = header + buffer; -#ifdef MSG_MORE - num_bytes = send (s, header, header_size, MSG_MORE); -#else - num_bytes = send (s, header, header_size); -#endif -#endif + ret = writev (s, vector, iovcnt); + eno = errno; + if ((ret == header_len + buffer_len) && have_cork) + { + // response complete, definitely uncork! + // setsockopt (cork-off); + setsockopt (s, IPPROTO_TCP, TCP_CORK, 0, sizeof (int)); + // connection->sk_tcp_cork_nopush_on = true; + } + errno = eno; + return ret; } - if (0 != errno) - return -1; - if (0 == errno) - return num_bytes; +#endif + + errno = eno; + return ret; }