Blame src/examples/upgrade_example.c

Packit 875988
/*
Packit 875988
     This file is part of libmicrohttpd
Packit 875988
     Copyright (C) 2016 Christian Grothoff (and other contributing authors)
Packit 875988
Packit 875988
     This library is free software; you can redistribute it and/or
Packit 875988
     modify it under the terms of the GNU Lesser General Public
Packit 875988
     License as published by the Free Software Foundation; either
Packit 875988
     version 2.1 of the License, or (at your option) any later version.
Packit 875988
Packit 875988
     This library is distributed in the hope that it will be useful,
Packit 875988
     but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 875988
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 875988
     Lesser General Public License for more details.
Packit 875988
Packit 875988
     You should have received a copy of the GNU Lesser General Public
Packit 875988
     License along with this library; if not, write to the Free Software
Packit 875988
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 875988
*/
Packit 875988
/**
Packit 875988
 * @file upgrade_example.c
Packit 875988
 * @brief example for how to use libmicrohttpd upgrade
Packit 875988
 * @author Christian Grothoff
Packit 875988
 *
Packit 875988
 * Telnet to the HTTP server, use this in the request:
Packit 875988
 * GET / http/1.1
Packit 875988
 * Connection: Upgrade
Packit 875988
 *
Packit 875988
 * After this, whatever you type will be echo'ed back to you.
Packit 875988
 */
Packit 875988
Packit 875988
#include "platform.h"
Packit 875988
#include <microhttpd.h>
Packit 875988
#include <pthread.h>
Packit 875988
Packit 875988
#define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>"
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Change socket to blocking.
Packit 875988
 *
Packit 875988
 * @param fd the socket to manipulate
Packit 875988
 * @return non-zero if succeeded, zero otherwise
Packit 875988
 */
Packit 875988
static void
Packit 875988
make_blocking (MHD_socket fd)
Packit 875988
{
Packit 875988
#if defined(MHD_POSIX_SOCKETS)
Packit 875988
  int flags;
Packit 875988
Packit 875988
  flags = fcntl (fd, F_GETFL);
Packit 875988
  if (-1 == flags)
Packit 875988
    return;
Packit 875988
  if ((flags & ~O_NONBLOCK) != flags)
Packit 875988
    if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
Packit 875988
      abort ();
Packit 875988
#elif defined(MHD_WINSOCK_SOCKETS)
Packit 875988
  unsigned long flags = 1;
Packit 875988
Packit 875988
  ioctlsocket (fd, FIONBIO, &flags);
Packit 875988
#endif /* MHD_WINSOCK_SOCKETS */
Packit 875988
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
static void
Packit 875988
send_all (MHD_socket sock,
Packit 875988
          const char *buf,
Packit 875988
          size_t len)
Packit 875988
{
Packit 875988
  ssize_t ret;
Packit 875988
  size_t off;
Packit 875988
Packit 875988
  make_blocking (sock);
Packit 875988
  for (off = 0; off < len; off += ret)
Packit 875988
    {
Packit 875988
      ret = send (sock,
Packit 875988
                  &buf[off],
Packit 875988
                  len - off,
Packit 875988
                  0);
Packit 875988
      if (0 > ret)
Packit 875988
        {
Packit 875988
          if (EAGAIN == errno)
Packit 875988
            {
Packit 875988
              ret = 0;
Packit 875988
              continue;
Packit 875988
            }
Packit 875988
          break;
Packit 875988
        }
Packit 875988
      if (0 == ret)
Packit 875988
        break;
Packit 875988
    }
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
struct MyData
Packit 875988
{
Packit 875988
  struct MHD_UpgradeResponseHandle *urh;
Packit 875988
  char *extra_in;
Packit 875988
  size_t extra_in_size;
Packit 875988
  MHD_socket sock;
Packit 875988
};
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Main function for the thread that runs the interaction with
Packit 875988
 * the upgraded socket.  Writes what it reads.
Packit 875988
 *
Packit 875988
 * @param cls the `struct MyData`
Packit 875988
 */
Packit 875988
static void *
Packit 875988
run_usock (void *cls)
Packit 875988
{
Packit 875988
  struct MyData *md = cls;
Packit 875988
  struct MHD_UpgradeResponseHandle *urh = md->urh;
Packit 875988
  char buf[128];
Packit 875988
  ssize_t got;
Packit 875988
Packit 875988
  make_blocking (md->sock);
Packit 875988
  /* start by sending extra data MHD may have already read, if any */
Packit 875988
  if (0 != md->extra_in_size)
Packit 875988
    {
Packit 875988
      send_all (md->sock,
Packit 875988
                md->extra_in,
Packit 875988
                md->extra_in_size);
Packit 875988
      free (md->extra_in);
Packit 875988
    }
Packit 875988
  /* now echo in a loop */
Packit 875988
  while (1)
Packit 875988
    {
Packit 875988
      got = recv (md->sock,
Packit 875988
                  buf,
Packit 875988
                  sizeof (buf),
Packit 875988
                  0);
Packit 875988
      if (0 >= got)
Packit 875988
        break;
Packit 875988
      send_all (md->sock,
Packit 875988
                buf,
Packit 875988
                got);
Packit 875988
    }
Packit 875988
  free (md);
Packit 875988
  MHD_upgrade_action (urh,
Packit 875988
                      MHD_UPGRADE_ACTION_CLOSE);
Packit 875988
  return NULL;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Function called after a protocol "upgrade" response was sent
Packit 875988
 * successfully and the socket should now be controlled by some
Packit 875988
 * protocol other than HTTP.
Packit 875988
 *
Packit 875988
 * Any data already received on the socket will be made available in
Packit 875988
 * @e extra_in.  This can happen if the application sent extra data
Packit 875988
 * before MHD send the upgrade response.  The application should
Packit 875988
 * treat data from @a extra_in as if it had read it from the socket.
Packit 875988
 *
Packit 875988
 * Note that the application must not close() @a sock directly,
Packit 875988
 * but instead use #MHD_upgrade_action() for special operations
Packit 875988
 * on @a sock.
Packit 875988
 *
Packit 875988
 * Data forwarding to "upgraded" @a sock will be started as soon
Packit 875988
 * as this function return.
Packit 875988
 *
Packit 875988
 * Except when in 'thread-per-connection' mode, implementations
Packit 875988
 * of this function should never block (as it will still be called
Packit 875988
 * from within the main event loop).
Packit 875988
 *
Packit 875988
 * @param cls closure, whatever was given to #MHD_create_response_for_upgrade().
Packit 875988
 * @param connection original HTTP connection handle,
Packit 875988
 *                   giving the function a last chance
Packit 875988
 *                   to inspect the original HTTP request
Packit 875988
 * @param con_cls last value left in `con_cls` of the `MHD_AccessHandlerCallback`
Packit 875988
 * @param extra_in if we happened to have read bytes after the
Packit 875988
 *                 HTTP header already (because the client sent
Packit 875988
 *                 more than the HTTP header of the request before
Packit 875988
 *                 we sent the upgrade response),
Packit 875988
 *                 these are the extra bytes already read from @a sock
Packit 875988
 *                 by MHD.  The application should treat these as if
Packit 875988
 *                 it had read them from @a sock.
Packit 875988
 * @param extra_in_size number of bytes in @a extra_in
Packit 875988
 * @param sock socket to use for bi-directional communication
Packit 875988
 *        with the client.  For HTTPS, this may not be a socket
Packit 875988
 *        that is directly connected to the client and thus certain
Packit 875988
 *        operations (TCP-specific setsockopt(), getsockopt(), etc.)
Packit 875988
 *        may not work as expected (as the socket could be from a
Packit 875988
 *        socketpair() or a TCP-loopback).  The application is expected
Packit 875988
 *        to perform read()/recv() and write()/send() calls on the socket.
Packit 875988
 *        The application may also call shutdown(), but must not call
Packit 875988
 *        close() directly.
Packit 875988
 * @param urh argument for #MHD_upgrade_action()s on this @a connection.
Packit 875988
 *        Applications must eventually use this callback to (indirectly)
Packit 875988
 *        perform the close() action on the @a sock.
Packit 875988
 */
Packit 875988
static void
Packit 875988
uh_cb (void *cls,
Packit 875988
       struct MHD_Connection *connection,
Packit 875988
       void *con_cls,
Packit 875988
       const char *extra_in,
Packit 875988
       size_t extra_in_size,
Packit 875988
       MHD_socket sock,
Packit 875988
       struct MHD_UpgradeResponseHandle *urh)
Packit 875988
{
Packit 875988
  struct MyData *md;
Packit 875988
  pthread_t pt;
Packit 875988
  (void)cls;         /* Unused. Silent compiler warning. */
Packit 875988
  (void)connection;  /* Unused. Silent compiler warning. */
Packit 875988
  (void)con_cls;     /* Unused. Silent compiler warning. */
Packit 875988
Packit 875988
  md = malloc (sizeof (struct MyData));
Packit 875988
  if (NULL == md)
Packit 875988
    abort ();
Packit 875988
  memset (md, 0, sizeof (struct MyData));
Packit 875988
  if (0 != extra_in_size)
Packit 875988
    {
Packit 875988
      md->extra_in = malloc (extra_in_size);
Packit 875988
      if (NULL == md->extra_in)
Packit 875988
        abort ();
Packit 875988
      memcpy (md->extra_in,
Packit 875988
              extra_in,
Packit 875988
              extra_in_size);
Packit 875988
    }
Packit 875988
  md->extra_in_size = extra_in_size;
Packit 875988
  md->sock = sock;
Packit 875988
  md->urh = urh;
Packit 875988
  if (0 != pthread_create (&pt,
Packit 875988
                           NULL,
Packit 875988
                           &run_usock,
Packit 875988
                           md))
Packit 875988
    abort ();
Packit 875988
  /* Note that by detaching like this we make it impossible to ensure
Packit 875988
     a clean shutdown, as the we stop the daemon even if a worker thread
Packit 875988
     is still running. Alas, this is a simple example... */
Packit 875988
  pthread_detach (pt);
Packit 875988
Packit 875988
  /* This callback must return as soon as possible. */
Packit 875988
Packit 875988
  /* Data forwarding to "upgraded" socket will be started
Packit 875988
   * after return from this callback. */
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
static int
Packit 875988
ahc_echo (void *cls,
Packit 875988
          struct MHD_Connection *connection,
Packit 875988
          const char *url,
Packit 875988
          const char *method,
Packit 875988
          const char *version,
Packit 875988
          const char *upload_data,
Packit 875988
          size_t *upload_data_size,
Packit 875988
          void **ptr)
Packit 875988
{
Packit 875988
  static int aptr;
Packit 875988
  struct MHD_Response *response;
Packit 875988
  int ret;
Packit 875988
  (void)cls;               /* Unused. Silent compiler warning. */
Packit 875988
  (void)url;               /* Unused. Silent compiler warning. */
Packit 875988
  (void)version;           /* Unused. Silent compiler warning. */
Packit 875988
  (void)upload_data;       /* Unused. Silent compiler warning. */
Packit 875988
  (void)upload_data_size;  /* Unused. Silent compiler warning. */
Packit 875988
Packit 875988
  if (0 != strcmp (method, "GET"))
Packit 875988
    return MHD_NO;              /* unexpected method */
Packit 875988
  if (&aptr != *ptr)
Packit 875988
    {
Packit 875988
      /* do never respond on first call */
Packit 875988
      *ptr = &apt;;
Packit 875988
      return MHD_YES;
Packit 875988
    }
Packit 875988
  *ptr = NULL;                  /* reset when done */
Packit 875988
  response = MHD_create_response_for_upgrade (&uh_cb,
Packit 875988
                                              NULL);
Packit 875988
Packit 875988
  MHD_add_response_header (response,
Packit 875988
                           MHD_HTTP_HEADER_UPGRADE,
Packit 875988
                           "Echo Server");
Packit 875988
  ret = MHD_queue_response (connection,
Packit 875988
                            MHD_HTTP_SWITCHING_PROTOCOLS,
Packit 875988
                            response);
Packit 875988
  MHD_destroy_response (response);
Packit 875988
  return ret;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
int
Packit 875988
main (int argc,
Packit 875988
      char *const *argv)
Packit 875988
{
Packit 875988
  struct MHD_Daemon *d;
Packit 875988
Packit 875988
  if (argc != 2)
Packit 875988
    {
Packit 875988
      printf ("%s PORT\n", argv[0]);
Packit 875988
      return 1;
Packit 875988
    }
Packit 875988
  d = MHD_start_daemon (MHD_ALLOW_UPGRADE | MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
Packit 875988
                        atoi (argv[1]),
Packit 875988
                        NULL, NULL,
Packit 875988
                        &ahc_echo, NULL,
Packit 875988
			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
Packit 875988
			MHD_OPTION_END);
Packit 875988
  if (d == NULL)
Packit 875988
    return 1;
Packit 875988
  (void) getc (stdin);
Packit 875988
  MHD_stop_daemon (d);
Packit 875988
  return 0;
Packit 875988
}