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 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 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 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 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 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 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 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 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