1 /*
2      This file is part of libmicrohttpd
3      Copyright (C) 2007, 2009, 2011, 2015 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 3, 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_concurrent_stop.c
23  * @brief test stopping server while concurrent GETs are ongoing
24  * @author Christian Grothoff
25  */
26 #include "MHD_config.h"
27 #include "platform.h"
28 #include <curl/curl.h>
29 #include <microhttpd.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33 #include "gauger.h"
34 
35 #ifdef CPU_COUNT
36 #undef CPU_COUNT
37 #endif
38 #define CPU_COUNT 40
39 
40 
41 /**
42  * How many rounds of operations do we do for each
43  * test (total number of requests will be ROUNDS * PAR).
44  */
45 #define ROUNDS 50000
46 
47 /**
48  * How many requests do we do in parallel?
49  */
50 #define PAR CPU_COUNT
51 
52 /**
53  * Do we use HTTP 1.1?
54  */
55 static int oneone;
56 
57 /**
58  * Response to return (re-used).
59  */
60 static struct MHD_Response *response;
61 
62 
63 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)64 copyBuffer (void *ptr,
65 	    size_t size, size_t nmemb,
66 	    void *ctx)
67 {
68   return size * nmemb;
69 }
70 
71 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)72 ahc_echo (void *cls,
73           struct MHD_Connection *connection,
74           const char *url,
75           const char *method,
76           const char *version,
77           const char *upload_data, size_t *upload_data_size,
78           void **unused)
79 {
80   static int ptr;
81   const char *me = cls;
82   int ret;
83 
84   if (0 != strcmp (me, method))
85     return MHD_NO;              /* unexpected method */
86   if (&ptr != *unused)
87     {
88       *unused = &ptr;
89       return MHD_YES;
90     }
91   *unused = NULL;
92   ret = MHD_queue_response (connection,
93                             MHD_HTTP_OK,
94                             response);
95   if (ret == MHD_NO)
96     abort ();
97   return ret;
98 }
99 
100 
101 static pid_t
do_gets(int port)102 do_gets (int port)
103 {
104   pid_t ret;
105   CURL *c;
106   CURLcode errornum;
107   unsigned int i;
108   unsigned int j;
109   pid_t par[PAR];
110   char url[64];
111 
112   sprintf(url, "http://127.0.0.1:%d/hello_world", port);
113 
114   ret = fork ();
115   if (ret == -1) abort ();
116   if (ret != 0)
117     return ret;
118   for (j=0;j<PAR;j++)
119     {
120       par[j] = fork ();
121       if (par[j] == 0)
122 	{
123 	  for (i=0;i<ROUNDS;i++)
124 	    {
125 	      c = curl_easy_init ();
126 	      curl_easy_setopt (c, CURLOPT_URL, url);
127 	      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
128 	      curl_easy_setopt (c, CURLOPT_WRITEDATA, NULL);
129 	      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
130 	      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
131 	      if (oneone)
132 		curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
133 	      else
134 		curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
135 	      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
136 	      /* NOTE: use of CONNECTTIMEOUT without also
137 		 setting NOSIGNAL results in really weird
138 		 crashes on my system! */
139 	      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
140 	      if (CURLE_OK != (errornum = curl_easy_perform (c)))
141 		{
142 		  curl_easy_cleanup (c);
143 		  _exit (1);
144 		}
145 	      curl_easy_cleanup (c);
146 	    }
147 	  _exit (0);
148 	}
149     }
150   for (j=0;j<PAR;j++)
151     waitpid (par[j], NULL, 0);
152   _exit (0);
153 }
154 
155 
156 static void
join_gets(pid_t pid)157 join_gets (pid_t pid)
158 {
159   int status;
160 
161   status = 1;
162   waitpid (pid, &status, 0);
163   if (0 != status)
164     abort ();
165 }
166 
167 
168 static int
testMultithreadedGet(int port,int poll_flag)169 testMultithreadedGet (int port, int poll_flag)
170 {
171   struct MHD_Daemon *d;
172   pid_t p;
173 
174   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG  | poll_flag,
175                         port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
176   if (d == NULL)
177     return 16;
178   p = do_gets (port);
179   sleep (1);
180   MHD_stop_daemon (d);
181   join_gets (p);
182   return 0;
183 }
184 
185 
186 static int
testMultithreadedPoolGet(int port,int poll_flag)187 testMultithreadedPoolGet (int port, int poll_flag)
188 {
189   struct MHD_Daemon *d;
190   pid_t p;
191 
192   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
193                         port,
194                         NULL, NULL,
195                         &ahc_echo, "GET",
196                         MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
197                         MHD_OPTION_END);
198   if (d == NULL)
199     return 16;
200   p = do_gets (port);
201   sleep (1);
202   MHD_stop_daemon (d);
203   join_gets (p);
204   return 0;
205 }
206 
207 
208 int
main(int argc,char * const * argv)209 main (int argc, char *const *argv)
210 {
211   unsigned int errorCount = 0;
212   int port = 1081;
213 
214   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
215     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
216   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
217     return 2;
218   response = MHD_create_response_from_buffer (strlen ("/hello_world"),
219 					      "/hello_world",
220 					      MHD_RESPMEM_MUST_COPY);
221   errorCount += testMultithreadedGet (port++, 0);
222   errorCount += testMultithreadedPoolGet (port++, 0);
223   MHD_destroy_response (response);
224   if (errorCount != 0)
225     fprintf (stderr, "Error (code: %u)\n", errorCount);
226   curl_global_cleanup ();
227   return errorCount != 0;       /* 0 == pass */
228 }
229