1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2008 Christian Grothoff 4 5 libmicrohttpd is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 2, or (at your 8 option) any later version. 9 10 libmicrohttpd is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libmicrohttpd; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 Boston, MA 02111-1307, USA. 19 */ 20 21 /** 22 * @file test_post_form.c 23 * @brief Testcase for libmicrohttpd POST operations using multipart/postform data 24 * @author Christian Grothoff 25 */ 26 27 #include "MHD_config.h" 28 #include "platform.h" 29 #include <curl/curl.h> 30 #include <microhttpd.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <time.h> 34 35 #ifndef WINDOWS 36 #include <unistd.h> 37 #endif 38 39 40 #include "socat.c" 41 42 static int oneone; 43 44 struct CBC 45 { 46 char *buf; 47 size_t pos; 48 size_t size; 49 }; 50 51 52 static void 53 completed_cb (void *cls, 54 struct MHD_Connection *connection, 55 void **con_cls, 56 enum MHD_RequestTerminationCode toe) 57 { 58 struct MHD_PostProcessor *pp = *con_cls; 59 60 if (NULL != pp) 61 MHD_destroy_post_processor (pp); 62 *con_cls = NULL; 63 } 64 65 66 static size_t 67 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 68 { 69 struct CBC *cbc = ctx; 70 71 if (cbc->pos + size * nmemb > cbc->size) 72 return 0; /* overflow */ 73 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 74 cbc->pos += size * nmemb; 75 return size * nmemb; 76 } 77 78 /** 79 * Note that this post_iterator is not perfect 80 * in that it fails to support incremental processing. 81 * (to be fixed in the future) 82 */ 83 static int 84 post_iterator (void *cls, 85 enum MHD_ValueKind kind, 86 const char *key, 87 const char *filename, 88 const char *content_type, 89 const char *transfer_encoding, 90 const char *value, uint64_t off, size_t size) 91 { 92 int *eok = cls; 93 94 if (key == NULL) 95 return MHD_YES; 96 #if 0 97 fprintf (stderr, "PI sees %s-%.*s\n", key, size, value); 98 #endif 99 if ((0 == strcmp (key, "name")) && 100 (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size))) 101 (*eok) |= 1; 102 if ((0 == strcmp (key, "project")) && 103 (size == strlen ("curl")) && (0 == strncmp (value, "curl", size))) 104 (*eok) |= 2; 105 return MHD_YES; 106 } 107 108 109 static int 110 ahc_echo (void *cls, 111 struct MHD_Connection *connection, 112 const char *url, 113 const char *method, 114 const char *version, 115 const char *upload_data, size_t *upload_data_size, 116 void **unused) 117 { 118 static int eok; 119 struct MHD_Response *response; 120 struct MHD_PostProcessor *pp; 121 int ret; 122 123 if (0 != strcmp ("POST", method)) 124 { 125 return MHD_NO; /* unexpected method */ 126 } 127 pp = *unused; 128 if (pp == NULL) 129 { 130 eok = 0; 131 pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok); 132 if (pp == NULL) 133 return MHD_NO; 134 *unused = pp; 135 } 136 MHD_post_process (pp, upload_data, *upload_data_size); 137 if ((eok == 3) && (0 == *upload_data_size)) 138 { 139 response = MHD_create_response_from_buffer (strlen (url), 140 (void *) url, 141 MHD_RESPMEM_MUST_COPY); 142 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 143 MHD_destroy_response (response); 144 MHD_destroy_post_processor (pp); 145 *unused = NULL; 146 return ret; 147 } 148 *upload_data_size = 0; 149 return MHD_YES; 150 } 151 152 static struct curl_httppost * 153 make_form () 154 { 155 struct curl_httppost *post = NULL; 156 struct curl_httppost *last = NULL; 157 158 curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", 159 CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END); 160 curl_formadd (&post, &last, CURLFORM_COPYNAME, "project", 161 CURLFORM_COPYCONTENTS, "curl", CURLFORM_END); 162 return post; 163 } 164 165 166 static int 167 testInternalPost () 168 { 169 struct MHD_Daemon *d; 170 CURL *c; 171 char buf[2048]; 172 struct CBC cbc; 173 int i; 174 struct curl_httppost *pd; 175 176 cbc.buf = buf; 177 cbc.size = 2048; 178 cbc.pos = 0; 179 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ , 180 11080, NULL, NULL, &ahc_echo, NULL, 181 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 182 MHD_OPTION_END); 183 if (d == NULL) 184 return 1; 185 zzuf_socat_start (); 186 for (i = 0; i < LOOP_COUNT; i++) 187 { 188 fprintf (stderr, "."); 189 c = curl_easy_init (); 190 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world"); 191 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 192 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 193 pd = make_form (); 194 curl_easy_setopt (c, CURLOPT_HTTPPOST, pd); 195 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 196 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); 197 if (oneone) 198 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 199 else 200 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 201 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); 202 // NOTE: use of CONNECTTIMEOUT without also 203 // setting NOSIGNAL results in really weird 204 // crashes on my system! 205 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 206 curl_easy_perform (c); 207 curl_easy_cleanup (c); 208 curl_formfree (pd); 209 } 210 fprintf (stderr, "\n"); 211 zzuf_socat_stop (); 212 MHD_stop_daemon (d); 213 return 0; 214 } 215 216 217 static int 218 testMultithreadedPost () 219 { 220 struct MHD_Daemon *d; 221 CURL *c; 222 char buf[2048]; 223 struct CBC cbc; 224 int i; 225 struct curl_httppost *pd; 226 227 cbc.buf = buf; 228 cbc.size = 2048; 229 cbc.pos = 0; 230 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ , 231 11080, NULL, NULL, &ahc_echo, NULL, 232 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 233 MHD_OPTION_END); 234 if (d == NULL) 235 return 16; 236 zzuf_socat_start (); 237 for (i = 0; i < LOOP_COUNT; i++) 238 { 239 fprintf (stderr, "."); 240 c = curl_easy_init (); 241 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world"); 242 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 243 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 244 pd = make_form (); 245 curl_easy_setopt (c, CURLOPT_HTTPPOST, pd); 246 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 247 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); 248 if (oneone) 249 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 250 else 251 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 252 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); 253 // NOTE: use of CONNECTTIMEOUT without also 254 // setting NOSIGNAL results in really weird 255 // crashes on my system! 256 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 257 curl_easy_perform (c); 258 curl_easy_cleanup (c); 259 curl_formfree (pd); 260 } 261 fprintf (stderr, "\n"); 262 zzuf_socat_stop (); 263 MHD_stop_daemon (d); 264 return 0; 265 } 266 267 268 static int 269 testExternalPost () 270 { 271 struct MHD_Daemon *d; 272 CURL *c; 273 char buf[2048]; 274 struct CBC cbc; 275 CURLM *multi; 276 CURLMcode mret; 277 fd_set rs; 278 fd_set ws; 279 fd_set es; 280 int max; 281 int running; 282 time_t start; 283 struct timeval tv; 284 struct curl_httppost *pd; 285 int i; 286 287 multi = NULL; 288 cbc.buf = buf; 289 cbc.size = 2048; 290 cbc.pos = 0; 291 d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ , 292 1082, NULL, NULL, &ahc_echo, NULL, 293 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 294 MHD_OPTION_END); 295 if (d == NULL) 296 return 256; 297 multi = curl_multi_init (); 298 if (multi == NULL) 299 { 300 MHD_stop_daemon (d); 301 return 512; 302 } 303 zzuf_socat_start (); 304 for (i = 0; i < LOOP_COUNT; i++) 305 { 306 fprintf (stderr, "."); 307 308 c = curl_easy_init (); 309 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1082/hello_world"); 310 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 311 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 312 pd = make_form (); 313 curl_easy_setopt (c, CURLOPT_HTTPPOST, pd); 314 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 315 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 316 if (oneone) 317 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 318 else 319 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 320 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); 321 // NOTE: use of CONNECTTIMEOUT without also 322 // setting NOSIGNAL results in really weird 323 // crashes on my system! 324 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 325 326 327 mret = curl_multi_add_handle (multi, c); 328 if (mret != CURLM_OK) 329 { 330 curl_multi_cleanup (multi); 331 curl_formfree (pd); 332 curl_easy_cleanup (c); 333 zzuf_socat_stop (); 334 MHD_stop_daemon (d); 335 return 1024; 336 } 337 start = time (NULL); 338 while ((time (NULL) - start < 5) && (c != NULL)) 339 { 340 max = 0; 341 FD_ZERO (&rs); 342 FD_ZERO (&ws); 343 FD_ZERO (&es); 344 curl_multi_perform (multi, &running); 345 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); 346 if (mret != CURLM_OK) 347 { 348 curl_multi_remove_handle (multi, c); 349 curl_multi_cleanup (multi); 350 curl_easy_cleanup (c); 351 zzuf_socat_stop (); 352 MHD_stop_daemon (d); 353 curl_formfree (pd); 354 return 2048; 355 } 356 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 357 { 358 curl_multi_remove_handle (multi, c); 359 curl_multi_cleanup (multi); 360 curl_easy_cleanup (c); 361 curl_formfree (pd); 362 zzuf_socat_stop (); 363 MHD_stop_daemon (d); 364 return 4096; 365 } 366 tv.tv_sec = 0; 367 tv.tv_usec = 1000; 368 select (max + 1, &rs, &ws, &es, &tv); 369 curl_multi_perform (multi, &running); 370 if (running == 0) 371 { 372 curl_multi_info_read (multi, &running); 373 curl_multi_remove_handle (multi, c); 374 curl_easy_cleanup (c); 375 c = NULL; 376 } 377 MHD_run (d); 378 } 379 if (c != NULL) 380 { 381 curl_multi_remove_handle (multi, c); 382 curl_easy_cleanup (c); 383 } 384 curl_formfree (pd); 385 } 386 fprintf (stderr, "\n"); 387 zzuf_socat_stop (); 388 389 MHD_stop_daemon (d); 390 return 0; 391 } 392 393 394 int 395 main (int argc, char *const *argv) 396 { 397 unsigned int errorCount = 0; 398 399 oneone = (NULL != strrchr (argv[0], (int) '/')) ? 400 (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; 401 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 402 return 2; 403 errorCount += testInternalPost (); 404 errorCount += testMultithreadedPost (); 405 errorCount += testExternalPost (); 406 if (errorCount != 0) 407 fprintf (stderr, "Error (code: %u)\n", errorCount); 408 curl_global_cleanup (); 409 return errorCount != 0; /* 0 == pass */ 410 } 411