aboutsummaryrefslogtreecommitdiff
path: root/src/testcurl/test_add_conn.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testcurl/test_add_conn.c')
-rw-r--r--src/testcurl/test_add_conn.c1040
1 files changed, 1040 insertions, 0 deletions
diff --git a/src/testcurl/test_add_conn.c b/src/testcurl/test_add_conn.c
new file mode 100644
index 00000000..52aa2831
--- /dev/null
+++ b/src/testcurl/test_add_conn.c
@@ -0,0 +1,1040 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007, 2009, 2011 Christian Grothoff
4 Copyright (C) 2020 Karlson2k (Evgeny Grin) - large rework, multithreading.
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 * @file test_add_conn.c
23 * @brief Testcase for libmicrohttpd GET operations
24 * @author Christian Grothoff
25 * @author Karlson2k (Evgeny Grin)
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#include "test_helpers.h"
35#include "mhd_sockets.h" /* only macros used */
36
37
38#ifdef _WIN32
39#ifndef WIN32_LEAN_AND_MEAN
40#define WIN32_LEAN_AND_MEAN 1
41#endif /* !WIN32_LEAN_AND_MEAN */
42#include <windows.h>
43#endif
44
45#ifndef WINDOWS
46#include <unistd.h>
47#include <sys/socket.h>
48#endif
49
50#ifdef HAVE_PTHREAD_H
51#include <pthread.h>
52#endif /* HAVE_PTHREAD_H */
53
54#if defined(CPU_COUNT) && (CPU_COUNT + 0) < 2
55#undef CPU_COUNT
56#endif
57#if ! defined(CPU_COUNT)
58#define CPU_COUNT 2
59#endif
60
61/* Could be increased to facilitate debugging */
62#define TIMEOUTS_VAL 5
63
64#define EXPECTED_URI_BASE_PATH "/hello_world"
65#define EXPECTED_URI_QUERY "a=%26&b=c"
66#define EXPECTED_URI_FULL_PATH EXPECTED_URI_BASE_PATH "?" EXPECTED_URI_QUERY
67
68static int oneone;
69static int no_listen;
70static int global_port;
71
72struct CBC
73{
74 char *buf;
75 size_t pos;
76 size_t size;
77};
78
79
80static size_t
81copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
82{
83 struct CBC *cbc = ctx;
84
85 if (cbc->pos + size * nmemb > cbc->size)
86 return 0; /* overflow */
87 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
88 cbc->pos += size * nmemb;
89 return size * nmemb;
90}
91
92
93static void *
94log_cb (void *cls,
95 const char *uri,
96 struct MHD_Connection *con)
97{
98 (void) cls;
99 (void) con;
100 if (0 != strcmp (uri,
101 EXPECTED_URI_FULL_PATH))
102 {
103 fprintf (stderr,
104 "Wrong URI: `%s'\n",
105 uri);
106 _exit (22);
107 }
108 return NULL;
109}
110
111
112static enum MHD_Result
113ahc_echo (void *cls,
114 struct MHD_Connection *connection,
115 const char *url,
116 const char *method,
117 const char *version,
118 const char *upload_data, size_t *upload_data_size,
119 void **unused)
120{
121 static int ptr;
122 const char *me = cls;
123 struct MHD_Response *response;
124 enum MHD_Result ret;
125 const char *v;
126 (void) version;
127 (void) upload_data;
128 (void) upload_data_size; /* Unused. Silence compiler warning. */
129
130 if (0 != strcasecmp (me, method))
131 return MHD_NO; /* unexpected method */
132 if (&ptr != *unused)
133 {
134 *unused = &ptr;
135 return MHD_YES;
136 }
137 *unused = NULL;
138 v = MHD_lookup_connection_value (connection,
139 MHD_GET_ARGUMENT_KIND,
140 "a");
141 if ( (NULL == v) ||
142 (0 != strcmp ("&",
143 v)) )
144 {
145 fprintf (stderr, "Found while looking for 'a=&': 'a=%s'\n",
146 NULL == v ? "NULL" : v);
147 _exit (17);
148 }
149 v = NULL;
150 if (MHD_YES != MHD_lookup_connection_value_n (connection,
151 MHD_GET_ARGUMENT_KIND,
152 "b",
153 1,
154 &v,
155 NULL))
156 {
157 fprintf (stderr, "Not found 'b' GET argument.\n");
158 _exit (18);
159 }
160 if ( (NULL == v) ||
161 (0 != strcmp ("c",
162 v)) )
163 {
164 fprintf (stderr, "Found while looking for 'b=c': 'b=%s'\n",
165 NULL == v ? "NULL" : v);
166 _exit (19);
167 }
168 response = MHD_create_response_from_buffer (strlen (url),
169 (void *) url,
170 MHD_RESPMEM_MUST_COPY);
171 ret = MHD_queue_response (connection,
172 MHD_HTTP_OK,
173 response);
174 MHD_destroy_response (response);
175 if (ret == MHD_NO)
176 {
177 fprintf (stderr, "Failed to queue response.\n");
178 _exit (19);
179 }
180 return ret;
181}
182
183
184static void
185_externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
186{
187 if ((NULL != errDesc) && (0 != errDesc[0]))
188 fprintf (stderr, "%s", errDesc);
189 else
190 fprintf (stderr, "System or external library call failed");
191 if ((NULL != funcName) && (0 != funcName[0]))
192 fprintf (stderr, " in %s", funcName);
193 if (0 < lineNum)
194 fprintf (stderr, " at line %d", lineNum);
195
196 fprintf (stderr, ".\nLast errno value: %d\n", (int) errno);
197#ifdef MHD_WINSOCK_SOCKETS
198 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
199#endif /* MHD_WINSOCK_SOCKETS */
200 fflush (stderr);
201 _exit (99);
202}
203
204
205#if defined(HAVE___FUNC__)
206#define externalErrorExit(ignore) \
207 _externalErrorExit_func(NULL, __func__, __LINE__)
208#define externalErrorExitDesc(errDesc) \
209 _externalErrorExit_func(errDesc, __func__, __LINE__)
210#elif defined(HAVE___FUNCTION__)
211#define externalErrorExit(ignore) \
212 _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
213#define externalErrorExitDesc(errDesc) \
214 _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
215#else
216#define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
217#define externalErrorExitDesc(errDesc) \
218 _externalErrorExit_func(errDesc, NULL, __LINE__)
219#endif
220
221
222/* Static const value, indicates that result value was not set yet */
223static const int eMarker = 0xCE;
224
225
226static MHD_socket
227createListeningSocket (int *pport)
228{
229 MHD_socket skt;
230 static const int on = 1;
231 struct sockaddr_in sin;
232 socklen_t sin_len;
233
234 skt = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
235 if (MHD_INVALID_SOCKET == skt)
236 externalErrorExitDesc ("socket() failed");
237
238#ifdef MHD_POSIX_SOCKETS
239 setsockopt (skt, SOL_SOCKET, SO_REUSEADDR, (void*) &on, sizeof (on));
240 /* Ignore possible error */
241#endif /* MHD_POSIX_SOCKETS */
242
243 memset (&sin, 0, sizeof(sin));
244 sin.sin_family = AF_INET;
245 sin.sin_port = htons (*pport);
246 sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
247 if (0 != bind (skt, (struct sockaddr*) &sin, sizeof(sin)))
248 externalErrorExitDesc ("bind() failed");
249
250 if (0 != listen (skt, SOMAXCONN))
251 externalErrorExitDesc ("listen() failed");
252
253 if (0 == *pport)
254 {
255 memset (&sin, 0, sizeof(sin));
256 sin_len = sizeof(sin);
257 if (0 != getsockname (skt, (struct sockaddr *) &sin, &sin_len))
258 externalErrorExitDesc ("getsockname() failed");
259
260 if (sizeof(sin) < sin_len)
261 externalErrorExitDesc ("getsockname() failed");
262
263 if (AF_INET != sin.sin_family)
264 externalErrorExitDesc ("getsockname() returned wrong socket family");
265
266 *pport = (int) ntohs (sin.sin_port);
267 }
268
269 return skt;
270}
271
272
273static MHD_socket
274acceptTimeLimited (MHD_socket lstn_sk, struct sockaddr *paddr,
275 socklen_t *paddr_len)
276{
277 fd_set rs;
278 struct timeval timeoutval;
279 MHD_socket accepted;
280
281 FD_ZERO (&rs);
282 FD_SET (lstn_sk, &rs);
283 timeoutval.tv_sec = TIMEOUTS_VAL;
284 timeoutval.tv_usec = 0;
285 if (1 != select (((int) lstn_sk) + 1, &rs, NULL, NULL, &timeoutval))
286 externalErrorExitDesc ("select() failed");
287
288 accepted = accept (lstn_sk, paddr, paddr_len);
289 if (MHD_INVALID_SOCKET == accepted)
290 externalErrorExitDesc ("accept() failed");
291
292 return accepted;
293}
294
295
296struct addConnParam
297{
298 struct MHD_Daemon *d;
299
300 MHD_socket lstn_sk;
301
302 /* Non-zero indicate error */
303 volatile int result;
304
305#ifdef HAVE_PTHREAD_H
306 pthread_t addConnThread;
307#endif /* HAVE_PTHREAD_H */
308};
309
310static int
311doAcceptAndAddConnInThread (struct addConnParam *p)
312{
313 MHD_socket newConn;
314 struct sockaddr addr;
315 socklen_t addr_len = sizeof(addr);
316
317 newConn = acceptTimeLimited (p->lstn_sk, &addr, &addr_len);
318
319 p->result = (MHD_YES == MHD_add_connection (p->d, newConn, &addr, addr_len)) ?
320 0 : 1;
321 if (p->result)
322 fprintf (stderr, "MHD_add_connection() failed, errno=%d.\n", errno);
323 return p->result;
324}
325
326
327#ifdef HAVE_PTHREAD_H
328static void *
329doAcceptAndAddConn (void *param)
330{
331 struct addConnParam *p = param;
332
333 (void) doAcceptAndAddConnInThread (p);
334
335 return (void*) p;
336}
337
338
339static void
340startThreadAddConn (struct addConnParam *param)
341{
342 /* thread must reset this value to zero if succeed */
343 param->result = eMarker;
344
345 if (0 != pthread_create (&param->addConnThread, NULL, &doAcceptAndAddConn,
346 (void*) param))
347 externalErrorExitDesc ("pthread_create() failed");
348}
349
350
351static int
352finishThreadAddConn (struct addConnParam *param)
353{
354 struct addConnParam *result;
355
356 if (0 != pthread_join (param->addConnThread, (void**) &result))
357 externalErrorExitDesc ("pthread_join() failed");
358
359 if (param != result)
360 abort (); /* Test used in a wrong way */
361
362 if (eMarker == param->result)
363 abort (); /* Test used in a wrong way */
364
365 return result->result;
366}
367
368
369#endif /* HAVE_PTHREAD_H */
370
371
372struct curlQueryParams
373{
374 /* Destination path for CURL query */
375 const char *queryPath;
376
377 /* Destination port for CURL query */
378 int queryPort;
379
380 /* CURL query result error flag */
381 volatile int queryError;
382
383#ifdef HAVE_PTHREAD_H
384 pthread_t queryThread;
385#endif /* HAVE_PTHREAD_H */
386};
387
388static CURL *
389curlEasyInitForTest (const char *queryPath, int port, struct CBC *pcbc)
390{
391 CURL *c;
392
393 c = curl_easy_init ();
394 if (NULL == c)
395 {
396 fprintf (stderr, "curl_easy_init() failed.\n");
397 _exit (99);
398 }
399 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
400 curl_easy_setopt (c, CURLOPT_URL, queryPath);
401 curl_easy_setopt (c, CURLOPT_PORT, (long) port);
402 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
403 curl_easy_setopt (c, CURLOPT_WRITEDATA, pcbc);
404 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, (long) TIMEOUTS_VAL);
405 curl_easy_setopt (c, CURLOPT_TIMEOUT, (long) TIMEOUTS_VAL);
406 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
407 if (oneone)
408 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
409 else
410 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
411
412 return c;
413}
414
415
416static int
417doCurlQueryInThread (struct curlQueryParams *p)
418{
419 CURL *c;
420 char buf[2048];
421 struct CBC cbc;
422 CURLcode errornum;
423
424 if (NULL == p->queryPath)
425 abort ();
426
427 if (0 == p->queryPort)
428 abort ();
429
430 cbc.buf = buf;
431 cbc.size = sizeof(buf);
432 cbc.pos = 0;
433
434 c = curlEasyInitForTest (p->queryPath, p->queryPort, &cbc);
435
436 errornum = curl_easy_perform (c);
437 if (CURLE_OK != errornum)
438 {
439 fprintf (stderr,
440 "curl_easy_perform failed: `%s'\n",
441 curl_easy_strerror (errornum));
442 p->queryError = 2;
443 }
444 else
445 {
446 if (cbc.pos != strlen (EXPECTED_URI_BASE_PATH))
447 {
448 fprintf (stderr, "curl reports wrong size of MHD reply body data.\n");
449 p->queryError = 4;
450 }
451 else if (0 != strncmp (EXPECTED_URI_BASE_PATH, cbc.buf,
452 strlen (EXPECTED_URI_BASE_PATH)))
453 {
454 fprintf (stderr, "curl reports wrong MHD reply body data.\n");
455 p->queryError = 4;
456 }
457 else
458 p->queryError = 0;
459 }
460 curl_easy_cleanup (c);
461
462 return p->queryError;
463}
464
465
466#ifdef HAVE_PTHREAD_H
467static void *
468doCurlQuery (void *param)
469{
470 struct curlQueryParams *p = (struct curlQueryParams*) param;
471
472 (void) doCurlQueryInThread (p);
473
474 return param;
475}
476
477
478static void
479startThreadCurlQuery (struct curlQueryParams *param)
480{
481 /* thread must reset this value to zero if succeed */
482 param->queryError = eMarker;
483
484 if (0 != pthread_create (&param->queryThread, NULL, &doCurlQuery,
485 (void*) param))
486 externalErrorExitDesc ("pthread_create() failed");
487}
488
489
490static int
491finishThreadCurlQuery (struct curlQueryParams *param)
492{
493 struct curlQueryParams *result;
494
495 if (0 != pthread_join (param->queryThread, (void**) &result))
496 externalErrorExitDesc ("pthread_join() failed");
497
498 if (param != result)
499 abort (); /* Test used in wrong way */
500
501 if (eMarker == param->queryError)
502 abort (); /* Test used in wrong way */
503
504 return result->queryError;
505}
506
507
508/* Perform test queries and shut down MHD daemon */
509static int
510performTestQueries (struct MHD_Daemon *d, int d_port)
511{
512 struct curlQueryParams qParam;
513 struct addConnParam aParam;
514 int a_port = 0; /* Additional listening socket port */
515 int ret = 0; /* Return value */
516
517 qParam.queryPath = "http://127.0.0.1" EXPECTED_URI_FULL_PATH;
518 qParam.queryPort = 0; /* autoassign */
519
520 aParam.d = d;
521 aParam.lstn_sk = createListeningSocket (&a_port); /* Sets a_port */
522
523 /* Test of adding connection in the same thread */
524 qParam.queryError = eMarker; /* to be zeroed in new thread */
525 qParam.queryPort = a_port; /* Connect to additional socket */
526 startThreadCurlQuery (&qParam);
527 ret |= doAcceptAndAddConnInThread (&aParam);
528 ret |= finishThreadCurlQuery (&qParam);
529
530 if (! no_listen)
531 {
532 /* Test of the daemon itself can accept and process new connection. */
533 ret <<= 3; /* Remember errors for each step */
534 qParam.queryPort = d_port; /* Connect to the daemon */
535 ret |= doCurlQueryInThread (&qParam);
536 }
537
538 /* Test of adding connection in an external thread */
539 ret <<= 3; /* Remember errors for each step */
540 aParam.result = eMarker; /* to be zeroed in new thread */
541 qParam.queryPort = a_port; /* Connect to the daemon */
542 startThreadAddConn (&aParam);
543 ret |= doCurlQueryInThread (&qParam);
544 ret |= finishThreadAddConn (&aParam);
545
546 (void) MHD_socket_close_ (aParam.lstn_sk);
547 MHD_stop_daemon (d);
548
549 return ret;
550}
551
552
553#endif /* HAVE_PTHREAD_H */
554
555enum testMhdThreadsType
556{
557 testMhdThreadExternal = 0,
558 testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD,
559 testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION
560 | MHD_USE_INTERNAL_POLLING_THREAD,
561 testMhdThreadInternalPool
562};
563
564enum testMhdPollType
565{
566 testMhdPollBySelect = 0,
567 testMhdPollByPoll = MHD_USE_POLL,
568 testMhdPollByEpoll = MHD_USE_EPOLL,
569 testMhdPollAuto = MHD_USE_AUTO
570};
571
572static struct MHD_Daemon *
573startTestMhdDaemon (enum testMhdThreadsType thrType,
574 enum testMhdPollType pollType, int *pport)
575{
576 struct MHD_Daemon *d;
577 const union MHD_DaemonInfo *dinfo;
578
579 if ( (0 == *pport) &&
580 (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
581 *pport = oneone ? 1550 : 1570;
582
583 if (testMhdThreadInternalPool != thrType)
584 d = MHD_start_daemon (((int) thrType) | ((int) pollType)
585 | (thrType == testMhdThreadExternal ?
586 0 : MHD_USE_ITC)
587 | (no_listen ? MHD_USE_NO_LISTEN_SOCKET : 0)
588 | MHD_USE_ERROR_LOG,
589 *pport, NULL, NULL,
590 &ahc_echo, "GET",
591 MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
592 MHD_OPTION_END);
593 else
594 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | ((int) pollType)
595 | MHD_USE_ITC
596 | (no_listen ? MHD_USE_NO_LISTEN_SOCKET : 0)
597 | MHD_USE_ERROR_LOG,
598 *pport, NULL, NULL,
599 &ahc_echo, "GET",
600 MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
601 MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
602 MHD_OPTION_END);
603
604 if (NULL == d)
605 {
606 fprintf (stderr, "Failed to start MHD daemon, errno=%d.\n", errno);
607 abort ();
608 }
609
610 if ((! no_listen) && (0 == *pport))
611 {
612 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
613 if ((NULL == dinfo) || (0 == dinfo->port) )
614 {
615 fprintf (stderr, "MHD_get_daemon_info() failed.\n");
616 abort ();
617 }
618 *pport = (int) dinfo->port;
619 }
620
621 return d;
622}
623
624
625/* Test runners */
626
627
628static int
629testExternalGet (void)
630{
631 struct MHD_Daemon *d;
632 CURL *c_d;
633 char buf_d[2048];
634 struct CBC cbc_d;
635 CURL *c_a;
636 char buf_a[2048];
637 struct CBC cbc_a;
638 CURLM *multi;
639 time_t start;
640 struct timeval tv;
641 int d_port = global_port; /* Daemon's port */
642 int a_port = 0; /* Additional listening socket port */
643 struct addConnParam aParam;
644 int ret = 0; /* Return value of the test */
645
646 d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port);
647
648 aParam.d = d;
649 aParam.lstn_sk = createListeningSocket (&a_port);
650
651 multi = NULL;
652 cbc_d.buf = buf_d;
653 cbc_d.size = sizeof(buf_d);
654 cbc_d.pos = 0;
655 cbc_a.buf = buf_a;
656 cbc_a.size = sizeof(buf_a);
657 cbc_a.pos = 0;
658
659 if (! no_listen)
660 c_d = curlEasyInitForTest ("http://127.0.0.1" EXPECTED_URI_FULL_PATH,
661 d_port, &cbc_d);
662
663 c_a = curlEasyInitForTest ("http://127.0.0.1" EXPECTED_URI_FULL_PATH,
664 a_port, &cbc_a);
665
666 multi = curl_multi_init ();
667 if (multi == NULL)
668 {
669 fprintf (stderr, "curl_multi_init() failed.\n");
670 _exit (99);
671 }
672 if (! no_listen)
673 {
674 if (CURLM_OK != curl_multi_add_handle (multi, c_d))
675 {
676 fprintf (stderr, "curl_multi_add_handle() failed.\n");
677 _exit (99);
678 }
679 }
680
681 if (CURLM_OK != curl_multi_add_handle (multi, c_a))
682 {
683 fprintf (stderr, "curl_multi_add_handle() failed.\n");
684 _exit (99);
685 }
686
687 start = time (NULL);
688 while (time (NULL) - start <= TIMEOUTS_VAL)
689 {
690 fd_set rs;
691 fd_set ws;
692 fd_set es;
693 MHD_socket maxMhdSk;
694 int maxCurlSk;
695 int running;
696
697 maxMhdSk = MHD_INVALID_SOCKET;
698 maxCurlSk = -1;
699 FD_ZERO (&rs);
700 FD_ZERO (&ws);
701 FD_ZERO (&es);
702 curl_multi_perform (multi, &running);
703 if (0 == running)
704 {
705 struct CURLMsg *msg;
706 int msgLeft;
707 int totalMsgs = 0;
708 do
709 {
710 msg = curl_multi_info_read (multi, &msgLeft);
711 if (NULL == msg)
712 {
713 fprintf (stderr, "curl_multi_info_read failed, NULL returned.\n");
714 _exit (99);
715 }
716 totalMsgs++;
717 if (CURLMSG_DONE == msg->msg)
718 {
719 if (CURLE_OK != msg->data.result)
720 {
721 fprintf (stderr, "curl_multi_info_read failed, error: '%s'\n",
722 curl_easy_strerror (msg->data.result));
723 ret |= 2;
724 }
725 }
726 } while (msgLeft > 0);
727 if ((no_listen ? 1 : 2) != totalMsgs)
728 {
729 fprintf (stderr,
730 "curl_multi_info_read returned wrong "
731 "number of results (%d).\n",
732 totalMsgs);
733 _exit (99);
734 }
735 break; /* All transfers have finished. */
736 }
737 if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
738 {
739 fprintf (stderr, "curl_multi_fdset() failed.\n");
740 _exit (99);
741 }
742 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
743 {
744 ret |= 8;
745 break;
746 }
747 FD_SET (aParam.lstn_sk, &rs);
748 if (maxMhdSk < aParam.lstn_sk)
749 maxMhdSk = aParam.lstn_sk;
750 tv.tv_sec = 0;
751 tv.tv_usec = 1000;
752#ifdef MHD_POSIX_SOCKETS
753 if (maxMhdSk > maxCurlSk)
754 maxCurlSk = maxMhdSk;
755#endif /* MHD_POSIX_SOCKETS */
756 if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
757 {
758#ifdef MHD_POSIX_SOCKETS
759 if (EINTR != errno)
760 externalErrorExitDesc ("select() failed");
761#else
762 if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
763 ws.
764 fd_count)
765 || (0 != es.fd_count) )
766 externalErrorExitDesc ("select() failed");
767 Sleep (1000);
768#endif
769 }
770 if (FD_ISSET (aParam.lstn_sk, &rs))
771 ret |= doAcceptAndAddConnInThread (&aParam);
772
773 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
774 {
775 fprintf (stderr, "MHD_run_from_select() failed.\n");
776 ret |= 1;
777 break;
778 }
779 }
780
781 MHD_stop_daemon (d);
782 (void) MHD_socket_close_ (aParam.lstn_sk);
783
784 if (! no_listen)
785 {
786 curl_multi_remove_handle (multi, c_d);
787 curl_easy_cleanup (c_d);
788 if (cbc_d.pos != strlen ("/hello_world"))
789 {
790 fprintf (stderr,
791 "curl reports wrong size of MHD reply body data at line %d.\n",
792 __LINE__);
793 ret |= 4;
794 }
795 if (0 != strncmp ("/hello_world", cbc_d.buf, strlen ("/hello_world")))
796 {
797 fprintf (stderr, "curl reports wrong MHD reply body data at line %d.\n",
798 __LINE__);
799 ret |= 4;
800 }
801 }
802 curl_multi_remove_handle (multi, c_a);
803 curl_easy_cleanup (c_a);
804 curl_multi_cleanup (multi);
805 if (cbc_a.pos != strlen ("/hello_world"))
806 {
807 fprintf (stderr,
808 "curl reports wrong size of MHD reply body data at line %d.\n",
809 __LINE__);
810 ret |= 4;
811 }
812 if (0 != strncmp ("/hello_world", cbc_a.buf, strlen ("/hello_world")))
813 {
814 fprintf (stderr, "curl reports wrong MHD reply body data at line %d.\n",
815 __LINE__);
816 ret |= 4;
817 }
818 return ret;
819}
820
821
822#ifdef HAVE_PTHREAD_H
823static int
824testInternalGet (enum testMhdPollType pollType)
825{
826 struct MHD_Daemon *d;
827 int d_port = global_port; /* Daemon's port */
828
829 d = startTestMhdDaemon (testMhdThreadInternal, pollType,
830 &d_port);
831
832 return performTestQueries (d, d_port);
833}
834
835
836static int
837testMultithreadedGet (enum testMhdPollType pollType)
838{
839 struct MHD_Daemon *d;
840 int d_port = global_port; /* Daemon's port */
841
842 d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType,
843 &d_port);
844
845 return performTestQueries (d, d_port);
846}
847
848
849static int
850testMultithreadedPoolGet (enum testMhdPollType pollType)
851{
852 struct MHD_Daemon *d;
853 int d_port = global_port; /* Daemon's port */
854
855 d = startTestMhdDaemon (testMhdThreadInternalPool, pollType,
856 &d_port);
857
858 return performTestQueries (d, d_port);
859}
860
861
862static int
863testStopRace (enum testMhdPollType pollType)
864{
865 struct MHD_Daemon *d;
866 int d_port = global_port; /* Daemon's port */
867 int a_port = 0; /* Additional listening socket port */
868 struct sockaddr_in sin;
869 MHD_socket fd1;
870 MHD_socket fd2;
871 struct addConnParam aParam;
872 int ret = 0; /* Return value of the test */
873
874 d = startTestMhdDaemon (testMhdThreadInternal, pollType,
875 &d_port);
876
877 if (! no_listen)
878 {
879 fd1 = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
880 if (MHD_INVALID_SOCKET == fd1)
881 externalErrorExitDesc ("socket() failed");
882
883 memset (&sin, 0, sizeof(sin));
884 sin.sin_family = AF_INET;
885 sin.sin_port = htons (d_port);
886 sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
887 if (connect (fd1, (struct sockaddr *) (&sin), sizeof(sin)) < 0)
888 externalErrorExitDesc ("socket() failed");
889 }
890
891 aParam.d = d;
892 aParam.lstn_sk = createListeningSocket (&a_port); /* Sets a_port */
893 startThreadAddConn (&aParam);
894
895 fd2 = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
896 if (MHD_INVALID_SOCKET == fd2)
897 externalErrorExitDesc ("socket() failed");
898 memset (&sin, 0, sizeof(sin));
899 sin.sin_family = AF_INET;
900 sin.sin_port = htons (a_port);
901 sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
902 if (connect (fd2, (struct sockaddr *) (&sin), sizeof(sin)) < 0)
903 externalErrorExitDesc ("socket() failed");
904 ret |= finishThreadAddConn (&aParam);
905
906 /* Let the thread get going. */
907 usleep (500000);
908
909 MHD_stop_daemon (d);
910
911 if (! no_listen)
912 (void) MHD_socket_close_ (fd1);
913 (void) MHD_socket_close_ (aParam.lstn_sk);
914 (void) MHD_socket_close_ (fd2);
915
916 return ret;
917}
918
919
920#endif /* HAVE_PTHREAD_H */
921
922
923int
924main (int argc, char *const *argv)
925{
926 unsigned int errorCount = 0;
927 unsigned int test_result = 0;
928 int verbose = 0;
929
930 if ((NULL == argv) || (0 == argv[0]))
931 return 99;
932 oneone = has_in_name (argv[0], "11");
933 no_listen = has_in_name (argv[0], "_nolisten");
934 verbose = ! has_param (argc, argv, "-q") || has_param (argc, argv, "--quiet");
935 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
936 return 2;
937 /* Could be set to non-zero value to enforce using specific port
938 * in the test */
939 global_port = 0;
940 test_result = testExternalGet ();
941 if (test_result)
942 fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result);
943 else if (verbose)
944 printf ("PASSED: testExternalGet ().\n");
945 errorCount += test_result;
946#ifdef HAVE_PTHREAD_H
947 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
948 {
949 test_result = testInternalGet (testMhdPollBySelect);
950 if (test_result)
951 fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect) - %u.\n",
952 test_result);
953 else if (verbose)
954 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
955 errorCount += test_result;
956 test_result = testMultithreadedGet (testMhdPollBySelect);
957 if (test_result)
958 fprintf (stderr,
959 "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n",
960 test_result);
961 else if (verbose)
962 printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
963 errorCount += test_result;
964 test_result = testMultithreadedPoolGet (testMhdPollBySelect);
965 if (test_result)
966 fprintf (stderr,
967 "FAILED: testMultithreadedPoolGet (testMhdPollBySelect) - %u.\n",
968 test_result);
969 else if (verbose)
970 printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
971 errorCount += test_result;
972 test_result = testStopRace (testMhdPollBySelect);
973 if (test_result)
974 fprintf (stderr, "FAILED: testStopRace (testMhdPollBySelect) - %u.\n",
975 test_result);
976 else if (verbose)
977 printf ("PASSED: testStopRace (testMhdPollBySelect).\n");
978 errorCount += test_result;
979 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
980 {
981 test_result = testInternalGet (testMhdPollByPoll);
982 if (test_result)
983 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n",
984 test_result);
985 else if (verbose)
986 printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
987 errorCount += test_result;
988 test_result = testMultithreadedGet (testMhdPollByPoll);
989 if (test_result)
990 fprintf (stderr,
991 "FAILED: testMultithreadedGet (testMhdPollByPoll) - %u.\n",
992 test_result);
993 else if (verbose)
994 printf ("PASSED: testMultithreadedGet (testMhdPollByPoll).\n");
995 errorCount += test_result;
996 test_result = testMultithreadedPoolGet (testMhdPollByPoll);
997 if (test_result)
998 fprintf (stderr,
999 "FAILED: testMultithreadedPoolGet (testMhdPollByPoll) - %u.\n",
1000 test_result);
1001 else if (verbose)
1002 printf ("PASSED: testMultithreadedPoolGet (testMhdPollByPoll).\n");
1003 errorCount += test_result;
1004 test_result = testStopRace (testMhdPollByPoll);
1005 if (test_result)
1006 fprintf (stderr, "FAILED: testStopRace (testMhdPollByPoll) - %u.\n",
1007 test_result);
1008 else if (verbose)
1009 printf ("PASSED: testStopRace (testMhdPollByPoll).\n");
1010 errorCount += test_result;
1011 }
1012 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
1013 {
1014 test_result = testInternalGet (testMhdPollByEpoll);
1015 if (test_result)
1016 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll) - %u.\n",
1017 test_result);
1018 else if (verbose)
1019 printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n");
1020 errorCount += test_result;
1021 test_result = testMultithreadedPoolGet (testMhdPollByEpoll);
1022 if (test_result)
1023 fprintf (stderr,
1024 "FAILED: testMultithreadedPoolGet (testMhdPollByEpoll) - %u.\n",
1025 test_result);
1026 else if (verbose)
1027 printf ("PASSED: testMultithreadedPoolGet (testMhdPollByEpoll).\n");
1028 errorCount += test_result;
1029 }
1030 }
1031#endif /* HAVE_PTHREAD_H */
1032 if (0 != errorCount)
1033 fprintf (stderr,
1034 "Error (code: %u)\n",
1035 errorCount);
1036 else if (verbose)
1037 printf ("All tests passed.\n");
1038 curl_global_cleanup ();
1039 return (errorCount == 0) ? 0 : 1; /* 0 == pass */
1040}