1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2009, 2011 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 test_get.c 23 * @brief Testcase for libmicrohttpd GET operations 24 * TODO: test parsing of query 25 * @author Christian Grothoff 26 */ 27 28 #include "MHD_config.h" 29 #include "platform.h" 30 #include "platform_interface.h" 31 #include <curl/curl.h> 32 #include <microhttpd.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <time.h> 36 37 #ifdef _WIN32 38 #ifndef WIN32_LEAN_AND_MEAN 39 #define WIN32_LEAN_AND_MEAN 1 40 #endif /* !WIN32_LEAN_AND_MEAN */ 41 #include <windows.h> 42 #endif 43 44 #ifndef WINDOWS 45 #include <unistd.h> 46 #include <sys/socket.h> 47 #endif 48 49 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2 50 #undef CPU_COUNT 51 #endif 52 #if !defined(CPU_COUNT) 53 #define CPU_COUNT 2 54 #endif 55 56 static int oneone; 57 58 struct CBC 59 { 60 char *buf; 61 size_t pos; 62 size_t size; 63 }; 64 65 static size_t 66 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 67 { 68 struct CBC *cbc = ctx; 69 70 if (cbc->pos + size * nmemb > cbc->size) 71 return 0; /* overflow */ 72 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 73 cbc->pos += size * nmemb; 74 return size * nmemb; 75 } 76 77 static int 78 ahc_echo (void *cls, 79 struct MHD_Connection *connection, 80 const char *url, 81 const char *method, 82 const char *version, 83 const char *upload_data, size_t *upload_data_size, 84 void **unused) 85 { 86 static int ptr; 87 const char *me = cls; 88 struct MHD_Response *response; 89 int ret; 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 response = MHD_create_response_from_buffer (strlen (url), 100 (void *) url, 101 MHD_RESPMEM_MUST_COPY); 102 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 103 MHD_destroy_response (response); 104 if (ret == MHD_NO) 105 abort (); 106 return ret; 107 } 108 109 110 static int 111 testInternalGet (int poll_flag) 112 { 113 struct MHD_Daemon *d; 114 CURL *c; 115 char buf[2048]; 116 struct CBC cbc; 117 CURLcode errornum; 118 119 cbc.buf = buf; 120 cbc.size = 2048; 121 cbc.pos = 0; 122 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag, 123 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 124 if (d == NULL) 125 return 1; 126 c = curl_easy_init (); 127 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/hello_world"); 128 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 129 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 130 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 131 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 132 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 133 if (oneone) 134 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 135 else 136 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 137 /* NOTE: use of CONNECTTIMEOUT without also 138 setting NOSIGNAL results in really weird 139 crashes on my system!*/ 140 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 141 if (CURLE_OK != (errornum = curl_easy_perform (c))) 142 { 143 fprintf (stderr, 144 "curl_easy_perform failed: `%s'\n", 145 curl_easy_strerror (errornum)); 146 curl_easy_cleanup (c); 147 MHD_stop_daemon (d); 148 return 2; 149 } 150 curl_easy_cleanup (c); 151 MHD_stop_daemon (d); 152 if (cbc.pos != strlen ("/hello_world")) 153 return 4; 154 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 155 return 8; 156 return 0; 157 } 158 159 160 static int 161 testMultithreadedGet (int poll_flag) 162 { 163 struct MHD_Daemon *d; 164 CURL *c; 165 char buf[2048]; 166 struct CBC cbc; 167 CURLcode errornum; 168 169 cbc.buf = buf; 170 cbc.size = 2048; 171 cbc.pos = 0; 172 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG | poll_flag, 173 1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 174 if (d == NULL) 175 return 16; 176 c = curl_easy_init (); 177 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world"); 178 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 179 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 180 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 181 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 182 if (oneone) 183 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 184 else 185 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 186 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 187 /* NOTE: use of CONNECTTIMEOUT without also 188 setting NOSIGNAL results in really weird 189 crashes on my system! */ 190 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 191 if (CURLE_OK != (errornum = curl_easy_perform (c))) 192 { 193 fprintf (stderr, 194 "curl_easy_perform failed: `%s'\n", 195 curl_easy_strerror (errornum)); 196 curl_easy_cleanup (c); 197 MHD_stop_daemon (d); 198 return 32; 199 } 200 curl_easy_cleanup (c); 201 MHD_stop_daemon (d); 202 if (cbc.pos != strlen ("/hello_world")) 203 return 64; 204 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 205 return 128; 206 return 0; 207 } 208 209 210 static int 211 testMultithreadedPoolGet (int poll_flag) 212 { 213 struct MHD_Daemon *d; 214 CURL *c; 215 char buf[2048]; 216 struct CBC cbc; 217 CURLcode errornum; 218 219 cbc.buf = buf; 220 cbc.size = 2048; 221 cbc.pos = 0; 222 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag, 223 1081, NULL, NULL, &ahc_echo, "GET", 224 MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END); 225 if (d == NULL) 226 return 16; 227 c = curl_easy_init (); 228 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world"); 229 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 230 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 231 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 232 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 233 if (oneone) 234 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 235 else 236 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 237 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 238 /* NOTE: use of CONNECTTIMEOUT without also 239 setting NOSIGNAL results in really weird 240 crashes on my system!*/ 241 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 242 if (CURLE_OK != (errornum = curl_easy_perform (c))) 243 { 244 fprintf (stderr, 245 "curl_easy_perform failed: `%s'\n", 246 curl_easy_strerror (errornum)); 247 curl_easy_cleanup (c); 248 MHD_stop_daemon (d); 249 return 32; 250 } 251 curl_easy_cleanup (c); 252 MHD_stop_daemon (d); 253 if (cbc.pos != strlen ("/hello_world")) 254 return 64; 255 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 256 return 128; 257 return 0; 258 } 259 260 261 static int 262 testExternalGet () 263 { 264 struct MHD_Daemon *d; 265 CURL *c; 266 char buf[2048]; 267 struct CBC cbc; 268 CURLM *multi; 269 CURLMcode mret; 270 fd_set rs; 271 fd_set ws; 272 fd_set es; 273 MHD_socket max; 274 int running; 275 struct CURLMsg *msg; 276 time_t start; 277 struct timeval tv; 278 279 multi = NULL; 280 cbc.buf = buf; 281 cbc.size = 2048; 282 cbc.pos = 0; 283 d = MHD_start_daemon (MHD_USE_DEBUG, 284 1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 285 if (d == NULL) 286 return 256; 287 c = curl_easy_init (); 288 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world"); 289 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 290 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 291 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 292 if (oneone) 293 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 294 else 295 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 296 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 297 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 298 /* NOTE: use of CONNECTTIMEOUT without also 299 setting NOSIGNAL results in really weird 300 crashes on my system! */ 301 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 302 303 304 multi = curl_multi_init (); 305 if (multi == NULL) 306 { 307 curl_easy_cleanup (c); 308 MHD_stop_daemon (d); 309 return 512; 310 } 311 mret = curl_multi_add_handle (multi, c); 312 if (mret != CURLM_OK) 313 { 314 curl_multi_cleanup (multi); 315 curl_easy_cleanup (c); 316 MHD_stop_daemon (d); 317 return 1024; 318 } 319 start = time (NULL); 320 while ((time (NULL) - start < 5) && (multi != NULL)) 321 { 322 max = 0; 323 FD_ZERO (&rs); 324 FD_ZERO (&ws); 325 FD_ZERO (&es); 326 curl_multi_perform (multi, &running); 327 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); 328 if (mret != CURLM_OK) 329 { 330 curl_multi_remove_handle (multi, c); 331 curl_multi_cleanup (multi); 332 curl_easy_cleanup (c); 333 MHD_stop_daemon (d); 334 return 2048; 335 } 336 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 337 { 338 curl_multi_remove_handle (multi, c); 339 curl_multi_cleanup (multi); 340 curl_easy_cleanup (c); 341 MHD_stop_daemon (d); 342 return 4096; 343 } 344 tv.tv_sec = 0; 345 tv.tv_usec = 1000; 346 select (max + 1, &rs, &ws, &es, &tv); 347 curl_multi_perform (multi, &running); 348 if (running == 0) 349 { 350 msg = curl_multi_info_read (multi, &running); 351 if (msg == NULL) 352 break; 353 if (msg->msg == CURLMSG_DONE) 354 { 355 if (msg->data.result != CURLE_OK) 356 printf ("%s failed at %s:%d: `%s'\n", 357 "curl_multi_perform", 358 __FILE__, 359 __LINE__, curl_easy_strerror (msg->data.result)); 360 curl_multi_remove_handle (multi, c); 361 curl_multi_cleanup (multi); 362 curl_easy_cleanup (c); 363 c = NULL; 364 multi = NULL; 365 } 366 } 367 MHD_run (d); 368 } 369 if (multi != NULL) 370 { 371 curl_multi_remove_handle (multi, c); 372 curl_easy_cleanup (c); 373 curl_multi_cleanup (multi); 374 } 375 MHD_stop_daemon (d); 376 if (cbc.pos != strlen ("/hello_world")) 377 return 8192; 378 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 379 return 16384; 380 return 0; 381 } 382 383 384 static int 385 testUnknownPortGet (int poll_flag) 386 { 387 struct MHD_Daemon *d; 388 const union MHD_DaemonInfo *di; 389 CURL *c; 390 char buf[2048]; 391 struct CBC cbc; 392 CURLcode errornum; 393 394 struct sockaddr_in addr; 395 socklen_t addr_len = sizeof(addr); 396 memset(&addr, 0, sizeof(addr)); 397 addr.sin_family = AF_INET; 398 addr.sin_port = 0; 399 addr.sin_addr.s_addr = INADDR_ANY; 400 401 cbc.buf = buf; 402 cbc.size = 2048; 403 cbc.pos = 0; 404 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag, 405 1, NULL, NULL, &ahc_echo, "GET", 406 MHD_OPTION_SOCK_ADDR, &addr, 407 MHD_OPTION_END); 408 if (d == NULL) 409 return 32768; 410 411 di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD); 412 if (di == NULL) 413 return 65536; 414 415 if (0 != getsockname(di->listen_fd, (struct sockaddr *) &addr, &addr_len)) 416 return 131072; 417 418 if (addr.sin_family != AF_INET) 419 return 26214; 420 421 snprintf(buf, sizeof(buf), "http://127.0.0.1:%hu/hello_world", 422 ntohs(addr.sin_port)); 423 424 c = curl_easy_init (); 425 curl_easy_setopt (c, CURLOPT_URL, buf); 426 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 427 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 428 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 429 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 430 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 431 if (oneone) 432 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 433 else 434 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 435 /* NOTE: use of CONNECTTIMEOUT without also 436 setting NOSIGNAL results in really weird 437 crashes on my system! */ 438 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 439 if (CURLE_OK != (errornum = curl_easy_perform (c))) 440 { 441 fprintf (stderr, 442 "curl_easy_perform failed: `%s'\n", 443 curl_easy_strerror (errornum)); 444 curl_easy_cleanup (c); 445 MHD_stop_daemon (d); 446 return 524288; 447 } 448 curl_easy_cleanup (c); 449 MHD_stop_daemon (d); 450 if (cbc.pos != strlen ("/hello_world")) 451 return 1048576; 452 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 453 return 2097152; 454 return 0; 455 } 456 457 458 static int 459 testStopRace (int poll_flag) 460 { 461 struct sockaddr_in sin; 462 MHD_socket fd; 463 struct MHD_Daemon *d; 464 465 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG | poll_flag, 466 1081, NULL, NULL, &ahc_echo, "GET", 467 MHD_OPTION_CONNECTION_TIMEOUT, 5, MHD_OPTION_END); 468 if (d == NULL) 469 return 16; 470 471 fd = socket (PF_INET, SOCK_STREAM, 0); 472 if (fd == MHD_INVALID_SOCKET) 473 { 474 fprintf(stderr, "socket error\n"); 475 return 256; 476 } 477 478 memset(&sin, 0, sizeof(sin)); 479 sin.sin_family = AF_INET; 480 sin.sin_port = htons(1081); 481 sin.sin_addr.s_addr = htonl(0x7f000001); 482 483 if (connect (fd, (struct sockaddr *)(&sin), sizeof(sin)) < 0) 484 { 485 fprintf(stderr, "connect error\n"); 486 MHD_socket_close_ (fd); 487 return 512; 488 } 489 490 /* printf("Waiting\n"); */ 491 /* Let the thread get going. */ 492 usleep(500000); 493 494 /* printf("Stopping daemon\n"); */ 495 MHD_stop_daemon (d); 496 497 MHD_socket_close_ (fd); 498 499 /* printf("good\n"); */ 500 return 0; 501 } 502 503 504 static int 505 ahc_empty (void *cls, 506 struct MHD_Connection *connection, 507 const char *url, 508 const char *method, 509 const char *version, 510 const char *upload_data, size_t *upload_data_size, 511 void **unused) 512 { 513 static int ptr; 514 struct MHD_Response *response; 515 int ret; 516 517 if (0 != strcmp ("GET", method)) 518 return MHD_NO; /* unexpected method */ 519 if (&ptr != *unused) 520 { 521 *unused = &ptr; 522 return MHD_YES; 523 } 524 *unused = NULL; 525 response = MHD_create_response_from_buffer (0, 526 NULL, 527 MHD_RESPMEM_PERSISTENT); 528 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 529 MHD_destroy_response (response); 530 if (ret == MHD_NO) 531 abort (); 532 return ret; 533 } 534 535 536 static int 537 curlExcessFound(CURL *c, curl_infotype type, char *data, size_t size, void *cls) 538 { 539 static const char *excess_found = "Excess found"; 540 const size_t str_size = strlen (excess_found); 541 542 if (CURLINFO_TEXT == type 543 && size >= str_size 544 && 0 == strncmp(excess_found, data, str_size)) 545 *(int *)cls = 1; 546 return 0; 547 } 548 549 550 static int 551 testEmptyGet (int poll_flag) 552 { 553 struct MHD_Daemon *d; 554 CURL *c; 555 char buf[2048]; 556 struct CBC cbc; 557 CURLcode errornum; 558 int excess_found = 0; 559 560 cbc.buf = buf; 561 cbc.size = 2048; 562 cbc.pos = 0; 563 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag, 564 11081, NULL, NULL, &ahc_empty, NULL, MHD_OPTION_END); 565 if (d == NULL) 566 return 4194304; 567 c = curl_easy_init (); 568 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); 569 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 570 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 571 curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, &curlExcessFound); 572 curl_easy_setopt (c, CURLOPT_DEBUGDATA, &excess_found); 573 curl_easy_setopt (c, CURLOPT_VERBOSE, 1); 574 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 575 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 576 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 577 if (oneone) 578 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 579 else 580 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 581 /* NOTE: use of CONNECTTIMEOUT without also 582 setting NOSIGNAL results in really weird 583 crashes on my system!*/ 584 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 585 if (CURLE_OK != (errornum = curl_easy_perform (c))) 586 { 587 fprintf (stderr, 588 "curl_easy_perform failed: `%s'\n", 589 curl_easy_strerror (errornum)); 590 curl_easy_cleanup (c); 591 MHD_stop_daemon (d); 592 return 8388608; 593 } 594 curl_easy_cleanup (c); 595 MHD_stop_daemon (d); 596 if (cbc.pos != 0) 597 return 16777216; 598 if (excess_found) 599 return 33554432; 600 return 0; 601 } 602 603 604 int 605 main (int argc, char *const *argv) 606 { 607 unsigned int errorCount = 0; 608 609 oneone = (NULL != strrchr (argv[0], (int) '/')) ? 610 (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; 611 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 612 return 2; 613 errorCount += testInternalGet (0); 614 errorCount += testMultithreadedGet (0); 615 errorCount += testMultithreadedPoolGet (0); 616 errorCount += testUnknownPortGet (0); 617 errorCount += testStopRace (0); 618 errorCount += testExternalGet (); 619 errorCount += testEmptyGet (0); 620 if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_POLL)) 621 { 622 errorCount += testInternalGet(MHD_USE_POLL); 623 errorCount += testMultithreadedGet(MHD_USE_POLL); 624 errorCount += testMultithreadedPoolGet(MHD_USE_POLL); 625 errorCount += testUnknownPortGet(MHD_USE_POLL); 626 errorCount += testStopRace(MHD_USE_POLL); 627 errorCount += testEmptyGet(MHD_USE_POLL); 628 } 629 if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_EPOLL)) 630 { 631 errorCount += testInternalGet(MHD_USE_EPOLL_LINUX_ONLY); 632 errorCount += testMultithreadedPoolGet(MHD_USE_EPOLL_LINUX_ONLY); 633 errorCount += testUnknownPortGet(MHD_USE_EPOLL_LINUX_ONLY); 634 errorCount += testEmptyGet(MHD_USE_EPOLL_LINUX_ONLY); 635 } 636 if (errorCount != 0) 637 fprintf (stderr, "Error (code: %u)\n", errorCount); 638 curl_global_cleanup (); 639 return errorCount != 0; /* 0 == pass */ 640 } 641