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, ©Buffer);
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, ©Buffer);
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, ©Buffer);
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, ©Buffer);
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