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