Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/easy.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*************************************************************************** | |
| 2 * _ _ ____ _ | |
| 3 * Project ___| | | | _ \| | | |
| 4 * / __| | | | |_) | | | |
| 5 * | (__| |_| | _ <| |___ | |
| 6 * \___|\___/|_| \_\_____| | |
| 7 * | |
| 8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. | |
| 9 * | |
| 10 * This software is licensed as described in the file COPYING, which | |
| 11 * you should have received as part of this distribution. The terms | |
| 12 * are also available at https://curl.haxx.se/docs/copyright.html. | |
| 13 * | |
| 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell | |
| 15 * copies of the Software, and permit persons to whom the Software is | |
| 16 * furnished to do so, under the terms of the COPYING file. | |
| 17 * | |
| 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | |
| 19 * KIND, either express or implied. | |
| 20 * | |
| 21 ***************************************************************************/ | |
| 22 | |
| 23 #include "curl_setup.h" | |
| 24 | |
| 25 /* | |
| 26 * See comment in curl_memory.h for the explanation of this sanity check. | |
| 27 */ | |
| 28 | |
| 29 #ifdef CURLX_NO_MEMORY_CALLBACKS | |
| 30 #error "libcurl shall not ever be built with CURLX_NO_MEMORY_CALLBACKS defined" | |
| 31 #endif | |
| 32 | |
| 33 #ifdef HAVE_NETINET_IN_H | |
| 34 #include <netinet/in.h> | |
| 35 #endif | |
| 36 #ifdef HAVE_NETDB_H | |
| 37 #include <netdb.h> | |
| 38 #endif | |
| 39 #ifdef HAVE_ARPA_INET_H | |
| 40 #include <arpa/inet.h> | |
| 41 #endif | |
| 42 #ifdef HAVE_NET_IF_H | |
| 43 #include <net/if.h> | |
| 44 #endif | |
| 45 #ifdef HAVE_SYS_IOCTL_H | |
| 46 #include <sys/ioctl.h> | |
| 47 #endif | |
| 48 | |
| 49 #ifdef HAVE_SYS_PARAM_H | |
| 50 #include <sys/param.h> | |
| 51 #endif | |
| 52 | |
| 53 #include "urldata.h" | |
| 54 #include <curl/curl.h> | |
| 55 #include "transfer.h" | |
| 56 #include "vtls/vtls.h" | |
| 57 #include "url.h" | |
| 58 #include "getinfo.h" | |
| 59 #include "hostip.h" | |
| 60 #include "share.h" | |
| 61 #include "strdup.h" | |
| 62 #include "progress.h" | |
| 63 #include "easyif.h" | |
| 64 #include "multiif.h" | |
| 65 #include "select.h" | |
| 66 #include "sendf.h" /* for failf function prototype */ | |
| 67 #include "connect.h" /* for Curl_getconnectinfo */ | |
| 68 #include "slist.h" | |
| 69 #include "mime.h" | |
| 70 #include "amigaos.h" | |
| 71 #include "non-ascii.h" | |
| 72 #include "warnless.h" | |
| 73 #include "multiif.h" | |
| 74 #include "sigpipe.h" | |
| 75 #include "ssh.h" | |
| 76 #include "setopt.h" | |
| 77 #include "http_digest.h" | |
| 78 #include "system_win32.h" | |
| 79 | |
| 80 /* The last 3 #include files should be in this order */ | |
| 81 #include "curl_printf.h" | |
| 82 #include "curl_memory.h" | |
| 83 #include "memdebug.h" | |
| 84 | |
| 85 void Curl_version_init(void); | |
| 86 | |
| 87 /* true globals -- for curl_global_init() and curl_global_cleanup() */ | |
| 88 static unsigned int initialized; | |
| 89 static long init_flags; | |
| 90 | |
| 91 /* | |
| 92 * strdup (and other memory functions) is redefined in complicated | |
| 93 * ways, but at this point it must be defined as the system-supplied strdup | |
| 94 * so the callback pointer is initialized correctly. | |
| 95 */ | |
| 96 #if defined(_WIN32_WCE) | |
| 97 #define system_strdup _strdup | |
| 98 #elif !defined(HAVE_STRDUP) | |
| 99 #define system_strdup curlx_strdup | |
| 100 #else | |
| 101 #define system_strdup strdup | |
| 102 #endif | |
| 103 | |
| 104 #if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) | |
| 105 # pragma warning(disable:4232) /* MSVC extension, dllimport identity */ | |
| 106 #endif | |
| 107 | |
| 108 #ifndef __SYMBIAN32__ | |
| 109 /* | |
| 110 * If a memory-using function (like curl_getenv) is used before | |
| 111 * curl_global_init() is called, we need to have these pointers set already. | |
| 112 */ | |
| 113 curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; | |
| 114 curl_free_callback Curl_cfree = (curl_free_callback)free; | |
| 115 curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; | |
| 116 curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; | |
| 117 curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; | |
| 118 #if defined(WIN32) && defined(UNICODE) | |
| 119 curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; | |
| 120 #endif | |
| 121 #else | |
| 122 /* | |
| 123 * Symbian OS doesn't support initialization to code in writable static data. | |
| 124 * Initialization will occur in the curl_global_init() call. | |
| 125 */ | |
| 126 curl_malloc_callback Curl_cmalloc; | |
| 127 curl_free_callback Curl_cfree; | |
| 128 curl_realloc_callback Curl_crealloc; | |
| 129 curl_strdup_callback Curl_cstrdup; | |
| 130 curl_calloc_callback Curl_ccalloc; | |
| 131 #endif | |
| 132 | |
| 133 #if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) | |
| 134 # pragma warning(default:4232) /* MSVC extension, dllimport identity */ | |
| 135 #endif | |
| 136 | |
| 137 /** | |
| 138 * curl_global_init() globally initializes curl given a bitwise set of the | |
| 139 * different features of what to initialize. | |
| 140 */ | |
| 141 static CURLcode global_init(long flags, bool memoryfuncs) | |
| 142 { | |
| 143 if(initialized++) | |
| 144 return CURLE_OK; | |
| 145 | |
| 146 if(memoryfuncs) { | |
| 147 /* Setup the default memory functions here (again) */ | |
| 148 Curl_cmalloc = (curl_malloc_callback)malloc; | |
| 149 Curl_cfree = (curl_free_callback)free; | |
| 150 Curl_crealloc = (curl_realloc_callback)realloc; | |
| 151 Curl_cstrdup = (curl_strdup_callback)system_strdup; | |
| 152 Curl_ccalloc = (curl_calloc_callback)calloc; | |
| 153 #if defined(WIN32) && defined(UNICODE) | |
| 154 Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; | |
| 155 #endif | |
| 156 } | |
| 157 | |
| 158 if(!Curl_ssl_init()) { | |
| 159 DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); | |
| 160 return CURLE_FAILED_INIT; | |
| 161 } | |
| 162 | |
| 163 #ifdef WIN32 | |
| 164 if(Curl_win32_init(flags)) { | |
| 165 DEBUGF(fprintf(stderr, "Error: win32_init failed\n")); | |
| 166 return CURLE_FAILED_INIT; | |
| 167 } | |
| 168 #endif | |
| 169 | |
| 170 #ifdef __AMIGA__ | |
| 171 if(!Curl_amiga_init()) { | |
| 172 DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n")); | |
| 173 return CURLE_FAILED_INIT; | |
| 174 } | |
| 175 #endif | |
| 176 | |
| 177 #ifdef NETWARE | |
| 178 if(netware_init()) { | |
| 179 DEBUGF(fprintf(stderr, "Warning: LONG namespace not available\n")); | |
| 180 } | |
| 181 #endif | |
| 182 | |
| 183 if(Curl_resolver_global_init()) { | |
| 184 DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); | |
| 185 return CURLE_FAILED_INIT; | |
| 186 } | |
| 187 | |
| 188 (void)Curl_ipv6works(); | |
| 189 | |
| 190 #if defined(USE_SSH) | |
| 191 if(Curl_ssh_init()) { | |
| 192 return CURLE_FAILED_INIT; | |
| 193 } | |
| 194 #endif | |
| 195 | |
| 196 if(flags & CURL_GLOBAL_ACK_EINTR) | |
| 197 Curl_ack_eintr = 1; | |
| 198 | |
| 199 init_flags = flags; | |
| 200 | |
| 201 Curl_version_init(); | |
| 202 | |
| 203 return CURLE_OK; | |
| 204 } | |
| 205 | |
| 206 | |
| 207 /** | |
| 208 * curl_global_init() globally initializes curl given a bitwise set of the | |
| 209 * different features of what to initialize. | |
| 210 */ | |
| 211 CURLcode curl_global_init(long flags) | |
| 212 { | |
| 213 return global_init(flags, TRUE); | |
| 214 } | |
| 215 | |
| 216 /* | |
| 217 * curl_global_init_mem() globally initializes curl and also registers the | |
| 218 * user provided callback routines. | |
| 219 */ | |
| 220 CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, | |
| 221 curl_free_callback f, curl_realloc_callback r, | |
| 222 curl_strdup_callback s, curl_calloc_callback c) | |
| 223 { | |
| 224 /* Invalid input, return immediately */ | |
| 225 if(!m || !f || !r || !s || !c) | |
| 226 return CURLE_FAILED_INIT; | |
| 227 | |
| 228 if(initialized) { | |
| 229 /* Already initialized, don't do it again, but bump the variable anyway to | |
| 230 work like curl_global_init() and require the same amount of cleanup | |
| 231 calls. */ | |
| 232 initialized++; | |
| 233 return CURLE_OK; | |
| 234 } | |
| 235 | |
| 236 /* set memory functions before global_init() in case it wants memory | |
| 237 functions */ | |
| 238 Curl_cmalloc = m; | |
| 239 Curl_cfree = f; | |
| 240 Curl_cstrdup = s; | |
| 241 Curl_crealloc = r; | |
| 242 Curl_ccalloc = c; | |
| 243 | |
| 244 /* Call the actual init function, but without setting */ | |
| 245 return global_init(flags, FALSE); | |
| 246 } | |
| 247 | |
| 248 /** | |
| 249 * curl_global_cleanup() globally cleanups curl, uses the value of | |
| 250 * "init_flags" to determine what needs to be cleaned up and what doesn't. | |
| 251 */ | |
| 252 void curl_global_cleanup(void) | |
| 253 { | |
| 254 if(!initialized) | |
| 255 return; | |
| 256 | |
| 257 if(--initialized) | |
| 258 return; | |
| 259 | |
| 260 Curl_ssl_cleanup(); | |
| 261 Curl_resolver_global_cleanup(); | |
| 262 | |
| 263 #ifdef WIN32 | |
| 264 Curl_win32_cleanup(init_flags); | |
| 265 #endif | |
| 266 | |
| 267 Curl_amiga_cleanup(); | |
| 268 | |
| 269 Curl_ssh_cleanup(); | |
| 270 | |
| 271 init_flags = 0; | |
| 272 } | |
| 273 | |
| 274 /* | |
| 275 * curl_easy_init() is the external interface to alloc, setup and init an | |
| 276 * easy handle that is returned. If anything goes wrong, NULL is returned. | |
| 277 */ | |
| 278 struct Curl_easy *curl_easy_init(void) | |
| 279 { | |
| 280 CURLcode result; | |
| 281 struct Curl_easy *data; | |
| 282 | |
| 283 /* Make sure we inited the global SSL stuff */ | |
| 284 if(!initialized) { | |
| 285 result = curl_global_init(CURL_GLOBAL_DEFAULT); | |
| 286 if(result) { | |
| 287 /* something in the global init failed, return nothing */ | |
| 288 DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n")); | |
| 289 return NULL; | |
| 290 } | |
| 291 } | |
| 292 | |
| 293 /* We use curl_open() with undefined URL so far */ | |
| 294 result = Curl_open(&data); | |
| 295 if(result) { | |
| 296 DEBUGF(fprintf(stderr, "Error: Curl_open failed\n")); | |
| 297 return NULL; | |
| 298 } | |
| 299 | |
| 300 return data; | |
| 301 } | |
| 302 | |
| 303 #ifdef CURLDEBUG | |
| 304 | |
| 305 struct socketmonitor { | |
| 306 struct socketmonitor *next; /* the next node in the list or NULL */ | |
| 307 struct pollfd socket; /* socket info of what to monitor */ | |
| 308 }; | |
| 309 | |
| 310 struct events { | |
| 311 long ms; /* timeout, run the timeout function when reached */ | |
| 312 bool msbump; /* set TRUE when timeout is set by callback */ | |
| 313 int num_sockets; /* number of nodes in the monitor list */ | |
| 314 struct socketmonitor *list; /* list of sockets to monitor */ | |
| 315 int running_handles; /* store the returned number */ | |
| 316 }; | |
| 317 | |
| 318 /* events_timer | |
| 319 * | |
| 320 * Callback that gets called with a new value when the timeout should be | |
| 321 * updated. | |
| 322 */ | |
| 323 | |
| 324 static int events_timer(struct Curl_multi *multi, /* multi handle */ | |
| 325 long timeout_ms, /* see above */ | |
| 326 void *userp) /* private callback pointer */ | |
| 327 { | |
| 328 struct events *ev = userp; | |
| 329 (void)multi; | |
| 330 if(timeout_ms == -1) | |
| 331 /* timeout removed */ | |
| 332 timeout_ms = 0; | |
| 333 else if(timeout_ms == 0) | |
| 334 /* timeout is already reached! */ | |
| 335 timeout_ms = 1; /* trigger asap */ | |
| 336 | |
| 337 ev->ms = timeout_ms; | |
| 338 ev->msbump = TRUE; | |
| 339 return 0; | |
| 340 } | |
| 341 | |
| 342 | |
| 343 /* poll2cselect | |
| 344 * | |
| 345 * convert from poll() bit definitions to libcurl's CURL_CSELECT_* ones | |
| 346 */ | |
| 347 static int poll2cselect(int pollmask) | |
| 348 { | |
| 349 int omask = 0; | |
| 350 if(pollmask & POLLIN) | |
| 351 omask |= CURL_CSELECT_IN; | |
| 352 if(pollmask & POLLOUT) | |
| 353 omask |= CURL_CSELECT_OUT; | |
| 354 if(pollmask & POLLERR) | |
| 355 omask |= CURL_CSELECT_ERR; | |
| 356 return omask; | |
| 357 } | |
| 358 | |
| 359 | |
| 360 /* socketcb2poll | |
| 361 * | |
| 362 * convert from libcurl' CURL_POLL_* bit definitions to poll()'s | |
| 363 */ | |
| 364 static short socketcb2poll(int pollmask) | |
| 365 { | |
| 366 short omask = 0; | |
| 367 if(pollmask & CURL_POLL_IN) | |
| 368 omask |= POLLIN; | |
| 369 if(pollmask & CURL_POLL_OUT) | |
| 370 omask |= POLLOUT; | |
| 371 return omask; | |
| 372 } | |
| 373 | |
| 374 /* events_socket | |
| 375 * | |
| 376 * Callback that gets called with information about socket activity to | |
| 377 * monitor. | |
| 378 */ | |
| 379 static int events_socket(struct Curl_easy *easy, /* easy handle */ | |
| 380 curl_socket_t s, /* socket */ | |
| 381 int what, /* see above */ | |
| 382 void *userp, /* private callback | |
| 383 pointer */ | |
| 384 void *socketp) /* private socket | |
| 385 pointer */ | |
| 386 { | |
| 387 struct events *ev = userp; | |
| 388 struct socketmonitor *m; | |
| 389 struct socketmonitor *prev = NULL; | |
| 390 | |
| 391 #if defined(CURL_DISABLE_VERBOSE_STRINGS) | |
| 392 (void) easy; | |
| 393 #endif | |
| 394 (void)socketp; | |
| 395 | |
| 396 m = ev->list; | |
| 397 while(m) { | |
| 398 if(m->socket.fd == s) { | |
| 399 | |
| 400 if(what == CURL_POLL_REMOVE) { | |
| 401 struct socketmonitor *nxt = m->next; | |
| 402 /* remove this node from the list of monitored sockets */ | |
| 403 if(prev) | |
| 404 prev->next = nxt; | |
| 405 else | |
| 406 ev->list = nxt; | |
| 407 free(m); | |
| 408 m = nxt; | |
| 409 infof(easy, "socket cb: socket %d REMOVED\n", s); | |
| 410 } | |
| 411 else { | |
| 412 /* The socket 's' is already being monitored, update the activity | |
| 413 mask. Convert from libcurl bitmask to the poll one. */ | |
| 414 m->socket.events = socketcb2poll(what); | |
| 415 infof(easy, "socket cb: socket %d UPDATED as %s%s\n", s, | |
| 416 (what&CURL_POLL_IN)?"IN":"", | |
| 417 (what&CURL_POLL_OUT)?"OUT":""); | |
| 418 } | |
| 419 break; | |
| 420 } | |
| 421 prev = m; | |
| 422 m = m->next; /* move to next node */ | |
| 423 } | |
| 424 if(!m) { | |
| 425 if(what == CURL_POLL_REMOVE) { | |
| 426 /* this happens a bit too often, libcurl fix perhaps? */ | |
| 427 /* fprintf(stderr, | |
| 428 "%s: socket %d asked to be REMOVED but not present!\n", | |
| 429 __func__, s); */ | |
| 430 } | |
| 431 else { | |
| 432 m = malloc(sizeof(struct socketmonitor)); | |
| 433 if(m) { | |
| 434 m->next = ev->list; | |
| 435 m->socket.fd = s; | |
| 436 m->socket.events = socketcb2poll(what); | |
| 437 m->socket.revents = 0; | |
| 438 ev->list = m; | |
| 439 infof(easy, "socket cb: socket %d ADDED as %s%s\n", s, | |
| 440 (what&CURL_POLL_IN)?"IN":"", | |
| 441 (what&CURL_POLL_OUT)?"OUT":""); | |
| 442 } | |
| 443 else | |
| 444 return CURLE_OUT_OF_MEMORY; | |
| 445 } | |
| 446 } | |
| 447 | |
| 448 return 0; | |
| 449 } | |
| 450 | |
| 451 | |
| 452 /* | |
| 453 * events_setup() | |
| 454 * | |
| 455 * Do the multi handle setups that only event-based transfers need. | |
| 456 */ | |
| 457 static void events_setup(struct Curl_multi *multi, struct events *ev) | |
| 458 { | |
| 459 /* timer callback */ | |
| 460 curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, events_timer); | |
| 461 curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ev); | |
| 462 | |
| 463 /* socket callback */ | |
| 464 curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, events_socket); | |
| 465 curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev); | |
| 466 } | |
| 467 | |
| 468 | |
| 469 /* wait_or_timeout() | |
| 470 * | |
| 471 * waits for activity on any of the given sockets, or the timeout to trigger. | |
| 472 */ | |
| 473 | |
| 474 static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) | |
| 475 { | |
| 476 bool done = FALSE; | |
| 477 CURLMcode mcode = CURLM_OK; | |
| 478 CURLcode result = CURLE_OK; | |
| 479 | |
| 480 while(!done) { | |
| 481 CURLMsg *msg; | |
| 482 struct socketmonitor *m; | |
| 483 struct pollfd *f; | |
| 484 struct pollfd fds[4]; | |
| 485 int numfds = 0; | |
| 486 int pollrc; | |
| 487 int i; | |
| 488 struct curltime before; | |
| 489 struct curltime after; | |
| 490 | |
| 491 /* populate the fds[] array */ | |
| 492 for(m = ev->list, f = &fds[0]; m; m = m->next) { | |
| 493 f->fd = m->socket.fd; | |
| 494 f->events = m->socket.events; | |
| 495 f->revents = 0; | |
| 496 /* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */ | |
| 497 f++; | |
| 498 numfds++; | |
| 499 } | |
| 500 | |
| 501 /* get the time stamp to use to figure out how long poll takes */ | |
| 502 before = Curl_now(); | |
| 503 | |
| 504 /* wait for activity or timeout */ | |
| 505 pollrc = Curl_poll(fds, numfds, (int)ev->ms); | |
| 506 | |
| 507 after = Curl_now(); | |
| 508 | |
| 509 ev->msbump = FALSE; /* reset here */ | |
| 510 | |
| 511 if(0 == pollrc) { | |
| 512 /* timeout! */ | |
| 513 ev->ms = 0; | |
| 514 /* fprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */ | |
| 515 mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, | |
| 516 &ev->running_handles); | |
| 517 } | |
| 518 else if(pollrc > 0) { | |
| 519 /* loop over the monitored sockets to see which ones had activity */ | |
| 520 for(i = 0; i< numfds; i++) { | |
| 521 if(fds[i].revents) { | |
| 522 /* socket activity, tell libcurl */ | |
| 523 int act = poll2cselect(fds[i].revents); /* convert */ | |
| 524 infof(multi->easyp, "call curl_multi_socket_action(socket %d)\n", | |
| 525 fds[i].fd); | |
| 526 mcode = curl_multi_socket_action(multi, fds[i].fd, act, | |
| 527 &ev->running_handles); | |
| 528 } | |
| 529 } | |
| 530 | |
| 531 if(!ev->msbump) { | |
| 532 /* If nothing updated the timeout, we decrease it by the spent time. | |
| 533 * If it was updated, it has the new timeout time stored already. | |
| 534 */ | |
| 535 timediff_t timediff = Curl_timediff(after, before); | |
| 536 if(timediff > 0) { | |
| 537 if(timediff > ev->ms) | |
| 538 ev->ms = 0; | |
| 539 else | |
| 540 ev->ms -= (long)timediff; | |
| 541 } | |
| 542 } | |
| 543 } | |
| 544 else | |
| 545 return CURLE_RECV_ERROR; | |
| 546 | |
| 547 if(mcode) | |
| 548 return CURLE_URL_MALFORMAT; | |
| 549 | |
| 550 /* we don't really care about the "msgs_in_queue" value returned in the | |
| 551 second argument */ | |
| 552 msg = curl_multi_info_read(multi, &pollrc); | |
| 553 if(msg) { | |
| 554 result = msg->data.result; | |
| 555 done = TRUE; | |
| 556 } | |
| 557 } | |
| 558 | |
| 559 return result; | |
| 560 } | |
| 561 | |
| 562 | |
| 563 /* easy_events() | |
| 564 * | |
| 565 * Runs a transfer in a blocking manner using the events-based API | |
| 566 */ | |
| 567 static CURLcode easy_events(struct Curl_multi *multi) | |
| 568 { | |
| 569 /* this struct is made static to allow it to be used after this function | |
| 570 returns and curl_multi_remove_handle() is called */ | |
| 571 static struct events evs = {2, FALSE, 0, NULL, 0}; | |
| 572 | |
| 573 /* if running event-based, do some further multi inits */ | |
| 574 events_setup(multi, &evs); | |
| 575 | |
| 576 return wait_or_timeout(multi, &evs); | |
| 577 } | |
| 578 #else /* CURLDEBUG */ | |
| 579 /* when not built with debug, this function doesn't exist */ | |
| 580 #define easy_events(x) CURLE_NOT_BUILT_IN | |
| 581 #endif | |
| 582 | |
| 583 static CURLcode easy_transfer(struct Curl_multi *multi) | |
| 584 { | |
| 585 bool done = FALSE; | |
| 586 CURLMcode mcode = CURLM_OK; | |
| 587 CURLcode result = CURLE_OK; | |
| 588 | |
| 589 while(!done && !mcode) { | |
| 590 int still_running = 0; | |
| 591 | |
| 592 mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL); | |
| 593 | |
| 594 if(!mcode) | |
| 595 mcode = curl_multi_perform(multi, &still_running); | |
| 596 | |
| 597 /* only read 'still_running' if curl_multi_perform() return OK */ | |
| 598 if(!mcode && !still_running) { | |
| 599 int rc; | |
| 600 CURLMsg *msg = curl_multi_info_read(multi, &rc); | |
| 601 if(msg) { | |
| 602 result = msg->data.result; | |
| 603 done = TRUE; | |
| 604 } | |
| 605 } | |
| 606 } | |
| 607 | |
| 608 /* Make sure to return some kind of error if there was a multi problem */ | |
| 609 if(mcode) { | |
| 610 result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY : | |
| 611 /* The other multi errors should never happen, so return | |
| 612 something suitably generic */ | |
| 613 CURLE_BAD_FUNCTION_ARGUMENT; | |
| 614 } | |
| 615 | |
| 616 return result; | |
| 617 } | |
| 618 | |
| 619 | |
| 620 /* | |
| 621 * easy_perform() is the external interface that performs a blocking | |
| 622 * transfer as previously setup. | |
| 623 * | |
| 624 * CONCEPT: This function creates a multi handle, adds the easy handle to it, | |
| 625 * runs curl_multi_perform() until the transfer is done, then detaches the | |
| 626 * easy handle, destroys the multi handle and returns the easy handle's return | |
| 627 * code. | |
| 628 * | |
| 629 * REALITY: it can't just create and destroy the multi handle that easily. It | |
| 630 * needs to keep it around since if this easy handle is used again by this | |
| 631 * function, the same multi handle must be re-used so that the same pools and | |
| 632 * caches can be used. | |
| 633 * | |
| 634 * DEBUG: if 'events' is set TRUE, this function will use a replacement engine | |
| 635 * instead of curl_multi_perform() and use curl_multi_socket_action(). | |
| 636 */ | |
| 637 static CURLcode easy_perform(struct Curl_easy *data, bool events) | |
| 638 { | |
| 639 struct Curl_multi *multi; | |
| 640 CURLMcode mcode; | |
| 641 CURLcode result = CURLE_OK; | |
| 642 SIGPIPE_VARIABLE(pipe_st); | |
| 643 | |
| 644 if(!data) | |
| 645 return CURLE_BAD_FUNCTION_ARGUMENT; | |
| 646 | |
| 647 if(data->set.errorbuffer) | |
| 648 /* clear this as early as possible */ | |
| 649 data->set.errorbuffer[0] = 0; | |
| 650 | |
| 651 if(data->multi) { | |
| 652 failf(data, "easy handle already used in multi handle"); | |
| 653 return CURLE_FAILED_INIT; | |
| 654 } | |
| 655 | |
| 656 if(data->multi_easy) | |
| 657 multi = data->multi_easy; | |
| 658 else { | |
| 659 /* this multi handle will only ever have a single easy handled attached | |
| 660 to it, so make it use minimal hashes */ | |
| 661 multi = Curl_multi_handle(1, 3); | |
| 662 if(!multi) | |
| 663 return CURLE_OUT_OF_MEMORY; | |
| 664 data->multi_easy = multi; | |
| 665 } | |
| 666 | |
| 667 if(multi->in_callback) | |
| 668 return CURLE_RECURSIVE_API_CALL; | |
| 669 | |
| 670 /* Copy the MAXCONNECTS option to the multi handle */ | |
| 671 curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); | |
| 672 | |
| 673 mcode = curl_multi_add_handle(multi, data); | |
| 674 if(mcode) { | |
| 675 curl_multi_cleanup(multi); | |
| 676 if(mcode == CURLM_OUT_OF_MEMORY) | |
| 677 return CURLE_OUT_OF_MEMORY; | |
| 678 return CURLE_FAILED_INIT; | |
| 679 } | |
| 680 | |
| 681 sigpipe_ignore(data, &pipe_st); | |
| 682 | |
| 683 /* assign this after curl_multi_add_handle() since that function checks for | |
| 684 it and rejects this handle otherwise */ | |
| 685 data->multi = multi; | |
| 686 | |
| 687 /* run the transfer */ | |
| 688 result = events ? easy_events(multi) : easy_transfer(multi); | |
| 689 | |
| 690 /* ignoring the return code isn't nice, but atm we can't really handle | |
| 691 a failure here, room for future improvement! */ | |
| 692 (void)curl_multi_remove_handle(multi, data); | |
| 693 | |
| 694 sigpipe_restore(&pipe_st); | |
| 695 | |
| 696 /* The multi handle is kept alive, owned by the easy handle */ | |
| 697 return result; | |
| 698 } | |
| 699 | |
| 700 | |
| 701 /* | |
| 702 * curl_easy_perform() is the external interface that performs a blocking | |
| 703 * transfer as previously setup. | |
| 704 */ | |
| 705 CURLcode curl_easy_perform(struct Curl_easy *data) | |
| 706 { | |
| 707 return easy_perform(data, FALSE); | |
| 708 } | |
| 709 | |
| 710 #ifdef CURLDEBUG | |
| 711 /* | |
| 712 * curl_easy_perform_ev() is the external interface that performs a blocking | |
| 713 * transfer using the event-based API internally. | |
| 714 */ | |
| 715 CURLcode curl_easy_perform_ev(struct Curl_easy *data) | |
| 716 { | |
| 717 return easy_perform(data, TRUE); | |
| 718 } | |
| 719 | |
| 720 #endif | |
| 721 | |
| 722 /* | |
| 723 * curl_easy_cleanup() is the external interface to cleaning/freeing the given | |
| 724 * easy handle. | |
| 725 */ | |
| 726 void curl_easy_cleanup(struct Curl_easy *data) | |
| 727 { | |
| 728 SIGPIPE_VARIABLE(pipe_st); | |
| 729 | |
| 730 if(!data) | |
| 731 return; | |
| 732 | |
| 733 sigpipe_ignore(data, &pipe_st); | |
| 734 Curl_close(data); | |
| 735 sigpipe_restore(&pipe_st); | |
| 736 } | |
| 737 | |
| 738 /* | |
| 739 * curl_easy_getinfo() is an external interface that allows an app to retrieve | |
| 740 * information from a performed transfer and similar. | |
| 741 */ | |
| 742 #undef curl_easy_getinfo | |
| 743 CURLcode curl_easy_getinfo(struct Curl_easy *data, CURLINFO info, ...) | |
| 744 { | |
| 745 va_list arg; | |
| 746 void *paramp; | |
| 747 CURLcode result; | |
| 748 | |
| 749 va_start(arg, info); | |
| 750 paramp = va_arg(arg, void *); | |
| 751 | |
| 752 result = Curl_getinfo(data, info, paramp); | |
| 753 | |
| 754 va_end(arg); | |
| 755 return result; | |
| 756 } | |
| 757 | |
| 758 static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) | |
| 759 { | |
| 760 CURLcode result = CURLE_OK; | |
| 761 enum dupstring i; | |
| 762 | |
| 763 /* Copy src->set into dst->set first, then deal with the strings | |
| 764 afterwards */ | |
| 765 dst->set = src->set; | |
| 766 Curl_mime_initpart(&dst->set.mimepost, dst); | |
| 767 | |
| 768 /* clear all string pointers first */ | |
| 769 memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); | |
| 770 | |
| 771 /* duplicate all strings */ | |
| 772 for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) { | |
| 773 result = Curl_setstropt(&dst->set.str[i], src->set.str[i]); | |
| 774 if(result) | |
| 775 return result; | |
| 776 } | |
| 777 | |
| 778 /* duplicate memory areas pointed to */ | |
| 779 i = STRING_COPYPOSTFIELDS; | |
| 780 if(src->set.postfieldsize && src->set.str[i]) { | |
| 781 /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ | |
| 782 dst->set.str[i] = Curl_memdup(src->set.str[i], | |
| 783 curlx_sotouz(src->set.postfieldsize)); | |
| 784 if(!dst->set.str[i]) | |
| 785 return CURLE_OUT_OF_MEMORY; | |
| 786 /* point to the new copy */ | |
| 787 dst->set.postfields = dst->set.str[i]; | |
| 788 } | |
| 789 | |
| 790 /* Duplicate mime data. */ | |
| 791 result = Curl_mime_duppart(&dst->set.mimepost, &src->set.mimepost); | |
| 792 | |
| 793 if(src->set.resolve) | |
| 794 dst->change.resolve = dst->set.resolve; | |
| 795 | |
| 796 return result; | |
| 797 } | |
| 798 | |
| 799 /* | |
| 800 * curl_easy_duphandle() is an external interface to allow duplication of a | |
| 801 * given input easy handle. The returned handle will be a new working handle | |
| 802 * with all options set exactly as the input source handle. | |
| 803 */ | |
| 804 struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) | |
| 805 { | |
| 806 struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy)); | |
| 807 if(NULL == outcurl) | |
| 808 goto fail; | |
| 809 | |
| 810 /* | |
| 811 * We setup a few buffers we need. We should probably make them | |
| 812 * get setup on-demand in the code, as that would probably decrease | |
| 813 * the likeliness of us forgetting to init a buffer here in the future. | |
| 814 */ | |
| 815 outcurl->set.buffer_size = data->set.buffer_size; | |
| 816 outcurl->state.buffer = malloc(outcurl->set.buffer_size + 1); | |
| 817 if(!outcurl->state.buffer) | |
| 818 goto fail; | |
| 819 | |
| 820 outcurl->state.headerbuff = malloc(HEADERSIZE); | |
| 821 if(!outcurl->state.headerbuff) | |
| 822 goto fail; | |
| 823 outcurl->state.headersize = HEADERSIZE; | |
| 824 | |
| 825 /* copy all userdefined values */ | |
| 826 if(dupset(outcurl, data)) | |
| 827 goto fail; | |
| 828 | |
| 829 /* the connection cache is setup on demand */ | |
| 830 outcurl->state.conn_cache = NULL; | |
| 831 | |
| 832 outcurl->state.lastconnect = NULL; | |
| 833 | |
| 834 outcurl->progress.flags = data->progress.flags; | |
| 835 outcurl->progress.callback = data->progress.callback; | |
| 836 | |
| 837 if(data->cookies) { | |
| 838 /* If cookies are enabled in the parent handle, we enable them | |
| 839 in the clone as well! */ | |
| 840 outcurl->cookies = Curl_cookie_init(data, | |
| 841 data->cookies->filename, | |
| 842 outcurl->cookies, | |
| 843 data->set.cookiesession); | |
| 844 if(!outcurl->cookies) | |
| 845 goto fail; | |
| 846 } | |
| 847 | |
| 848 /* duplicate all values in 'change' */ | |
| 849 if(data->change.cookielist) { | |
| 850 outcurl->change.cookielist = | |
| 851 Curl_slist_duplicate(data->change.cookielist); | |
| 852 if(!outcurl->change.cookielist) | |
| 853 goto fail; | |
| 854 } | |
| 855 | |
| 856 if(data->change.url) { | |
| 857 outcurl->change.url = strdup(data->change.url); | |
| 858 if(!outcurl->change.url) | |
| 859 goto fail; | |
| 860 outcurl->change.url_alloc = TRUE; | |
| 861 } | |
| 862 | |
| 863 if(data->change.referer) { | |
| 864 outcurl->change.referer = strdup(data->change.referer); | |
| 865 if(!outcurl->change.referer) | |
| 866 goto fail; | |
| 867 outcurl->change.referer_alloc = TRUE; | |
| 868 } | |
| 869 | |
| 870 /* Reinitialize an SSL engine for the new handle | |
| 871 * note: the engine name has already been copied by dupset */ | |
| 872 if(outcurl->set.str[STRING_SSL_ENGINE]) { | |
| 873 if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE])) | |
| 874 goto fail; | |
| 875 } | |
| 876 | |
| 877 /* Clone the resolver handle, if present, for the new handle */ | |
| 878 if(Curl_resolver_duphandle(outcurl, | |
| 879 &outcurl->state.resolver, | |
| 880 data->state.resolver)) | |
| 881 goto fail; | |
| 882 | |
| 883 Curl_convert_setup(outcurl); | |
| 884 | |
| 885 Curl_initinfo(outcurl); | |
| 886 | |
| 887 outcurl->magic = CURLEASY_MAGIC_NUMBER; | |
| 888 | |
| 889 /* we reach this point and thus we are OK */ | |
| 890 | |
| 891 return outcurl; | |
| 892 | |
| 893 fail: | |
| 894 | |
| 895 if(outcurl) { | |
| 896 curl_slist_free_all(outcurl->change.cookielist); | |
| 897 outcurl->change.cookielist = NULL; | |
| 898 Curl_safefree(outcurl->state.buffer); | |
| 899 Curl_safefree(outcurl->state.headerbuff); | |
| 900 Curl_safefree(outcurl->change.url); | |
| 901 Curl_safefree(outcurl->change.referer); | |
| 902 Curl_freeset(outcurl); | |
| 903 free(outcurl); | |
| 904 } | |
| 905 | |
| 906 return NULL; | |
| 907 } | |
| 908 | |
| 909 /* | |
| 910 * curl_easy_reset() is an external interface that allows an app to re- | |
| 911 * initialize a session handle to the default values. | |
| 912 */ | |
| 913 void curl_easy_reset(struct Curl_easy *data) | |
| 914 { | |
| 915 long old_buffer_size = data->set.buffer_size; | |
| 916 | |
| 917 Curl_free_request_state(data); | |
| 918 | |
| 919 /* zero out UserDefined data: */ | |
| 920 Curl_freeset(data); | |
| 921 memset(&data->set, 0, sizeof(struct UserDefined)); | |
| 922 (void)Curl_init_userdefined(data); | |
| 923 | |
| 924 /* zero out Progress data: */ | |
| 925 memset(&data->progress, 0, sizeof(struct Progress)); | |
| 926 | |
| 927 /* zero out PureInfo data: */ | |
| 928 Curl_initinfo(data); | |
| 929 | |
| 930 data->progress.flags |= PGRS_HIDE; | |
| 931 data->state.current_speed = -1; /* init to negative == impossible */ | |
| 932 | |
| 933 /* zero out authentication data: */ | |
| 934 memset(&data->state.authhost, 0, sizeof(struct auth)); | |
| 935 memset(&data->state.authproxy, 0, sizeof(struct auth)); | |
| 936 | |
| 937 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) | |
| 938 Curl_http_auth_cleanup_digest(data); | |
| 939 #endif | |
| 940 | |
| 941 /* resize receive buffer */ | |
| 942 if(old_buffer_size != data->set.buffer_size) { | |
| 943 char *newbuff = realloc(data->state.buffer, data->set.buffer_size + 1); | |
| 944 if(!newbuff) { | |
| 945 DEBUGF(fprintf(stderr, "Error: realloc of buffer failed\n")); | |
| 946 /* nothing we can do here except use the old size */ | |
| 947 data->set.buffer_size = old_buffer_size; | |
| 948 } | |
| 949 else | |
| 950 data->state.buffer = newbuff; | |
| 951 } | |
| 952 } | |
| 953 | |
| 954 /* | |
| 955 * curl_easy_pause() allows an application to pause or unpause a specific | |
| 956 * transfer and direction. This function sets the full new state for the | |
| 957 * current connection this easy handle operates on. | |
| 958 * | |
| 959 * NOTE: if you have the receiving paused and you call this function to remove | |
| 960 * the pausing, you may get your write callback called at this point. | |
| 961 * | |
| 962 * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h | |
| 963 * | |
| 964 * NOTE: This is one of few API functions that are allowed to be called from | |
| 965 * within a callback. | |
| 966 */ | |
| 967 CURLcode curl_easy_pause(struct Curl_easy *data, int action) | |
| 968 { | |
| 969 struct SingleRequest *k = &data->req; | |
| 970 CURLcode result = CURLE_OK; | |
| 971 | |
| 972 /* first switch off both pause bits */ | |
| 973 int newstate = k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE); | |
| 974 | |
| 975 /* set the new desired pause bits */ | |
| 976 newstate |= ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) | | |
| 977 ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0); | |
| 978 | |
| 979 /* put it back in the keepon */ | |
| 980 k->keepon = newstate; | |
| 981 | |
| 982 if(!(newstate & KEEP_RECV_PAUSE) && data->state.tempcount) { | |
| 983 /* there are buffers for sending that can be delivered as the receive | |
| 984 pausing is lifted! */ | |
| 985 unsigned int i; | |
| 986 unsigned int count = data->state.tempcount; | |
| 987 struct tempbuf writebuf[3]; /* there can only be three */ | |
| 988 struct connectdata *conn = data->conn; | |
| 989 struct Curl_easy *saved_data = NULL; | |
| 990 | |
| 991 /* copy the structs to allow for immediate re-pausing */ | |
| 992 for(i = 0; i < data->state.tempcount; i++) { | |
| 993 writebuf[i] = data->state.tempwrite[i]; | |
| 994 data->state.tempwrite[i].buf = NULL; | |
| 995 } | |
| 996 data->state.tempcount = 0; | |
| 997 | |
| 998 /* set the connection's current owner */ | |
| 999 if(conn->data != data) { | |
| 1000 saved_data = conn->data; | |
| 1001 conn->data = data; | |
| 1002 } | |
| 1003 | |
| 1004 for(i = 0; i < count; i++) { | |
| 1005 /* even if one function returns error, this loops through and frees all | |
| 1006 buffers */ | |
| 1007 if(!result) | |
| 1008 result = Curl_client_write(conn, writebuf[i].type, writebuf[i].buf, | |
| 1009 writebuf[i].len); | |
| 1010 free(writebuf[i].buf); | |
| 1011 } | |
| 1012 | |
| 1013 /* recover previous owner of the connection */ | |
| 1014 if(saved_data) | |
| 1015 conn->data = saved_data; | |
| 1016 | |
| 1017 if(result) | |
| 1018 return result; | |
| 1019 } | |
| 1020 | |
| 1021 /* if there's no error and we're not pausing both directions, we want | |
| 1022 to have this handle checked soon */ | |
| 1023 if(!result && | |
| 1024 ((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != | |
| 1025 (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) ) { | |
| 1026 Curl_expire(data, 0, EXPIRE_RUN_NOW); /* get this handle going again */ | |
| 1027 if(data->multi) | |
| 1028 Curl_update_timer(data->multi); | |
| 1029 } | |
| 1030 | |
| 1031 /* This transfer may have been moved in or out of the bundle, update | |
| 1032 the corresponding socket callback, if used */ | |
| 1033 Curl_updatesocket(data); | |
| 1034 | |
| 1035 return result; | |
| 1036 } | |
| 1037 | |
| 1038 | |
| 1039 static CURLcode easy_connection(struct Curl_easy *data, | |
| 1040 curl_socket_t *sfd, | |
| 1041 struct connectdata **connp) | |
| 1042 { | |
| 1043 if(data == NULL) | |
| 1044 return CURLE_BAD_FUNCTION_ARGUMENT; | |
| 1045 | |
| 1046 /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */ | |
| 1047 if(!data->set.connect_only) { | |
| 1048 failf(data, "CONNECT_ONLY is required!"); | |
| 1049 return CURLE_UNSUPPORTED_PROTOCOL; | |
| 1050 } | |
| 1051 | |
| 1052 *sfd = Curl_getconnectinfo(data, connp); | |
| 1053 | |
| 1054 if(*sfd == CURL_SOCKET_BAD) { | |
| 1055 failf(data, "Failed to get recent socket"); | |
| 1056 return CURLE_UNSUPPORTED_PROTOCOL; | |
| 1057 } | |
| 1058 | |
| 1059 return CURLE_OK; | |
| 1060 } | |
| 1061 | |
| 1062 /* | |
| 1063 * Receives data from the connected socket. Use after successful | |
| 1064 * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. | |
| 1065 * Returns CURLE_OK on success, error code on error. | |
| 1066 */ | |
| 1067 CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen, | |
| 1068 size_t *n) | |
| 1069 { | |
| 1070 curl_socket_t sfd; | |
| 1071 CURLcode result; | |
| 1072 ssize_t n1; | |
| 1073 struct connectdata *c; | |
| 1074 | |
| 1075 if(Curl_is_in_callback(data)) | |
| 1076 return CURLE_RECURSIVE_API_CALL; | |
| 1077 | |
| 1078 result = easy_connection(data, &sfd, &c); | |
| 1079 if(result) | |
| 1080 return result; | |
| 1081 | |
| 1082 *n = 0; | |
| 1083 result = Curl_read(c, sfd, buffer, buflen, &n1); | |
| 1084 | |
| 1085 if(result) | |
| 1086 return result; | |
| 1087 | |
| 1088 *n = (size_t)n1; | |
| 1089 | |
| 1090 return CURLE_OK; | |
| 1091 } | |
| 1092 | |
| 1093 /* | |
| 1094 * Sends data over the connected socket. Use after successful | |
| 1095 * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. | |
| 1096 */ | |
| 1097 CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, | |
| 1098 size_t buflen, size_t *n) | |
| 1099 { | |
| 1100 curl_socket_t sfd; | |
| 1101 CURLcode result; | |
| 1102 ssize_t n1; | |
| 1103 struct connectdata *c = NULL; | |
| 1104 | |
| 1105 if(Curl_is_in_callback(data)) | |
| 1106 return CURLE_RECURSIVE_API_CALL; | |
| 1107 | |
| 1108 result = easy_connection(data, &sfd, &c); | |
| 1109 if(result) | |
| 1110 return result; | |
| 1111 | |
| 1112 *n = 0; | |
| 1113 result = Curl_write(c, sfd, buffer, buflen, &n1); | |
| 1114 | |
| 1115 if(n1 == -1) | |
| 1116 return CURLE_SEND_ERROR; | |
| 1117 | |
| 1118 /* detect EAGAIN */ | |
| 1119 if(!result && !n1) | |
| 1120 return CURLE_AGAIN; | |
| 1121 | |
| 1122 *n = (size_t)n1; | |
| 1123 | |
| 1124 return result; | |
| 1125 } | |
| 1126 | |
| 1127 /* | |
| 1128 * Wrapper to call functions in Curl_conncache_foreach() | |
| 1129 * | |
| 1130 * Returns always 0. | |
| 1131 */ | |
| 1132 static int conn_upkeep(struct connectdata *conn, | |
| 1133 void *param) | |
| 1134 { | |
| 1135 /* Param is unused. */ | |
| 1136 (void)param; | |
| 1137 | |
| 1138 if(conn->handler->connection_check) { | |
| 1139 /* Do a protocol-specific keepalive check on the connection. */ | |
| 1140 conn->handler->connection_check(conn, CONNCHECK_KEEPALIVE); | |
| 1141 } | |
| 1142 | |
| 1143 return 0; /* continue iteration */ | |
| 1144 } | |
| 1145 | |
| 1146 static CURLcode upkeep(struct conncache *conn_cache, void *data) | |
| 1147 { | |
| 1148 /* Loop over every connection and make connection alive. */ | |
| 1149 Curl_conncache_foreach(data, | |
| 1150 conn_cache, | |
| 1151 data, | |
| 1152 conn_upkeep); | |
| 1153 return CURLE_OK; | |
| 1154 } | |
| 1155 | |
| 1156 /* | |
| 1157 * Performs connection upkeep for the given session handle. | |
| 1158 */ | |
| 1159 CURLcode curl_easy_upkeep(struct Curl_easy *data) | |
| 1160 { | |
| 1161 /* Verify that we got an easy handle we can work with. */ | |
| 1162 if(!GOOD_EASY_HANDLE(data)) | |
| 1163 return CURLE_BAD_FUNCTION_ARGUMENT; | |
| 1164 | |
| 1165 if(data->multi_easy) { | |
| 1166 /* Use the common function to keep connections alive. */ | |
| 1167 return upkeep(&data->multi_easy->conn_cache, data); | |
| 1168 } | |
| 1169 else { | |
| 1170 /* No connections, so just return success */ | |
| 1171 return CURLE_OK; | |
| 1172 } | |
| 1173 } |
