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_post.c
23  * @brief  Testcase for libmicrohttpd POST operations using URL-encoding
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 
40 #include "socat.c"
41 
42 #define POST_DATA "name=daniel&project=curl"
43 
44 static int oneone;
45 
46 struct CBC
47 {
48   char *buf;
49   size_t pos;
50   size_t size;
51 };
52 
53 
54 static void
completed_cb(void * cls,struct MHD_Connection * connection,void ** con_cls,enum MHD_RequestTerminationCode toe)55 completed_cb (void *cls,
56 	      struct MHD_Connection *connection,
57 	      void **con_cls,
58 	      enum MHD_RequestTerminationCode toe)
59 {
60   struct MHD_PostProcessor *pp = *con_cls;
61 
62   if (NULL != pp)
63     MHD_destroy_post_processor (pp);
64   *con_cls = NULL;
65 }
66 
67 
68 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)69 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
70 {
71   struct CBC *cbc = ctx;
72 
73   if (cbc->pos + size * nmemb > cbc->size)
74     return 0;                   /* overflow */
75   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
76   cbc->pos += size * nmemb;
77   return size * nmemb;
78 }
79 
80 
81 /**
82  * Note that this post_iterator is not perfect
83  * in that it fails to support incremental processing.
84  * (to be fixed in the future)
85  */
86 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)87 post_iterator (void *cls,
88                enum MHD_ValueKind kind,
89                const char *key,
90                const char *filename,
91                const char *content_type,
92                const char *transfer_encoding,
93                const char *value, uint64_t off, size_t size)
94 {
95   int *eok = cls;
96 
97   if ((0 == strcmp (key, "name")) &&
98       (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size)))
99     (*eok) |= 1;
100   if ((0 == strcmp (key, "project")) &&
101       (size == strlen ("curl")) && (0 == strncmp (value, "curl", size)))
102     (*eok) |= 2;
103   return MHD_YES;
104 }
105 
106 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)107 ahc_echo (void *cls,
108           struct MHD_Connection *connection,
109           const char *url,
110           const char *method,
111           const char *version,
112           const char *upload_data, size_t *upload_data_size,
113           void **unused)
114 {
115   static int eok;
116   struct MHD_Response *response;
117   struct MHD_PostProcessor *pp;
118   int ret;
119 
120   if (0 != strcmp ("POST", method))
121     {
122       return MHD_NO;            /* unexpected method */
123     }
124   pp = *unused;
125   if (pp == NULL)
126     {
127       eok = 0;
128       pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok);
129       *unused = pp;
130     }
131   MHD_post_process (pp, upload_data, *upload_data_size);
132   if ((eok == 3) && (0 == *upload_data_size))
133     {
134       response = MHD_create_response_from_buffer (strlen (url),
135 						  (void *) url,
136 						  MHD_RESPMEM_MUST_COPY);
137       ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
138       MHD_destroy_response (response);
139       MHD_destroy_post_processor (pp);
140       *unused = NULL;
141       return ret;
142     }
143   *upload_data_size = 0;
144   return MHD_YES;
145 }
146 
147 
148 static int
testInternalPost()149 testInternalPost ()
150 {
151   struct MHD_Daemon *d;
152   CURL *c;
153   char buf[2048];
154   struct CBC cbc;
155   int i;
156 
157   cbc.buf = buf;
158   cbc.size = 2048;
159   cbc.pos = 0;
160   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
161                         11080, NULL, NULL, &ahc_echo, NULL,
162 			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
163 			MHD_OPTION_END);
164   if (d == NULL)
165     return 1;
166   zzuf_socat_start ();
167   for (i = 0; i < LOOP_COUNT; i++)
168     {
169       fprintf (stderr, ".");
170 
171       c = curl_easy_init ();
172       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
173       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
174       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
175       curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
176       curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
177       curl_easy_setopt (c, CURLOPT_POST, 1L);
178       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
179       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
180       if (oneone)
181         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
182       else
183         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
184       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
185       // NOTE: use of CONNECTTIMEOUT without also
186       //   setting NOSIGNAL results in really weird
187       //   crashes on my system!
188       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
189       curl_easy_perform (c);
190       curl_easy_cleanup (c);
191     }
192   fprintf (stderr, "\n");
193   zzuf_socat_stop ();
194   MHD_stop_daemon (d);
195 
196   return 0;
197 }
198 
199 static int
testMultithreadedPost()200 testMultithreadedPost ()
201 {
202   struct MHD_Daemon *d;
203   CURL *c;
204   char buf[2048];
205   struct CBC cbc;
206   int i;
207 
208   cbc.buf = buf;
209   cbc.size = 2048;
210   cbc.pos = 0;
211   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
212                         11080, NULL, NULL, &ahc_echo, NULL,
213 			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
214 			MHD_OPTION_END);
215   if (d == NULL)
216     return 16;
217 
218   zzuf_socat_start ();
219   for (i = 0; i < LOOP_COUNT; i++)
220     {
221       fprintf (stderr, ".");
222 
223       c = curl_easy_init ();
224       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
225       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
226       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
227       curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
228       curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
229       curl_easy_setopt (c, CURLOPT_POST, 1L);
230       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
231       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
232       if (oneone)
233         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
234       else
235         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
236       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
237       // NOTE: use of CONNECTTIMEOUT without also
238       //   setting NOSIGNAL results in really weird
239       //   crashes on my system!
240       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
241       curl_easy_perform (c);
242       curl_easy_cleanup (c);
243     }
244   fprintf (stderr, "\n");
245   zzuf_socat_stop ();
246 
247   MHD_stop_daemon (d);
248   return 0;
249 }
250 
251 
252 static int
testExternalPost()253 testExternalPost ()
254 {
255   struct MHD_Daemon *d;
256   CURL *c;
257   char buf[2048];
258   struct CBC cbc;
259   CURLM *multi;
260   CURLMcode mret;
261   fd_set rs;
262   fd_set ws;
263   fd_set es;
264   int max;
265   int running;
266   time_t start;
267   struct timeval tv;
268   int i;
269 
270   multi = NULL;
271   cbc.buf = buf;
272   cbc.size = 2048;
273   cbc.pos = 0;
274   d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ ,
275                         1082, NULL, NULL, &ahc_echo, NULL,
276 			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
277 			MHD_OPTION_END);
278   if (d == NULL)
279     return 256;
280   multi = curl_multi_init ();
281   if (multi == NULL)
282     {
283       MHD_stop_daemon (d);
284       return 512;
285     }
286 
287   zzuf_socat_start ();
288   for (i = 0; i < LOOP_COUNT; i++)
289     {
290       fprintf (stderr, ".");
291 
292 
293       c = curl_easy_init ();
294       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1082/hello_world");
295       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
296       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
297       curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
298       curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
299       curl_easy_setopt (c, CURLOPT_POST, 1L);
300       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
301       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
302       if (oneone)
303         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
304       else
305         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
306       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
307       // NOTE: use of CONNECTTIMEOUT without also
308       //   setting NOSIGNAL results in really weird
309       //   crashes on my system!
310       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
311 
312 
313       mret = curl_multi_add_handle (multi, c);
314       if (mret != CURLM_OK)
315         {
316           curl_multi_cleanup (multi);
317           curl_easy_cleanup (c);
318           zzuf_socat_stop ();
319           MHD_stop_daemon (d);
320           return 1024;
321         }
322       start = time (NULL);
323       while ((time (NULL) - start < 5) && (c != NULL))
324         {
325           max = 0;
326           FD_ZERO (&rs);
327           FD_ZERO (&ws);
328           FD_ZERO (&es);
329           curl_multi_perform (multi, &running);
330           mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
331           if (mret != CURLM_OK)
332             {
333               curl_multi_remove_handle (multi, c);
334               curl_multi_cleanup (multi);
335               curl_easy_cleanup (c);
336               zzuf_socat_stop ();
337               MHD_stop_daemon (d);
338               return 2048;
339             }
340           if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
341             {
342               curl_multi_remove_handle (multi, c);
343               curl_multi_cleanup (multi);
344               curl_easy_cleanup (c);
345               zzuf_socat_stop ();
346               MHD_stop_daemon (d);
347               return 4096;
348             }
349           tv.tv_sec = 0;
350           tv.tv_usec = 1000;
351           select (max + 1, &rs, &ws, &es, &tv);
352           curl_multi_perform (multi, &running);
353           if (running == 0)
354             {
355               curl_multi_info_read (multi, &running);
356               curl_multi_remove_handle (multi, c);
357               curl_easy_cleanup (c);
358               c = NULL;
359             }
360           MHD_run (d);
361         }
362       if (c != NULL)
363         {
364           curl_multi_remove_handle (multi, c);
365           curl_easy_cleanup (c);
366         }
367 
368     }
369   fprintf (stderr, "\n");
370   curl_multi_cleanup (multi);
371   zzuf_socat_stop ();
372 
373   MHD_stop_daemon (d);
374   return 0;
375 }
376 
377 
378 int
main(int argc,char * const * argv)379 main (int argc, char *const *argv)
380 {
381   unsigned int errorCount = 0;
382 
383   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
384     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
385   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
386     return 2;
387   errorCount += testInternalPost ();
388   errorCount += testMultithreadedPost ();
389   errorCount += testExternalPost ();
390   if (errorCount != 0)
391     fprintf (stderr, "Error (code: %u)\n", errorCount);
392   curl_global_cleanup ();
393   return errorCount != 0;       /* 0 == pass */
394 }
395