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 daemontest_post_loop.c
23 * @brief Testcase for libmicrohttpd POST operations using URL-encoding
24 * @author Christian Grothoff (inspired by bug report #1296)
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 #include <gauger.h>
35
36 #ifndef WINDOWS
37 #include <unistd.h>
38 #endif
39
40 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
41 #undef CPU_COUNT
42 #endif
43 #if !defined(CPU_COUNT)
44 #define CPU_COUNT 2
45 #endif
46
47 #define POST_DATA "<?xml version='1.0' ?>\n<xml>\n<data-id>1</data-id>\n</xml>\n"
48
49 #define LOOPCOUNT 1000
50
51 static int oneone;
52
53 struct CBC
54 {
55 char *buf;
56 size_t pos;
57 size_t size;
58 };
59
60 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)61 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
62 {
63 struct CBC *cbc = ctx;
64
65 if (cbc->pos + size * nmemb > cbc->size)
66 return 0; /* overflow */
67 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
68 cbc->pos += size * nmemb;
69 return size * nmemb;
70 }
71
72 static int
ahc_echo(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** mptr)73 ahc_echo (void *cls,
74 struct MHD_Connection *connection,
75 const char *url,
76 const char *method,
77 const char *version,
78 const char *upload_data, size_t *upload_data_size,
79 void **mptr)
80 {
81 static int marker;
82 struct MHD_Response *response;
83 int ret;
84
85 if (0 != strcmp ("POST", method))
86 {
87 printf ("METHOD: %s\n", method);
88 return MHD_NO; /* unexpected method */
89 }
90 if ((*mptr != NULL) && (0 == *upload_data_size))
91 {
92 if (*mptr != &marker)
93 abort ();
94 response = MHD_create_response_from_buffer (2, "OK",
95 MHD_RESPMEM_PERSISTENT);
96 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
97 MHD_destroy_response (response);
98 *mptr = NULL;
99 return ret;
100 }
101 if (strlen (POST_DATA) != *upload_data_size)
102 return MHD_YES;
103 *upload_data_size = 0;
104 *mptr = ▮
105 return MHD_YES;
106 }
107
108
109 static int
testInternalPost()110 testInternalPost ()
111 {
112 struct MHD_Daemon *d;
113 CURL *c;
114 char buf[2048];
115 struct CBC cbc;
116 CURLcode errornum;
117 int i;
118 char url[1024];
119
120 cbc.buf = buf;
121 cbc.size = 2048;
122 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
123 1080, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
124 if (d == NULL)
125 return 1;
126 for (i = 0; i < LOOPCOUNT; i++)
127 {
128 if (99 == i % 100)
129 fprintf (stderr, ".");
130 c = curl_easy_init ();
131 cbc.pos = 0;
132 buf[0] = '\0';
133 sprintf (url, "http://127.0.0.1:1080/hw%d", i);
134 curl_easy_setopt (c, CURLOPT_URL, url);
135 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
136 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
137 curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
138 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
139 curl_easy_setopt (c, CURLOPT_POST, 1L);
140 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
141 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
142 if (oneone)
143 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
144 else
145 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
146 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
147 // NOTE: use of CONNECTTIMEOUT without also
148 // setting NOSIGNAL results in really weird
149 // crashes on my system!
150 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
151 if (CURLE_OK != (errornum = curl_easy_perform (c)))
152 {
153 fprintf (stderr,
154 "curl_easy_perform failed: `%s'\n",
155 curl_easy_strerror (errornum));
156 curl_easy_cleanup (c);
157 MHD_stop_daemon (d);
158 return 2;
159 }
160 curl_easy_cleanup (c);
161 if ((buf[0] != 'O') || (buf[1] != 'K'))
162 {
163 MHD_stop_daemon (d);
164 return 4;
165 }
166 }
167 MHD_stop_daemon (d);
168 if (LOOPCOUNT >= 99)
169 fprintf (stderr, "\n");
170 return 0;
171 }
172
173 static int
testMultithreadedPost()174 testMultithreadedPost ()
175 {
176 struct MHD_Daemon *d;
177 CURL *c;
178 char buf[2048];
179 struct CBC cbc;
180 CURLcode errornum;
181 int i;
182 char url[1024];
183
184 cbc.buf = buf;
185 cbc.size = 2048;
186 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
187 1081, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
188 if (d == NULL)
189 return 16;
190 for (i = 0; i < LOOPCOUNT; i++)
191 {
192 if (99 == i % 100)
193 fprintf (stderr, ".");
194 c = curl_easy_init ();
195 cbc.pos = 0;
196 buf[0] = '\0';
197 sprintf (url, "http://127.0.0.1:1081/hw%d", i);
198 curl_easy_setopt (c, CURLOPT_URL, url);
199 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
200 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
201 curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
202 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
203 curl_easy_setopt (c, CURLOPT_POST, 1L);
204 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
205 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
206 if (oneone)
207 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
208 else
209 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
210 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
211 // NOTE: use of CONNECTTIMEOUT without also
212 // setting NOSIGNAL results in really weird
213 // crashes on my system!
214 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
215 if (CURLE_OK != (errornum = curl_easy_perform (c)))
216 {
217 fprintf (stderr,
218 "curl_easy_perform failed: `%s'\n",
219 curl_easy_strerror (errornum));
220 curl_easy_cleanup (c);
221 MHD_stop_daemon (d);
222 return 32;
223 }
224 curl_easy_cleanup (c);
225 if ((buf[0] != 'O') || (buf[1] != 'K'))
226 {
227 MHD_stop_daemon (d);
228 return 64;
229 }
230 }
231 MHD_stop_daemon (d);
232 if (LOOPCOUNT >= 99)
233 fprintf (stderr, "\n");
234 return 0;
235 }
236
237 static int
testMultithreadedPoolPost()238 testMultithreadedPoolPost ()
239 {
240 struct MHD_Daemon *d;
241 CURL *c;
242 char buf[2048];
243 struct CBC cbc;
244 CURLcode errornum;
245 int i;
246 char url[1024];
247
248 cbc.buf = buf;
249 cbc.size = 2048;
250 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
251 1081, NULL, NULL, &ahc_echo, NULL,
252 MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
253 if (d == NULL)
254 return 16;
255 for (i = 0; i < LOOPCOUNT; i++)
256 {
257 if (99 == i % 100)
258 fprintf (stderr, ".");
259 c = curl_easy_init ();
260 cbc.pos = 0;
261 buf[0] = '\0';
262 sprintf (url, "http://127.0.0.1:1081/hw%d", i);
263 curl_easy_setopt (c, CURLOPT_URL, url);
264 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
265 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
266 curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
267 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
268 curl_easy_setopt (c, CURLOPT_POST, 1L);
269 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
270 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
271 if (oneone)
272 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
273 else
274 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
275 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
276 // NOTE: use of CONNECTTIMEOUT without also
277 // setting NOSIGNAL results in really weird
278 // crashes on my system!
279 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
280 if (CURLE_OK != (errornum = curl_easy_perform (c)))
281 {
282 fprintf (stderr,
283 "curl_easy_perform failed: `%s'\n",
284 curl_easy_strerror (errornum));
285 curl_easy_cleanup (c);
286 MHD_stop_daemon (d);
287 return 32;
288 }
289 curl_easy_cleanup (c);
290 if ((buf[0] != 'O') || (buf[1] != 'K'))
291 {
292 MHD_stop_daemon (d);
293 return 64;
294 }
295 }
296 MHD_stop_daemon (d);
297 if (LOOPCOUNT >= 99)
298 fprintf (stderr, "\n");
299 return 0;
300 }
301
302 static int
testExternalPost()303 testExternalPost ()
304 {
305 struct MHD_Daemon *d;
306 CURL *c;
307 char buf[2048];
308 struct CBC cbc;
309 CURLM *multi;
310 CURLMcode mret;
311 fd_set rs;
312 fd_set ws;
313 fd_set es;
314 MHD_socket max;
315 int running;
316 struct CURLMsg *msg;
317 time_t start;
318 struct timeval tv;
319 int i;
320 unsigned long long timeout;
321 long ctimeout;
322 char url[1024];
323
324 multi = NULL;
325 cbc.buf = buf;
326 cbc.size = 2048;
327 cbc.pos = 0;
328 d = MHD_start_daemon (MHD_USE_DEBUG,
329 1082, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
330 if (d == NULL)
331 return 256;
332 multi = curl_multi_init ();
333 if (multi == NULL)
334 {
335 MHD_stop_daemon (d);
336 return 512;
337 }
338 for (i = 0; i < LOOPCOUNT; i++)
339 {
340 if (99 == i % 100)
341 fprintf (stderr, ".");
342 c = curl_easy_init ();
343 cbc.pos = 0;
344 buf[0] = '\0';
345 sprintf (url, "http://127.0.0.1:1082/hw%d", i);
346 curl_easy_setopt (c, CURLOPT_URL, url);
347 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
348 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
349 curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
350 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
351 curl_easy_setopt (c, CURLOPT_POST, 1L);
352 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
353 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
354 if (oneone)
355 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
356 else
357 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
358 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
359 // NOTE: use of CONNECTTIMEOUT without also
360 // setting NOSIGNAL results in really weird
361 // crashes on my system!
362 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
363 mret = curl_multi_add_handle (multi, c);
364 if (mret != CURLM_OK)
365 {
366 curl_multi_cleanup (multi);
367 curl_easy_cleanup (c);
368 MHD_stop_daemon (d);
369 return 1024;
370 }
371 start = time (NULL);
372 while ((time (NULL) - start < 5) && (multi != NULL))
373 {
374 max = 0;
375 FD_ZERO (&rs);
376 FD_ZERO (&ws);
377 FD_ZERO (&es);
378 while (CURLM_CALL_MULTI_PERFORM ==
379 curl_multi_perform (multi, &running));
380 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
381 if (mret != CURLM_OK)
382 {
383 curl_multi_remove_handle (multi, c);
384 curl_multi_cleanup (multi);
385 curl_easy_cleanup (c);
386 MHD_stop_daemon (d);
387 return 2048;
388 }
389 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
390 {
391 curl_multi_remove_handle (multi, c);
392 curl_multi_cleanup (multi);
393 curl_easy_cleanup (c);
394 MHD_stop_daemon (d);
395 return 4096;
396 }
397 if (MHD_NO == MHD_get_timeout (d, &timeout))
398 timeout = 100; /* 100ms == INFTY -- CURL bug... */
399 if ((CURLM_OK == curl_multi_timeout (multi, &ctimeout)) &&
400 (ctimeout < timeout) && (ctimeout >= 0))
401 timeout = ctimeout;
402 if ( (c == NULL) || (running == 0) )
403 timeout = 0; /* terminate quickly... */
404 tv.tv_sec = timeout / 1000;
405 tv.tv_usec = (timeout % 1000) * 1000;
406 if (-1 == select (max + 1, &rs, &ws, &es, &tv))
407 {
408 if (EINTR == errno)
409 continue;
410 fprintf (stderr,
411 "select failed: %s\n",
412 strerror (errno));
413 break;
414 }
415 while (CURLM_CALL_MULTI_PERFORM ==
416 curl_multi_perform (multi, &running));
417 if (running == 0)
418 {
419 msg = curl_multi_info_read (multi, &running);
420 if (msg == NULL)
421 break;
422 if (msg->msg == CURLMSG_DONE)
423 {
424 if (msg->data.result != CURLE_OK)
425 printf ("%s failed at %s:%d: `%s'\n",
426 "curl_multi_perform",
427 __FILE__,
428 __LINE__, curl_easy_strerror (msg->data.result));
429 curl_multi_remove_handle (multi, c);
430 curl_easy_cleanup (c);
431 c = NULL;
432 }
433 }
434 MHD_run (d);
435 }
436 if (c != NULL)
437 {
438 curl_multi_remove_handle (multi, c);
439 curl_easy_cleanup (c);
440 }
441 if ((buf[0] != 'O') || (buf[1] != 'K'))
442 {
443 curl_multi_cleanup (multi);
444 MHD_stop_daemon (d);
445 return 8192;
446 }
447 }
448 curl_multi_cleanup (multi);
449 MHD_stop_daemon (d);
450 if (LOOPCOUNT >= 99)
451 fprintf (stderr, "\n");
452 return 0;
453 }
454
455
456 /**
457 * Time this round was started.
458 */
459 static unsigned long long start_time;
460
461
462 /**
463 * Get the current timestamp
464 *
465 * @return current time in ms
466 */
467 static unsigned long long
now()468 now ()
469 {
470 struct timeval tv;
471
472 gettimeofday (&tv, NULL);
473 return (((unsigned long long) tv.tv_sec * 1000LL) +
474 ((unsigned long long) tv.tv_usec / 1000LL));
475 }
476
477
478 int
main(int argc,char * const * argv)479 main (int argc, char *const *argv)
480 {
481 unsigned int errorCount = 0;
482
483 oneone = (NULL != strrchr (argv[0], (int) '/')) ?
484 (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
485 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
486 return 2;
487 start_time = now();
488 errorCount += testInternalPost ();
489 fprintf (stderr,
490 oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
491 "internal select",
492 (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
493 GAUGER ("internal select",
494 oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
495 (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
496 "requests/s");
497 start_time = now();
498 errorCount += testMultithreadedPost ();
499 fprintf (stderr,
500 oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
501 "multithreaded post",
502 (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
503 GAUGER ("Multithreaded select",
504 oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
505 (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
506 "requests/s");
507 start_time = now();
508 errorCount += testMultithreadedPoolPost ();
509 fprintf (stderr,
510 oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
511 "thread with pool",
512 (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
513 GAUGER ("thread with pool",
514 oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
515 (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
516 "requests/s");
517 start_time = now();
518 errorCount += testExternalPost ();
519 fprintf (stderr,
520 oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
521 "external select",
522 (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
523 GAUGER ("external select",
524 oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
525 (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
526 "requests/s");
527 if (errorCount != 0)
528 fprintf (stderr, "Error (code: %u)\n", errorCount);
529 curl_global_cleanup ();
530 return errorCount != 0; /* 0 == pass */
531 }
532