• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_large_put.c
23  * @brief  Testcase for libmicrohttpd PUT operations
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 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
40 #undef CPU_COUNT
41 #endif
42 #if !defined(CPU_COUNT)
43 #define CPU_COUNT 2
44 #endif
45 
46 static int oneone;
47 
48 /**
49  * Do not make this much larger since we will hit the
50  * MHD default buffer limit and the test code is not
51  * written for incremental upload processing...
52  * (larger values will likely cause MHD to generate
53  * an internal server error -- which would be avoided
54  * by writing the putBuffer method in a more general
55  * fashion).
56  */
57 #define PUT_SIZE (256 * 1024)
58 
59 static char *put_buffer;
60 
61 struct CBC
62 {
63   char *buf;
64   size_t pos;
65   size_t size;
66 };
67 
68 static size_t
putBuffer(void * stream,size_t size,size_t nmemb,void * ptr)69 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
70 {
71   unsigned int *pos = ptr;
72   unsigned int wrt;
73 
74   wrt = size * nmemb;
75   if (wrt > PUT_SIZE - (*pos))
76     wrt = PUT_SIZE - (*pos);
77   memcpy (stream, &put_buffer[*pos], wrt);
78   (*pos) += wrt;
79   return wrt;
80 }
81 
82 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)83 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
84 {
85   struct CBC *cbc = ctx;
86 
87   if (cbc->pos + size * nmemb > cbc->size)
88     return 0;                   /* overflow */
89   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
90   cbc->pos += size * nmemb;
91   return size * nmemb;
92 }
93 
94 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 ** unused)95 ahc_echo (void *cls,
96           struct MHD_Connection *connection,
97           const char *url,
98           const char *method,
99           const char *version,
100           const char *upload_data, size_t *upload_data_size,
101           void **unused)
102 {
103   int *done = cls;
104   struct MHD_Response *response;
105   int ret;
106 
107   if (0 != strcmp ("PUT", method))
108     return MHD_NO;              /* unexpected method */
109   if ((*done) == 0)
110     {
111       if (*upload_data_size != PUT_SIZE)
112         {
113 #if 0
114           fprintf (stderr,
115                    "Waiting for more data (%u/%u)...\n",
116                    *upload_data_size, PUT_SIZE);
117 #endif
118           return MHD_YES;       /* not yet ready */
119         }
120       if (0 == memcmp (upload_data, put_buffer, PUT_SIZE))
121         {
122           *upload_data_size = 0;
123         }
124       else
125         {
126           printf ("Invalid upload data!\n");
127           return MHD_NO;
128         }
129       *done = 1;
130       return MHD_YES;
131     }
132   response = MHD_create_response_from_buffer (strlen (url),
133 					      (void *) url,
134 					      MHD_RESPMEM_MUST_COPY);
135   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
136   MHD_destroy_response (response);
137   return ret;
138 }
139 
140 
141 static int
testInternalPut()142 testInternalPut ()
143 {
144   struct MHD_Daemon *d;
145   CURL *c;
146   struct CBC cbc;
147   unsigned int pos = 0;
148   int done_flag = 0;
149   CURLcode errornum;
150   char buf[2048];
151 
152   cbc.buf = buf;
153   cbc.size = 2048;
154   cbc.pos = 0;
155   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
156                         1080,
157                         NULL, NULL, &ahc_echo, &done_flag,
158 			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024),
159 			MHD_OPTION_END);
160   if (d == NULL)
161     return 1;
162   c = curl_easy_init ();
163   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
164   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
165   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
166   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
167   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
168   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
169   curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
170   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
171   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
172   if (oneone)
173     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
174   else
175     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
176   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
177   // NOTE: use of CONNECTTIMEOUT without also
178   //   setting NOSIGNAL results in really weird
179   //   crashes on my system!
180   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
181   if (CURLE_OK != (errornum = curl_easy_perform (c)))
182     {
183       fprintf (stderr,
184                "curl_easy_perform failed: `%s'\n",
185                curl_easy_strerror (errornum));
186       curl_easy_cleanup (c);
187       MHD_stop_daemon (d);
188       return 2;
189     }
190   curl_easy_cleanup (c);
191   MHD_stop_daemon (d);
192   if (cbc.pos != strlen ("/hello_world"))
193     return 4;
194   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
195     return 8;
196   return 0;
197 }
198 
199 static int
testMultithreadedPut()200 testMultithreadedPut ()
201 {
202   struct MHD_Daemon *d;
203   CURL *c;
204   struct CBC cbc;
205   unsigned int pos = 0;
206   int done_flag = 0;
207   CURLcode errornum;
208   char buf[2048];
209 
210   cbc.buf = buf;
211   cbc.size = 2048;
212   cbc.pos = 0;
213   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
214                         1081,
215                         NULL, NULL, &ahc_echo, &done_flag,
216 			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024),
217 			MHD_OPTION_END);
218   if (d == NULL)
219     return 16;
220   c = curl_easy_init ();
221   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
222   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
223   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
224   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
225   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
226   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
227   curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
228   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
229   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
230   if (oneone)
231     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
232   else
233     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
234   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
235   // NOTE: use of CONNECTTIMEOUT without also
236   //   setting NOSIGNAL results in really weird
237   //   crashes on my system!
238   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
239   if (CURLE_OK != (errornum = curl_easy_perform (c)))
240     {
241       fprintf (stderr,
242                "curl_easy_perform failed: `%s'\n",
243                curl_easy_strerror (errornum));
244       curl_easy_cleanup (c);
245       MHD_stop_daemon (d);
246       return 32;
247     }
248   curl_easy_cleanup (c);
249   MHD_stop_daemon (d);
250   if (cbc.pos != strlen ("/hello_world"))
251     {
252       fprintf (stderr, "Got invalid response `%.*s'\n", (int)cbc.pos, cbc.buf);
253       return 64;
254     }
255   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
256     return 128;
257   return 0;
258 }
259 
260 static int
testMultithreadedPoolPut()261 testMultithreadedPoolPut ()
262 {
263   struct MHD_Daemon *d;
264   CURL *c;
265   struct CBC cbc;
266   unsigned int pos = 0;
267   int done_flag = 0;
268   CURLcode errornum;
269   char buf[2048];
270 
271   cbc.buf = buf;
272   cbc.size = 2048;
273   cbc.pos = 0;
274   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
275                         1081,
276                         NULL, NULL, &ahc_echo, &done_flag,
277                         MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
278 			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024),
279 			MHD_OPTION_END);
280   if (d == NULL)
281     return 16;
282   c = curl_easy_init ();
283   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
284   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
285   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
286   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
287   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
288   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
289   curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
290   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
291   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
292   if (oneone)
293     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
294   else
295     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
296   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
297   // NOTE: use of CONNECTTIMEOUT without also
298   //   setting NOSIGNAL results in really weird
299   //   crashes on my system!
300   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
301   if (CURLE_OK != (errornum = curl_easy_perform (c)))
302     {
303       fprintf (stderr,
304                "curl_easy_perform failed: `%s'\n",
305                curl_easy_strerror (errornum));
306       curl_easy_cleanup (c);
307       MHD_stop_daemon (d);
308       return 32;
309     }
310   curl_easy_cleanup (c);
311   MHD_stop_daemon (d);
312   if (cbc.pos != strlen ("/hello_world"))
313     {
314       fprintf (stderr, "Got invalid response `%.*s'\n", (int)cbc.pos, cbc.buf);
315       return 64;
316     }
317   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
318     return 128;
319   return 0;
320 }
321 
322 static int
testExternalPut()323 testExternalPut ()
324 {
325   struct MHD_Daemon *d;
326   CURL *c;
327   struct CBC cbc;
328   CURLM *multi;
329   CURLMcode mret;
330   fd_set rs;
331   fd_set ws;
332   fd_set es;
333   MHD_socket max;
334   int running;
335   struct CURLMsg *msg;
336   time_t start;
337   struct timeval tv;
338   unsigned int pos = 0;
339   int done_flag = 0;
340   char buf[2048];
341 
342   cbc.buf = buf;
343   cbc.size = 2048;
344   cbc.pos = 0;
345   multi = NULL;
346   d = MHD_start_daemon (MHD_USE_DEBUG,
347                         1082,
348                         NULL, NULL, &ahc_echo, &done_flag,
349                         MHD_OPTION_CONNECTION_MEMORY_LIMIT,
350                         (size_t) (PUT_SIZE * 4), MHD_OPTION_END);
351   if (d == NULL)
352     return 256;
353   c = curl_easy_init ();
354   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world");
355   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
356   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
357   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
358   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
359   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
360   curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
361   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
362   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
363   if (oneone)
364     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
365   else
366     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
367   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
368   // NOTE: use of CONNECTTIMEOUT without also
369   //   setting NOSIGNAL results in really weird
370   //   crashes on my system!
371   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
372 
373 
374   multi = curl_multi_init ();
375   if (multi == NULL)
376     {
377       curl_easy_cleanup (c);
378       MHD_stop_daemon (d);
379       return 512;
380     }
381   mret = curl_multi_add_handle (multi, c);
382   if (mret != CURLM_OK)
383     {
384       curl_multi_cleanup (multi);
385       curl_easy_cleanup (c);
386       MHD_stop_daemon (d);
387       return 1024;
388     }
389   start = time (NULL);
390   while ((time (NULL) - start < 5) && (multi != NULL))
391     {
392       max = 0;
393       FD_ZERO (&rs);
394       FD_ZERO (&ws);
395       FD_ZERO (&es);
396       curl_multi_perform (multi, &running);
397       mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
398       if (mret != CURLM_OK)
399         {
400           curl_multi_remove_handle (multi, c);
401           curl_multi_cleanup (multi);
402           curl_easy_cleanup (c);
403           MHD_stop_daemon (d);
404           return 2048;
405         }
406       if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
407         {
408           curl_multi_remove_handle (multi, c);
409           curl_multi_cleanup (multi);
410           curl_easy_cleanup (c);
411           MHD_stop_daemon (d);
412           return 4096;
413         }
414       tv.tv_sec = 0;
415       tv.tv_usec = 1000;
416       select (max + 1, &rs, &ws, &es, &tv);
417       curl_multi_perform (multi, &running);
418       if (running == 0)
419         {
420           msg = curl_multi_info_read (multi, &running);
421           if (msg == NULL)
422             break;
423           if (msg->msg == CURLMSG_DONE)
424             {
425               if (msg->data.result != CURLE_OK)
426                 printf ("%s failed at %s:%d: `%s'\n",
427                         "curl_multi_perform",
428                         __FILE__,
429                         __LINE__, curl_easy_strerror (msg->data.result));
430               curl_multi_remove_handle (multi, c);
431               curl_multi_cleanup (multi);
432               curl_easy_cleanup (c);
433               c = NULL;
434               multi = NULL;
435             }
436         }
437       MHD_run (d);
438     }
439   if (multi != NULL)
440     {
441       curl_multi_remove_handle (multi, c);
442       curl_easy_cleanup (c);
443       curl_multi_cleanup (multi);
444     }
445   MHD_stop_daemon (d);
446   if (cbc.pos != strlen ("/hello_world"))
447     {
448       fprintf (stderr, "Got invalid response `%.*s'\n", (int)cbc.pos, cbc.buf);
449       return 8192;
450     }
451   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
452     return 16384;
453   return 0;
454 }
455 
456 
457 
458 int
main(int argc,char * const * argv)459 main (int argc, char *const *argv)
460 {
461   unsigned int errorCount = 0;
462 
463   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
464     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
465   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
466     return 2;
467   put_buffer = malloc (PUT_SIZE);
468   if (NULL == put_buffer) return 1;
469   memset (put_buffer, 1, PUT_SIZE);
470   errorCount += testInternalPut ();
471   errorCount += testMultithreadedPut ();
472   errorCount += testMultithreadedPoolPut ();
473   errorCount += testExternalPut ();
474   free (put_buffer);
475   if (errorCount != 0)
476     fprintf (stderr, "Error (code: %u)\n", errorCount);
477   curl_global_cleanup ();
478   return errorCount != 0;       /* 0 == pass */
479 }
480