libmicrohttpd

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

upgrade.c (12960B)


      1 /* Feel free to use this example code in any way
      2    you see fit (Public Domain) */
      3 
      4 /**
      5  * @file upgrade.c
      6  * @brief   Minimal example for the HTTP "Upgrade" API of libmicrohttpd.
      7  *          A "GET /" carrying "Connection: Upgrade" and "Upgrade: echo"
      8  *          is answered with "101 Switching Protocols"; afterwards the
      9  *          raw socket speaks a trivial line-based echo protocol until
     10  *          the client sends the line "QUIT" or closes the connection.
     11  * @author  Christian Grothoff
     12  */
     13 
     14 #include <sys/types.h>
     15 #ifndef _WIN32
     16 #include <sys/select.h>
     17 #include <sys/socket.h>
     18 #include <fcntl.h>
     19 #else
     20 #include <winsock2.h>
     21 #endif
     22 #include <microhttpd.h>
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 #include <errno.h>
     27 
     28 #define PORT 8888
     29 
     30 /**
     31  * Name of the protocol we are willing to switch to.  Any name would
     32  * do; "echo" is not registered with IANA and is only used here to keep
     33  * the example self-contained.
     34  */
     35 #define PROTOCOL "echo"
     36 
     37 /**
     38  * Maximum length of a line of the echo protocol.  Longer lines are
     39  * truncated.
     40  */
     41 #define LINE_SIZE 1024
     42 
     43 #define PAGE_UPGRADE_REQUIRED \
     44   "This resource speaks the \"" PROTOCOL "\" protocol only.\n"
     45 
     46 #define PAGE_NOT_FOUND \
     47   "Not found.\n"
     48 
     49 
     50 /**
     51  * State of the line-based echo protocol for one connection.
     52  */
     53 struct EchoState
     54 {
     55   /**
     56    * Bytes of the line received so far (without the line terminator).
     57    */
     58   char line[LINE_SIZE];
     59 
     60   /**
     61    * Number of valid bytes in @e line.
     62    */
     63   size_t line_len;
     64 };
     65 
     66 
     67 /**
     68  * Compare the first @a len bytes of @a a and @a b, ignoring case.
     69  * Only ASCII letters are folded, which is all HTTP tokens may contain.
     70  *
     71  * @param a first string
     72  * @param b second string
     73  * @param len number of bytes to compare
     74  * @return 1 if the strings match, 0 if they do not
     75  */
     76 static int
     77 equal_caseless (const char *a,
     78                 const char *b,
     79                 size_t len)
     80 {
     81   size_t i;
     82 
     83   for (i = 0; i < len; i++)
     84   {
     85     char ca = a[i];
     86     char cb = b[i];
     87 
     88     if (('A' <= ca) && ('Z' >= ca))
     89       ca = (char) (ca - 'A' + 'a');
     90     if (('A' <= cb) && ('Z' >= cb))
     91       cb = (char) (cb - 'A' + 'a');
     92     if (ca != cb)
     93       return 0;
     94   }
     95   return 1;
     96 }
     97 
     98 
     99 /**
    100  * Check whether the comma-separated list @a value contains the token
    101  * @a token.  Both the "Connection:" and the "Upgrade:" request header
    102  * fields are lists, so a client may legitimately send
    103  * "Connection: keep-alive, Upgrade"; a plain string comparison against
    104  * the whole field value would reject such a request.
    105  *
    106  * @param value the header field value, may be NULL
    107  * @param token the token to look for, compared case-insensitively
    108  * @return 1 if @a token is one of the tokens in @a value, 0 if not
    109  */
    110 static int
    111 has_token (const char *value,
    112            const char *token)
    113 {
    114   const size_t token_len = strlen (token);
    115   const char *pos = value;
    116 
    117   if (NULL == value)
    118     return 0;
    119   while ('\0' != *pos)
    120   {
    121     const char *end;
    122     size_t len;
    123 
    124     /* skip separators and optional whitespace before the token */
    125     while ((',' == *pos) || (' ' == *pos) || ('\t' == *pos))
    126       pos++;
    127     end = pos;
    128     while (('\0' != *end) && (',' != *end))
    129       end++;
    130     len = (size_t) (end - pos);
    131     /* strip optional whitespace after the token */
    132     while ((0 != len) &&
    133            ((' ' == pos[len - 1]) || ('\t' == pos[len - 1])))
    134       len--;
    135     if ((len == token_len) &&
    136         (equal_caseless (pos, token, token_len)))
    137       return 1;
    138     pos = end;
    139     if ('\0' != *pos)
    140       pos++;   /* skip the comma */
    141   }
    142   return 0;
    143 }
    144 
    145 
    146 /**
    147  * Send @a len bytes from @a buf over @a sock, retrying until either
    148  * everything was sent or the connection broke.
    149  *
    150  * @param sock the socket to write to
    151  * @param buf the data to send
    152  * @param len number of bytes in @a buf
    153  * @return 1 on success, 0 if the connection broke
    154  */
    155 static int
    156 send_all (MHD_socket sock,
    157           const char *buf,
    158           size_t len)
    159 {
    160   ssize_t ret;
    161   size_t off;
    162 
    163   for (off = 0; off < len; off += (size_t) ret)
    164   {
    165     ret = send (sock,
    166                 buf + off,
    167                 (int) (len - off),
    168                 0);
    169     if (0 > ret)
    170     {
    171       if (EINTR == errno)
    172       {
    173         ret = 0;
    174         continue;
    175       }
    176       return 0;
    177     }
    178     if (0 == ret)
    179       return 0;
    180   }
    181   return 1;
    182 }
    183 
    184 
    185 /**
    186  * Make @a sock blocking.  MHD hands out the socket in non-blocking
    187  * mode, see the tutorial chapter for the reason why.  This function
    188  * contains the only operating-system-dependent code of this example.
    189  *
    190  * @param sock the socket to modify
    191  */
    192 static void
    193 make_blocking (MHD_socket sock)
    194 {
    195 #ifndef _WIN32
    196   int flags;
    197 
    198   flags = fcntl (sock, F_GETFL);
    199   if (-1 == flags)
    200     abort ();
    201   if ((flags & ~O_NONBLOCK) != flags)
    202     if (-1 == fcntl (sock, F_SETFL, flags & ~O_NONBLOCK))
    203       abort ();
    204 #else  /* _WIN32 */
    205   unsigned long flags = 0;
    206 
    207   if (0 != ioctlsocket (sock, (int) FIONBIO, &flags))
    208     abort ();
    209 #endif /* _WIN32 */
    210 }
    211 
    212 
    213 /**
    214  * Feed @a size bytes of received data into the echo protocol state
    215  * machine, echoing back every complete line.
    216  *
    217  * @param sock the socket to echo to
    218  * @param es the protocol state of this connection
    219  * @param data the received bytes
    220  * @param size number of bytes in @a data
    221  * @return 1 if the client asked us to stop or the connection broke,
    222  *         0 if we should keep going
    223  */
    224 static int
    225 echo_feed (MHD_socket sock,
    226            struct EchoState *es,
    227            const char *data,
    228            size_t size)
    229 {
    230   size_t i;
    231 
    232   for (i = 0; i < size; i++)
    233   {
    234     if ('\n' != data[i])
    235     {
    236       if (es->line_len < sizeof (es->line))
    237         es->line[es->line_len++] = data[i];
    238       /* else: line too long, drop the excess bytes */
    239       continue;
    240     }
    241     /* A complete line was received; drop the CR of a CRLF terminator */
    242     if ((0 != es->line_len) &&
    243         ('\r' == es->line[es->line_len - 1]))
    244       es->line_len--;
    245     if ((4 == es->line_len) &&
    246         (equal_caseless (es->line, "QUIT", 4)))
    247     {
    248       (void) send_all (sock, "BYE\r\n", 5);
    249       es->line_len = 0;
    250       return 1;
    251     }
    252     if ((! send_all (sock, es->line, es->line_len)) ||
    253         (! send_all (sock, "\r\n", 2)))
    254       return 1;
    255     es->line_len = 0;
    256   }
    257   return 0;
    258 }
    259 
    260 
    261 /**
    262  * Called by MHD once the "101 Switching Protocols" response was sent.
    263  * From here on MHD is out of the picture: @a sock is an ordinary
    264  * socket and we are free to speak whatever protocol we like on it.
    265  *
    266  * As the daemon runs with #MHD_USE_THREAD_PER_CONNECTION, this
    267  * function has a thread of its own and may block for as long as it
    268  * wants.
    269  *
    270  * @param cls closure given to #MHD_create_response_for_upgrade()
    271  * @param connection the original HTTP connection
    272  * @param req_cls last value of @a req_cls of the access handler
    273  * @param extra_in bytes the client sent after the request header and
    274  *                 which MHD read together with the header
    275  * @param extra_in_size number of bytes in @a extra_in
    276  * @param sock the socket to talk to the client on
    277  * @param urh handle to pass to #MHD_upgrade_action()
    278  */
    279 static void
    280 upgrade_handler (void *cls,
    281                  struct MHD_Connection *connection,
    282                  void *req_cls,
    283                  const char *extra_in,
    284                  size_t extra_in_size,
    285                  MHD_socket sock,
    286                  struct MHD_UpgradeResponseHandle *urh)
    287 {
    288   struct EchoState es;
    289   char buf[256];
    290   ssize_t got;
    291   int done;
    292 
    293   (void) cls;         /* Unused. Silent compiler warning. */
    294   (void) connection;  /* Unused. Silent compiler warning. */
    295   (void) req_cls;     /* Unused. Silent compiler warning. */
    296 
    297   /* MHD hands out a non-blocking socket; this example wants to use
    298      plain blocking recv()/send() calls on it. */
    299   make_blocking (sock);
    300 
    301   es.line_len = 0;
    302 
    303   /* The client may have pipelined payload directly behind the request
    304      header, in which case MHD has read it already.  Those bytes are
    305      gone from the socket, so they must be processed first. */
    306   done = echo_feed (sock,
    307                     &es,
    308                     extra_in,
    309                     extra_in_size);
    310 
    311   while (0 == done)
    312   {
    313     got = recv (sock,
    314                 buf,
    315                 sizeof (buf),
    316                 0);
    317     if (0 > got)
    318     {
    319       if (EINTR == errno)
    320         continue;
    321       break;            /* read error */
    322     }
    323     if (0 == got)
    324       break;            /* client closed the connection */
    325     done = echo_feed (sock,
    326                       &es,
    327                       buf,
    328                       (size_t) got);
    329   }
    330 
    331   /* Hand the socket back to MHD.  This is the only legal way to get
    332      rid of it; never call close() on it ourselves, and note that the
    333      connection is not cleaned up before this call happens. */
    334   MHD_upgrade_action (urh,
    335                       MHD_UPGRADE_ACTION_CLOSE);
    336 }
    337 
    338 
    339 /**
    340  * Answer an ordinary HTTP request from a static string.
    341  *
    342  * @param connection the connection to answer
    343  * @param status_code the HTTP status code to use
    344  * @param page the response body
    345  * @param offer_upgrade if true, advertise our protocol in an
    346  *                      "Upgrade:" header field
    347  * @return #MHD_YES on success, #MHD_NO on error
    348  */
    349 static enum MHD_Result
    350 answer_plain (struct MHD_Connection *connection,
    351               unsigned int status_code,
    352               const char *page,
    353               int offer_upgrade)
    354 {
    355   struct MHD_Response *response;
    356   enum MHD_Result ret;
    357 
    358   response = MHD_create_response_from_buffer_static (strlen (page),
    359                                                      page);
    360   if (NULL == response)
    361     return MHD_NO;
    362   if (offer_upgrade)
    363     (void) MHD_add_response_header (response,
    364                                     MHD_HTTP_HEADER_UPGRADE,
    365                                     PROTOCOL);
    366   ret = MHD_queue_response (connection,
    367                             status_code,
    368                             response);
    369   MHD_destroy_response (response);
    370   return ret;
    371 }
    372 
    373 
    374 static enum MHD_Result
    375 access_handler (void *cls,
    376                 struct MHD_Connection *connection,
    377                 const char *url,
    378                 const char *method,
    379                 const char *version,
    380                 const char *upload_data,
    381                 size_t *upload_data_size,
    382                 void **req_cls)
    383 {
    384   struct MHD_Response *response;
    385   enum MHD_Result ret;
    386 
    387   (void) cls;               /* Unused. Silent compiler warning. */
    388   (void) upload_data;       /* Unused. Silent compiler warning. */
    389   (void) upload_data_size;  /* Unused. Silent compiler warning. */
    390   (void) req_cls;           /* Unused. Silent compiler warning. */
    391 
    392   if (0 != strcmp (url, "/"))
    393     return answer_plain (connection,
    394                          MHD_HTTP_NOT_FOUND,
    395                          PAGE_NOT_FOUND,
    396                          0);
    397   /* Upgrading requires HTTP/1.1 and a request that actually asks for
    398      our protocol.  A client that does not ask gets a plain 426. */
    399   if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) ||
    400       (0 != strcmp (version, MHD_HTTP_VERSION_1_1)) ||
    401       (! has_token (MHD_lookup_connection_value (connection,
    402                                                  MHD_HEADER_KIND,
    403                                                  MHD_HTTP_HEADER_CONNECTION),
    404                     "Upgrade")) ||
    405       (! has_token (MHD_lookup_connection_value (connection,
    406                                                  MHD_HEADER_KIND,
    407                                                  MHD_HTTP_HEADER_UPGRADE),
    408                     PROTOCOL)))
    409     return answer_plain (connection,
    410                          MHD_HTTP_UPGRADE_REQUIRED,
    411                          PAGE_UPGRADE_REQUIRED,
    412                          1);
    413 
    414   /* The request is fine, switch protocols.  MHD already put
    415      "Connection: Upgrade" into the response for us, we only have to
    416      name the protocol we switch to. */
    417   response = MHD_create_response_for_upgrade (&upgrade_handler,
    418                                               NULL);
    419   if (NULL == response)
    420     return MHD_NO;
    421   if (MHD_YES !=
    422       MHD_add_response_header (response,
    423                                MHD_HTTP_HEADER_UPGRADE,
    424                                PROTOCOL))
    425   {
    426     MHD_destroy_response (response);
    427     return MHD_NO;
    428   }
    429   ret = MHD_queue_response (connection,
    430                             MHD_HTTP_SWITCHING_PROTOCOLS,
    431                             response);
    432   /* The response may be destroyed right away: MHD keeps its own
    433      reference until the upgrade handler has been called. */
    434   MHD_destroy_response (response);
    435   return ret;
    436 }
    437 
    438 
    439 int
    440 main (void)
    441 {
    442   struct MHD_Daemon *daemon;
    443 
    444   daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    445                              | MHD_USE_INTERNAL_POLLING_THREAD
    446                              | MHD_ALLOW_UPGRADE
    447                              | MHD_USE_ERROR_LOG,
    448                              PORT, NULL, NULL,
    449                              &access_handler, NULL,
    450                              MHD_OPTION_END);
    451   if (NULL == daemon)
    452     return 1;
    453 
    454   (void) getchar ();
    455 
    456   MHD_stop_daemon (daemon);
    457   return 0;
    458 }