gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [libmicrohttpd] 01/154: mhd_send: Add initial version.


From: gnunet
Subject: [GNUnet-SVN] [libmicrohttpd] 01/154: mhd_send: Add initial version.
Date: Mon, 19 Aug 2019 10:15:13 +0200

This is an automated email from the git hooks/post-receive script.

ng0 pushed a commit to branch master
in repository libmicrohttpd.

commit fedd904512322d8037d47b0b0d60b6337bc4d19d
Author: ng0 <address@hidden>
AuthorDate: Mon Jun 17 16:26:15 2019 +0000

    mhd_send: Add initial version.
---
 src/microhttpd/mhd_send.c | 329 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 329 insertions(+)

diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c
new file mode 100644
index 00000000..abfe2783
--- /dev/null
+++ b/src/microhttpd/mhd_send.c
@@ -0,0 +1,329 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2019 ng0 <address@hidden>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  
USA
+
+ */
+
+/**
+ * @file microhttpd/mhd_send.c
+ * @brief Implementation of send() and sendfile() wrappers.
+ * @author ng0 <address@hidden>
+ */
+
+// to be used in: send_param_adapter, MHD_send_
+// and every place where sendfile(), sendfile64(), setsockopt()
+// are used.
+
+#include "platform.h"
+
+// NOTE: TCP_CORK == TCP_NOPUSH in FreeBSD.
+//       TCP_CORK is Linux.
+//       TCP_CORK/TCP_NOPUSH: don't send out partial frames.
+//       TCP_NODELAY: disable Nagle (aggregate data based on
+//       buffer pressur).
+
+enum MHD_SendSocketOptions
+{
+  /* definitely no corking (use NODELAY, or explicitly disable cork) */
+  MHD_SSO_NO_CORK = 0,
+  /* should enable corking (use MSG_MORE, or explicitly enable cork) */
+  MHD_SSO_MAY_CORK = 1,
+  /*
+   * consider tcpi_snd_mss and consider not corking for the header
+   * part if the size of the header is close to the MSS.
+   * Only used if we are NOT doing 100 Continue and are still
+   * sending the header (provided in full as the buffer to
+   * MHD_send_on_connection_ or as the header to
+   * MHD_send_on_connection2_).
+   */
+  MHD_SSO_HDR_CORK = 2
+};
+
+/*
+ * 
https://svnweb.freebsd.org/base/head/sys/netinet/tcp_usrreq.c?view=markup&pathrev=346360
+ * Approximately in 2007 work began to make TCP_NOPUSH in FreeBSD
+ * behave like TCP_CORK in Linux. Thus we define them to be one and
+ * the same, which again could be platform dependent (NetBSD does
+ * (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
+ * current state of the socket-options (cork/nocork/nodelay/whatever)
+ * and only call setsockopt when absolutely necessary.
+ *
+ * -- NOTES:
+ * Send 'buffer' on connection;
+ * change socket options as required,
+ * return -1 on error, otherwise # bytes sent.
+ *
+ * MHD_Connection is defined in ./internal.h
+ * MHD_socket is defined in lib/mhd_sockets.h and the type
+ * depends on the platform. However it is always a socket.
+ */
+ssize_t
+MHD_send_on_connection_ (struct MHD_Connection *connection,
+                         const char *buffer,
+                         size_t buffer_size,
+                         enum MHD_SendSocketOptions)
+{
+  size_t length, opt1, opt2;
+  ssize_t num_bytes;
+  int errno = 0;
+  /* s: the socket. */
+  MHD_socket s = connection->socket_fd;
+
+  /* Get socket options, change/set options if necessary. */
+  switch (MHD_SendSocketOptions)
+  {
+  /* No corking */
+  case 0:
+    if (false == connection->sk_tcp_nodelay_on)
+    {
+      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;
+      }
+    }
+  /* Do corking, do MSG_MORE instead if available */
+  case 1:
+#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 (false == connection->sk_tcp_cork_nopush_on)
+    {
+      /* 
+       * 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 (false == 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 2:
+    if (something_with_snd_mss > (sizeof (buffer - 10)))
+    { // magic guessing?
+      if ((! 100_Continue) && (sending_header))
+      {
+        // uncork
+        if (true == 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.
+      }
+    }
+  }
+}
+if (1 == MHD_SendSocketOptions)
+{
+#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;
+}
+
+// * Send header followed by buffer on connection;
+// * uses writev if possible to send both at once
+// * returns the sum of the number of bytes sent from
+// * both buffers, or -1 on error;
+// * if writev is
+// unavailable, this call MUST only send from 'header'
+// (as we cannot handle the case that the first write
+// succeeds and the 2nd fails!).
+ssize_t
+MHD_send_on_connection2_ (struct MHD_Connection *connection,
+                          const char *header,
+                          size_t header_size,
+                          const char *buffer,
+                          size_t buffer_size,
+                          enum MHD_SendSocketOptions)
+{
+       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>
+#ifdef WRITEV
+  int iovcnt;
+  // TODO: iovec/writev needs no alloc, but consider looking into mmap?
+  struct iovec vector[2];
+  vector[0].iov_base = header;
+  vector[0].iov_len = strlen (header);
+  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
+  // wait for phonecall clearing this up?
+  // COMMENTARY: 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, concatbuffer, concatsize, MSG_MORE);
+#else
+  num_bytes = send (s, concatbuffer, concatsize);
+#endif
+#endif
+  struct tcp_info *tcp_;
+  size_t opt1, opt2, length;
+  switch (MHD_SendSocketOptions)
+  {
+  case 0:
+         /* No corking */
+  case 1:
+  case 2:
+  }
+if (1 == MHD_SendSocketOptions)
+{
+       // bla
+}
+  if (0 != errno)
+    return -1;
+  if (0 == errno)
+    return num_bytes;
+}

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]