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 }