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.c 23 * @brief Testcase for libmicrohttpd POST operations using URL-encoding 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#define POST_DATA "name=daniel&project=curl" 43 44static int oneone; 45 46struct CBC 47{ 48 char *buf; 49 size_t pos; 50 size_t size; 51}; 52 53 54static void 55completed_cb (void *cls, 56 struct MHD_Connection *connection, 57 void **con_cls, 58 enum MHD_RequestTerminationCode toe) 59{ 60 struct MHD_PostProcessor *pp = *con_cls; 61 62 if (NULL != pp) 63 MHD_destroy_post_processor (pp); 64 *con_cls = NULL; 65} 66 67 68static size_t 69copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 70{ 71 struct CBC *cbc = ctx; 72 73 if (cbc->pos + size * nmemb > cbc->size) 74 return 0; /* overflow */ 75 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 76 cbc->pos += size * nmemb; 77 return size * nmemb; 78} 79 80 81/** 82 * Note that this post_iterator is not perfect 83 * in that it fails to support incremental processing. 84 * (to be fixed in the future) 85 */ 86static int 87post_iterator (void *cls, 88 enum MHD_ValueKind kind, 89 const char *key, 90 const char *filename, 91 const char *content_type, 92 const char *transfer_encoding, 93 const char *value, uint64_t off, size_t size) 94{ 95 int *eok = cls; 96 97 if ((0 == strcmp (key, "name")) && 98 (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size))) 99 (*eok) |= 1; 100 if ((0 == strcmp (key, "project")) && 101 (size == strlen ("curl")) && (0 == strncmp (value, "curl", size))) 102 (*eok) |= 2; 103 return MHD_YES; 104} 105 106static int 107ahc_echo (void *cls, 108 struct MHD_Connection *connection, 109 const char *url, 110 const char *method, 111 const char *version, 112 const char *upload_data, size_t *upload_data_size, 113 void **unused) 114{ 115 static int eok; 116 struct MHD_Response *response; 117 struct MHD_PostProcessor *pp; 118 int ret; 119 120 if (0 != strcmp ("POST", method)) 121 { 122 return MHD_NO; /* unexpected method */ 123 } 124 pp = *unused; 125 if (pp == NULL) 126 { 127 eok = 0; 128 pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok); 129 *unused = pp; 130 } 131 MHD_post_process (pp, upload_data, *upload_data_size); 132 if ((eok == 3) && (0 == *upload_data_size)) 133 { 134 response = MHD_create_response_from_buffer (strlen (url), 135 (void *) url, 136 MHD_RESPMEM_MUST_COPY); 137 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 138 MHD_destroy_response (response); 139 MHD_destroy_post_processor (pp); 140 *unused = NULL; 141 return ret; 142 } 143 *upload_data_size = 0; 144 return MHD_YES; 145} 146 147 148static int 149testInternalPost () 150{ 151 struct MHD_Daemon *d; 152 CURL *c; 153 char buf[2048]; 154 struct CBC cbc; 155 int i; 156 157 cbc.buf = buf; 158 cbc.size = 2048; 159 cbc.pos = 0; 160 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ , 161 11080, NULL, NULL, &ahc_echo, NULL, 162 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 163 MHD_OPTION_END); 164 if (d == NULL) 165 return 1; 166 zzuf_socat_start (); 167 for (i = 0; i < LOOP_COUNT; i++) 168 { 169 fprintf (stderr, "."); 170 171 c = curl_easy_init (); 172 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world"); 173 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 174 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 175 curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); 176 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); 177 curl_easy_setopt (c, CURLOPT_POST, 1L); 178 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 179 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); 180 if (oneone) 181 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 182 else 183 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 184 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); 185 // NOTE: use of CONNECTTIMEOUT without also 186 // setting NOSIGNAL results in really weird 187 // crashes on my system! 188 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 189 curl_easy_perform (c); 190 curl_easy_cleanup (c); 191 } 192 fprintf (stderr, "\n"); 193 zzuf_socat_stop (); 194 MHD_stop_daemon (d); 195 196 return 0; 197} 198 199static int 200testMultithreadedPost () 201{ 202 struct MHD_Daemon *d; 203 CURL *c; 204 char buf[2048]; 205 struct CBC cbc; 206 int i; 207 208 cbc.buf = buf; 209 cbc.size = 2048; 210 cbc.pos = 0; 211 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ , 212 11080, NULL, NULL, &ahc_echo, NULL, 213 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 214 MHD_OPTION_END); 215 if (d == NULL) 216 return 16; 217 218 zzuf_socat_start (); 219 for (i = 0; i < LOOP_COUNT; i++) 220 { 221 fprintf (stderr, "."); 222 223 c = curl_easy_init (); 224 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world"); 225 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 226 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 227 curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); 228 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); 229 curl_easy_setopt (c, CURLOPT_POST, 1L); 230 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 231 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); 232 if (oneone) 233 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 234 else 235 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 236 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); 237 // NOTE: use of CONNECTTIMEOUT without also 238 // setting NOSIGNAL results in really weird 239 // crashes on my system! 240 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 241 curl_easy_perform (c); 242 curl_easy_cleanup (c); 243 } 244 fprintf (stderr, "\n"); 245 zzuf_socat_stop (); 246 247 MHD_stop_daemon (d); 248 return 0; 249} 250 251 252static int 253testExternalPost () 254{ 255 struct MHD_Daemon *d; 256 CURL *c; 257 char buf[2048]; 258 struct CBC cbc; 259 CURLM *multi; 260 CURLMcode mret; 261 fd_set rs; 262 fd_set ws; 263 fd_set es; 264 int max; 265 int running; 266 time_t start; 267 struct timeval tv; 268 int i; 269 270 multi = NULL; 271 cbc.buf = buf; 272 cbc.size = 2048; 273 cbc.pos = 0; 274 d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ , 275 1082, NULL, NULL, &ahc_echo, NULL, 276 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 277 MHD_OPTION_END); 278 if (d == NULL) 279 return 256; 280 multi = curl_multi_init (); 281 if (multi == NULL) 282 { 283 MHD_stop_daemon (d); 284 return 512; 285 } 286 287 zzuf_socat_start (); 288 for (i = 0; i < LOOP_COUNT; i++) 289 { 290 fprintf (stderr, "."); 291 292 293 c = curl_easy_init (); 294 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1082/hello_world"); 295 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 296 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 297 curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); 298 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); 299 curl_easy_setopt (c, CURLOPT_POST, 1L); 300 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 301 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); 302 if (oneone) 303 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 304 else 305 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 306 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); 307 // NOTE: use of CONNECTTIMEOUT without also 308 // setting NOSIGNAL results in really weird 309 // crashes on my system! 310 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 311 312 313 mret = curl_multi_add_handle (multi, c); 314 if (mret != CURLM_OK) 315 { 316 curl_multi_cleanup (multi); 317 curl_easy_cleanup (c); 318 zzuf_socat_stop (); 319 MHD_stop_daemon (d); 320 return 1024; 321 } 322 start = time (NULL); 323 while ((time (NULL) - start < 5) && (c != NULL)) 324 { 325 max = 0; 326 FD_ZERO (&rs); 327 FD_ZERO (&ws); 328 FD_ZERO (&es); 329 curl_multi_perform (multi, &running); 330 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); 331 if (mret != CURLM_OK) 332 { 333 curl_multi_remove_handle (multi, c); 334 curl_multi_cleanup (multi); 335 curl_easy_cleanup (c); 336 zzuf_socat_stop (); 337 MHD_stop_daemon (d); 338 return 2048; 339 } 340 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 341 { 342 curl_multi_remove_handle (multi, c); 343 curl_multi_cleanup (multi); 344 curl_easy_cleanup (c); 345 zzuf_socat_stop (); 346 MHD_stop_daemon (d); 347 return 4096; 348 } 349 tv.tv_sec = 0; 350 tv.tv_usec = 1000; 351 select (max + 1, &rs, &ws, &es, &tv); 352 curl_multi_perform (multi, &running); 353 if (running == 0) 354 { 355 curl_multi_info_read (multi, &running); 356 curl_multi_remove_handle (multi, c); 357 curl_easy_cleanup (c); 358 c = NULL; 359 } 360 MHD_run (d); 361 } 362 if (c != NULL) 363 { 364 curl_multi_remove_handle (multi, c); 365 curl_easy_cleanup (c); 366 } 367 368 } 369 fprintf (stderr, "\n"); 370 curl_multi_cleanup (multi); 371 zzuf_socat_stop (); 372 373 MHD_stop_daemon (d); 374 return 0; 375} 376 377 378int 379main (int argc, char *const *argv) 380{ 381 unsigned int errorCount = 0; 382 383 oneone = (NULL != strrchr (argv[0], (int) '/')) ? 384 (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; 385 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 386 return 2; 387 errorCount += testInternalPost (); 388 errorCount += testMultithreadedPost (); 389 errorCount += testExternalPost (); 390 if (errorCount != 0) 391 fprintf (stderr, "Error (code: %u)\n", errorCount); 392 curl_global_cleanup (); 393 return errorCount != 0; /* 0 == pass */ 394} 395