• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = &marker;
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, &copyBuffer);
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, &copyBuffer);
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, &copyBuffer);
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, &copyBuffer);
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