1/* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007 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_postform.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#ifdef HAVE_GCRYPT_H 35#include <gcrypt.h> 36#endif 37 38#ifndef WINDOWS 39#include <unistd.h> 40#endif 41 42#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2 43#undef CPU_COUNT 44#endif 45#if !defined(CPU_COUNT) 46#define CPU_COUNT 2 47#endif 48 49static int oneone; 50 51struct CBC 52{ 53 char *buf; 54 size_t pos; 55 size_t size; 56}; 57 58 59static void 60completed_cb (void *cls, 61 struct MHD_Connection *connection, 62 void **con_cls, 63 enum MHD_RequestTerminationCode toe) 64{ 65 struct MHD_PostProcessor *pp = *con_cls; 66 67 if (NULL != pp) 68 MHD_destroy_post_processor (pp); 69 *con_cls = NULL; 70} 71 72 73static size_t 74copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 75{ 76 struct CBC *cbc = ctx; 77 78 if (cbc->pos + size * nmemb > cbc->size) 79 return 0; /* overflow */ 80 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 81 cbc->pos += size * nmemb; 82 return size * nmemb; 83} 84 85 86/** 87 * Note that this post_iterator is not perfect 88 * in that it fails to support incremental processing. 89 * (to be fixed in the future) 90 */ 91static int 92post_iterator (void *cls, 93 enum MHD_ValueKind kind, 94 const char *key, 95 const char *filename, 96 const char *content_type, 97 const char *transfer_encoding, 98 const char *value, uint64_t off, size_t size) 99{ 100 int *eok = cls; 101 102#if 0 103 fprintf (stderr, "PI sees %s-%.*s\n", key, size, value); 104#endif 105 if ((0 == strcmp (key, "name")) && 106 (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size))) 107 (*eok) |= 1; 108 if ((0 == strcmp (key, "project")) && 109 (size == strlen ("curl")) && (0 == strncmp (value, "curl", size))) 110 (*eok) |= 2; 111 return MHD_YES; 112} 113 114 115static int 116ahc_echo (void *cls, 117 struct MHD_Connection *connection, 118 const char *url, 119 const char *method, 120 const char *version, 121 const char *upload_data, size_t *upload_data_size, 122 void **unused) 123{ 124 static int eok; 125 struct MHD_Response *response; 126 struct MHD_PostProcessor *pp; 127 int ret; 128 129 if (0 != strcmp ("POST", method)) 130 { 131 printf ("METHOD: %s\n", method); 132 return MHD_NO; /* unexpected method */ 133 } 134 pp = *unused; 135 if (pp == NULL) 136 { 137 eok = 0; 138 pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok); 139 if (pp == NULL) 140 abort (); 141 *unused = pp; 142 } 143 MHD_post_process (pp, upload_data, *upload_data_size); 144 if ((eok == 3) && (0 == *upload_data_size)) 145 { 146 response = MHD_create_response_from_buffer (strlen (url), 147 (void *) url, 148 MHD_RESPMEM_MUST_COPY); 149 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 150 MHD_destroy_response (response); 151 MHD_destroy_post_processor (pp); 152 *unused = NULL; 153 return ret; 154 } 155 *upload_data_size = 0; 156 return MHD_YES; 157} 158 159static struct curl_httppost * 160make_form () 161{ 162 struct curl_httppost *post = NULL; 163 struct curl_httppost *last = NULL; 164 165 curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", 166 CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END); 167 curl_formadd (&post, &last, CURLFORM_COPYNAME, "project", 168 CURLFORM_COPYCONTENTS, "curl", CURLFORM_END); 169 return post; 170} 171 172 173static int 174testInternalPost () 175{ 176 struct MHD_Daemon *d; 177 CURL *c; 178 char buf[2048]; 179 struct CBC cbc; 180 CURLcode errornum; 181 struct curl_httppost *pd; 182 183 cbc.buf = buf; 184 cbc.size = 2048; 185 cbc.pos = 0; 186 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 187 1080, NULL, NULL, &ahc_echo, NULL, 188 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 189 MHD_OPTION_END); 190 if (d == NULL) 191 return 1; 192 c = curl_easy_init (); 193 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world"); 194 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 195 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 196 pd = make_form (); 197 curl_easy_setopt (c, CURLOPT_HTTPPOST, pd); 198 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 199 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 200 if (oneone) 201 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 202 else 203 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 204 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 205 // NOTE: use of CONNECTTIMEOUT without also 206 // setting NOSIGNAL results in really weird 207 // crashes on my system! 208 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 209 if (CURLE_OK != (errornum = curl_easy_perform (c))) 210 { 211 fprintf (stderr, 212 "curl_easy_perform failed: `%s'\n", 213 curl_easy_strerror (errornum)); 214 curl_easy_cleanup (c); 215 curl_formfree (pd); 216 MHD_stop_daemon (d); 217 return 2; 218 } 219 curl_easy_cleanup (c); 220 curl_formfree (pd); 221 MHD_stop_daemon (d); 222 if (cbc.pos != strlen ("/hello_world")) 223 return 4; 224 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 225 return 8; 226 return 0; 227} 228 229static int 230testMultithreadedPost () 231{ 232 struct MHD_Daemon *d; 233 CURL *c; 234 char buf[2048]; 235 struct CBC cbc; 236 CURLcode errornum; 237 struct curl_httppost *pd; 238 239 cbc.buf = buf; 240 cbc.size = 2048; 241 cbc.pos = 0; 242 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, 243 1081, NULL, NULL, &ahc_echo, NULL, 244 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 245 MHD_OPTION_END); 246 if (d == NULL) 247 return 16; 248 c = curl_easy_init (); 249 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world"); 250 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 251 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 252 pd = make_form (); 253 curl_easy_setopt (c, CURLOPT_HTTPPOST, pd); 254 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 255 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 256 if (oneone) 257 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 258 else 259 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 260 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L); 261 // NOTE: use of CONNECTTIMEOUT without also 262 // setting NOSIGNAL results in really weird 263 // crashes on my system! 264 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 265 if (CURLE_OK != (errornum = curl_easy_perform (c))) 266 { 267 fprintf (stderr, 268 "curl_easy_perform failed: `%s'\n", 269 curl_easy_strerror (errornum)); 270 curl_easy_cleanup (c); 271 curl_formfree (pd); 272 MHD_stop_daemon (d); 273 return 32; 274 } 275 curl_easy_cleanup (c); 276 curl_formfree (pd); 277 MHD_stop_daemon (d); 278 if (cbc.pos != strlen ("/hello_world")) 279 return 64; 280 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 281 return 128; 282 return 0; 283} 284 285static int 286testMultithreadedPoolPost () 287{ 288 struct MHD_Daemon *d; 289 CURL *c; 290 char buf[2048]; 291 struct CBC cbc; 292 CURLcode errornum; 293 struct curl_httppost *pd; 294 295 cbc.buf = buf; 296 cbc.size = 2048; 297 cbc.pos = 0; 298 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 299 1081, NULL, NULL, &ahc_echo, NULL, 300 MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, 301 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 302 MHD_OPTION_END); 303 if (d == NULL) 304 return 16; 305 c = curl_easy_init (); 306 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world"); 307 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 308 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 309 pd = make_form (); 310 curl_easy_setopt (c, CURLOPT_HTTPPOST, pd); 311 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 312 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 313 if (oneone) 314 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 315 else 316 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 317 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L); 318 // NOTE: use of CONNECTTIMEOUT without also 319 // setting NOSIGNAL results in really weird 320 // crashes on my system! 321 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 322 if (CURLE_OK != (errornum = curl_easy_perform (c))) 323 { 324 fprintf (stderr, 325 "curl_easy_perform failed: `%s'\n", 326 curl_easy_strerror (errornum)); 327 curl_easy_cleanup (c); 328 curl_formfree (pd); 329 MHD_stop_daemon (d); 330 return 32; 331 } 332 curl_easy_cleanup (c); 333 curl_formfree (pd); 334 MHD_stop_daemon (d); 335 if (cbc.pos != strlen ("/hello_world")) 336 return 64; 337 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 338 return 128; 339 return 0; 340} 341 342static int 343testExternalPost () 344{ 345 struct MHD_Daemon *d; 346 CURL *c; 347 char buf[2048]; 348 struct CBC cbc; 349 CURLM *multi; 350 CURLMcode mret; 351 fd_set rs; 352 fd_set ws; 353 fd_set es; 354 MHD_socket max; 355 int running; 356 struct CURLMsg *msg; 357 time_t start; 358 struct timeval tv; 359 struct curl_httppost *pd; 360 361 multi = NULL; 362 cbc.buf = buf; 363 cbc.size = 2048; 364 cbc.pos = 0; 365 d = MHD_start_daemon (MHD_USE_DEBUG, 366 1082, NULL, NULL, &ahc_echo, NULL, 367 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 368 MHD_OPTION_END); 369 if (d == NULL) 370 return 256; 371 c = curl_easy_init (); 372 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world"); 373 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 374 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 375 pd = make_form (); 376 curl_easy_setopt (c, CURLOPT_HTTPPOST, pd); 377 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 378 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 379 if (oneone) 380 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 381 else 382 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 383 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 384 // NOTE: use of CONNECTTIMEOUT without also 385 // setting NOSIGNAL results in really weird 386 // crashes on my system! 387 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 388 389 390 multi = curl_multi_init (); 391 if (multi == NULL) 392 { 393 curl_easy_cleanup (c); 394 curl_formfree (pd); 395 MHD_stop_daemon (d); 396 return 512; 397 } 398 mret = curl_multi_add_handle (multi, c); 399 if (mret != CURLM_OK) 400 { 401 curl_multi_cleanup (multi); 402 curl_formfree (pd); 403 curl_easy_cleanup (c); 404 MHD_stop_daemon (d); 405 return 1024; 406 } 407 start = time (NULL); 408 while ((time (NULL) - start < 5) && (multi != NULL)) 409 { 410 max = 0; 411 FD_ZERO (&rs); 412 FD_ZERO (&ws); 413 FD_ZERO (&es); 414 curl_multi_perform (multi, &running); 415 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); 416 if (mret != CURLM_OK) 417 { 418 curl_multi_remove_handle (multi, c); 419 curl_multi_cleanup (multi); 420 curl_easy_cleanup (c); 421 MHD_stop_daemon (d); 422 curl_formfree (pd); 423 return 2048; 424 } 425 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 426 { 427 curl_multi_remove_handle (multi, c); 428 curl_multi_cleanup (multi); 429 curl_easy_cleanup (c); 430 curl_formfree (pd); 431 MHD_stop_daemon (d); 432 return 4096; 433 } 434 tv.tv_sec = 0; 435 tv.tv_usec = 1000; 436 select (max + 1, &rs, &ws, &es, &tv); 437 curl_multi_perform (multi, &running); 438 if (running == 0) 439 { 440 msg = curl_multi_info_read (multi, &running); 441 if (msg == NULL) 442 break; 443 if (msg->msg == CURLMSG_DONE) 444 { 445 if (msg->data.result != CURLE_OK) 446 printf ("%s failed at %s:%d: `%s'\n", 447 "curl_multi_perform", 448 __FILE__, 449 __LINE__, curl_easy_strerror (msg->data.result)); 450 curl_multi_remove_handle (multi, c); 451 curl_multi_cleanup (multi); 452 curl_easy_cleanup (c); 453 c = NULL; 454 multi = NULL; 455 } 456 } 457 MHD_run (d); 458 } 459 if (multi != NULL) 460 { 461 curl_multi_remove_handle (multi, c); 462 curl_easy_cleanup (c); 463 curl_multi_cleanup (multi); 464 } 465 curl_formfree (pd); 466 MHD_stop_daemon (d); 467 if (cbc.pos != strlen ("/hello_world")) 468 return 8192; 469 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 470 return 16384; 471 return 0; 472} 473 474 475int 476main (int argc, char *const *argv) 477{ 478 unsigned int errorCount = 0; 479 480#ifdef HAVE_GCRYPT_H 481 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); 482#ifdef GCRYCTL_INITIALIZATION_FINISHED 483 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 484#endif 485#endif 486 oneone = (NULL != strrchr (argv[0], (int) '/')) ? 487 (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; 488 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 489 return 2; 490 errorCount += testInternalPost (); 491 errorCount += testMultithreadedPost (); 492 errorCount += testMultithreadedPoolPost (); 493 errorCount += testExternalPost (); 494 if (errorCount != 0) 495 fprintf (stderr, "Error (code: %u)\n", errorCount); 496 curl_global_cleanup (); 497 return errorCount != 0; /* 0 == pass */ 498} 499