largepost.c (9315B)
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 }