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