libmicrohttpd

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

upgrade_example.c (9470B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2016 Christian Grothoff (and other contributing authors)
      4      Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
      5 
      6      This library is free software; you can redistribute it and/or
      7      modify it under the terms of the GNU Lesser General Public
      8      License as published by the Free Software Foundation; either
      9      version 2.1 of the License, or (at your option) any later version.
     10 
     11      This library is distributed in the hope that it will be useful,
     12      but WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      Lesser General Public License for more details.
     15 
     16      You should have received a copy of the GNU Lesser General Public
     17      License along with this library; if not, write to the Free Software
     18      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     19 */
     20 /**
     21  * @file upgrade_example.c
     22  * @brief example for how to use libmicrohttpd upgrade
     23  * @author Christian Grothoff
     24  * @author Karlson2k (Evgeny Grin)
     25  *
     26  * Telnet to the HTTP server, use this in the request:
     27  * GET / http/1.1
     28  * Connection: Upgrade
     29  *
     30  * After this, whatever you type will be echo'ed back to you.
     31  */
     32 
     33 #include "platform.h"
     34 #include <microhttpd.h>
     35 #include <pthread.h>
     36 #include <errno.h>
     37 
     38 #define PAGE \
     39   "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>"
     40 
     41 
     42 /**
     43  * Change socket to blocking.
     44  *
     45  * @param fd the socket to manipulate
     46  */
     47 static void
     48 make_blocking (MHD_socket fd)
     49 {
     50 #if defined(MHD_POSIX_SOCKETS)
     51   int flags;
     52 
     53   flags = fcntl (fd, F_GETFL);
     54   if (-1 == flags)
     55     abort ();
     56   if ((flags & ~O_NONBLOCK) != flags)
     57     if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
     58       abort ();
     59 #elif defined(MHD_WINSOCK_SOCKETS)
     60   unsigned long flags = 0;
     61 
     62   if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
     63     abort ();
     64 #endif /* MHD_WINSOCK_SOCKETS */
     65 }
     66 
     67 
     68 static void
     69 send_all (MHD_socket sock,
     70           const char *buf,
     71           size_t len)
     72 {
     73   ssize_t ret;
     74   size_t off;
     75 
     76   make_blocking (sock);
     77   for (off = 0; off < len; off += (size_t) ret)
     78   {
     79     ret = send (sock,
     80                 &buf[off],
     81 #if ! defined(_WIN32) || defined(__CYGWIN__)
     82                 len - off,
     83 #else  /* Native W32 */
     84                 (int) (len - off),
     85 #endif /* Native W32 */
     86                 0);
     87     if (0 > ret)
     88     {
     89       if (EAGAIN == errno)
     90       {
     91         ret = 0;
     92         continue;
     93       }
     94       break;
     95     }
     96     if (0 == ret)
     97       break;
     98   }
     99 }
    100 
    101 
    102 struct MyData
    103 {
    104   struct MHD_UpgradeResponseHandle *urh;
    105   char *extra_in;
    106   size_t extra_in_size;
    107   MHD_socket sock;
    108 };
    109 
    110 
    111 /**
    112  * Main function for the thread that runs the interaction with
    113  * the upgraded socket.  Writes what it reads.
    114  *
    115  * @param cls the `struct MyData`
    116  */
    117 static void *
    118 run_usock (void *cls)
    119 {
    120   struct MyData *md = cls;
    121   struct MHD_UpgradeResponseHandle *urh = md->urh;
    122   char buf[128];
    123   ssize_t got;
    124 
    125   make_blocking (md->sock);
    126   /* start by sending extra data MHD may have already read, if any */
    127   if (0 != md->extra_in_size)
    128   {
    129     send_all (md->sock,
    130               md->extra_in,
    131               md->extra_in_size);
    132     free (md->extra_in);
    133   }
    134   /* now echo in a loop */
    135   while (1)
    136   {
    137     got = recv (md->sock,
    138                 buf,
    139                 sizeof (buf),
    140                 0);
    141     if (0 >= got)
    142       break;
    143     send_all (md->sock,
    144               buf,
    145               (size_t) got);
    146   }
    147   free (md);
    148   MHD_upgrade_action (urh,
    149                       MHD_UPGRADE_ACTION_CLOSE);
    150   return NULL;
    151 }
    152 
    153 
    154 /**
    155  * Function called after a protocol "upgrade" response was sent
    156  * successfully and the socket should now be controlled by some
    157  * protocol other than HTTP.
    158  *
    159  * Any data already received on the socket will be made available in
    160  * @e extra_in.  This can happen if the application sent extra data
    161  * before MHD send the upgrade response.  The application should
    162  * treat data from @a extra_in as if it had read it from the socket.
    163  *
    164  * Note that the application must not close() @a sock directly,
    165  * but instead use #MHD_upgrade_action() for special operations
    166  * on @a sock.
    167  *
    168  * Data forwarding to "upgraded" @a sock will be started as soon
    169  * as this function return.
    170  *
    171  * Except when in 'thread-per-connection' mode, implementations
    172  * of this function should never block (as it will still be called
    173  * from within the main event loop).
    174  *
    175  * @param cls closure, whatever was given to #MHD_create_response_for_upgrade().
    176  * @param connection original HTTP connection handle,
    177  *                   giving the function a last chance
    178  *                   to inspect the original HTTP request
    179  * @param req_cls last value left in `req_cls` of the `MHD_AccessHandlerCallback`
    180  * @param extra_in if we happened to have read bytes after the
    181  *                 HTTP header already (because the client sent
    182  *                 more than the HTTP header of the request before
    183  *                 we sent the upgrade response),
    184  *                 these are the extra bytes already read from @a sock
    185  *                 by MHD.  The application should treat these as if
    186  *                 it had read them from @a sock.
    187  * @param extra_in_size number of bytes in @a extra_in
    188  * @param sock socket to use for bi-directional communication
    189  *        with the client.  For HTTPS, this may not be a socket
    190  *        that is directly connected to the client and thus certain
    191  *        operations (TCP-specific setsockopt(), getsockopt(), etc.)
    192  *        may not work as expected (as the socket could be from a
    193  *        socketpair() or a TCP-loopback).  The application is expected
    194  *        to perform read()/recv() and write()/send() calls on the socket.
    195  *        The application may also call shutdown(), but must not call
    196  *        close() directly.
    197  * @param urh argument for #MHD_upgrade_action()s on this @a connection.
    198  *        Applications must eventually use this callback to (indirectly)
    199  *        perform the close() action on the @a sock.
    200  */
    201 static void
    202 uh_cb (void *cls,
    203        struct MHD_Connection *connection,
    204        void *req_cls,
    205        const char *extra_in,
    206        size_t extra_in_size,
    207        MHD_socket sock,
    208        struct MHD_UpgradeResponseHandle *urh)
    209 {
    210   struct MyData *md;
    211   pthread_t pt;
    212   (void) cls;         /* Unused. Silent compiler warning. */
    213   (void) connection;  /* Unused. Silent compiler warning. */
    214   (void) req_cls;     /* Unused. Silent compiler warning. */
    215 
    216   md = malloc (sizeof (struct MyData));
    217   if (NULL == md)
    218     abort ();
    219   memset (md, 0, sizeof (struct MyData));
    220   if (0 != extra_in_size)
    221   {
    222     md->extra_in = malloc (extra_in_size);
    223     if (NULL == md->extra_in)
    224       abort ();
    225     memcpy (md->extra_in,
    226             extra_in,
    227             extra_in_size);
    228   }
    229   md->extra_in_size = extra_in_size;
    230   md->sock = sock;
    231   md->urh = urh;
    232   if (0 != pthread_create (&pt,
    233                            NULL,
    234                            &run_usock,
    235                            md))
    236     abort ();
    237   /* Note that by detaching like this we make it impossible to ensure
    238      a clean shutdown, as the we stop the daemon even if a worker thread
    239      is still running. Alas, this is a simple example... */
    240   pthread_detach (pt);
    241 
    242   /* This callback must return as soon as possible. */
    243 
    244   /* Data forwarding to "upgraded" socket will be started
    245    * after return from this callback. */
    246 }
    247 
    248 
    249 static enum MHD_Result
    250 ahc_echo (void *cls,
    251           struct MHD_Connection *connection,
    252           const char *url,
    253           const char *method,
    254           const char *version,
    255           const char *upload_data,
    256           size_t *upload_data_size,
    257           void **req_cls)
    258 {
    259   static int aptr;
    260   struct MHD_Response *response;
    261   enum MHD_Result ret;
    262   (void) cls;               /* Unused. Silent compiler warning. */
    263   (void) url;               /* Unused. Silent compiler warning. */
    264   (void) version;           /* Unused. Silent compiler warning. */
    265   (void) upload_data;       /* Unused. Silent compiler warning. */
    266   (void) upload_data_size;  /* Unused. Silent compiler warning. */
    267 
    268   if (0 != strcmp (method, "GET"))
    269     return MHD_NO;              /* unexpected method */
    270   if (&aptr != *req_cls)
    271   {
    272     /* do never respond on first call */
    273     *req_cls = &aptr;
    274     return MHD_YES;
    275   }
    276   *req_cls = NULL;                  /* reset when done */
    277   response = MHD_create_response_for_upgrade (&uh_cb,
    278                                               NULL);
    279 
    280   MHD_add_response_header (response,
    281                            MHD_HTTP_HEADER_UPGRADE,
    282                            "Echo Server");
    283   ret = MHD_queue_response (connection,
    284                             MHD_HTTP_SWITCHING_PROTOCOLS,
    285                             response);
    286   MHD_destroy_response (response);
    287   return ret;
    288 }
    289 
    290 
    291 int
    292 main (int argc,
    293       char *const *argv)
    294 {
    295   struct MHD_Daemon *d;
    296   unsigned int port;
    297 
    298   if ( (argc != 2) ||
    299        (1 != sscanf (argv[1], "%u", &port)) ||
    300        (65535 < port) )
    301   {
    302     printf ("%s PORT\n", argv[0]);
    303     return 1;
    304   }
    305   d = MHD_start_daemon (MHD_ALLOW_UPGRADE | MHD_USE_AUTO
    306                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    307                         (uint16_t) port,
    308                         NULL, NULL,
    309                         &ahc_echo, NULL,
    310                         MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
    311                         MHD_OPTION_END);
    312   if (d == NULL)
    313     return 1;
    314   (void) getc (stdin);
    315   MHD_stop_daemon (d);
    316   return 0;
    317 }