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, ©Buffer);
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, ©Buffer);
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, ©Buffer);
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, ©Buffer);
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, ©Buffer);
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