libmicrohttpd

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

largepost.c (9406B)


      1 /* Feel free to use this example code in any way
      2    you see fit (Public Domain) */
      3 
      4 #include <sys/types.h>
      5 #ifndef _WIN32
      6 #include <sys/select.h>
      7 #include <sys/socket.h>
      8 #else
      9 #include <winsock2.h>
     10 #endif
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <microhttpd.h>
     15 
     16 #if defined(_MSC_VER) && _MSC_VER + 0 <= 1800
     17 /* Substitution is OK while return value is not used */
     18 #define snprintf _snprintf
     19 #endif
     20 
     21 #define PORT            8888
     22 #define POSTBUFFERSIZE  512
     23 #define MAXCLIENTS      2
     24 
     25 enum ConnectionType
     26 {
     27   GET = 0,
     28   POST = 1
     29 };
     30 
     31 static unsigned int nr_of_uploading_clients = 0;
     32 
     33 
     34 /**
     35  * Information we keep per connection.
     36  */
     37 struct connection_info_struct
     38 {
     39   enum ConnectionType connectiontype;
     40 
     41   /**
     42    * Handle to the POST processing state.
     43    */
     44   struct MHD_PostProcessor *postprocessor;
     45 
     46   /**
     47    * File handle where we write uploaded data.
     48    */
     49   FILE *fp;
     50 
     51   /**
     52    * HTTP response body we will return, NULL if not yet known.
     53    */
     54   const char *answerstring;
     55 
     56   /**
     57    * HTTP status code we will return, 0 for undecided.
     58    */
     59   unsigned int answercode;
     60 };
     61 
     62 
     63 #define ASKPAGE \
     64   "<html><body>\n" \
     65   "Upload a file, please!<br>\n" \
     66   "There are %u clients uploading at the moment.<br>\n" \
     67   "<form action=\"/filepost\" method=\"post\" enctype=\"multipart/form-data\">\n" \
     68   "<input name=\"file\" type=\"file\">\n" \
     69   "<input type=\"submit\" value=\" Send \"></form>\n" \
     70   "</body></html>"
     71 static const char *busypage =
     72   "<html><body>This server is busy, please try again later.</body></html>";
     73 static const char *completepage =
     74   "<html><body>The upload has been completed.</body></html>";
     75 static const char *errorpage =
     76   "<html><body>This doesn't seem to be right.</body></html>";
     77 static const char *servererrorpage =
     78   "<html><body>Invalid request.</body></html>";
     79 static const char *fileexistspage =
     80   "<html><body>This file already exists.</body></html>";
     81 static const char *fileioerror =
     82   "<html><body>IO error writing to disk.</body></html>";
     83 static const char *const postprocerror =
     84   "<html><head><title>Error</title></head><body>Error processing POST data</body></html>";
     85 
     86 
     87 static enum MHD_Result
     88 send_page (struct MHD_Connection *connection,
     89            const char *page,
     90            unsigned int status_code)
     91 {
     92   enum MHD_Result ret;
     93   struct MHD_Response *response;
     94 
     95   /* NOTE: we let MHD make its own copy of the page, as some of the
     96      pages we serve live in a buffer on the stack of the caller. */
     97   response = MHD_create_response_from_buffer_copy (strlen (page), page);
     98   if (! response)
     99     return MHD_NO;
    100   if (MHD_YES !=
    101       MHD_add_response_header (response,
    102                                MHD_HTTP_HEADER_CONTENT_TYPE,
    103                                "text/html"))
    104   {
    105     fprintf (stderr,
    106              "Failed to set content type header!\n");
    107   }
    108   ret = MHD_queue_response (connection,
    109                             status_code,
    110                             response);
    111   MHD_destroy_response (response);
    112 
    113   return ret;
    114 }
    115 
    116 
    117 static enum MHD_Result
    118 iterate_post (void *coninfo_cls,
    119               enum MHD_ValueKind kind,
    120               const char *key,
    121               const char *filename,
    122               const char *content_type,
    123               const char *transfer_encoding,
    124               const char *data,
    125               uint64_t off,
    126               size_t size)
    127 {
    128   struct connection_info_struct *con_info = coninfo_cls;
    129   FILE *fp;
    130   (void) kind;               /* Unused. Silent compiler warning. */
    131   (void) content_type;       /* Unused. Silent compiler warning. */
    132   (void) transfer_encoding;  /* Unused. Silent compiler warning. */
    133   (void) off;                /* Unused. Silent compiler warning. */
    134 
    135   if (0 != strcmp (key, "file"))
    136   {
    137     con_info->answerstring = servererrorpage;
    138     con_info->answercode = MHD_HTTP_BAD_REQUEST;
    139     return MHD_YES;
    140   }
    141 
    142   if (! con_info->fp)
    143   {
    144     if (0 != con_info->answercode)   /* something went wrong */
    145       return MHD_YES;
    146     if (NULL != (fp = fopen (filename, "rb")))
    147     {
    148       fclose (fp);
    149       con_info->answerstring = fileexistspage;
    150       con_info->answercode = MHD_HTTP_FORBIDDEN;
    151       return MHD_YES;
    152     }
    153     /* NOTE: This is technically a race with the 'fopen()' above,
    154        but there is no easy fix, short of moving to open(O_EXCL)
    155        instead of using fopen(). For the example, we do not care. */
    156     con_info->fp = fopen (filename, "ab");
    157     if (! con_info->fp)
    158     {
    159       con_info->answerstring = fileioerror;
    160       con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
    161       return MHD_YES;
    162     }
    163   }
    164 
    165   if (size > 0)
    166   {
    167     if (! fwrite (data, sizeof (char), size, con_info->fp))
    168     {
    169       con_info->answerstring = fileioerror;
    170       con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
    171       return MHD_YES;
    172     }
    173   }
    174 
    175   return MHD_YES;
    176 }
    177 
    178 
    179 static void
    180 request_completed (void *cls,
    181                    struct MHD_Connection *connection,
    182                    void **req_cls,
    183                    enum MHD_RequestTerminationCode toe)
    184 {
    185   struct connection_info_struct *con_info = *req_cls;
    186   (void) cls;         /* Unused. Silent compiler warning. */
    187   (void) connection;  /* Unused. Silent compiler warning. */
    188   (void) toe;         /* Unused. Silent compiler warning. */
    189 
    190   if (NULL == con_info)
    191     return;
    192 
    193   if (con_info->connectiontype == POST)
    194   {
    195     if (NULL != con_info->postprocessor)
    196     {
    197       MHD_destroy_post_processor (con_info->postprocessor);
    198       nr_of_uploading_clients--;
    199     }
    200 
    201     if (con_info->fp)
    202       fclose (con_info->fp);
    203   }
    204 
    205   free (con_info);
    206   *req_cls = NULL;
    207 }
    208 
    209 
    210 static enum MHD_Result
    211 answer_to_connection (void *cls,
    212                       struct MHD_Connection *connection,
    213                       const char *url,
    214                       const char *method,
    215                       const char *version,
    216                       const char *upload_data,
    217                       size_t *upload_data_size,
    218                       void **req_cls)
    219 {
    220   (void) cls;               /* Unused. Silent compiler warning. */
    221   (void) url;               /* Unused. Silent compiler warning. */
    222   (void) version;           /* Unused. Silent compiler warning. */
    223 
    224   if (NULL == *req_cls)
    225   {
    226     /* First call, setup data structures */
    227     struct connection_info_struct *con_info;
    228 
    229     if (nr_of_uploading_clients >= MAXCLIENTS)
    230       return send_page (connection,
    231                         busypage,
    232                         MHD_HTTP_SERVICE_UNAVAILABLE);
    233 
    234     con_info = malloc (sizeof (struct connection_info_struct));
    235     if (NULL == con_info)
    236       return MHD_NO;
    237     con_info->answercode = 0;   /* none yet */
    238     con_info->fp = NULL;
    239 
    240     if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
    241     {
    242       con_info->postprocessor =
    243         MHD_create_post_processor (connection,
    244                                    POSTBUFFERSIZE,
    245                                    &iterate_post,
    246                                    (void *) con_info);
    247 
    248       if (NULL == con_info->postprocessor)
    249       {
    250         free (con_info);
    251         return MHD_NO;
    252       }
    253 
    254       nr_of_uploading_clients++;
    255 
    256       con_info->connectiontype = POST;
    257     }
    258     else
    259     {
    260       con_info->connectiontype = GET;
    261     }
    262 
    263     *req_cls = (void *) con_info;
    264 
    265     return MHD_YES;
    266   }
    267 
    268   if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
    269   {
    270     /* We just return the standard form for uploads on all GET requests */
    271     char buffer[1024];
    272 
    273     snprintf (buffer,
    274               sizeof (buffer),
    275               ASKPAGE,
    276               nr_of_uploading_clients);
    277     return send_page (connection,
    278                       buffer,
    279                       MHD_HTTP_OK);
    280   }
    281 
    282   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
    283   {
    284     struct connection_info_struct *con_info = *req_cls;
    285 
    286     if (0 != *upload_data_size)
    287     {
    288       /* Upload not yet done */
    289       if (0 != con_info->answercode)
    290       {
    291         /* we already know the answer, skip rest of upload */
    292         *upload_data_size = 0;
    293         return MHD_YES;
    294       }
    295       if (MHD_YES !=
    296           MHD_post_process (con_info->postprocessor,
    297                             upload_data,
    298                             *upload_data_size))
    299       {
    300         con_info->answerstring = postprocerror;
    301         con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
    302       }
    303       *upload_data_size = 0;
    304 
    305       return MHD_YES;
    306     }
    307     /* Upload finished */
    308     if (NULL != con_info->fp)
    309     {
    310       fclose (con_info->fp);
    311       con_info->fp = NULL;
    312     }
    313     if (0 == con_info->answercode)
    314     {
    315       /* No errors encountered, declare success */
    316       con_info->answerstring = completepage;
    317       con_info->answercode = MHD_HTTP_OK;
    318     }
    319     return send_page (connection,
    320                       con_info->answerstring,
    321                       con_info->answercode);
    322   }
    323 
    324   /* Not a GET or a POST, generate error */
    325   return send_page (connection,
    326                     errorpage,
    327                     MHD_HTTP_BAD_REQUEST);
    328 }
    329 
    330 
    331 int
    332 main (void)
    333 {
    334   struct MHD_Daemon *daemon;
    335 
    336   daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD,
    337                              PORT, NULL, NULL,
    338                              &answer_to_connection, NULL,
    339                              MHD_OPTION_NOTIFY_COMPLETED, &request_completed,
    340                              NULL,
    341                              MHD_OPTION_END);
    342   if (NULL == daemon)
    343   {
    344     fprintf (stderr,
    345              "Failed to start daemon.\n");
    346     return 1;
    347   }
    348   (void) getchar ();
    349   MHD_stop_daemon (daemon);
    350   return 0;
    351 }