• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2      This file is part of libmicrohttpd
3      Copyright (C) 2007, 2009 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 daemontest_get_sendfile.c
23  * @brief  Testcase for libmicrohttpd response from FD
24  * @author Christian Grothoff
25  */
26 
27 #include "MHD_config.h"
28 #include "platform.h"
29 #include "platform_interface.h"
30 #include <curl/curl.h>
31 #include <microhttpd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 #include <sys/types.h>
36 #include <fcntl.h>
37 
38 #ifndef WINDOWS
39 #include <sys/socket.h>
40 #include <unistd.h>
41 #endif
42 
43 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
44 #undef CPU_COUNT
45 #endif
46 #if !defined(CPU_COUNT)
47 #define CPU_COUNT 2
48 #endif
49 
50 #define TESTSTR "This is the content of the test file we are sending using sendfile (if available)"
51 
52 char *sourcefile;
53 
54 static int oneone;
55 
56 struct CBC
57 {
58   char *buf;
59   size_t pos;
60   size_t size;
61 };
62 
63 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)64 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
65 {
66   struct CBC *cbc = ctx;
67 
68   if (cbc->pos + size * nmemb > cbc->size)
69     return 0;                   /* overflow */
70   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
71   cbc->pos += size * nmemb;
72   return size * nmemb;
73 }
74 
75 
76 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)77 ahc_echo (void *cls,
78           struct MHD_Connection *connection,
79           const char *url,
80           const char *method,
81           const char *version,
82           const char *upload_data, size_t *upload_data_size,
83           void **unused)
84 {
85   static int ptr;
86   const char *me = cls;
87   struct MHD_Response *response;
88   int ret;
89   int fd;
90 
91   if (0 != strcmp (me, method))
92     return MHD_NO;              /* unexpected method */
93   if (&ptr != *unused)
94     {
95       *unused = &ptr;
96       return MHD_YES;
97     }
98   *unused = NULL;
99   fd = open (sourcefile, O_RDONLY);
100   if (fd == -1)
101     {
102       fprintf (stderr, "Failed to open `%s': %s\n",
103 	       sourcefile,
104 	       MHD_strerror_ (errno));
105       exit (1);
106     }
107   response = MHD_create_response_from_fd (strlen (TESTSTR), fd);
108   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
109   MHD_destroy_response (response);
110   if (ret == MHD_NO)
111     abort ();
112   return ret;
113 }
114 
115 
116 static int
testInternalGet()117 testInternalGet ()
118 {
119   struct MHD_Daemon *d;
120   CURL *c;
121   char buf[2048];
122   struct CBC cbc;
123   CURLcode errornum;
124 
125   cbc.buf = buf;
126   cbc.size = 2048;
127   cbc.pos = 0;
128   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
129                         11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
130   if (d == NULL)
131     return 1;
132   c = curl_easy_init ();
133   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/");
134   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
135   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
136   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
137   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
138   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
139   if (oneone)
140     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
141   else
142     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
143   /* NOTE: use of CONNECTTIMEOUT without also
144      setting NOSIGNAL results in really weird
145      crashes on my system!*/
146   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
147   if (CURLE_OK != (errornum = curl_easy_perform (c)))
148     {
149       fprintf (stderr,
150                "curl_easy_perform failed: `%s'\n",
151                curl_easy_strerror (errornum));
152       curl_easy_cleanup (c);
153       MHD_stop_daemon (d);
154       return 2;
155     }
156   curl_easy_cleanup (c);
157   MHD_stop_daemon (d);
158   if (cbc.pos != strlen (TESTSTR))
159     return 4;
160   if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
161     return 8;
162   return 0;
163 }
164 
165 static int
testMultithreadedGet()166 testMultithreadedGet ()
167 {
168   struct MHD_Daemon *d;
169   CURL *c;
170   char buf[2048];
171   struct CBC cbc;
172   CURLcode errornum;
173 
174   cbc.buf = buf;
175   cbc.size = 2048;
176   cbc.pos = 0;
177   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
178                         1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
179   if (d == NULL)
180     return 16;
181   c = curl_easy_init ();
182   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/");
183   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
184   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
185   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
186   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
187   if (oneone)
188     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
189   else
190     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
191   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
192   /* NOTE: use of CONNECTTIMEOUT without also
193      setting NOSIGNAL results in really weird
194      crashes on my system! */
195   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
196   if (CURLE_OK != (errornum = curl_easy_perform (c)))
197     {
198       fprintf (stderr,
199                "curl_easy_perform failed: `%s'\n",
200                curl_easy_strerror (errornum));
201       curl_easy_cleanup (c);
202       MHD_stop_daemon (d);
203       return 32;
204     }
205   curl_easy_cleanup (c);
206   MHD_stop_daemon (d);
207   if (cbc.pos != strlen (TESTSTR))
208     return 64;
209   if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
210     return 128;
211   return 0;
212 }
213 
214 static int
testMultithreadedPoolGet()215 testMultithreadedPoolGet ()
216 {
217   struct MHD_Daemon *d;
218   CURL *c;
219   char buf[2048];
220   struct CBC cbc;
221   CURLcode errornum;
222 
223   cbc.buf = buf;
224   cbc.size = 2048;
225   cbc.pos = 0;
226   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
227                         1081, NULL, NULL, &ahc_echo, "GET",
228                         MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
229   if (d == NULL)
230     return 16;
231   c = curl_easy_init ();
232   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/");
233   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
234   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
235   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
236   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
237   if (oneone)
238     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
239   else
240     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
241   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
242   /* NOTE: use of CONNECTTIMEOUT without also
243      setting NOSIGNAL results in really weird
244      crashes on my system!*/
245   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
246   if (CURLE_OK != (errornum = curl_easy_perform (c)))
247     {
248       fprintf (stderr,
249                "curl_easy_perform failed: `%s'\n",
250                curl_easy_strerror (errornum));
251       curl_easy_cleanup (c);
252       MHD_stop_daemon (d);
253       return 32;
254     }
255   curl_easy_cleanup (c);
256   MHD_stop_daemon (d);
257   if (cbc.pos != strlen (TESTSTR))
258     return 64;
259   if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
260     return 128;
261   return 0;
262 }
263 
264 static int
testExternalGet()265 testExternalGet ()
266 {
267   struct MHD_Daemon *d;
268   CURL *c;
269   char buf[2048];
270   struct CBC cbc;
271   CURLM *multi;
272   CURLMcode mret;
273   fd_set rs;
274   fd_set ws;
275   fd_set es;
276   MHD_socket max;
277   int running;
278   struct CURLMsg *msg;
279   time_t start;
280   struct timeval tv;
281 
282   multi = NULL;
283   cbc.buf = buf;
284   cbc.size = 2048;
285   cbc.pos = 0;
286   d = MHD_start_daemon (MHD_USE_DEBUG,
287                         1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
288   if (d == NULL)
289     return 256;
290   c = curl_easy_init ();
291   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/");
292   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
293   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
294   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
295   if (oneone)
296     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
297   else
298     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
299   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
300   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
301   /* NOTE: use of CONNECTTIMEOUT without also
302      setting NOSIGNAL results in really weird
303      crashes on my system! */
304   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
305 
306 
307   multi = curl_multi_init ();
308   if (multi == NULL)
309     {
310       curl_easy_cleanup (c);
311       MHD_stop_daemon (d);
312       return 512;
313     }
314   mret = curl_multi_add_handle (multi, c);
315   if (mret != CURLM_OK)
316     {
317       curl_multi_cleanup (multi);
318       curl_easy_cleanup (c);
319       MHD_stop_daemon (d);
320       return 1024;
321     }
322   start = time (NULL);
323   while ((time (NULL) - start < 5) && (multi != NULL))
324     {
325       max = 0;
326       FD_ZERO (&rs);
327       FD_ZERO (&ws);
328       FD_ZERO (&es);
329       curl_multi_perform (multi, &running);
330       mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
331       if (mret != CURLM_OK)
332         {
333           curl_multi_remove_handle (multi, c);
334           curl_multi_cleanup (multi);
335           curl_easy_cleanup (c);
336           MHD_stop_daemon (d);
337           return 2048;
338         }
339       if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
340         {
341           curl_multi_remove_handle (multi, c);
342           curl_multi_cleanup (multi);
343           curl_easy_cleanup (c);
344           MHD_stop_daemon (d);
345           return 4096;
346         }
347       tv.tv_sec = 0;
348       tv.tv_usec = 1000;
349       select (max + 1, &rs, &ws, &es, &tv);
350       curl_multi_perform (multi, &running);
351       if (running == 0)
352         {
353           msg = curl_multi_info_read (multi, &running);
354           if (msg == NULL)
355             break;
356           if (msg->msg == CURLMSG_DONE)
357             {
358               if (msg->data.result != CURLE_OK)
359                 printf ("%s failed at %s:%d: `%s'\n",
360                         "curl_multi_perform",
361                         __FILE__,
362                         __LINE__, curl_easy_strerror (msg->data.result));
363               curl_multi_remove_handle (multi, c);
364               curl_multi_cleanup (multi);
365               curl_easy_cleanup (c);
366               c = NULL;
367               multi = NULL;
368             }
369         }
370       MHD_run (d);
371     }
372   if (multi != NULL)
373     {
374       curl_multi_remove_handle (multi, c);
375       curl_easy_cleanup (c);
376       curl_multi_cleanup (multi);
377     }
378   MHD_stop_daemon (d);
379   if (cbc.pos != strlen (TESTSTR))
380     return 8192;
381   if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
382     return 16384;
383   return 0;
384 }
385 
386 static int
testUnknownPortGet()387 testUnknownPortGet ()
388 {
389   struct MHD_Daemon *d;
390   const union MHD_DaemonInfo *di;
391   CURL *c;
392   char buf[2048];
393   struct CBC cbc;
394   CURLcode errornum;
395 
396   struct sockaddr_in addr;
397   socklen_t addr_len = sizeof(addr);
398   memset(&addr, 0, sizeof(addr));
399   addr.sin_family = AF_INET;
400   addr.sin_port = 0;
401   addr.sin_addr.s_addr = INADDR_ANY;
402 
403   cbc.buf = buf;
404   cbc.size = 2048;
405   cbc.pos = 0;
406   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
407                         1, NULL, NULL, &ahc_echo, "GET",
408                         MHD_OPTION_SOCK_ADDR, &addr,
409                         MHD_OPTION_END);
410   if (d == NULL)
411     return 32768;
412 
413   di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
414   if (di == NULL)
415     return 65536;
416 
417   if (0 != getsockname(di->listen_fd, (struct sockaddr *) &addr, &addr_len))
418     return 131072;
419 
420   if (addr.sin_family != AF_INET)
421     return 26214;
422 
423   snprintf(buf, sizeof(buf), "http://127.0.0.1:%hu/",
424            ntohs(addr.sin_port));
425 
426   c = curl_easy_init ();
427   curl_easy_setopt (c, CURLOPT_URL, buf);
428   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
429   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
430   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
431   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
432   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
433   if (oneone)
434     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
435   else
436     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
437   /* NOTE: use of CONNECTTIMEOUT without also
438      setting NOSIGNAL results in really weird
439      crashes on my system! */
440   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
441   if (CURLE_OK != (errornum = curl_easy_perform (c)))
442     {
443       fprintf (stderr,
444                "curl_easy_perform failed: `%s'\n",
445                curl_easy_strerror (errornum));
446       curl_easy_cleanup (c);
447       MHD_stop_daemon (d);
448       return 524288;
449     }
450   curl_easy_cleanup (c);
451   MHD_stop_daemon (d);
452   if (cbc.pos != strlen (TESTSTR))
453     return 1048576;
454   if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
455     return 2097152;
456   return 0;
457 }
458 
459 
460 int
main(int argc,char * const * argv)461 main (int argc, char *const *argv)
462 {
463   unsigned int errorCount = 0;
464   const char *tmp;
465   FILE *f;
466 
467   if ( (NULL == (tmp = getenv ("TMPDIR"))) &&
468        (NULL == (tmp = getenv ("TMP"))) &&
469        (NULL == (tmp = getenv ("TEMP"))) )
470     tmp = "/tmp";
471   sourcefile = malloc (strlen (tmp) + 32);
472   sprintf (sourcefile,
473 	   "%s/%s",
474 	   tmp,
475 	   "test-mhd-sendfile");
476   f = fopen (sourcefile, "w");
477   if (NULL == f)
478     {
479       fprintf (stderr, "failed to write test file\n");
480       free (sourcefile);
481       return 1;
482     }
483   fwrite (TESTSTR, strlen (TESTSTR), 1, f);
484   fclose (f);
485   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
486     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
487   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
488     return 2;
489   errorCount += testInternalGet ();
490   errorCount += testMultithreadedGet ();
491   errorCount += testMultithreadedPoolGet ();
492   errorCount += testExternalGet ();
493   errorCount += testUnknownPortGet ();
494   if (errorCount != 0)
495     fprintf (stderr, "Error (code: %u)\n", errorCount);
496   curl_global_cleanup ();
497   unlink (sourcefile);
498   free (sourcefile);
499   return errorCount != 0;       /* 0 == pass */
500 }
501