1 /*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007 Christian Grothoff
4 Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
5
6 libmicrohttpd is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 2, or (at your
9 option) any later version.
10
11 libmicrohttpd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with libmicrohttpd; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20 */
21
22 /**
23 * @file daemontest_put.c
24 * @brief Testcase for libmicrohttpd PUT operations
25 * @author Christian Grothoff
26 * @author Karlson2k (Evgeny Grin)
27 */
28
29 #include "MHD_config.h"
30 #include "platform.h"
31 #include <curl/curl.h>
32 #include <microhttpd.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36 #include "mhd_has_in_name.h"
37
38 #ifndef WINDOWS
39 #include <unistd.h>
40 #endif
41
42 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
43 #undef MHD_CPU_COUNT
44 #endif
45 #if ! defined(MHD_CPU_COUNT)
46 #define MHD_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 static size_t
putBuffer(void * stream,size_t size,size_t nmemb,void * ptr)59 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
60 {
61 unsigned int *pos = ptr;
62 unsigned int wrt;
63
64 wrt = size * nmemb;
65 if (wrt > 8 - (*pos))
66 wrt = 8 - (*pos);
67 memcpy (stream, &("Hello123"[*pos]), wrt);
68 (*pos) += wrt;
69 return wrt;
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 static enum MHD_Result
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)87 ahc_echo (void *cls,
88 struct MHD_Connection *connection,
89 const char *url,
90 const char *method,
91 const char *version,
92 const char *upload_data, size_t *upload_data_size,
93 void **unused)
94 {
95 int *done = cls;
96 struct MHD_Response *response;
97 enum MHD_Result ret;
98 (void) version; (void) unused; /* Unused. Silent compiler warning. */
99
100 if (0 != strcmp ("PUT", method))
101 return MHD_NO; /* unexpected method */
102 if ((*done) == 0)
103 {
104 if (*upload_data_size != 8)
105 return MHD_YES; /* not yet ready */
106 if (0 == memcmp (upload_data, "Hello123", 8))
107 {
108 *upload_data_size = 0;
109 }
110 else
111 {
112 printf ("Invalid upload data `%8s'!\n", upload_data);
113 return MHD_NO;
114 }
115 *done = 1;
116 return MHD_YES;
117 }
118 response = MHD_create_response_from_buffer (strlen (url), (void *) url,
119 MHD_RESPMEM_MUST_COPY);
120 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
121 MHD_destroy_response (response);
122 return ret;
123 }
124
125
126 static int
testInternalPut()127 testInternalPut ()
128 {
129 struct MHD_Daemon *d;
130 CURL *c;
131 char buf[2048];
132 struct CBC cbc;
133 unsigned int pos = 0;
134 int done_flag = 0;
135 CURLcode errornum;
136 int port;
137
138 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
139 port = 0;
140 else
141 {
142 port = 1450;
143 if (oneone)
144 port += 10;
145 }
146
147 cbc.buf = buf;
148 cbc.size = 2048;
149 cbc.pos = 0;
150 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
151 port,
152 NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
153 if (d == NULL)
154 return 1;
155 if (0 == port)
156 {
157 const union MHD_DaemonInfo *dinfo;
158 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
159 if ((NULL == dinfo) || (0 == dinfo->port) )
160 {
161 MHD_stop_daemon (d); return 32;
162 }
163 port = (int) dinfo->port;
164 }
165 c = curl_easy_init ();
166 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
167 curl_easy_setopt (c, CURLOPT_PORT, (long) port);
168 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
169 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
170 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
171 curl_easy_setopt (c, CURLOPT_READDATA, &pos);
172 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
173 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
174 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
175 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
176 if (oneone)
177 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
178 else
179 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
180 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
181 /* NOTE: use of CONNECTTIMEOUT without also
182 * setting NOSIGNAL results in really weird
183 * crashes on my system! */
184 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
185 if (CURLE_OK != (errornum = curl_easy_perform (c)))
186 {
187 fprintf (stderr,
188 "curl_easy_perform failed: `%s'\n",
189 curl_easy_strerror (errornum));
190 curl_easy_cleanup (c);
191 MHD_stop_daemon (d);
192 return 2;
193 }
194 curl_easy_cleanup (c);
195 MHD_stop_daemon (d);
196 if (cbc.pos != strlen ("/hello_world"))
197 return 4;
198 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
199 return 8;
200 return 0;
201 }
202
203
204 static int
testMultithreadedPut()205 testMultithreadedPut ()
206 {
207 struct MHD_Daemon *d;
208 CURL *c;
209 char buf[2048];
210 struct CBC cbc;
211 unsigned int pos = 0;
212 int done_flag = 0;
213 CURLcode errornum;
214 int port;
215
216 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
217 port = 0;
218 else
219 {
220 port = 1451;
221 if (oneone)
222 port += 10;
223 }
224
225 cbc.buf = buf;
226 cbc.size = 2048;
227 cbc.pos = 0;
228 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
229 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
230 port,
231 NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
232 if (d == NULL)
233 return 16;
234 if (0 == port)
235 {
236 const union MHD_DaemonInfo *dinfo;
237 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
238 if ((NULL == dinfo) || (0 == dinfo->port) )
239 {
240 MHD_stop_daemon (d); return 32;
241 }
242 port = (int) dinfo->port;
243 }
244 c = curl_easy_init ();
245 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
246 curl_easy_setopt (c, CURLOPT_PORT, (long) port);
247 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
248 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
249 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
250 curl_easy_setopt (c, CURLOPT_READDATA, &pos);
251 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
252 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
253 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
254 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
255 if (oneone)
256 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
257 else
258 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
259 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
260 /* NOTE: use of CONNECTTIMEOUT without also
261 * setting NOSIGNAL results in really weird
262 * crashes on my system! */
263 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
264 if (CURLE_OK != (errornum = curl_easy_perform (c)))
265 {
266 fprintf (stderr,
267 "curl_easy_perform failed: `%s'\n",
268 curl_easy_strerror (errornum));
269 curl_easy_cleanup (c);
270 MHD_stop_daemon (d);
271 return 32;
272 }
273 curl_easy_cleanup (c);
274 MHD_stop_daemon (d);
275 if (cbc.pos != strlen ("/hello_world"))
276 return 64;
277 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
278 return 128;
279
280 return 0;
281 }
282
283
284 static int
testMultithreadedPoolPut()285 testMultithreadedPoolPut ()
286 {
287 struct MHD_Daemon *d;
288 CURL *c;
289 char buf[2048];
290 struct CBC cbc;
291 unsigned int pos = 0;
292 int done_flag = 0;
293 CURLcode errornum;
294 int port;
295
296 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
297 port = 0;
298 else
299 {
300 port = 1452;
301 if (oneone)
302 port += 10;
303 }
304
305 cbc.buf = buf;
306 cbc.size = 2048;
307 cbc.pos = 0;
308 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
309 port,
310 NULL, NULL, &ahc_echo, &done_flag,
311 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
312 MHD_OPTION_END);
313 if (d == NULL)
314 return 16;
315 if (0 == port)
316 {
317 const union MHD_DaemonInfo *dinfo;
318 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
319 if ((NULL == dinfo) || (0 == dinfo->port) )
320 {
321 MHD_stop_daemon (d); return 32;
322 }
323 port = (int) dinfo->port;
324 }
325 c = curl_easy_init ();
326 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
327 curl_easy_setopt (c, CURLOPT_PORT, (long) port);
328 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
329 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
330 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
331 curl_easy_setopt (c, CURLOPT_READDATA, &pos);
332 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
333 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
334 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
335 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
336 if (oneone)
337 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
338 else
339 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
340 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
341 /* NOTE: use of CONNECTTIMEOUT without also
342 * setting NOSIGNAL results in really weird
343 * crashes on my system! */
344 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
345 if (CURLE_OK != (errornum = curl_easy_perform (c)))
346 {
347 fprintf (stderr,
348 "curl_easy_perform failed: `%s'\n",
349 curl_easy_strerror (errornum));
350 curl_easy_cleanup (c);
351 MHD_stop_daemon (d);
352 return 32;
353 }
354 curl_easy_cleanup (c);
355 MHD_stop_daemon (d);
356 if (cbc.pos != strlen ("/hello_world"))
357 return 64;
358 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
359 return 128;
360
361 return 0;
362 }
363
364
365 static int
testExternalPut()366 testExternalPut ()
367 {
368 struct MHD_Daemon *d;
369 CURL *c;
370 char buf[2048];
371 struct CBC cbc;
372 CURLM *multi;
373 CURLMcode mret;
374 fd_set rs;
375 fd_set ws;
376 fd_set es;
377 MHD_socket maxsock;
378 int maxposixs; /* Max socket number unused on W32 */
379 int running;
380 struct CURLMsg *msg;
381 time_t start;
382 struct timeval tv;
383 unsigned int pos = 0;
384 int done_flag = 0;
385 int port;
386
387 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
388 port = 0;
389 else
390 {
391 port = 1453;
392 if (oneone)
393 port += 10;
394 }
395
396 multi = NULL;
397 cbc.buf = buf;
398 cbc.size = 2048;
399 cbc.pos = 0;
400 d = MHD_start_daemon (MHD_USE_ERROR_LOG,
401 port,
402 NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
403 if (d == NULL)
404 return 256;
405 if (0 == port)
406 {
407 const union MHD_DaemonInfo *dinfo;
408 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
409 if ((NULL == dinfo) || (0 == dinfo->port) )
410 {
411 MHD_stop_daemon (d); return 32;
412 }
413 port = (int) dinfo->port;
414 }
415 c = curl_easy_init ();
416 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
417 curl_easy_setopt (c, CURLOPT_PORT, (long) port);
418 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
419 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
420 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
421 curl_easy_setopt (c, CURLOPT_READDATA, &pos);
422 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
423 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
424 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
425 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
426 if (oneone)
427 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
428 else
429 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
430 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
431 /* NOTE: use of CONNECTTIMEOUT without also
432 * setting NOSIGNAL results in really weird
433 * crashes on my system! */
434 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
435
436
437 multi = curl_multi_init ();
438 if (multi == NULL)
439 {
440 curl_easy_cleanup (c);
441 MHD_stop_daemon (d);
442 return 512;
443 }
444 mret = curl_multi_add_handle (multi, c);
445 if (mret != CURLM_OK)
446 {
447 curl_multi_cleanup (multi);
448 curl_easy_cleanup (c);
449 MHD_stop_daemon (d);
450 return 1024;
451 }
452 start = time (NULL);
453 while ((time (NULL) - start < 5) && (multi != NULL))
454 {
455 maxsock = MHD_INVALID_SOCKET;
456 maxposixs = -1;
457 FD_ZERO (&rs);
458 FD_ZERO (&ws);
459 FD_ZERO (&es);
460 curl_multi_perform (multi, &running);
461 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs);
462 if (mret != CURLM_OK)
463 {
464 curl_multi_remove_handle (multi, c);
465 curl_multi_cleanup (multi);
466 curl_easy_cleanup (c);
467 MHD_stop_daemon (d);
468 return 2048;
469 }
470 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock))
471 {
472 curl_multi_remove_handle (multi, c);
473 curl_multi_cleanup (multi);
474 curl_easy_cleanup (c);
475 MHD_stop_daemon (d);
476 return 4096;
477 }
478 #ifdef MHD_POSIX_SOCKETS
479 if (maxsock > maxposixs)
480 maxposixs = maxsock;
481 #endif /* MHD_POSIX_SOCKETS */
482 tv.tv_sec = 0;
483 tv.tv_usec = 1000;
484 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
485 {
486 #ifdef MHD_POSIX_SOCKETS
487 if (EINTR != errno)
488 {
489 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
490 (int) errno, __LINE__);
491 fflush (stderr);
492 exit (99);
493 }
494 #else
495 if ((WSAEINVAL != WSAGetLastError ()) ||
496 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
497 {
498 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
499 (int) WSAGetLastError (), __LINE__);
500 fflush (stderr);
501 exit (99);
502 }
503 Sleep (1);
504 #endif
505 }
506 curl_multi_perform (multi, &running);
507 if (0 == running)
508 {
509 int pending;
510 int curl_fine = 0;
511 while (NULL != (msg = curl_multi_info_read (multi, &pending)))
512 {
513 if (msg->msg == CURLMSG_DONE)
514 {
515 if (msg->data.result == CURLE_OK)
516 curl_fine = 1;
517 else
518 {
519 fprintf (stderr,
520 "%s failed at %s:%d: `%s'\n",
521 "curl_multi_perform",
522 __FILE__,
523 __LINE__, curl_easy_strerror (msg->data.result));
524 abort ();
525 }
526 }
527 }
528 if (! curl_fine)
529 {
530 fprintf (stderr, "libcurl haven't returned OK code\n");
531 abort ();
532 }
533 curl_multi_remove_handle (multi, c);
534 curl_multi_cleanup (multi);
535 curl_easy_cleanup (c);
536 c = NULL;
537 multi = NULL;
538 }
539 MHD_run (d);
540 }
541 if (multi != NULL)
542 {
543 curl_multi_remove_handle (multi, c);
544 curl_easy_cleanup (c);
545 curl_multi_cleanup (multi);
546 }
547 MHD_stop_daemon (d);
548 if (cbc.pos != strlen ("/hello_world"))
549 return 8192;
550 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
551 return 16384;
552 return 0;
553 }
554
555
556 int
main(int argc,char * const * argv)557 main (int argc, char *const *argv)
558 {
559 unsigned int errorCount = 0;
560 (void) argc; /* Unused. Silent compiler warning. */
561
562 if ((NULL == argv) || (0 == argv[0]))
563 return 99;
564 oneone = has_in_name (argv[0], "11");
565 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
566 return 2;
567 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
568 {
569 errorCount += testInternalPut ();
570 errorCount += testMultithreadedPut ();
571 errorCount += testMultithreadedPoolPut ();
572 }
573 errorCount += testExternalPut ();
574 if (errorCount != 0)
575 fprintf (stderr, "Error (code: %u)\n", errorCount);
576 curl_global_cleanup ();
577 return (0 == errorCount) ? 0 : 1; /* 0 == pass */
578 }
579