• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2      This file is part of libmicrohttpd
3      Copyright (C) 2013, 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 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  * @file test_quiesce.c
22  * @brief  Testcase for libmicrohttpd quiescing
23  * @author Christian Grothoff
24  */
25 
26 #include "MHD_config.h"
27 #include "platform.h"
28 #include "platform_interface.h"
29 #include <curl/curl.h>
30 #include <microhttpd.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
34 #include <sys/types.h>
35 #include <pthread.h>
36 
37 #ifndef WINDOWS
38 #include <unistd.h>
39 #include <sys/socket.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 
50 struct CBC
51 {
52   char *buf;
53   size_t pos;
54   size_t size;
55 };
56 
57 
58 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)59 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
60 {
61   struct CBC *cbc = ctx;
62 
63   if (cbc->pos + size * nmemb > cbc->size)
64     return 0;                   /* overflow */
65   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
66   cbc->pos += size * nmemb;
67   return size * nmemb;
68 }
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   struct MHD_Response *response;
83   int ret;
84 
85   if (0 != strcmp (me, method))
86     return MHD_NO;              /* unexpected method */
87   if (&ptr != *unused)
88     {
89       *unused = &ptr;
90       return MHD_YES;
91     }
92   *unused = NULL;
93   response = MHD_create_response_from_buffer (strlen (url),
94   				      (void *) url,
95 					      MHD_RESPMEM_MUST_COPY);
96   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
97   MHD_destroy_response (response);
98   if (ret == MHD_NO)
99     abort ();
100   return ret;
101 }
102 
103 
104 static void
request_completed(void * cls,struct MHD_Connection * connection,void ** con_cls,enum MHD_RequestTerminationCode code)105 request_completed (void *cls, struct MHD_Connection *connection,
106 		   void **con_cls, enum MHD_RequestTerminationCode code)
107 {
108   int *done = (int *)cls;
109   *done = 1;
110 }
111 
112 
113 static void *
ServeOneRequest(void * param)114 ServeOneRequest(void *param)
115 {
116   struct MHD_Daemon *d;
117   fd_set rs;
118   fd_set ws;
119   fd_set es;
120   MHD_socket fd, max;
121   time_t start;
122   struct timeval tv;
123   int done = 0;
124 
125   fd = (MHD_socket) (intptr_t) param;
126 
127   d = MHD_start_daemon (MHD_USE_DEBUG,
128                         1082, NULL, NULL, &ahc_echo, "GET",
129                         MHD_OPTION_LISTEN_SOCKET, fd,
130                         MHD_OPTION_NOTIFY_COMPLETED, &request_completed, &done,
131                         MHD_OPTION_END);
132   if (d == NULL)
133     return "MHD_start_daemon() failed";
134 
135   start = time (NULL);
136   while ((time (NULL) - start < 5) && done == 0)
137     {
138       max = 0;
139       FD_ZERO (&rs);
140       FD_ZERO (&ws);
141       FD_ZERO (&es);
142       if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
143         {
144           MHD_stop_daemon (d);
145           MHD_socket_close_(fd);
146           return "MHD_get_fdset() failed";
147         }
148       tv.tv_sec = 0;
149       tv.tv_usec = 1000;
150       MHD_SYS_select_ (max + 1, &rs, &ws, &es, &tv);
151       MHD_run (d);
152     }
153   MHD_stop_daemon (d);
154   MHD_socket_close_(fd);
155   return NULL;
156 }
157 
158 
159 static CURL *
setupCURL(void * cbc)160 setupCURL (void *cbc)
161 {
162   CURL *c;
163 
164   c = curl_easy_init ();
165   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/hello_world");
166   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
167   curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc);
168   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
169   curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, 150L);
170   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, 150L);
171   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
172   /* NOTE: use of CONNECTTIMEOUT without also
173      setting NOSIGNAL results in really weird
174      crashes on my system!*/
175   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
176 
177   return c;
178 }
179 
180 
181 static int
testGet(int type,int pool_count,int poll_flag)182 testGet (int type, int pool_count, int poll_flag)
183 {
184   struct MHD_Daemon *d;
185   CURL *c;
186   char buf[2048];
187   struct CBC cbc;
188   CURLcode errornum;
189   MHD_socket fd;
190   pthread_t thrd;
191   const char *thrdRet;
192 
193   cbc.buf = buf;
194   cbc.size = 2048;
195   cbc.pos = 0;
196   if (pool_count > 0) {
197     d = MHD_start_daemon (type | MHD_USE_DEBUG | MHD_USE_PIPE_FOR_SHUTDOWN | poll_flag,
198                           11080, NULL, NULL, &ahc_echo, "GET",
199                           MHD_OPTION_THREAD_POOL_SIZE, pool_count, MHD_OPTION_END);
200 
201   } else {
202     d = MHD_start_daemon (type | MHD_USE_DEBUG | MHD_USE_PIPE_FOR_SHUTDOWN | poll_flag,
203                           11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
204   }
205   if (d == NULL)
206     return 1;
207 
208   c = setupCURL(&cbc);
209 
210   if (CURLE_OK != (errornum = curl_easy_perform (c)))
211     {
212       fprintf (stderr,
213                "curl_easy_perform failed: `%s'\n",
214                curl_easy_strerror (errornum));
215       curl_easy_cleanup (c);
216       MHD_stop_daemon (d);
217       return 2;
218     }
219 
220   if (cbc.pos != strlen ("/hello_world")) {
221     curl_easy_cleanup (c);
222     MHD_stop_daemon (d);
223     return 4;
224   }
225   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) {
226     curl_easy_cleanup (c);
227     MHD_stop_daemon (d);
228     return 8;
229   }
230 
231   fd = MHD_quiesce_daemon (d);
232   if (0 != pthread_create(&thrd, NULL, &ServeOneRequest, (void*)(intptr_t) fd))
233     {
234       fprintf (stderr, "pthread_create failed\n");
235       curl_easy_cleanup (c);
236       MHD_stop_daemon (d);
237       return 16;
238     }
239 
240   cbc.pos = 0;
241   if (CURLE_OK != (errornum = curl_easy_perform (c)))
242     {
243       fprintf (stderr,
244                "curl_easy_perform failed: `%s'\n",
245                curl_easy_strerror (errornum));
246       curl_easy_cleanup (c);
247       MHD_stop_daemon (d);
248       return 2;
249     }
250 
251   if (0 != pthread_join(thrd, (void**)&thrdRet))
252     {
253       fprintf (stderr, "pthread_join failed\n");
254       curl_easy_cleanup (c);
255       MHD_stop_daemon (d);
256       return 16;
257     }
258   if (NULL != thrdRet)
259     {
260       fprintf (stderr, "ServeOneRequest() error: %s\n", thrdRet);
261       curl_easy_cleanup (c);
262       MHD_stop_daemon (d);
263       return 16;
264     }
265 
266   if (cbc.pos != strlen ("/hello_world"))
267     {
268       fprintf(stderr, "%s\n", cbc.buf);
269       curl_easy_cleanup (c);
270       MHD_stop_daemon (d);
271       MHD_socket_close_(fd);
272       return 4;
273     }
274   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
275     {
276       fprintf(stderr, "%s\n", cbc.buf);
277       curl_easy_cleanup (c);
278       MHD_stop_daemon (d);
279       MHD_socket_close_(fd);
280       return 8;
281     }
282 
283   /* at this point, the forked server quit, and the new
284    * server has quiesced, so new requests should fail
285    */
286   if (CURLE_OK == (errornum = curl_easy_perform (c)))
287     {
288       fprintf (stderr, "curl_easy_perform should fail\n");
289       curl_easy_cleanup (c);
290       MHD_stop_daemon (d);
291       MHD_socket_close_(fd);
292       return 2;
293     }
294   curl_easy_cleanup (c);
295   MHD_stop_daemon (d);
296   MHD_socket_close_(fd);
297 
298   return 0;
299 }
300 
301 
302 static int
testExternalGet()303 testExternalGet ()
304 {
305   struct MHD_Daemon *d;
306   CURL *c;
307   char buf[2048];
308   struct CBC cbc;
309   CURLM *multi;
310   CURLMcode mret;
311   fd_set rs;
312   fd_set ws;
313   fd_set es;
314   MHD_socket max;
315   int running;
316   struct CURLMsg *msg;
317   time_t start;
318   struct timeval tv;
319   int i;
320   MHD_socket fd;
321 
322   multi = NULL;
323   cbc.buf = buf;
324   cbc.size = 2048;
325   cbc.pos = 0;
326   d = MHD_start_daemon (MHD_USE_DEBUG,
327                         11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
328   if (d == NULL)
329     return 256;
330   c = setupCURL(&cbc);
331 
332   multi = curl_multi_init ();
333   if (multi == NULL)
334     {
335       curl_easy_cleanup (c);
336       MHD_stop_daemon (d);
337       return 512;
338     }
339   mret = curl_multi_add_handle (multi, c);
340   if (mret != CURLM_OK)
341     {
342       curl_multi_cleanup (multi);
343       curl_easy_cleanup (c);
344       MHD_stop_daemon (d);
345       return 1024;
346     }
347 
348   for (i = 0; i < 2; i++) {
349     start = time (NULL);
350     while ((time (NULL) - start < 5) && (multi != NULL))
351       {
352         max = 0;
353         FD_ZERO (&rs);
354         FD_ZERO (&ws);
355         FD_ZERO (&es);
356         curl_multi_perform (multi, &running);
357         mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
358         if (mret != CURLM_OK)
359           {
360             curl_multi_remove_handle (multi, c);
361             curl_multi_cleanup (multi);
362             curl_easy_cleanup (c);
363             MHD_stop_daemon (d);
364             return 2048;
365           }
366         if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
367           {
368             curl_multi_remove_handle (multi, c);
369             curl_multi_cleanup (multi);
370             curl_easy_cleanup (c);
371             MHD_stop_daemon (d);
372             return 4096;
373           }
374         tv.tv_sec = 0;
375         tv.tv_usec = 1000;
376         select (max + 1, &rs, &ws, &es, &tv);
377         curl_multi_perform (multi, &running);
378         if (running == 0)
379           {
380             msg = curl_multi_info_read (multi, &running);
381             if (msg == NULL)
382               break;
383             if (msg->msg == CURLMSG_DONE)
384               {
385                 if (i == 0 && msg->data.result != CURLE_OK)
386                   printf ("%s failed at %s:%d: `%s'\n",
387                           "curl_multi_perform",
388                           __FILE__,
389                           __LINE__, curl_easy_strerror (msg->data.result));
390                 else if (i == 1 && msg->data.result == CURLE_OK)
391                   printf ("%s should have failed at %s:%d\n",
392                           "curl_multi_perform",
393                           __FILE__,
394                           __LINE__);
395                 curl_multi_remove_handle (multi, c);
396                 curl_multi_cleanup (multi);
397                 curl_easy_cleanup (c);
398                 c = NULL;
399                 multi = NULL;
400               }
401           }
402         MHD_run (d);
403       }
404 
405       if (i == 0) {
406         /* quiesce the daemon on the 1st iteration, so the 2nd should fail */
407         fd = MHD_quiesce_daemon(d);
408 	if (MHD_INVALID_SOCKET == fd)
409 	  abort ();
410 	MHD_socket_close_ (fd);
411         c = setupCURL (&cbc);
412         multi = curl_multi_init ();
413         mret = curl_multi_add_handle (multi, c);
414       }
415     }
416   if (multi != NULL)
417     {
418       curl_multi_remove_handle (multi, c);
419       curl_easy_cleanup (c);
420       curl_multi_cleanup (multi);
421     }
422   MHD_stop_daemon (d);
423   if (cbc.pos != strlen ("/hello_world"))
424     return 8192;
425   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
426     return 16384;
427   return 0;
428 }
429 
430 
431 int
main(int argc,char * const * argv)432 main (int argc, char *const *argv)
433 {
434   unsigned int errorCount = 0;
435 
436   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
437     return 2;
438   errorCount += testGet (MHD_USE_SELECT_INTERNALLY, 0, 0);
439   errorCount += testGet (MHD_USE_THREAD_PER_CONNECTION, 0, 0);
440   errorCount += testGet (MHD_USE_SELECT_INTERNALLY, CPU_COUNT, 0);
441   errorCount += testExternalGet ();
442   if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_POLL))
443     {
444       errorCount += testGet(MHD_USE_SELECT_INTERNALLY, 0, MHD_USE_POLL);
445       errorCount += testGet (MHD_USE_THREAD_PER_CONNECTION, 0, MHD_USE_POLL);
446       errorCount += testGet (MHD_USE_SELECT_INTERNALLY, CPU_COUNT, MHD_USE_POLL);
447     }
448   if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_EPOLL))
449     {
450       errorCount += testGet (MHD_USE_SELECT_INTERNALLY, 0, MHD_USE_EPOLL_LINUX_ONLY);
451       errorCount += testGet (MHD_USE_SELECT_INTERNALLY, CPU_COUNT, MHD_USE_EPOLL_LINUX_ONLY);
452     }
453   if (errorCount != 0)
454     fprintf (stderr, "Error (code: %u)\n", errorCount);
455   curl_global_cleanup ();
456   return errorCount != 0;       /* 0 == pass */
457 }
458