mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
mhd_send: Restructure.
This commit is contained in:
+151
-211
@@ -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;
|
||||
}
|
||||
}
|
||||
#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 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;
|
||||
}
|
||||
}
|
||||
#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.
|
||||
}
|
||||
setsockopt (s, IPPROTO_TCP, TCP_NOPUSH, 1, sizeof (int));
|
||||
// TODO: set corknopush to true here?
|
||||
// connection->sk_tcp_cork_nopush_on = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (MHD_SendSocketOptions == 1)
|
||||
{
|
||||
#ifdef MSG_MORE
|
||||
num_bytes = send (s, buffer, buffer_size, MSG_MORE);
|
||||
#else
|
||||
num_bytes = send (s, buffer, buffer_size);
|
||||
#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-off);
|
||||
setsockopt (s, IPPROTO_TCP, TCP_NODELAY, 0, sizeof (int));
|
||||
connection->sk_tcp_nodelay = false;
|
||||
}
|
||||
// ...
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
#elif HAVE_NOPUSH
|
||||
// We don't have MSG_MORE.
|
||||
if (use_corknopush)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
#endif
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user