libmicrohttpd

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

largepost.c (9273B)


      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   response = MHD_create_response_from_buffer_static (strlen (page), page);
     96   if (! response)
     97     return MHD_NO;
     98   if (MHD_YES !=
     99       MHD_add_response_header (response,
    100                                MHD_HTTP_HEADER_CONTENT_TYPE,
    101                                "text/html"))
    102   {
    103     fprintf (stderr,
    104              "Failed to set content type header!\n");
    105   }
    106   ret = MHD_queue_response (connection,
    107                             status_code,
    108                             response);
    109   MHD_destroy_response (response);
    110 
    111   return ret;
    112 }
    113 
    114 
    115 static enum MHD_Result
    116 iterate_post (void *coninfo_cls,
    117               enum MHD_ValueKind kind,
    118               const char *key,
    119               const char *filename,
    120               const char *content_type,
    121               const char *transfer_encoding,
    122               const char *data,
    123               uint64_t off,
    124               size_t size)
    125 {
    126   struct connection_info_struct *con_info = coninfo_cls;
    127   FILE *fp;
    128   (void) kind;               /* Unused. Silent compiler warning. */
    129   (void) content_type;       /* Unused. Silent compiler warning. */
    130   (void) transfer_encoding;  /* Unused. Silent compiler warning. */
    131   (void) off;                /* Unused. Silent compiler warning. */
    132 
    133   if (0 != strcmp (key, "file"))
    134   {
    135     con_info->answerstring = servererrorpage;
    136     con_info->answercode = MHD_HTTP_BAD_REQUEST;
    137     return MHD_YES;
    138   }
    139 
    140   if (! con_info->fp)
    141   {
    142     if (0 != con_info->answercode)   /* something went wrong */
    143       return MHD_YES;
    144     if (NULL != (fp = fopen (filename, "rb")))
    145     {
    146       fclose (fp);
    147       con_info->answerstring = fileexistspage;
    148       con_info->answercode = MHD_HTTP_FORBIDDEN;
    149       return MHD_YES;
    150     }
    151     /* NOTE: This is technically a race with the 'fopen()' above,
    152        but there is no easy fix, short of moving to open(O_EXCL)
    153        instead of using fopen(). For the example, we do not care. */
    154     con_info->fp = fopen (filename, "ab");
    155     if (! con_info->fp)
    156     {
    157       con_info->answerstring = fileioerror;
    158       con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
    159       return MHD_YES;
    160     }
    161   }
    162 
    163   if (size > 0)
    164   {
    165     if (! fwrite (data, sizeof (char), size, con_info->fp))
    166     {
    167       con_info->answerstring = fileioerror;
    168       con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
    169       return MHD_YES;
    170     }
    171   }
    172 
    173   return MHD_YES;
    174 }
    175 
    176 
    177 static void
    178 request_completed (void *cls,
    179                    struct MHD_Connection *connection,
    180                    void **req_cls,
    181                    enum MHD_RequestTerminationCode toe)
    182 {
    183   struct connection_info_struct *con_info = *req_cls;
    184   (void) cls;         /* Unused. Silent compiler warning. */
    185   (void) connection;  /* Unused. Silent compiler warning. */
    186   (void) toe;         /* Unused. Silent compiler warning. */
    187 
    188   if (NULL == con_info)
    189     return;
    190 
    191   if (con_info->connectiontype == POST)
    192   {
    193     if (NULL != con_info->postprocessor)
    194     {
    195       MHD_destroy_post_processor (con_info->postprocessor);
    196       nr_of_uploading_clients--;
    197     }
    198 
    199     if (con_info->fp)
    200       fclose (con_info->fp);
    201   }
    202 
    203   free (con_info);
    204   *req_cls = NULL;
    205 }
    206 
    207 
    208 static enum MHD_Result
    209 answer_to_connection (void *cls,
    210                       struct MHD_Connection *connection,
    211                       const char *url,
    212                       const char *method,
    213                       const char *version,
    214                       const char *upload_data,
    215                       size_t *upload_data_size,
    216                       void **req_cls)
    217 {
    218   (void) cls;               /* Unused. Silent compiler warning. */
    219   (void) url;               /* Unused. Silent compiler warning. */
    220   (void) version;           /* Unused. Silent compiler warning. */
    221 
    222   if (NULL == *req_cls)
    223   {
    224     /* First call, setup data structures */
    225     struct connection_info_struct *con_info;
    226 
    227     if (nr_of_uploading_clients >= MAXCLIENTS)
    228       return send_page (connection,
    229                         busypage,
    230                         MHD_HTTP_SERVICE_UNAVAILABLE);
    231 
    232     con_info = malloc (sizeof (struct connection_info_struct));
    233     if (NULL == con_info)
    234       return MHD_NO;
    235     con_info->answercode = 0;   /* none yet */
    236     con_info->fp = NULL;
    237 
    238     if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
    239     {
    240       con_info->postprocessor =
    241         MHD_create_post_processor (connection,
    242                                    POSTBUFFERSIZE,
    243                                    &iterate_post,
    244                                    (void *) con_info);
    245 
    246       if (NULL == con_info->postprocessor)
    247       {
    248         free (con_info);
    249         return MHD_NO;
    250       }
    251 
    252       nr_of_uploading_clients++;
    253 
    254       con_info->connectiontype = POST;
    255     }
    256     else
    257     {
    258       con_info->connectiontype = GET;
    259     }
    260 
    261     *req_cls = (void *) con_info;
    262 
    263     return MHD_YES;
    264   }
    265 
    266   if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
    267   {
    268     /* We just return the standard form for uploads on all GET requests */
    269     char buffer[1024];
    270 
    271     snprintf (buffer,
    272               sizeof (buffer),
    273               ASKPAGE,
    274               nr_of_uploading_clients);
    275     return send_page (connection,
    276                       buffer,
    277                       MHD_HTTP_OK);
    278   }
    279 
    280   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
    281   {
    282     struct connection_info_struct *con_info = *req_cls;
    283 
    284     if (0 != *upload_data_size)
    285     {
    286       /* Upload not yet done */
    287       if (0 != con_info->answercode)
    288       {
    289         /* we already know the answer, skip rest of upload */
    290         *upload_data_size = 0;
    291         return MHD_YES;
    292       }
    293       if (MHD_YES !=
    294           MHD_post_process (con_info->postprocessor,
    295                             upload_data,
    296                             *upload_data_size))
    297       {
    298         con_info->answerstring = postprocerror;
    299         con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
    300       }
    301       *upload_data_size = 0;
    302 
    303       return MHD_YES;
    304     }
    305     /* Upload finished */
    306     if (NULL != con_info->fp)
    307     {
    308       fclose (con_info->fp);
    309       con_info->fp = NULL;
    310     }
    311     if (0 == con_info->answercode)
    312     {
    313       /* No errors encountered, declare success */
    314       con_info->answerstring = completepage;
    315       con_info->answercode = MHD_HTTP_OK;
    316     }
    317     return send_page (connection,
    318                       con_info->answerstring,
    319                       con_info->answercode);
    320   }
    321 
    322   /* Note a GET or a POST, generate error */
    323   return send_page (connection,
    324                     errorpage,
    325                     MHD_HTTP_BAD_REQUEST);
    326 }
    327 
    328 
    329 int
    330 main (void)
    331 {
    332   struct MHD_Daemon *daemon;
    333 
    334   daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD,
    335                              PORT, NULL, NULL,
    336                              &answer_to_connection, NULL,
    337                              MHD_OPTION_NOTIFY_COMPLETED, &request_completed,
    338                              NULL,
    339                              MHD_OPTION_END);
    340   if (NULL == daemon)
    341   {
    342     fprintf (stderr,
    343              "Failed to start daemon.\n");
    344     return 1;
    345   }
    346   (void) getchar ();
    347   MHD_stop_daemon (daemon);
    348   return 0;
    349 }