libmicrohttpd

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

callbackresponse.c (8267B)


      1 /* Feel free to use this example code in any way
      2    you see fit (Public Domain) */
      3 
      4 /**
      5  * @file callbackresponse.c
      6  * @brief   Example for generating a response on the fly with
      7  *          MHD_create_response_from_callback().  The body is not a
      8  *          file and is never assembled in memory as a whole; it is
      9  *          produced block by block into a single heap buffer that is
     10  *          refilled every time libmicrohttpd asks for more data.
     11  *          "GET /" streams the document with an unknown size (and thus
     12  *          with chunked transfer encoding), "GET /sized" streams the
     13  *          very same document with a "Content-Length" announced up
     14  *          front, and "GET /error" aborts the stream in the middle to
     15  *          show MHD_CONTENT_READER_END_WITH_ERROR.
     16  * @author  Christian Grothoff
     17  */
     18 
     19 #include <sys/types.h>
     20 #ifndef _WIN32
     21 #include <sys/select.h>
     22 #include <sys/socket.h>
     23 #else
     24 #include <winsock2.h>
     25 #endif
     26 #include <microhttpd.h>
     27 #include <stdio.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 
     31 #define PORT 8888
     32 
     33 /**
     34  * Length of one generated line, including the newline character.
     35  */
     36 #define LINE_SIZE 64
     37 
     38 /**
     39  * Size of the heap buffer that is refilled over and over again.  This
     40  * is the amount of data we produce in one go; it has nothing to do
     41  * with the block size we pass to MHD.
     42  */
     43 #define BUFFER_SIZE (64 * LINE_SIZE)
     44 
     45 /**
     46  * Number of times the buffer is refilled before the stream ends.  The
     47  * complete document is BLOCK_COUNT * BUFFER_SIZE bytes long.
     48  */
     49 #define BLOCK_COUNT 64
     50 
     51 /**
     52  * Block size we ask MHD to use when it queries our callback.  MHD
     53  * allocates a buffer of this size together with the response; it is an
     54  * advisory value and MHD may ask for less.
     55  */
     56 #define IO_BLOCK_SIZE 1024
     57 
     58 /**
     59  * For the "/error" URL: number of blocks to deliver before the stream
     60  * is aborted with MHD_CONTENT_READER_END_WITH_ERROR.
     61  */
     62 #define ABORT_AFTER 3
     63 
     64 
     65 /**
     66  * State we keep for one response that is being streamed.  One such
     67  * structure is allocated per request and released by the free callback
     68  * that we hand to MHD_create_response_from_callback().
     69  */
     70 struct ResponseContext
     71 {
     72   /**
     73    * Heap buffer holding the block that is currently being delivered.
     74    * Refilled by generate_block() whenever it has been drained.
     75    */
     76   char *buf;
     77 
     78   /**
     79    * Number of valid bytes in @e buf.
     80    */
     81   size_t fill;
     82 
     83   /**
     84    * Number of bytes of @e buf that were already handed to MHD.
     85    */
     86   size_t off;
     87 
     88   /**
     89    * Number of blocks generated so far, and thus the number of the
     90    * block that generate_block() will produce next.
     91    */
     92   unsigned int block;
     93 
     94   /**
     95    * Non-zero if this stream is supposed to fail in the middle.
     96    */
     97   int fail;
     98 };
     99 
    100 
    101 /**
    102  * Produce the next block of the document into @a ctx->buf.  This
    103  * stands in for whatever expensive computation, database query or
    104  * network fetch a real application would perform here.  Note that the
    105  * same buffer is reused for every block, so the memory needed by the
    106  * server does not grow with the size of the document.
    107  *
    108  * Every line is exactly LINE_SIZE bytes long, which is what allows us
    109  * to announce an exact "Content-Length" for the "/sized" URL.
    110  *
    111  * @param ctx the per-response state to refill
    112  */
    113 static void
    114 generate_block (struct ResponseContext *ctx)
    115 {
    116   unsigned int i;
    117   char *line;
    118   char label[32];
    119   int len;
    120 
    121   for (i = 0; i < BUFFER_SIZE / LINE_SIZE; i++)
    122   {
    123     line = &ctx->buf[i * LINE_SIZE];
    124     memset (line, '.', LINE_SIZE);
    125     len = snprintf (label,
    126                     sizeof (label),
    127                     "block %3u line %3u ",
    128                     ctx->block,
    129                     i);
    130     if ((0 < len) && (LINE_SIZE > (size_t) len))
    131       memcpy (line, label, (size_t) len);
    132     line[LINE_SIZE - 1] = '\n';
    133   }
    134   ctx->fill = BUFFER_SIZE;
    135   ctx->off = 0;
    136   ctx->block++;
    137 }
    138 
    139 
    140 /**
    141  * Callback used by MHD to obtain the next piece of the response body.
    142  *
    143  * @param cls our `struct ResponseContext`
    144  * @param pos number of bytes already returned for this response
    145  * @param buf where to copy the data
    146  * @param max maximum number of bytes to copy to @a buf
    147  * @return number of bytes written to @a buf,
    148  *         MHD_CONTENT_READER_END_OF_STREAM for the regular end,
    149  *         MHD_CONTENT_READER_END_WITH_ERROR to abort the transfer
    150  */
    151 static ssize_t
    152 content_reader (void *cls,
    153                 uint64_t pos,
    154                 char *buf,
    155                 size_t max)
    156 {
    157   struct ResponseContext *ctx = cls;
    158   size_t ready;
    159 
    160   /* We generate a pure stream and never look back, so the position is
    161      of no interest to us.  MHD guarantees that it is the sum of all
    162      non-negative values we returned so far. */
    163   (void) pos;                   /* Unused. Silent compiler warning. */
    164 
    165   if (ctx->off == ctx->fill)
    166   {
    167     /* Everything we had was passed on, produce the next block. */
    168     if (BLOCK_COUNT == ctx->block)
    169       return MHD_CONTENT_READER_END_OF_STREAM;
    170     if ((0 != ctx->fail) && (ABORT_AFTER == ctx->block))
    171       return MHD_CONTENT_READER_END_WITH_ERROR;
    172     generate_block (ctx);
    173   }
    174   /* Hand over as much of the current block as MHD is willing to take;
    175      the rest follows on the next invocation. */
    176   ready = ctx->fill - ctx->off;
    177   if (ready > max)
    178     ready = max;
    179   memcpy (buf, &ctx->buf[ctx->off], ready);
    180   ctx->off += ready;
    181   return (ssize_t) ready;
    182 }
    183 
    184 
    185 /**
    186  * Release the resources of a `struct ResponseContext`.  MHD calls this
    187  * once the response is destroyed, which happens no matter whether the
    188  * body was transmitted completely or the client went away early.
    189  *
    190  * @param cls our `struct ResponseContext`
    191  */
    192 static void
    193 free_context (void *cls)
    194 {
    195   struct ResponseContext *ctx = cls;
    196 
    197   free (ctx->buf);
    198   free (ctx);
    199 }
    200 
    201 
    202 static enum MHD_Result
    203 answer_to_connection (void *cls, struct MHD_Connection *connection,
    204                       const char *url, const char *method,
    205                       const char *version, const char *upload_data,
    206                       size_t *upload_data_size, void **req_cls)
    207 {
    208   struct ResponseContext *ctx;
    209   struct MHD_Response *response;
    210   enum MHD_Result ret;
    211   uint64_t size;
    212   (void) cls;               /* Unused. Silent compiler warning. */
    213   (void) version;           /* Unused. Silent compiler warning. */
    214   (void) upload_data;       /* Unused. Silent compiler warning. */
    215   (void) upload_data_size;  /* Unused. Silent compiler warning. */
    216   (void) req_cls;           /* Unused. Silent compiler warning. */
    217 
    218   if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
    219     return MHD_NO;
    220 
    221   /* Allocate the state of this particular response before the response
    222      object exists; from now on the free callback owns it. */
    223   ctx = malloc (sizeof (struct ResponseContext));
    224   if (NULL == ctx)
    225     return MHD_NO;
    226   ctx->buf = malloc (BUFFER_SIZE);
    227   if (NULL == ctx->buf)
    228   {
    229     free (ctx);
    230     return MHD_NO;
    231   }
    232   ctx->fill = 0;
    233   ctx->off = 0;
    234   ctx->block = 0;
    235   ctx->fail = (0 == strcmp (url, "/error"));
    236 
    237   /* "/sized" announces the length in advance, everything else is sent
    238      with an unknown size and thus with chunked transfer encoding. */
    239   if (0 == strcmp (url, "/sized"))
    240     size = (uint64_t) BLOCK_COUNT * BUFFER_SIZE;
    241   else
    242     size = MHD_SIZE_UNKNOWN;
    243 
    244   response = MHD_create_response_from_callback (size,
    245                                                 IO_BLOCK_SIZE,
    246                                                 &content_reader,
    247                                                 ctx,
    248                                                 &free_context);
    249   if (NULL == response)
    250   {
    251     /* The response was never created, so nobody will call the free
    252        callback for us. */
    253     free_context (ctx);
    254     return MHD_NO;
    255   }
    256   (void) MHD_add_response_header (response,
    257                                   MHD_HTTP_HEADER_CONTENT_TYPE,
    258                                   "text/plain");
    259   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    260   MHD_destroy_response (response);
    261 
    262   return ret;
    263 }
    264 
    265 
    266 int
    267 main (void)
    268 {
    269   struct MHD_Daemon *daemon;
    270 
    271   daemon = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
    272                              PORT, NULL, NULL,
    273                              &answer_to_connection, NULL, MHD_OPTION_END);
    274   if (NULL == daemon)
    275     return 1;
    276 
    277   (void) getchar ();
    278 
    279   MHD_stop_daemon (daemon);
    280   return 0;
    281 }