Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/multi.c @ 3:2c135c81b16c
MERGE: upstream PyMuPDF 1.26.4 with MuPDF 1.26.7
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:44:09 +0200 |
| parents | b50eed0cc0ef |
| children |
comparison
equal
deleted
inserted
replaced
| 0:6015a75abc2d | 3:2c135c81b16c |
|---|---|
| 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 #include <curl/curl.h> | |
| 26 | |
| 27 #include "urldata.h" | |
| 28 #include "transfer.h" | |
| 29 #include "url.h" | |
| 30 #include "connect.h" | |
| 31 #include "progress.h" | |
| 32 #include "easyif.h" | |
| 33 #include "share.h" | |
| 34 #include "psl.h" | |
| 35 #include "multiif.h" | |
| 36 #include "sendf.h" | |
| 37 #include "timeval.h" | |
| 38 #include "http.h" | |
| 39 #include "select.h" | |
| 40 #include "warnless.h" | |
| 41 #include "speedcheck.h" | |
| 42 #include "conncache.h" | |
| 43 #include "multihandle.h" | |
| 44 #include "sigpipe.h" | |
| 45 #include "vtls/vtls.h" | |
| 46 #include "connect.h" | |
| 47 #include "http_proxy.h" | |
| 48 #include "http2.h" | |
| 49 /* The last 3 #include files should be in this order */ | |
| 50 #include "curl_printf.h" | |
| 51 #include "curl_memory.h" | |
| 52 #include "memdebug.h" | |
| 53 | |
| 54 /* | |
| 55 CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 | |
| 56 to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every | |
| 57 CURL handle takes 45-50 K memory, therefore this 3K are not significant. | |
| 58 */ | |
| 59 #ifndef CURL_SOCKET_HASH_TABLE_SIZE | |
| 60 #define CURL_SOCKET_HASH_TABLE_SIZE 911 | |
| 61 #endif | |
| 62 | |
| 63 #ifndef CURL_CONNECTION_HASH_SIZE | |
| 64 #define CURL_CONNECTION_HASH_SIZE 97 | |
| 65 #endif | |
| 66 | |
| 67 #define CURL_MULTI_HANDLE 0x000bab1e | |
| 68 | |
| 69 #define GOOD_MULTI_HANDLE(x) \ | |
| 70 ((x) && (x)->type == CURL_MULTI_HANDLE) | |
| 71 | |
| 72 static CURLMcode singlesocket(struct Curl_multi *multi, | |
| 73 struct Curl_easy *data); | |
| 74 static CURLMcode add_next_timeout(struct curltime now, | |
| 75 struct Curl_multi *multi, | |
| 76 struct Curl_easy *d); | |
| 77 static CURLMcode multi_timeout(struct Curl_multi *multi, | |
| 78 long *timeout_ms); | |
| 79 static void process_pending_handles(struct Curl_multi *multi); | |
| 80 static void detach_connnection(struct Curl_easy *data); | |
| 81 | |
| 82 #ifdef DEBUGBUILD | |
| 83 static const char * const statename[]={ | |
| 84 "INIT", | |
| 85 "CONNECT_PEND", | |
| 86 "CONNECT", | |
| 87 "WAITRESOLVE", | |
| 88 "WAITCONNECT", | |
| 89 "WAITPROXYCONNECT", | |
| 90 "SENDPROTOCONNECT", | |
| 91 "PROTOCONNECT", | |
| 92 "DO", | |
| 93 "DOING", | |
| 94 "DO_MORE", | |
| 95 "DO_DONE", | |
| 96 "PERFORM", | |
| 97 "TOOFAST", | |
| 98 "DONE", | |
| 99 "COMPLETED", | |
| 100 "MSGSENT", | |
| 101 }; | |
| 102 #endif | |
| 103 | |
| 104 /* function pointer called once when switching TO a state */ | |
| 105 typedef void (*init_multistate_func)(struct Curl_easy *data); | |
| 106 | |
| 107 static void Curl_init_completed(struct Curl_easy *data) | |
| 108 { | |
| 109 /* this is a completed transfer */ | |
| 110 | |
| 111 /* Important: reset the conn pointer so that we don't point to memory | |
| 112 that could be freed anytime */ | |
| 113 detach_connnection(data); | |
| 114 Curl_expire_clear(data); /* stop all timers */ | |
| 115 } | |
| 116 | |
| 117 /* always use this function to change state, to make debugging easier */ | |
| 118 static void mstate(struct Curl_easy *data, CURLMstate state | |
| 119 #ifdef DEBUGBUILD | |
| 120 , int lineno | |
| 121 #endif | |
| 122 ) | |
| 123 { | |
| 124 CURLMstate oldstate = data->mstate; | |
| 125 static const init_multistate_func finit[CURLM_STATE_LAST] = { | |
| 126 NULL, /* INIT */ | |
| 127 NULL, /* CONNECT_PEND */ | |
| 128 Curl_init_CONNECT, /* CONNECT */ | |
| 129 NULL, /* WAITRESOLVE */ | |
| 130 NULL, /* WAITCONNECT */ | |
| 131 NULL, /* WAITPROXYCONNECT */ | |
| 132 NULL, /* SENDPROTOCONNECT */ | |
| 133 NULL, /* PROTOCONNECT */ | |
| 134 Curl_connect_free, /* DO */ | |
| 135 NULL, /* DOING */ | |
| 136 NULL, /* DO_MORE */ | |
| 137 NULL, /* DO_DONE */ | |
| 138 NULL, /* PERFORM */ | |
| 139 NULL, /* TOOFAST */ | |
| 140 NULL, /* DONE */ | |
| 141 Curl_init_completed, /* COMPLETED */ | |
| 142 NULL /* MSGSENT */ | |
| 143 }; | |
| 144 | |
| 145 #if defined(DEBUGBUILD) && defined(CURL_DISABLE_VERBOSE_STRINGS) | |
| 146 (void) lineno; | |
| 147 #endif | |
| 148 | |
| 149 if(oldstate == state) | |
| 150 /* don't bother when the new state is the same as the old state */ | |
| 151 return; | |
| 152 | |
| 153 data->mstate = state; | |
| 154 | |
| 155 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) | |
| 156 if(data->mstate >= CURLM_STATE_CONNECT_PEND && | |
| 157 data->mstate < CURLM_STATE_COMPLETED) { | |
| 158 long connection_id = -5000; | |
| 159 | |
| 160 if(data->conn) | |
| 161 connection_id = data->conn->connection_id; | |
| 162 | |
| 163 infof(data, | |
| 164 "STATE: %s => %s handle %p; line %d (connection #%ld)\n", | |
| 165 statename[oldstate], statename[data->mstate], | |
| 166 (void *)data, lineno, connection_id); | |
| 167 } | |
| 168 #endif | |
| 169 | |
| 170 if(state == CURLM_STATE_COMPLETED) | |
| 171 /* changing to COMPLETED means there's one less easy handle 'alive' */ | |
| 172 data->multi->num_alive--; | |
| 173 | |
| 174 /* if this state has an init-function, run it */ | |
| 175 if(finit[state]) | |
| 176 finit[state](data); | |
| 177 } | |
| 178 | |
| 179 #ifndef DEBUGBUILD | |
| 180 #define multistate(x,y) mstate(x,y) | |
| 181 #else | |
| 182 #define multistate(x,y) mstate(x,y, __LINE__) | |
| 183 #endif | |
| 184 | |
| 185 /* | |
| 186 * We add one of these structs to the sockhash for each socket | |
| 187 */ | |
| 188 | |
| 189 struct Curl_sh_entry { | |
| 190 struct curl_hash transfers; /* hash of transfers using this socket */ | |
| 191 unsigned int action; /* what combined action READ/WRITE this socket waits | |
| 192 for */ | |
| 193 void *socketp; /* settable by users with curl_multi_assign() */ | |
| 194 unsigned int users; /* number of transfers using this */ | |
| 195 unsigned int readers; /* this many transfers want to read */ | |
| 196 unsigned int writers; /* this many transfers want to write */ | |
| 197 }; | |
| 198 /* bits for 'action' having no bits means this socket is not expecting any | |
| 199 action */ | |
| 200 #define SH_READ 1 | |
| 201 #define SH_WRITE 2 | |
| 202 | |
| 203 /* look up a given socket in the socket hash, skip invalid sockets */ | |
| 204 static struct Curl_sh_entry *sh_getentry(struct curl_hash *sh, | |
| 205 curl_socket_t s) | |
| 206 { | |
| 207 if(s != CURL_SOCKET_BAD) { | |
| 208 /* only look for proper sockets */ | |
| 209 return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t)); | |
| 210 } | |
| 211 return NULL; | |
| 212 } | |
| 213 | |
| 214 #define TRHASH_SIZE 13 | |
| 215 static size_t trhash(void *key, size_t key_length, size_t slots_num) | |
| 216 { | |
| 217 size_t keyval = (size_t)*(struct Curl_easy **)key; | |
| 218 (void) key_length; | |
| 219 | |
| 220 return (keyval % slots_num); | |
| 221 } | |
| 222 | |
| 223 static size_t trhash_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) | |
| 224 { | |
| 225 (void)k1_len; | |
| 226 (void)k2_len; | |
| 227 | |
| 228 return *(struct Curl_easy **)k1 == *(struct Curl_easy **)k2; | |
| 229 } | |
| 230 | |
| 231 static void trhash_dtor(void *nada) | |
| 232 { | |
| 233 (void)nada; | |
| 234 } | |
| 235 | |
| 236 | |
| 237 /* make sure this socket is present in the hash for this handle */ | |
| 238 static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh, | |
| 239 curl_socket_t s) | |
| 240 { | |
| 241 struct Curl_sh_entry *there = sh_getentry(sh, s); | |
| 242 struct Curl_sh_entry *check; | |
| 243 | |
| 244 if(there) { | |
| 245 /* it is present, return fine */ | |
| 246 return there; | |
| 247 } | |
| 248 | |
| 249 /* not present, add it */ | |
| 250 check = calloc(1, sizeof(struct Curl_sh_entry)); | |
| 251 if(!check) | |
| 252 return NULL; /* major failure */ | |
| 253 | |
| 254 if(Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash, | |
| 255 trhash_compare, trhash_dtor)) { | |
| 256 free(check); | |
| 257 return NULL; | |
| 258 } | |
| 259 | |
| 260 /* make/add new hash entry */ | |
| 261 if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { | |
| 262 free(check); | |
| 263 return NULL; /* major failure */ | |
| 264 } | |
| 265 | |
| 266 return check; /* things are good in sockhash land */ | |
| 267 } | |
| 268 | |
| 269 | |
| 270 /* delete the given socket + handle from the hash */ | |
| 271 static void sh_delentry(struct Curl_sh_entry *entry, | |
| 272 struct curl_hash *sh, curl_socket_t s) | |
| 273 { | |
| 274 Curl_hash_destroy(&entry->transfers); | |
| 275 | |
| 276 /* We remove the hash entry. This will end up in a call to | |
| 277 sh_freeentry(). */ | |
| 278 Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t)); | |
| 279 } | |
| 280 | |
| 281 /* | |
| 282 * free a sockhash entry | |
| 283 */ | |
| 284 static void sh_freeentry(void *freethis) | |
| 285 { | |
| 286 struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis; | |
| 287 | |
| 288 free(p); | |
| 289 } | |
| 290 | |
| 291 static size_t fd_key_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) | |
| 292 { | |
| 293 (void) k1_len; (void) k2_len; | |
| 294 | |
| 295 return (*((curl_socket_t *) k1)) == (*((curl_socket_t *) k2)); | |
| 296 } | |
| 297 | |
| 298 static size_t hash_fd(void *key, size_t key_length, size_t slots_num) | |
| 299 { | |
| 300 curl_socket_t fd = *((curl_socket_t *) key); | |
| 301 (void) key_length; | |
| 302 | |
| 303 return (fd % slots_num); | |
| 304 } | |
| 305 | |
| 306 /* | |
| 307 * sh_init() creates a new socket hash and returns the handle for it. | |
| 308 * | |
| 309 * Quote from README.multi_socket: | |
| 310 * | |
| 311 * "Some tests at 7000 and 9000 connections showed that the socket hash lookup | |
| 312 * is somewhat of a bottle neck. Its current implementation may be a bit too | |
| 313 * limiting. It simply has a fixed-size array, and on each entry in the array | |
| 314 * it has a linked list with entries. So the hash only checks which list to | |
| 315 * scan through. The code I had used so for used a list with merely 7 slots | |
| 316 * (as that is what the DNS hash uses) but with 7000 connections that would | |
| 317 * make an average of 1000 nodes in each list to run through. I upped that to | |
| 318 * 97 slots (I believe a prime is suitable) and noticed a significant speed | |
| 319 * increase. I need to reconsider the hash implementation or use a rather | |
| 320 * large default value like this. At 9000 connections I was still below 10us | |
| 321 * per call." | |
| 322 * | |
| 323 */ | |
| 324 static int sh_init(struct curl_hash *hash, int hashsize) | |
| 325 { | |
| 326 return Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare, | |
| 327 sh_freeentry); | |
| 328 } | |
| 329 | |
| 330 /* | |
| 331 * multi_addmsg() | |
| 332 * | |
| 333 * Called when a transfer is completed. Adds the given msg pointer to | |
| 334 * the list kept in the multi handle. | |
| 335 */ | |
| 336 static CURLMcode multi_addmsg(struct Curl_multi *multi, | |
| 337 struct Curl_message *msg) | |
| 338 { | |
| 339 Curl_llist_insert_next(&multi->msglist, multi->msglist.tail, msg, | |
| 340 &msg->list); | |
| 341 return CURLM_OK; | |
| 342 } | |
| 343 | |
| 344 struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ | |
| 345 int chashsize) /* connection hash */ | |
| 346 { | |
| 347 struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); | |
| 348 | |
| 349 if(!multi) | |
| 350 return NULL; | |
| 351 | |
| 352 multi->type = CURL_MULTI_HANDLE; | |
| 353 | |
| 354 if(Curl_mk_dnscache(&multi->hostcache)) | |
| 355 goto error; | |
| 356 | |
| 357 if(sh_init(&multi->sockhash, hashsize)) | |
| 358 goto error; | |
| 359 | |
| 360 if(Curl_conncache_init(&multi->conn_cache, chashsize)) | |
| 361 goto error; | |
| 362 | |
| 363 Curl_llist_init(&multi->msglist, NULL); | |
| 364 Curl_llist_init(&multi->pending, NULL); | |
| 365 | |
| 366 multi->multiplexing = CURLPIPE_MULTIPLEX; | |
| 367 | |
| 368 /* -1 means it not set by user, use the default value */ | |
| 369 multi->maxconnects = -1; | |
| 370 return multi; | |
| 371 | |
| 372 error: | |
| 373 | |
| 374 Curl_hash_destroy(&multi->sockhash); | |
| 375 Curl_hash_destroy(&multi->hostcache); | |
| 376 Curl_conncache_destroy(&multi->conn_cache); | |
| 377 Curl_llist_destroy(&multi->msglist, NULL); | |
| 378 Curl_llist_destroy(&multi->pending, NULL); | |
| 379 | |
| 380 free(multi); | |
| 381 return NULL; | |
| 382 } | |
| 383 | |
| 384 struct Curl_multi *curl_multi_init(void) | |
| 385 { | |
| 386 return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE, | |
| 387 CURL_CONNECTION_HASH_SIZE); | |
| 388 } | |
| 389 | |
| 390 CURLMcode curl_multi_add_handle(struct Curl_multi *multi, | |
| 391 struct Curl_easy *data) | |
| 392 { | |
| 393 /* First, make some basic checks that the CURLM handle is a good handle */ | |
| 394 if(!GOOD_MULTI_HANDLE(multi)) | |
| 395 return CURLM_BAD_HANDLE; | |
| 396 | |
| 397 /* Verify that we got a somewhat good easy handle too */ | |
| 398 if(!GOOD_EASY_HANDLE(data)) | |
| 399 return CURLM_BAD_EASY_HANDLE; | |
| 400 | |
| 401 /* Prevent users from adding same easy handle more than once and prevent | |
| 402 adding to more than one multi stack */ | |
| 403 if(data->multi) | |
| 404 return CURLM_ADDED_ALREADY; | |
| 405 | |
| 406 if(multi->in_callback) | |
| 407 return CURLM_RECURSIVE_API_CALL; | |
| 408 | |
| 409 /* Initialize timeout list for this handle */ | |
| 410 Curl_llist_init(&data->state.timeoutlist, NULL); | |
| 411 | |
| 412 /* | |
| 413 * No failure allowed in this function beyond this point. And no | |
| 414 * modification of easy nor multi handle allowed before this except for | |
| 415 * potential multi's connection cache growing which won't be undone in this | |
| 416 * function no matter what. | |
| 417 */ | |
| 418 if(data->set.errorbuffer) | |
| 419 data->set.errorbuffer[0] = 0; | |
| 420 | |
| 421 /* set the easy handle */ | |
| 422 multistate(data, CURLM_STATE_INIT); | |
| 423 | |
| 424 /* for multi interface connections, we share DNS cache automatically if the | |
| 425 easy handle's one is currently not set. */ | |
| 426 if(!data->dns.hostcache || | |
| 427 (data->dns.hostcachetype == HCACHE_NONE)) { | |
| 428 data->dns.hostcache = &multi->hostcache; | |
| 429 data->dns.hostcachetype = HCACHE_MULTI; | |
| 430 } | |
| 431 | |
| 432 /* Point to the shared or multi handle connection cache */ | |
| 433 if(data->share && (data->share->specifier & (1<< CURL_LOCK_DATA_CONNECT))) | |
| 434 data->state.conn_cache = &data->share->conn_cache; | |
| 435 else | |
| 436 data->state.conn_cache = &multi->conn_cache; | |
| 437 | |
| 438 #ifdef USE_LIBPSL | |
| 439 /* Do the same for PSL. */ | |
| 440 if(data->share && (data->share->specifier & (1 << CURL_LOCK_DATA_PSL))) | |
| 441 data->psl = &data->share->psl; | |
| 442 else | |
| 443 data->psl = &multi->psl; | |
| 444 #endif | |
| 445 | |
| 446 /* We add the new entry last in the list. */ | |
| 447 data->next = NULL; /* end of the line */ | |
| 448 if(multi->easyp) { | |
| 449 struct Curl_easy *last = multi->easylp; | |
| 450 last->next = data; | |
| 451 data->prev = last; | |
| 452 multi->easylp = data; /* the new last node */ | |
| 453 } | |
| 454 else { | |
| 455 /* first node, make prev NULL! */ | |
| 456 data->prev = NULL; | |
| 457 multi->easylp = multi->easyp = data; /* both first and last */ | |
| 458 } | |
| 459 | |
| 460 /* make the Curl_easy refer back to this multi handle */ | |
| 461 data->multi = multi; | |
| 462 | |
| 463 /* Set the timeout for this handle to expire really soon so that it will | |
| 464 be taken care of even when this handle is added in the midst of operation | |
| 465 when only the curl_multi_socket() API is used. During that flow, only | |
| 466 sockets that time-out or have actions will be dealt with. Since this | |
| 467 handle has no action yet, we make sure it times out to get things to | |
| 468 happen. */ | |
| 469 Curl_expire(data, 0, EXPIRE_RUN_NOW); | |
| 470 | |
| 471 /* increase the node-counter */ | |
| 472 multi->num_easy++; | |
| 473 | |
| 474 /* increase the alive-counter */ | |
| 475 multi->num_alive++; | |
| 476 | |
| 477 /* A somewhat crude work-around for a little glitch in Curl_update_timer() | |
| 478 that happens if the lastcall time is set to the same time when the handle | |
| 479 is removed as when the next handle is added, as then the check in | |
| 480 Curl_update_timer() that prevents calling the application multiple times | |
| 481 with the same timer info will not trigger and then the new handle's | |
| 482 timeout will not be notified to the app. | |
| 483 | |
| 484 The work-around is thus simply to clear the 'lastcall' variable to force | |
| 485 Curl_update_timer() to always trigger a callback to the app when a new | |
| 486 easy handle is added */ | |
| 487 memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); | |
| 488 | |
| 489 /* The closure handle only ever has default timeouts set. To improve the | |
| 490 state somewhat we clone the timeouts from each added handle so that the | |
| 491 closure handle always has the same timeouts as the most recently added | |
| 492 easy handle. */ | |
| 493 data->state.conn_cache->closure_handle->set.timeout = data->set.timeout; | |
| 494 data->state.conn_cache->closure_handle->set.server_response_timeout = | |
| 495 data->set.server_response_timeout; | |
| 496 data->state.conn_cache->closure_handle->set.no_signal = | |
| 497 data->set.no_signal; | |
| 498 | |
| 499 Curl_update_timer(multi); | |
| 500 return CURLM_OK; | |
| 501 } | |
| 502 | |
| 503 #if 0 | |
| 504 /* Debug-function, used like this: | |
| 505 * | |
| 506 * Curl_hash_print(multi->sockhash, debug_print_sock_hash); | |
| 507 * | |
| 508 * Enable the hash print function first by editing hash.c | |
| 509 */ | |
| 510 static void debug_print_sock_hash(void *p) | |
| 511 { | |
| 512 struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; | |
| 513 | |
| 514 fprintf(stderr, " [easy %p/magic %x/socket %d]", | |
| 515 (void *)sh->data, sh->data->magic, (int)sh->socket); | |
| 516 } | |
| 517 #endif | |
| 518 | |
| 519 static CURLcode multi_done(struct Curl_easy *data, | |
| 520 CURLcode status, /* an error if this is called | |
| 521 after an error was detected */ | |
| 522 bool premature) | |
| 523 { | |
| 524 CURLcode result; | |
| 525 struct connectdata *conn = data->conn; | |
| 526 unsigned int i; | |
| 527 | |
| 528 DEBUGF(infof(data, "multi_done\n")); | |
| 529 | |
| 530 if(data->state.done) | |
| 531 /* Stop if multi_done() has already been called */ | |
| 532 return CURLE_OK; | |
| 533 | |
| 534 /* Stop the resolver and free its own resources (but not dns_entry yet). */ | |
| 535 Curl_resolver_kill(conn); | |
| 536 | |
| 537 /* Cleanup possible redirect junk */ | |
| 538 Curl_safefree(data->req.newurl); | |
| 539 Curl_safefree(data->req.location); | |
| 540 | |
| 541 switch(status) { | |
| 542 case CURLE_ABORTED_BY_CALLBACK: | |
| 543 case CURLE_READ_ERROR: | |
| 544 case CURLE_WRITE_ERROR: | |
| 545 /* When we're aborted due to a callback return code it basically have to | |
| 546 be counted as premature as there is trouble ahead if we don't. We have | |
| 547 many callbacks and protocols work differently, we could potentially do | |
| 548 this more fine-grained in the future. */ | |
| 549 premature = TRUE; | |
| 550 default: | |
| 551 break; | |
| 552 } | |
| 553 | |
| 554 /* this calls the protocol-specific function pointer previously set */ | |
| 555 if(conn->handler->done) | |
| 556 result = conn->handler->done(conn, status, premature); | |
| 557 else | |
| 558 result = status; | |
| 559 | |
| 560 if(CURLE_ABORTED_BY_CALLBACK != result) { | |
| 561 /* avoid this if we already aborted by callback to avoid this calling | |
| 562 another callback */ | |
| 563 CURLcode rc = Curl_pgrsDone(conn); | |
| 564 if(!result && rc) | |
| 565 result = CURLE_ABORTED_BY_CALLBACK; | |
| 566 } | |
| 567 | |
| 568 process_pending_handles(data->multi); /* connection / multiplex */ | |
| 569 | |
| 570 detach_connnection(data); | |
| 571 if(CONN_INUSE(conn)) { | |
| 572 /* Stop if still used. */ | |
| 573 DEBUGF(infof(data, "Connection still in use %zu, " | |
| 574 "no more multi_done now!\n", | |
| 575 conn->easyq.size)); | |
| 576 return CURLE_OK; | |
| 577 } | |
| 578 | |
| 579 data->state.done = TRUE; /* called just now! */ | |
| 580 | |
| 581 if(conn->dns_entry) { | |
| 582 Curl_resolv_unlock(data, conn->dns_entry); /* done with this */ | |
| 583 conn->dns_entry = NULL; | |
| 584 } | |
| 585 Curl_hostcache_prune(data); | |
| 586 Curl_safefree(data->state.ulbuf); | |
| 587 | |
| 588 /* if the transfer was completed in a paused state there can be buffered | |
| 589 data left to free */ | |
| 590 for(i = 0; i < data->state.tempcount; i++) { | |
| 591 free(data->state.tempwrite[i].buf); | |
| 592 } | |
| 593 data->state.tempcount = 0; | |
| 594 | |
| 595 /* if data->set.reuse_forbid is TRUE, it means the libcurl client has | |
| 596 forced us to close this connection. This is ignored for requests taking | |
| 597 place in a NTLM/NEGOTIATE authentication handshake | |
| 598 | |
| 599 if conn->bits.close is TRUE, it means that the connection should be | |
| 600 closed in spite of all our efforts to be nice, due to protocol | |
| 601 restrictions in our or the server's end | |
| 602 | |
| 603 if premature is TRUE, it means this connection was said to be DONE before | |
| 604 the entire request operation is complete and thus we can't know in what | |
| 605 state it is for re-using, so we're forced to close it. In a perfect world | |
| 606 we can add code that keep track of if we really must close it here or not, | |
| 607 but currently we have no such detail knowledge. | |
| 608 */ | |
| 609 | |
| 610 if((data->set.reuse_forbid | |
| 611 #if defined(USE_NTLM) | |
| 612 && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 || | |
| 613 conn->proxy_ntlm_state == NTLMSTATE_TYPE2) | |
| 614 #endif | |
| 615 #if defined(USE_SPNEGO) | |
| 616 && !(conn->http_negotiate_state == GSS_AUTHRECV || | |
| 617 conn->proxy_negotiate_state == GSS_AUTHRECV) | |
| 618 #endif | |
| 619 ) || conn->bits.close | |
| 620 || (premature && !(conn->handler->flags & PROTOPT_STREAM))) { | |
| 621 CURLcode res2 = Curl_disconnect(data, conn, premature); | |
| 622 | |
| 623 /* If we had an error already, make sure we return that one. But | |
| 624 if we got a new error, return that. */ | |
| 625 if(!result && res2) | |
| 626 result = res2; | |
| 627 } | |
| 628 else { | |
| 629 char buffer[256]; | |
| 630 /* create string before returning the connection */ | |
| 631 msnprintf(buffer, sizeof(buffer), | |
| 632 "Connection #%ld to host %s left intact", | |
| 633 conn->connection_id, | |
| 634 conn->bits.socksproxy ? conn->socks_proxy.host.dispname : | |
| 635 conn->bits.httpproxy ? conn->http_proxy.host.dispname : | |
| 636 conn->bits.conn_to_host ? conn->conn_to_host.dispname : | |
| 637 conn->host.dispname); | |
| 638 | |
| 639 /* the connection is no longer in use by this transfer */ | |
| 640 if(Curl_conncache_return_conn(conn)) { | |
| 641 /* remember the most recently used connection */ | |
| 642 data->state.lastconnect = conn; | |
| 643 infof(data, "%s\n", buffer); | |
| 644 } | |
| 645 else | |
| 646 data->state.lastconnect = NULL; | |
| 647 } | |
| 648 | |
| 649 Curl_free_request_state(data); | |
| 650 return result; | |
| 651 } | |
| 652 | |
| 653 CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, | |
| 654 struct Curl_easy *data) | |
| 655 { | |
| 656 struct Curl_easy *easy = data; | |
| 657 bool premature; | |
| 658 bool easy_owns_conn; | |
| 659 struct curl_llist_element *e; | |
| 660 | |
| 661 /* First, make some basic checks that the CURLM handle is a good handle */ | |
| 662 if(!GOOD_MULTI_HANDLE(multi)) | |
| 663 return CURLM_BAD_HANDLE; | |
| 664 | |
| 665 /* Verify that we got a somewhat good easy handle too */ | |
| 666 if(!GOOD_EASY_HANDLE(data)) | |
| 667 return CURLM_BAD_EASY_HANDLE; | |
| 668 | |
| 669 /* Prevent users from trying to remove same easy handle more than once */ | |
| 670 if(!data->multi) | |
| 671 return CURLM_OK; /* it is already removed so let's say it is fine! */ | |
| 672 | |
| 673 if(multi->in_callback) | |
| 674 return CURLM_RECURSIVE_API_CALL; | |
| 675 | |
| 676 premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE; | |
| 677 easy_owns_conn = (data->conn && (data->conn->data == easy)) ? | |
| 678 TRUE : FALSE; | |
| 679 | |
| 680 /* If the 'state' is not INIT or COMPLETED, we might need to do something | |
| 681 nice to put the easy_handle in a good known state when this returns. */ | |
| 682 if(premature) { | |
| 683 /* this handle is "alive" so we need to count down the total number of | |
| 684 alive connections when this is removed */ | |
| 685 multi->num_alive--; | |
| 686 } | |
| 687 | |
| 688 if(data->conn && | |
| 689 data->mstate > CURLM_STATE_DO && | |
| 690 data->mstate < CURLM_STATE_COMPLETED) { | |
| 691 /* Set connection owner so that the DONE function closes it. We can | |
| 692 safely do this here since connection is killed. */ | |
| 693 data->conn->data = easy; | |
| 694 streamclose(data->conn, "Removed with partial response"); | |
| 695 easy_owns_conn = TRUE; | |
| 696 } | |
| 697 | |
| 698 /* The timer must be shut down before data->multi is set to NULL, | |
| 699 else the timenode will remain in the splay tree after | |
| 700 curl_easy_cleanup is called. */ | |
| 701 Curl_expire_clear(data); | |
| 702 | |
| 703 if(data->conn) { | |
| 704 | |
| 705 /* we must call multi_done() here (if we still own the connection) so that | |
| 706 we don't leave a half-baked one around */ | |
| 707 if(easy_owns_conn) { | |
| 708 | |
| 709 /* multi_done() clears the conn->data field to lose the association | |
| 710 between the easy handle and the connection | |
| 711 | |
| 712 Note that this ignores the return code simply because there's | |
| 713 nothing really useful to do with it anyway! */ | |
| 714 (void)multi_done(data, data->result, premature); | |
| 715 } | |
| 716 } | |
| 717 | |
| 718 if(data->connect_queue.ptr) | |
| 719 /* the handle was in the pending list waiting for an available connection, | |
| 720 so go ahead and remove it */ | |
| 721 Curl_llist_remove(&multi->pending, &data->connect_queue, NULL); | |
| 722 | |
| 723 if(data->dns.hostcachetype == HCACHE_MULTI) { | |
| 724 /* stop using the multi handle's DNS cache, *after* the possible | |
| 725 multi_done() call above */ | |
| 726 data->dns.hostcache = NULL; | |
| 727 data->dns.hostcachetype = HCACHE_NONE; | |
| 728 } | |
| 729 | |
| 730 Curl_wildcard_dtor(&data->wildcard); | |
| 731 | |
| 732 /* destroy the timeout list that is held in the easy handle, do this *after* | |
| 733 multi_done() as that may actually call Curl_expire that uses this */ | |
| 734 Curl_llist_destroy(&data->state.timeoutlist, NULL); | |
| 735 | |
| 736 /* as this was using a shared connection cache we clear the pointer to that | |
| 737 since we're not part of that multi handle anymore */ | |
| 738 data->state.conn_cache = NULL; | |
| 739 | |
| 740 /* change state without using multistate(), only to make singlesocket() do | |
| 741 what we want */ | |
| 742 data->mstate = CURLM_STATE_COMPLETED; | |
| 743 singlesocket(multi, easy); /* to let the application know what sockets that | |
| 744 vanish with this handle */ | |
| 745 | |
| 746 /* Remove the association between the connection and the handle */ | |
| 747 if(data->conn) { | |
| 748 data->conn->data = NULL; | |
| 749 detach_connnection(data); | |
| 750 } | |
| 751 | |
| 752 #ifdef USE_LIBPSL | |
| 753 /* Remove the PSL association. */ | |
| 754 if(data->psl == &multi->psl) | |
| 755 data->psl = NULL; | |
| 756 #endif | |
| 757 | |
| 758 data->multi = NULL; /* clear the association to this multi handle */ | |
| 759 | |
| 760 /* make sure there's no pending message in the queue sent from this easy | |
| 761 handle */ | |
| 762 | |
| 763 for(e = multi->msglist.head; e; e = e->next) { | |
| 764 struct Curl_message *msg = e->ptr; | |
| 765 | |
| 766 if(msg->extmsg.easy_handle == easy) { | |
| 767 Curl_llist_remove(&multi->msglist, e, NULL); | |
| 768 /* there can only be one from this specific handle */ | |
| 769 break; | |
| 770 } | |
| 771 } | |
| 772 | |
| 773 /* make the previous node point to our next */ | |
| 774 if(data->prev) | |
| 775 data->prev->next = data->next; | |
| 776 else | |
| 777 multi->easyp = data->next; /* point to first node */ | |
| 778 | |
| 779 /* make our next point to our previous node */ | |
| 780 if(data->next) | |
| 781 data->next->prev = data->prev; | |
| 782 else | |
| 783 multi->easylp = data->prev; /* point to last node */ | |
| 784 | |
| 785 /* NOTE NOTE NOTE | |
| 786 We do not touch the easy handle here! */ | |
| 787 multi->num_easy--; /* one less to care about now */ | |
| 788 | |
| 789 Curl_update_timer(multi); | |
| 790 return CURLM_OK; | |
| 791 } | |
| 792 | |
| 793 /* Return TRUE if the application asked for multiplexing */ | |
| 794 bool Curl_multiplex_wanted(const struct Curl_multi *multi) | |
| 795 { | |
| 796 return (multi && (multi->multiplexing)); | |
| 797 } | |
| 798 | |
| 799 /* This is the only function that should clear data->conn. This will | |
| 800 occasionally be called with the pointer already cleared. */ | |
| 801 static void detach_connnection(struct Curl_easy *data) | |
| 802 { | |
| 803 struct connectdata *conn = data->conn; | |
| 804 if(conn) | |
| 805 Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL); | |
| 806 data->conn = NULL; | |
| 807 } | |
| 808 | |
| 809 /* This is the only function that should assign data->conn */ | |
| 810 void Curl_attach_connnection(struct Curl_easy *data, | |
| 811 struct connectdata *conn) | |
| 812 { | |
| 813 DEBUGASSERT(!data->conn); | |
| 814 DEBUGASSERT(conn); | |
| 815 data->conn = conn; | |
| 816 Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data, | |
| 817 &data->conn_queue); | |
| 818 } | |
| 819 | |
| 820 static int waitconnect_getsock(struct connectdata *conn, | |
| 821 curl_socket_t *sock) | |
| 822 { | |
| 823 int i; | |
| 824 int s = 0; | |
| 825 int rc = 0; | |
| 826 | |
| 827 #ifdef USE_SSL | |
| 828 if(CONNECT_FIRSTSOCKET_PROXY_SSL()) | |
| 829 return Curl_ssl_getsock(conn, sock); | |
| 830 #endif | |
| 831 | |
| 832 for(i = 0; i<2; i++) { | |
| 833 if(conn->tempsock[i] != CURL_SOCKET_BAD) { | |
| 834 sock[s] = conn->tempsock[i]; | |
| 835 rc |= GETSOCK_WRITESOCK(s); | |
| 836 #ifdef ENABLE_QUIC | |
| 837 if(conn->transport == TRNSPRT_QUIC) | |
| 838 /* when connecting QUIC, we want to read the socket too */ | |
| 839 rc |= GETSOCK_READSOCK(s); | |
| 840 #endif | |
| 841 s++; | |
| 842 } | |
| 843 } | |
| 844 | |
| 845 return rc; | |
| 846 } | |
| 847 | |
| 848 static int waitproxyconnect_getsock(struct connectdata *conn, | |
| 849 curl_socket_t *sock) | |
| 850 { | |
| 851 sock[0] = conn->sock[FIRSTSOCKET]; | |
| 852 | |
| 853 /* when we've sent a CONNECT to a proxy, we should rather wait for the | |
| 854 socket to become readable to be able to get the response headers */ | |
| 855 if(conn->connect_state) | |
| 856 return GETSOCK_READSOCK(0); | |
| 857 | |
| 858 return GETSOCK_WRITESOCK(0); | |
| 859 } | |
| 860 | |
| 861 static int domore_getsock(struct connectdata *conn, | |
| 862 curl_socket_t *socks) | |
| 863 { | |
| 864 if(conn && conn->handler->domore_getsock) | |
| 865 return conn->handler->domore_getsock(conn, socks); | |
| 866 return GETSOCK_BLANK; | |
| 867 } | |
| 868 | |
| 869 static int doing_getsock(struct connectdata *conn, | |
| 870 curl_socket_t *socks) | |
| 871 { | |
| 872 if(conn && conn->handler->doing_getsock) | |
| 873 return conn->handler->doing_getsock(conn, socks); | |
| 874 return GETSOCK_BLANK; | |
| 875 } | |
| 876 | |
| 877 static int protocol_getsock(struct connectdata *conn, | |
| 878 curl_socket_t *socks) | |
| 879 { | |
| 880 if(conn->handler->proto_getsock) | |
| 881 return conn->handler->proto_getsock(conn, socks); | |
| 882 /* Backup getsock logic. Since there is a live socket in use, we must wait | |
| 883 for it or it will be removed from watching when the multi_socket API is | |
| 884 used. */ | |
| 885 socks[0] = conn->sock[FIRSTSOCKET]; | |
| 886 return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0); | |
| 887 } | |
| 888 | |
| 889 /* returns bitmapped flags for this handle and its sockets. The 'socks[]' | |
| 890 array contains MAX_SOCKSPEREASYHANDLE entries. */ | |
| 891 static int multi_getsock(struct Curl_easy *data, | |
| 892 curl_socket_t *socks) | |
| 893 { | |
| 894 /* The no connection case can happen when this is called from | |
| 895 curl_multi_remove_handle() => singlesocket() => multi_getsock(). | |
| 896 */ | |
| 897 if(!data->conn) | |
| 898 return 0; | |
| 899 | |
| 900 if(data->mstate > CURLM_STATE_CONNECT && | |
| 901 data->mstate < CURLM_STATE_COMPLETED) { | |
| 902 /* Set up ownership correctly */ | |
| 903 data->conn->data = data; | |
| 904 } | |
| 905 | |
| 906 switch(data->mstate) { | |
| 907 default: | |
| 908 #if 0 /* switch back on these cases to get the compiler to check for all enums | |
| 909 to be present */ | |
| 910 case CURLM_STATE_TOOFAST: /* returns 0, so will not select. */ | |
| 911 case CURLM_STATE_COMPLETED: | |
| 912 case CURLM_STATE_MSGSENT: | |
| 913 case CURLM_STATE_INIT: | |
| 914 case CURLM_STATE_CONNECT: | |
| 915 case CURLM_STATE_WAITDO: | |
| 916 case CURLM_STATE_DONE: | |
| 917 case CURLM_STATE_LAST: | |
| 918 /* this will get called with CURLM_STATE_COMPLETED when a handle is | |
| 919 removed */ | |
| 920 #endif | |
| 921 return 0; | |
| 922 | |
| 923 case CURLM_STATE_WAITRESOLVE: | |
| 924 return Curl_resolv_getsock(data->conn, socks); | |
| 925 | |
| 926 case CURLM_STATE_PROTOCONNECT: | |
| 927 case CURLM_STATE_SENDPROTOCONNECT: | |
| 928 return protocol_getsock(data->conn, socks); | |
| 929 | |
| 930 case CURLM_STATE_DO: | |
| 931 case CURLM_STATE_DOING: | |
| 932 return doing_getsock(data->conn, socks); | |
| 933 | |
| 934 case CURLM_STATE_WAITPROXYCONNECT: | |
| 935 return waitproxyconnect_getsock(data->conn, socks); | |
| 936 | |
| 937 case CURLM_STATE_WAITCONNECT: | |
| 938 return waitconnect_getsock(data->conn, socks); | |
| 939 | |
| 940 case CURLM_STATE_DO_MORE: | |
| 941 return domore_getsock(data->conn, socks); | |
| 942 | |
| 943 case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch | |
| 944 to waiting for the same as the *PERFORM | |
| 945 states */ | |
| 946 case CURLM_STATE_PERFORM: | |
| 947 return Curl_single_getsock(data->conn, socks); | |
| 948 } | |
| 949 | |
| 950 } | |
| 951 | |
| 952 CURLMcode curl_multi_fdset(struct Curl_multi *multi, | |
| 953 fd_set *read_fd_set, fd_set *write_fd_set, | |
| 954 fd_set *exc_fd_set, int *max_fd) | |
| 955 { | |
| 956 /* Scan through all the easy handles to get the file descriptors set. | |
| 957 Some easy handles may not have connected to the remote host yet, | |
| 958 and then we must make sure that is done. */ | |
| 959 struct Curl_easy *data; | |
| 960 int this_max_fd = -1; | |
| 961 curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; | |
| 962 int i; | |
| 963 (void)exc_fd_set; /* not used */ | |
| 964 | |
| 965 if(!GOOD_MULTI_HANDLE(multi)) | |
| 966 return CURLM_BAD_HANDLE; | |
| 967 | |
| 968 if(multi->in_callback) | |
| 969 return CURLM_RECURSIVE_API_CALL; | |
| 970 | |
| 971 data = multi->easyp; | |
| 972 while(data) { | |
| 973 int bitmap = multi_getsock(data, sockbunch); | |
| 974 | |
| 975 for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { | |
| 976 curl_socket_t s = CURL_SOCKET_BAD; | |
| 977 | |
| 978 if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) { | |
| 979 FD_SET(sockbunch[i], read_fd_set); | |
| 980 s = sockbunch[i]; | |
| 981 } | |
| 982 if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) { | |
| 983 FD_SET(sockbunch[i], write_fd_set); | |
| 984 s = sockbunch[i]; | |
| 985 } | |
| 986 if(s == CURL_SOCKET_BAD) | |
| 987 /* this socket is unused, break out of loop */ | |
| 988 break; | |
| 989 if((int)s > this_max_fd) | |
| 990 this_max_fd = (int)s; | |
| 991 } | |
| 992 | |
| 993 data = data->next; /* check next handle */ | |
| 994 } | |
| 995 | |
| 996 *max_fd = this_max_fd; | |
| 997 | |
| 998 return CURLM_OK; | |
| 999 } | |
| 1000 | |
| 1001 #define NUM_POLLS_ON_STACK 10 | |
| 1002 | |
| 1003 static CURLMcode Curl_multi_wait(struct Curl_multi *multi, | |
| 1004 struct curl_waitfd extra_fds[], | |
| 1005 unsigned int extra_nfds, | |
| 1006 int timeout_ms, | |
| 1007 int *ret, | |
| 1008 bool extrawait) /* when no socket, wait */ | |
| 1009 { | |
| 1010 struct Curl_easy *data; | |
| 1011 curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; | |
| 1012 int bitmap; | |
| 1013 unsigned int i; | |
| 1014 unsigned int nfds = 0; | |
| 1015 unsigned int curlfds; | |
| 1016 bool ufds_malloc = FALSE; | |
| 1017 long timeout_internal; | |
| 1018 int retcode = 0; | |
| 1019 struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; | |
| 1020 struct pollfd *ufds = &a_few_on_stack[0]; | |
| 1021 | |
| 1022 if(!GOOD_MULTI_HANDLE(multi)) | |
| 1023 return CURLM_BAD_HANDLE; | |
| 1024 | |
| 1025 if(multi->in_callback) | |
| 1026 return CURLM_RECURSIVE_API_CALL; | |
| 1027 | |
| 1028 /* Count up how many fds we have from the multi handle */ | |
| 1029 data = multi->easyp; | |
| 1030 while(data) { | |
| 1031 bitmap = multi_getsock(data, sockbunch); | |
| 1032 | |
| 1033 for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { | |
| 1034 curl_socket_t s = CURL_SOCKET_BAD; | |
| 1035 | |
| 1036 if(bitmap & GETSOCK_READSOCK(i)) { | |
| 1037 ++nfds; | |
| 1038 s = sockbunch[i]; | |
| 1039 } | |
| 1040 if(bitmap & GETSOCK_WRITESOCK(i)) { | |
| 1041 ++nfds; | |
| 1042 s = sockbunch[i]; | |
| 1043 } | |
| 1044 if(s == CURL_SOCKET_BAD) { | |
| 1045 break; | |
| 1046 } | |
| 1047 } | |
| 1048 | |
| 1049 data = data->next; /* check next handle */ | |
| 1050 } | |
| 1051 | |
| 1052 /* If the internally desired timeout is actually shorter than requested from | |
| 1053 the outside, then use the shorter time! But only if the internal timer | |
| 1054 is actually larger than -1! */ | |
| 1055 (void)multi_timeout(multi, &timeout_internal); | |
| 1056 if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) | |
| 1057 timeout_ms = (int)timeout_internal; | |
| 1058 | |
| 1059 curlfds = nfds; /* number of internal file descriptors */ | |
| 1060 nfds += extra_nfds; /* add the externally provided ones */ | |
| 1061 | |
| 1062 if(nfds > NUM_POLLS_ON_STACK) { | |
| 1063 /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes | |
| 1064 big, so at 2^29 sockets this value might wrap. When a process gets | |
| 1065 the capability to actually handle over 500 million sockets this | |
| 1066 calculation needs a integer overflow check. */ | |
| 1067 ufds = malloc(nfds * sizeof(struct pollfd)); | |
| 1068 if(!ufds) | |
| 1069 return CURLM_OUT_OF_MEMORY; | |
| 1070 ufds_malloc = TRUE; | |
| 1071 } | |
| 1072 nfds = 0; | |
| 1073 | |
| 1074 /* only do the second loop if we found descriptors in the first stage run | |
| 1075 above */ | |
| 1076 | |
| 1077 if(curlfds) { | |
| 1078 /* Add the curl handles to our pollfds first */ | |
| 1079 data = multi->easyp; | |
| 1080 while(data) { | |
| 1081 bitmap = multi_getsock(data, sockbunch); | |
| 1082 | |
| 1083 for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { | |
| 1084 curl_socket_t s = CURL_SOCKET_BAD; | |
| 1085 | |
| 1086 if(bitmap & GETSOCK_READSOCK(i)) { | |
| 1087 ufds[nfds].fd = sockbunch[i]; | |
| 1088 ufds[nfds].events = POLLIN; | |
| 1089 ++nfds; | |
| 1090 s = sockbunch[i]; | |
| 1091 } | |
| 1092 if(bitmap & GETSOCK_WRITESOCK(i)) { | |
| 1093 ufds[nfds].fd = sockbunch[i]; | |
| 1094 ufds[nfds].events = POLLOUT; | |
| 1095 ++nfds; | |
| 1096 s = sockbunch[i]; | |
| 1097 } | |
| 1098 if(s == CURL_SOCKET_BAD) { | |
| 1099 break; | |
| 1100 } | |
| 1101 } | |
| 1102 | |
| 1103 data = data->next; /* check next handle */ | |
| 1104 } | |
| 1105 } | |
| 1106 | |
| 1107 /* Add external file descriptions from poll-like struct curl_waitfd */ | |
| 1108 for(i = 0; i < extra_nfds; i++) { | |
| 1109 ufds[nfds].fd = extra_fds[i].fd; | |
| 1110 ufds[nfds].events = 0; | |
| 1111 if(extra_fds[i].events & CURL_WAIT_POLLIN) | |
| 1112 ufds[nfds].events |= POLLIN; | |
| 1113 if(extra_fds[i].events & CURL_WAIT_POLLPRI) | |
| 1114 ufds[nfds].events |= POLLPRI; | |
| 1115 if(extra_fds[i].events & CURL_WAIT_POLLOUT) | |
| 1116 ufds[nfds].events |= POLLOUT; | |
| 1117 ++nfds; | |
| 1118 } | |
| 1119 | |
| 1120 if(nfds) { | |
| 1121 int pollrc; | |
| 1122 /* wait... */ | |
| 1123 pollrc = Curl_poll(ufds, nfds, timeout_ms); | |
| 1124 | |
| 1125 if(pollrc > 0) { | |
| 1126 retcode = pollrc; | |
| 1127 /* copy revents results from the poll to the curl_multi_wait poll | |
| 1128 struct, the bit values of the actual underlying poll() implementation | |
| 1129 may not be the same as the ones in the public libcurl API! */ | |
| 1130 for(i = 0; i < extra_nfds; i++) { | |
| 1131 unsigned short mask = 0; | |
| 1132 unsigned r = ufds[curlfds + i].revents; | |
| 1133 | |
| 1134 if(r & POLLIN) | |
| 1135 mask |= CURL_WAIT_POLLIN; | |
| 1136 if(r & POLLOUT) | |
| 1137 mask |= CURL_WAIT_POLLOUT; | |
| 1138 if(r & POLLPRI) | |
| 1139 mask |= CURL_WAIT_POLLPRI; | |
| 1140 | |
| 1141 extra_fds[i].revents = mask; | |
| 1142 } | |
| 1143 } | |
| 1144 } | |
| 1145 | |
| 1146 if(ufds_malloc) | |
| 1147 free(ufds); | |
| 1148 if(ret) | |
| 1149 *ret = retcode; | |
| 1150 if(!extrawait || extra_fds || curlfds) | |
| 1151 /* if any socket was checked */ | |
| 1152 ; | |
| 1153 else { | |
| 1154 long sleep_ms = 0; | |
| 1155 | |
| 1156 /* Avoid busy-looping when there's nothing particular to wait for */ | |
| 1157 if(!curl_multi_timeout(multi, &sleep_ms) && sleep_ms) { | |
| 1158 if(sleep_ms > timeout_ms) | |
| 1159 sleep_ms = timeout_ms; | |
| 1160 Curl_wait_ms((int)sleep_ms); | |
| 1161 } | |
| 1162 } | |
| 1163 | |
| 1164 return CURLM_OK; | |
| 1165 } | |
| 1166 | |
| 1167 CURLMcode curl_multi_wait(struct Curl_multi *multi, | |
| 1168 struct curl_waitfd extra_fds[], | |
| 1169 unsigned int extra_nfds, | |
| 1170 int timeout_ms, | |
| 1171 int *ret) | |
| 1172 { | |
| 1173 return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE); | |
| 1174 } | |
| 1175 | |
| 1176 CURLMcode curl_multi_poll(struct Curl_multi *multi, | |
| 1177 struct curl_waitfd extra_fds[], | |
| 1178 unsigned int extra_nfds, | |
| 1179 int timeout_ms, | |
| 1180 int *ret) | |
| 1181 { | |
| 1182 return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE); | |
| 1183 } | |
| 1184 | |
| 1185 /* | |
| 1186 * multi_ischanged() is called | |
| 1187 * | |
| 1188 * Returns TRUE/FALSE whether the state is changed to trigger a CONNECT_PEND | |
| 1189 * => CONNECT action. | |
| 1190 * | |
| 1191 * Set 'clear' to TRUE to have it also clear the state variable. | |
| 1192 */ | |
| 1193 static bool multi_ischanged(struct Curl_multi *multi, bool clear) | |
| 1194 { | |
| 1195 bool retval = multi->recheckstate; | |
| 1196 if(clear) | |
| 1197 multi->recheckstate = FALSE; | |
| 1198 return retval; | |
| 1199 } | |
| 1200 | |
| 1201 CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, | |
| 1202 struct Curl_easy *data, | |
| 1203 struct connectdata *conn) | |
| 1204 { | |
| 1205 CURLMcode rc; | |
| 1206 | |
| 1207 if(multi->in_callback) | |
| 1208 return CURLM_RECURSIVE_API_CALL; | |
| 1209 | |
| 1210 rc = curl_multi_add_handle(multi, data); | |
| 1211 if(!rc) { | |
| 1212 struct SingleRequest *k = &data->req; | |
| 1213 | |
| 1214 /* pass in NULL for 'conn' here since we don't want to init the | |
| 1215 connection, only this transfer */ | |
| 1216 Curl_init_do(data, NULL); | |
| 1217 | |
| 1218 /* take this handle to the perform state right away */ | |
| 1219 multistate(data, CURLM_STATE_PERFORM); | |
| 1220 Curl_attach_connnection(data, conn); | |
| 1221 k->keepon |= KEEP_RECV; /* setup to receive! */ | |
| 1222 } | |
| 1223 return rc; | |
| 1224 } | |
| 1225 | |
| 1226 /* | |
| 1227 * do_complete is called when the DO actions are complete. | |
| 1228 * | |
| 1229 * We init chunking and trailer bits to their default values here immediately | |
| 1230 * before receiving any header data for the current request. | |
| 1231 */ | |
| 1232 static void do_complete(struct connectdata *conn) | |
| 1233 { | |
| 1234 conn->data->req.chunk = FALSE; | |
| 1235 Curl_pgrsTime(conn->data, TIMER_PRETRANSFER); | |
| 1236 } | |
| 1237 | |
| 1238 static CURLcode multi_do(struct Curl_easy *data, bool *done) | |
| 1239 { | |
| 1240 CURLcode result = CURLE_OK; | |
| 1241 struct connectdata *conn = data->conn; | |
| 1242 | |
| 1243 DEBUGASSERT(conn); | |
| 1244 DEBUGASSERT(conn->handler); | |
| 1245 | |
| 1246 if(conn->handler->do_it) { | |
| 1247 /* generic protocol-specific function pointer set in curl_connect() */ | |
| 1248 result = conn->handler->do_it(conn, done); | |
| 1249 | |
| 1250 if(!result && *done) | |
| 1251 /* do_complete must be called after the protocol-specific DO function */ | |
| 1252 do_complete(conn); | |
| 1253 } | |
| 1254 return result; | |
| 1255 } | |
| 1256 | |
| 1257 /* | |
| 1258 * multi_do_more() is called during the DO_MORE multi state. It is basically a | |
| 1259 * second stage DO state which (wrongly) was introduced to support FTP's | |
| 1260 * second connection. | |
| 1261 * | |
| 1262 * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to | |
| 1263 * DOING state there's more work to do! | |
| 1264 */ | |
| 1265 | |
| 1266 static CURLcode multi_do_more(struct connectdata *conn, int *complete) | |
| 1267 { | |
| 1268 CURLcode result = CURLE_OK; | |
| 1269 | |
| 1270 *complete = 0; | |
| 1271 | |
| 1272 if(conn->handler->do_more) | |
| 1273 result = conn->handler->do_more(conn, complete); | |
| 1274 | |
| 1275 if(!result && (*complete == 1)) | |
| 1276 /* do_complete must be called after the protocol-specific DO function */ | |
| 1277 do_complete(conn); | |
| 1278 | |
| 1279 return result; | |
| 1280 } | |
| 1281 | |
| 1282 /* | |
| 1283 * We are doing protocol-specific connecting and this is being called over and | |
| 1284 * over from the multi interface until the connection phase is done on | |
| 1285 * protocol layer. | |
| 1286 */ | |
| 1287 | |
| 1288 static CURLcode protocol_connecting(struct connectdata *conn, | |
| 1289 bool *done) | |
| 1290 { | |
| 1291 CURLcode result = CURLE_OK; | |
| 1292 | |
| 1293 if(conn && conn->handler->connecting) { | |
| 1294 *done = FALSE; | |
| 1295 result = conn->handler->connecting(conn, done); | |
| 1296 } | |
| 1297 else | |
| 1298 *done = TRUE; | |
| 1299 | |
| 1300 return result; | |
| 1301 } | |
| 1302 | |
| 1303 /* | |
| 1304 * We are DOING this is being called over and over from the multi interface | |
| 1305 * until the DOING phase is done on protocol layer. | |
| 1306 */ | |
| 1307 | |
| 1308 static CURLcode protocol_doing(struct connectdata *conn, bool *done) | |
| 1309 { | |
| 1310 CURLcode result = CURLE_OK; | |
| 1311 | |
| 1312 if(conn && conn->handler->doing) { | |
| 1313 *done = FALSE; | |
| 1314 result = conn->handler->doing(conn, done); | |
| 1315 } | |
| 1316 else | |
| 1317 *done = TRUE; | |
| 1318 | |
| 1319 return result; | |
| 1320 } | |
| 1321 | |
| 1322 /* | |
| 1323 * We have discovered that the TCP connection has been successful, we can now | |
| 1324 * proceed with some action. | |
| 1325 * | |
| 1326 */ | |
| 1327 static CURLcode protocol_connect(struct connectdata *conn, | |
| 1328 bool *protocol_done) | |
| 1329 { | |
| 1330 CURLcode result = CURLE_OK; | |
| 1331 | |
| 1332 DEBUGASSERT(conn); | |
| 1333 DEBUGASSERT(protocol_done); | |
| 1334 | |
| 1335 *protocol_done = FALSE; | |
| 1336 | |
| 1337 if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) { | |
| 1338 /* We already are connected, get back. This may happen when the connect | |
| 1339 worked fine in the first call, like when we connect to a local server | |
| 1340 or proxy. Note that we don't know if the protocol is actually done. | |
| 1341 | |
| 1342 Unless this protocol doesn't have any protocol-connect callback, as | |
| 1343 then we know we're done. */ | |
| 1344 if(!conn->handler->connecting) | |
| 1345 *protocol_done = TRUE; | |
| 1346 | |
| 1347 return CURLE_OK; | |
| 1348 } | |
| 1349 | |
| 1350 if(!conn->bits.protoconnstart) { | |
| 1351 | |
| 1352 result = Curl_proxy_connect(conn, FIRSTSOCKET); | |
| 1353 if(result) | |
| 1354 return result; | |
| 1355 | |
| 1356 if(CONNECT_FIRSTSOCKET_PROXY_SSL()) | |
| 1357 /* wait for HTTPS proxy SSL initialization to complete */ | |
| 1358 return CURLE_OK; | |
| 1359 | |
| 1360 if(conn->bits.tunnel_proxy && conn->bits.httpproxy && | |
| 1361 Curl_connect_ongoing(conn)) | |
| 1362 /* when using an HTTP tunnel proxy, await complete tunnel establishment | |
| 1363 before proceeding further. Return CURLE_OK so we'll be called again */ | |
| 1364 return CURLE_OK; | |
| 1365 | |
| 1366 if(conn->handler->connect_it) { | |
| 1367 /* is there a protocol-specific connect() procedure? */ | |
| 1368 | |
| 1369 /* Call the protocol-specific connect function */ | |
| 1370 result = conn->handler->connect_it(conn, protocol_done); | |
| 1371 } | |
| 1372 else | |
| 1373 *protocol_done = TRUE; | |
| 1374 | |
| 1375 /* it has started, possibly even completed but that knowledge isn't stored | |
| 1376 in this bit! */ | |
| 1377 if(!result) | |
| 1378 conn->bits.protoconnstart = TRUE; | |
| 1379 } | |
| 1380 | |
| 1381 return result; /* pass back status */ | |
| 1382 } | |
| 1383 | |
| 1384 | |
| 1385 static CURLMcode multi_runsingle(struct Curl_multi *multi, | |
| 1386 struct curltime now, | |
| 1387 struct Curl_easy *data) | |
| 1388 { | |
| 1389 struct Curl_message *msg = NULL; | |
| 1390 bool connected; | |
| 1391 bool async; | |
| 1392 bool protocol_connected = FALSE; | |
| 1393 bool dophase_done = FALSE; | |
| 1394 bool done = FALSE; | |
| 1395 CURLMcode rc; | |
| 1396 CURLcode result = CURLE_OK; | |
| 1397 timediff_t timeout_ms; | |
| 1398 timediff_t recv_timeout_ms; | |
| 1399 timediff_t send_timeout_ms; | |
| 1400 int control; | |
| 1401 | |
| 1402 if(!GOOD_EASY_HANDLE(data)) | |
| 1403 return CURLM_BAD_EASY_HANDLE; | |
| 1404 | |
| 1405 do { | |
| 1406 /* A "stream" here is a logical stream if the protocol can handle that | |
| 1407 (HTTP/2), or the full connection for older protocols */ | |
| 1408 bool stream_error = FALSE; | |
| 1409 rc = CURLM_OK; | |
| 1410 | |
| 1411 DEBUGASSERT((data->mstate <= CURLM_STATE_CONNECT) || | |
| 1412 (data->mstate >= CURLM_STATE_DONE) || | |
| 1413 data->conn); | |
| 1414 if(!data->conn && | |
| 1415 data->mstate > CURLM_STATE_CONNECT && | |
| 1416 data->mstate < CURLM_STATE_DONE) { | |
| 1417 /* In all these states, the code will blindly access 'data->conn' | |
| 1418 so this is precaution that it isn't NULL. And it silences static | |
| 1419 analyzers. */ | |
| 1420 failf(data, "In state %d with no conn, bail out!\n", data->mstate); | |
| 1421 return CURLM_INTERNAL_ERROR; | |
| 1422 } | |
| 1423 | |
| 1424 if(multi_ischanged(multi, TRUE)) { | |
| 1425 DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!\n")); | |
| 1426 process_pending_handles(multi); /* multiplexed */ | |
| 1427 } | |
| 1428 | |
| 1429 if(data->conn && data->mstate > CURLM_STATE_CONNECT && | |
| 1430 data->mstate < CURLM_STATE_COMPLETED) { | |
| 1431 /* Make sure we set the connection's current owner */ | |
| 1432 data->conn->data = data; | |
| 1433 } | |
| 1434 | |
| 1435 if(data->conn && | |
| 1436 (data->mstate >= CURLM_STATE_CONNECT) && | |
| 1437 (data->mstate < CURLM_STATE_COMPLETED)) { | |
| 1438 /* we need to wait for the connect state as only then is the start time | |
| 1439 stored, but we must not check already completed handles */ | |
| 1440 timeout_ms = Curl_timeleft(data, &now, | |
| 1441 (data->mstate <= CURLM_STATE_DO)? | |
| 1442 TRUE:FALSE); | |
| 1443 | |
| 1444 if(timeout_ms < 0) { | |
| 1445 /* Handle timed out */ | |
| 1446 if(data->mstate == CURLM_STATE_WAITRESOLVE) | |
| 1447 failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T | |
| 1448 " milliseconds", | |
| 1449 Curl_timediff(now, data->progress.t_startsingle)); | |
| 1450 else if(data->mstate == CURLM_STATE_WAITCONNECT) | |
| 1451 failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T | |
| 1452 " milliseconds", | |
| 1453 Curl_timediff(now, data->progress.t_startsingle)); | |
| 1454 else { | |
| 1455 struct SingleRequest *k = &data->req; | |
| 1456 if(k->size != -1) { | |
| 1457 failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T | |
| 1458 " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" | |
| 1459 CURL_FORMAT_CURL_OFF_T " bytes received", | |
| 1460 Curl_timediff(now, data->progress.t_startsingle), | |
| 1461 k->bytecount, k->size); | |
| 1462 } | |
| 1463 else { | |
| 1464 failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T | |
| 1465 " milliseconds with %" CURL_FORMAT_CURL_OFF_T | |
| 1466 " bytes received", | |
| 1467 Curl_timediff(now, data->progress.t_startsingle), | |
| 1468 k->bytecount); | |
| 1469 } | |
| 1470 } | |
| 1471 | |
| 1472 /* Force connection closed if the connection has indeed been used */ | |
| 1473 if(data->mstate > CURLM_STATE_DO) { | |
| 1474 streamclose(data->conn, "Disconnected with pending data"); | |
| 1475 stream_error = TRUE; | |
| 1476 } | |
| 1477 result = CURLE_OPERATION_TIMEDOUT; | |
| 1478 (void)multi_done(data, result, TRUE); | |
| 1479 /* Skip the statemachine and go directly to error handling section. */ | |
| 1480 goto statemachine_end; | |
| 1481 } | |
| 1482 } | |
| 1483 | |
| 1484 switch(data->mstate) { | |
| 1485 case CURLM_STATE_INIT: | |
| 1486 /* init this transfer. */ | |
| 1487 result = Curl_pretransfer(data); | |
| 1488 | |
| 1489 if(!result) { | |
| 1490 /* after init, go CONNECT */ | |
| 1491 multistate(data, CURLM_STATE_CONNECT); | |
| 1492 Curl_pgrsTime(data, TIMER_STARTOP); | |
| 1493 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1494 } | |
| 1495 break; | |
| 1496 | |
| 1497 case CURLM_STATE_CONNECT_PEND: | |
| 1498 /* We will stay here until there is a connection available. Then | |
| 1499 we try again in the CURLM_STATE_CONNECT state. */ | |
| 1500 break; | |
| 1501 | |
| 1502 case CURLM_STATE_CONNECT: | |
| 1503 /* Connect. We want to get a connection identifier filled in. */ | |
| 1504 Curl_pgrsTime(data, TIMER_STARTSINGLE); | |
| 1505 if(data->set.timeout) | |
| 1506 Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); | |
| 1507 | |
| 1508 if(data->set.connecttimeout) | |
| 1509 Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); | |
| 1510 | |
| 1511 result = Curl_connect(data, &async, &protocol_connected); | |
| 1512 if(CURLE_NO_CONNECTION_AVAILABLE == result) { | |
| 1513 /* There was no connection available. We will go to the pending | |
| 1514 state and wait for an available connection. */ | |
| 1515 multistate(data, CURLM_STATE_CONNECT_PEND); | |
| 1516 | |
| 1517 /* add this handle to the list of connect-pending handles */ | |
| 1518 Curl_llist_insert_next(&multi->pending, multi->pending.tail, data, | |
| 1519 &data->connect_queue); | |
| 1520 result = CURLE_OK; | |
| 1521 break; | |
| 1522 } | |
| 1523 else if(data->state.previouslypending) { | |
| 1524 /* this transfer comes from the pending queue so try move another */ | |
| 1525 infof(data, "Transfer was pending, now try another\n"); | |
| 1526 process_pending_handles(data->multi); | |
| 1527 } | |
| 1528 | |
| 1529 if(!result) { | |
| 1530 if(async) | |
| 1531 /* We're now waiting for an asynchronous name lookup */ | |
| 1532 multistate(data, CURLM_STATE_WAITRESOLVE); | |
| 1533 else { | |
| 1534 /* after the connect has been sent off, go WAITCONNECT unless the | |
| 1535 protocol connect is already done and we can go directly to | |
| 1536 WAITDO or DO! */ | |
| 1537 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1538 | |
| 1539 if(protocol_connected) | |
| 1540 multistate(data, CURLM_STATE_DO); | |
| 1541 else { | |
| 1542 #ifndef CURL_DISABLE_HTTP | |
| 1543 if(Curl_connect_ongoing(data->conn)) | |
| 1544 multistate(data, CURLM_STATE_WAITPROXYCONNECT); | |
| 1545 else | |
| 1546 #endif | |
| 1547 multistate(data, CURLM_STATE_WAITCONNECT); | |
| 1548 } | |
| 1549 } | |
| 1550 } | |
| 1551 break; | |
| 1552 | |
| 1553 case CURLM_STATE_WAITRESOLVE: | |
| 1554 /* awaiting an asynch name resolve to complete */ | |
| 1555 { | |
| 1556 struct Curl_dns_entry *dns = NULL; | |
| 1557 struct connectdata *conn = data->conn; | |
| 1558 const char *hostname; | |
| 1559 | |
| 1560 DEBUGASSERT(conn); | |
| 1561 if(conn->bits.httpproxy) | |
| 1562 hostname = conn->http_proxy.host.name; | |
| 1563 else if(conn->bits.conn_to_host) | |
| 1564 hostname = conn->conn_to_host.name; | |
| 1565 else | |
| 1566 hostname = conn->host.name; | |
| 1567 | |
| 1568 /* check if we have the name resolved by now */ | |
| 1569 dns = Curl_fetch_addr(conn, hostname, (int)conn->port); | |
| 1570 | |
| 1571 if(dns) { | |
| 1572 #ifdef CURLRES_ASYNCH | |
| 1573 conn->async.dns = dns; | |
| 1574 conn->async.done = TRUE; | |
| 1575 #endif | |
| 1576 result = CURLE_OK; | |
| 1577 infof(data, "Hostname '%s' was found in DNS cache\n", hostname); | |
| 1578 } | |
| 1579 | |
| 1580 if(!dns) | |
| 1581 result = Curl_resolv_check(data->conn, &dns); | |
| 1582 | |
| 1583 /* Update sockets here, because the socket(s) may have been | |
| 1584 closed and the application thus needs to be told, even if it | |
| 1585 is likely that the same socket(s) will again be used further | |
| 1586 down. If the name has not yet been resolved, it is likely | |
| 1587 that new sockets have been opened in an attempt to contact | |
| 1588 another resolver. */ | |
| 1589 singlesocket(multi, data); | |
| 1590 | |
| 1591 if(dns) { | |
| 1592 /* Perform the next step in the connection phase, and then move on | |
| 1593 to the WAITCONNECT state */ | |
| 1594 result = Curl_once_resolved(data->conn, &protocol_connected); | |
| 1595 | |
| 1596 if(result) | |
| 1597 /* if Curl_once_resolved() returns failure, the connection struct | |
| 1598 is already freed and gone */ | |
| 1599 data->conn = NULL; /* no more connection */ | |
| 1600 else { | |
| 1601 /* call again please so that we get the next socket setup */ | |
| 1602 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1603 if(protocol_connected) | |
| 1604 multistate(data, CURLM_STATE_DO); | |
| 1605 else { | |
| 1606 #ifndef CURL_DISABLE_HTTP | |
| 1607 if(Curl_connect_ongoing(data->conn)) | |
| 1608 multistate(data, CURLM_STATE_WAITPROXYCONNECT); | |
| 1609 else | |
| 1610 #endif | |
| 1611 multistate(data, CURLM_STATE_WAITCONNECT); | |
| 1612 } | |
| 1613 } | |
| 1614 } | |
| 1615 | |
| 1616 if(result) { | |
| 1617 /* failure detected */ | |
| 1618 stream_error = TRUE; | |
| 1619 break; | |
| 1620 } | |
| 1621 } | |
| 1622 break; | |
| 1623 | |
| 1624 #ifndef CURL_DISABLE_HTTP | |
| 1625 case CURLM_STATE_WAITPROXYCONNECT: | |
| 1626 /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ | |
| 1627 DEBUGASSERT(data->conn); | |
| 1628 result = Curl_http_connect(data->conn, &protocol_connected); | |
| 1629 | |
| 1630 if(data->conn->bits.proxy_connect_closed) { | |
| 1631 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1632 /* connect back to proxy again */ | |
| 1633 result = CURLE_OK; | |
| 1634 multi_done(data, CURLE_OK, FALSE); | |
| 1635 multistate(data, CURLM_STATE_CONNECT); | |
| 1636 } | |
| 1637 else if(!result) { | |
| 1638 if((data->conn->http_proxy.proxytype != CURLPROXY_HTTPS || | |
| 1639 data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && | |
| 1640 Curl_connect_complete(data->conn)) { | |
| 1641 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1642 /* initiate protocol connect phase */ | |
| 1643 multistate(data, CURLM_STATE_SENDPROTOCONNECT); | |
| 1644 } | |
| 1645 } | |
| 1646 else if(result) | |
| 1647 stream_error = TRUE; | |
| 1648 break; | |
| 1649 #endif | |
| 1650 | |
| 1651 case CURLM_STATE_WAITCONNECT: | |
| 1652 /* awaiting a completion of an asynch TCP connect */ | |
| 1653 DEBUGASSERT(data->conn); | |
| 1654 result = Curl_is_connected(data->conn, FIRSTSOCKET, &connected); | |
| 1655 if(connected && !result) { | |
| 1656 #ifndef CURL_DISABLE_HTTP | |
| 1657 if((data->conn->http_proxy.proxytype == CURLPROXY_HTTPS && | |
| 1658 !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) || | |
| 1659 Curl_connect_ongoing(data->conn)) { | |
| 1660 multistate(data, CURLM_STATE_WAITPROXYCONNECT); | |
| 1661 break; | |
| 1662 } | |
| 1663 #endif | |
| 1664 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1665 multistate(data, data->conn->bits.tunnel_proxy? | |
| 1666 CURLM_STATE_WAITPROXYCONNECT: | |
| 1667 CURLM_STATE_SENDPROTOCONNECT); | |
| 1668 } | |
| 1669 else if(result) { | |
| 1670 /* failure detected */ | |
| 1671 Curl_posttransfer(data); | |
| 1672 multi_done(data, result, TRUE); | |
| 1673 stream_error = TRUE; | |
| 1674 break; | |
| 1675 } | |
| 1676 break; | |
| 1677 | |
| 1678 case CURLM_STATE_SENDPROTOCONNECT: | |
| 1679 result = protocol_connect(data->conn, &protocol_connected); | |
| 1680 if(!result && !protocol_connected) | |
| 1681 /* switch to waiting state */ | |
| 1682 multistate(data, CURLM_STATE_PROTOCONNECT); | |
| 1683 else if(!result) { | |
| 1684 /* protocol connect has completed, go WAITDO or DO */ | |
| 1685 multistate(data, CURLM_STATE_DO); | |
| 1686 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1687 } | |
| 1688 else if(result) { | |
| 1689 /* failure detected */ | |
| 1690 Curl_posttransfer(data); | |
| 1691 multi_done(data, result, TRUE); | |
| 1692 stream_error = TRUE; | |
| 1693 } | |
| 1694 break; | |
| 1695 | |
| 1696 case CURLM_STATE_PROTOCONNECT: | |
| 1697 /* protocol-specific connect phase */ | |
| 1698 result = protocol_connecting(data->conn, &protocol_connected); | |
| 1699 if(!result && protocol_connected) { | |
| 1700 /* after the connect has completed, go WAITDO or DO */ | |
| 1701 multistate(data, CURLM_STATE_DO); | |
| 1702 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1703 } | |
| 1704 else if(result) { | |
| 1705 /* failure detected */ | |
| 1706 Curl_posttransfer(data); | |
| 1707 multi_done(data, result, TRUE); | |
| 1708 stream_error = TRUE; | |
| 1709 } | |
| 1710 break; | |
| 1711 | |
| 1712 case CURLM_STATE_DO: | |
| 1713 if(data->set.connect_only) { | |
| 1714 /* keep connection open for application to use the socket */ | |
| 1715 connkeep(data->conn, "CONNECT_ONLY"); | |
| 1716 multistate(data, CURLM_STATE_DONE); | |
| 1717 result = CURLE_OK; | |
| 1718 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1719 } | |
| 1720 else { | |
| 1721 /* Perform the protocol's DO action */ | |
| 1722 result = multi_do(data, &dophase_done); | |
| 1723 | |
| 1724 /* When multi_do() returns failure, data->conn might be NULL! */ | |
| 1725 | |
| 1726 if(!result) { | |
| 1727 if(!dophase_done) { | |
| 1728 #ifndef CURL_DISABLE_FTP | |
| 1729 /* some steps needed for wildcard matching */ | |
| 1730 if(data->state.wildcardmatch) { | |
| 1731 struct WildcardData *wc = &data->wildcard; | |
| 1732 if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { | |
| 1733 /* skip some states if it is important */ | |
| 1734 multi_done(data, CURLE_OK, FALSE); | |
| 1735 multistate(data, CURLM_STATE_DONE); | |
| 1736 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1737 break; | |
| 1738 } | |
| 1739 } | |
| 1740 #endif | |
| 1741 /* DO was not completed in one function call, we must continue | |
| 1742 DOING... */ | |
| 1743 multistate(data, CURLM_STATE_DOING); | |
| 1744 rc = CURLM_OK; | |
| 1745 } | |
| 1746 | |
| 1747 /* after DO, go DO_DONE... or DO_MORE */ | |
| 1748 else if(data->conn->bits.do_more) { | |
| 1749 /* we're supposed to do more, but we need to sit down, relax | |
| 1750 and wait a little while first */ | |
| 1751 multistate(data, CURLM_STATE_DO_MORE); | |
| 1752 rc = CURLM_OK; | |
| 1753 } | |
| 1754 else { | |
| 1755 /* we're done with the DO, now DO_DONE */ | |
| 1756 multistate(data, CURLM_STATE_DO_DONE); | |
| 1757 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1758 } | |
| 1759 } | |
| 1760 else if((CURLE_SEND_ERROR == result) && | |
| 1761 data->conn->bits.reuse) { | |
| 1762 /* | |
| 1763 * In this situation, a connection that we were trying to use | |
| 1764 * may have unexpectedly died. If possible, send the connection | |
| 1765 * back to the CONNECT phase so we can try again. | |
| 1766 */ | |
| 1767 char *newurl = NULL; | |
| 1768 followtype follow = FOLLOW_NONE; | |
| 1769 CURLcode drc; | |
| 1770 | |
| 1771 drc = Curl_retry_request(data->conn, &newurl); | |
| 1772 if(drc) { | |
| 1773 /* a failure here pretty much implies an out of memory */ | |
| 1774 result = drc; | |
| 1775 stream_error = TRUE; | |
| 1776 } | |
| 1777 | |
| 1778 Curl_posttransfer(data); | |
| 1779 drc = multi_done(data, result, FALSE); | |
| 1780 | |
| 1781 /* When set to retry the connection, we must to go back to | |
| 1782 * the CONNECT state */ | |
| 1783 if(newurl) { | |
| 1784 if(!drc || (drc == CURLE_SEND_ERROR)) { | |
| 1785 follow = FOLLOW_RETRY; | |
| 1786 drc = Curl_follow(data, newurl, follow); | |
| 1787 if(!drc) { | |
| 1788 multistate(data, CURLM_STATE_CONNECT); | |
| 1789 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1790 result = CURLE_OK; | |
| 1791 } | |
| 1792 else { | |
| 1793 /* Follow failed */ | |
| 1794 result = drc; | |
| 1795 } | |
| 1796 } | |
| 1797 else { | |
| 1798 /* done didn't return OK or SEND_ERROR */ | |
| 1799 result = drc; | |
| 1800 } | |
| 1801 } | |
| 1802 else { | |
| 1803 /* Have error handler disconnect conn if we can't retry */ | |
| 1804 stream_error = TRUE; | |
| 1805 } | |
| 1806 free(newurl); | |
| 1807 } | |
| 1808 else { | |
| 1809 /* failure detected */ | |
| 1810 Curl_posttransfer(data); | |
| 1811 if(data->conn) | |
| 1812 multi_done(data, result, FALSE); | |
| 1813 stream_error = TRUE; | |
| 1814 } | |
| 1815 } | |
| 1816 break; | |
| 1817 | |
| 1818 case CURLM_STATE_DOING: | |
| 1819 /* we continue DOING until the DO phase is complete */ | |
| 1820 DEBUGASSERT(data->conn); | |
| 1821 result = protocol_doing(data->conn, &dophase_done); | |
| 1822 if(!result) { | |
| 1823 if(dophase_done) { | |
| 1824 /* after DO, go DO_DONE or DO_MORE */ | |
| 1825 multistate(data, data->conn->bits.do_more? | |
| 1826 CURLM_STATE_DO_MORE: | |
| 1827 CURLM_STATE_DO_DONE); | |
| 1828 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1829 } /* dophase_done */ | |
| 1830 } | |
| 1831 else { | |
| 1832 /* failure detected */ | |
| 1833 Curl_posttransfer(data); | |
| 1834 multi_done(data, result, FALSE); | |
| 1835 stream_error = TRUE; | |
| 1836 } | |
| 1837 break; | |
| 1838 | |
| 1839 case CURLM_STATE_DO_MORE: | |
| 1840 /* | |
| 1841 * When we are connected, DO MORE and then go DO_DONE | |
| 1842 */ | |
| 1843 DEBUGASSERT(data->conn); | |
| 1844 result = multi_do_more(data->conn, &control); | |
| 1845 | |
| 1846 if(!result) { | |
| 1847 if(control) { | |
| 1848 /* if positive, advance to DO_DONE | |
| 1849 if negative, go back to DOING */ | |
| 1850 multistate(data, control == 1? | |
| 1851 CURLM_STATE_DO_DONE: | |
| 1852 CURLM_STATE_DOING); | |
| 1853 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1854 } | |
| 1855 else | |
| 1856 /* stay in DO_MORE */ | |
| 1857 rc = CURLM_OK; | |
| 1858 } | |
| 1859 else { | |
| 1860 /* failure detected */ | |
| 1861 Curl_posttransfer(data); | |
| 1862 multi_done(data, result, FALSE); | |
| 1863 stream_error = TRUE; | |
| 1864 } | |
| 1865 break; | |
| 1866 | |
| 1867 case CURLM_STATE_DO_DONE: | |
| 1868 DEBUGASSERT(data->conn); | |
| 1869 if(data->conn->bits.multiplex) | |
| 1870 /* Check if we can move pending requests to send pipe */ | |
| 1871 process_pending_handles(multi); /* multiplexed */ | |
| 1872 | |
| 1873 /* Only perform the transfer if there's a good socket to work with. | |
| 1874 Having both BAD is a signal to skip immediately to DONE */ | |
| 1875 if((data->conn->sockfd != CURL_SOCKET_BAD) || | |
| 1876 (data->conn->writesockfd != CURL_SOCKET_BAD)) | |
| 1877 multistate(data, CURLM_STATE_PERFORM); | |
| 1878 else { | |
| 1879 #ifndef CURL_DISABLE_FTP | |
| 1880 if(data->state.wildcardmatch && | |
| 1881 ((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) { | |
| 1882 data->wildcard.state = CURLWC_DONE; | |
| 1883 } | |
| 1884 #endif | |
| 1885 multistate(data, CURLM_STATE_DONE); | |
| 1886 } | |
| 1887 rc = CURLM_CALL_MULTI_PERFORM; | |
| 1888 break; | |
| 1889 | |
| 1890 case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ | |
| 1891 DEBUGASSERT(data->conn); | |
| 1892 /* if both rates are within spec, resume transfer */ | |
| 1893 if(Curl_pgrsUpdate(data->conn)) | |
| 1894 result = CURLE_ABORTED_BY_CALLBACK; | |
| 1895 else | |
| 1896 result = Curl_speedcheck(data, now); | |
| 1897 | |
| 1898 if(!result) { | |
| 1899 send_timeout_ms = 0; | |
| 1900 if(data->set.max_send_speed > 0) | |
| 1901 send_timeout_ms = | |
| 1902 Curl_pgrsLimitWaitTime(data->progress.uploaded, | |
| 1903 data->progress.ul_limit_size, | |
| 1904 data->set.max_send_speed, | |
| 1905 data->progress.ul_limit_start, | |
| 1906 now); | |
| 1907 | |
| 1908 recv_timeout_ms = 0; | |
| 1909 if(data->set.max_recv_speed > 0) | |
| 1910 recv_timeout_ms = | |
| 1911 Curl_pgrsLimitWaitTime(data->progress.downloaded, | |
| 1912 data->progress.dl_limit_size, | |
| 1913 data->set.max_recv_speed, | |
| 1914 data->progress.dl_limit_start, | |
| 1915 now); | |
| 1916 | |
| 1917 if(!send_timeout_ms && !recv_timeout_ms) { | |
| 1918 multistate(data, CURLM_STATE_PERFORM); | |
| 1919 Curl_ratelimit(data, now); | |
| 1920 } | |
| 1921 else if(send_timeout_ms >= recv_timeout_ms) | |
| 1922 Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); | |
| 1923 else | |
| 1924 Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); | |
| 1925 } | |
| 1926 break; | |
| 1927 | |
| 1928 case CURLM_STATE_PERFORM: | |
| 1929 { | |
| 1930 char *newurl = NULL; | |
| 1931 bool retry = FALSE; | |
| 1932 bool comeback = FALSE; | |
| 1933 | |
| 1934 /* check if over send speed */ | |
| 1935 send_timeout_ms = 0; | |
| 1936 if(data->set.max_send_speed > 0) | |
| 1937 send_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded, | |
| 1938 data->progress.ul_limit_size, | |
| 1939 data->set.max_send_speed, | |
| 1940 data->progress.ul_limit_start, | |
| 1941 now); | |
| 1942 | |
| 1943 /* check if over recv speed */ | |
| 1944 recv_timeout_ms = 0; | |
| 1945 if(data->set.max_recv_speed > 0) | |
| 1946 recv_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded, | |
| 1947 data->progress.dl_limit_size, | |
| 1948 data->set.max_recv_speed, | |
| 1949 data->progress.dl_limit_start, | |
| 1950 now); | |
| 1951 | |
| 1952 if(send_timeout_ms || recv_timeout_ms) { | |
| 1953 Curl_ratelimit(data, now); | |
| 1954 multistate(data, CURLM_STATE_TOOFAST); | |
| 1955 if(send_timeout_ms >= recv_timeout_ms) | |
| 1956 Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); | |
| 1957 else | |
| 1958 Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); | |
| 1959 break; | |
| 1960 } | |
| 1961 | |
| 1962 /* read/write data if it is ready to do so */ | |
| 1963 result = Curl_readwrite(data->conn, data, &done, &comeback); | |
| 1964 | |
| 1965 if(done || (result == CURLE_RECV_ERROR)) { | |
| 1966 /* If CURLE_RECV_ERROR happens early enough, we assume it was a race | |
| 1967 * condition and the server closed the re-used connection exactly when | |
| 1968 * we wanted to use it, so figure out if that is indeed the case. | |
| 1969 */ | |
| 1970 CURLcode ret = Curl_retry_request(data->conn, &newurl); | |
| 1971 if(!ret) | |
| 1972 retry = (newurl)?TRUE:FALSE; | |
| 1973 else if(!result) | |
| 1974 result = ret; | |
| 1975 | |
| 1976 if(retry) { | |
| 1977 /* if we are to retry, set the result to OK and consider the | |
| 1978 request as done */ | |
| 1979 result = CURLE_OK; | |
| 1980 done = TRUE; | |
| 1981 } | |
| 1982 } | |
| 1983 else if((CURLE_HTTP2_STREAM == result) && | |
| 1984 Curl_h2_http_1_1_error(data->conn)) { | |
| 1985 CURLcode ret = Curl_retry_request(data->conn, &newurl); | |
| 1986 | |
| 1987 if(!ret) { | |
| 1988 infof(data, "Downgrades to HTTP/1.1!\n"); | |
| 1989 data->set.httpversion = CURL_HTTP_VERSION_1_1; | |
| 1990 /* clear the error message bit too as we ignore the one we got */ | |
| 1991 data->state.errorbuf = FALSE; | |
| 1992 if(!newurl) | |
| 1993 /* typically for HTTP_1_1_REQUIRED error on first flight */ | |
| 1994 newurl = strdup(data->change.url); | |
| 1995 /* if we are to retry, set the result to OK and consider the request | |
| 1996 as done */ | |
| 1997 retry = TRUE; | |
| 1998 result = CURLE_OK; | |
| 1999 done = TRUE; | |
| 2000 } | |
| 2001 else | |
| 2002 result = ret; | |
| 2003 } | |
| 2004 | |
| 2005 if(result) { | |
| 2006 /* | |
| 2007 * The transfer phase returned error, we mark the connection to get | |
| 2008 * closed to prevent being re-used. This is because we can't possibly | |
| 2009 * know if the connection is in a good shape or not now. Unless it is | |
| 2010 * a protocol which uses two "channels" like FTP, as then the error | |
| 2011 * happened in the data connection. | |
| 2012 */ | |
| 2013 | |
| 2014 if(!(data->conn->handler->flags & PROTOPT_DUAL) && | |
| 2015 result != CURLE_HTTP2_STREAM) | |
| 2016 streamclose(data->conn, "Transfer returned error"); | |
| 2017 | |
| 2018 Curl_posttransfer(data); | |
| 2019 multi_done(data, result, TRUE); | |
| 2020 } | |
| 2021 else if(done) { | |
| 2022 followtype follow = FOLLOW_NONE; | |
| 2023 | |
| 2024 /* call this even if the readwrite function returned error */ | |
| 2025 Curl_posttransfer(data); | |
| 2026 | |
| 2027 /* When we follow redirects or is set to retry the connection, we must | |
| 2028 to go back to the CONNECT state */ | |
| 2029 if(data->req.newurl || retry) { | |
| 2030 if(!retry) { | |
| 2031 /* if the URL is a follow-location and not just a retried request | |
| 2032 then figure out the URL here */ | |
| 2033 free(newurl); | |
| 2034 newurl = data->req.newurl; | |
| 2035 data->req.newurl = NULL; | |
| 2036 follow = FOLLOW_REDIR; | |
| 2037 } | |
| 2038 else | |
| 2039 follow = FOLLOW_RETRY; | |
| 2040 (void)multi_done(data, CURLE_OK, FALSE); | |
| 2041 /* multi_done() might return CURLE_GOT_NOTHING */ | |
| 2042 result = Curl_follow(data, newurl, follow); | |
| 2043 if(!result) { | |
| 2044 multistate(data, CURLM_STATE_CONNECT); | |
| 2045 rc = CURLM_CALL_MULTI_PERFORM; | |
| 2046 } | |
| 2047 free(newurl); | |
| 2048 } | |
| 2049 else { | |
| 2050 /* after the transfer is done, go DONE */ | |
| 2051 | |
| 2052 /* but first check to see if we got a location info even though we're | |
| 2053 not following redirects */ | |
| 2054 if(data->req.location) { | |
| 2055 free(newurl); | |
| 2056 newurl = data->req.location; | |
| 2057 data->req.location = NULL; | |
| 2058 result = Curl_follow(data, newurl, FOLLOW_FAKE); | |
| 2059 free(newurl); | |
| 2060 if(result) { | |
| 2061 stream_error = TRUE; | |
| 2062 result = multi_done(data, result, TRUE); | |
| 2063 } | |
| 2064 } | |
| 2065 | |
| 2066 if(!result) { | |
| 2067 multistate(data, CURLM_STATE_DONE); | |
| 2068 rc = CURLM_CALL_MULTI_PERFORM; | |
| 2069 } | |
| 2070 } | |
| 2071 } | |
| 2072 else if(comeback) | |
| 2073 rc = CURLM_CALL_MULTI_PERFORM; | |
| 2074 break; | |
| 2075 } | |
| 2076 | |
| 2077 case CURLM_STATE_DONE: | |
| 2078 /* this state is highly transient, so run another loop after this */ | |
| 2079 rc = CURLM_CALL_MULTI_PERFORM; | |
| 2080 | |
| 2081 if(data->conn) { | |
| 2082 CURLcode res; | |
| 2083 | |
| 2084 if(data->conn->bits.multiplex) | |
| 2085 /* Check if we can move pending requests to connection */ | |
| 2086 process_pending_handles(multi); /* multiplexing */ | |
| 2087 | |
| 2088 /* post-transfer command */ | |
| 2089 res = multi_done(data, result, FALSE); | |
| 2090 | |
| 2091 /* allow a previously set error code take precedence */ | |
| 2092 if(!result) | |
| 2093 result = res; | |
| 2094 | |
| 2095 /* | |
| 2096 * If there are other handles on the connection, multi_done won't set | |
| 2097 * conn to NULL. In such a case, curl_multi_remove_handle() can | |
| 2098 * access free'd data, if the connection is free'd and the handle | |
| 2099 * removed before we perform the processing in CURLM_STATE_COMPLETED | |
| 2100 */ | |
| 2101 if(data->conn) | |
| 2102 detach_connnection(data); | |
| 2103 } | |
| 2104 | |
| 2105 #ifndef CURL_DISABLE_FTP | |
| 2106 if(data->state.wildcardmatch) { | |
| 2107 if(data->wildcard.state != CURLWC_DONE) { | |
| 2108 /* if a wildcard is set and we are not ending -> lets start again | |
| 2109 with CURLM_STATE_INIT */ | |
| 2110 multistate(data, CURLM_STATE_INIT); | |
| 2111 break; | |
| 2112 } | |
| 2113 } | |
| 2114 #endif | |
| 2115 /* after we have DONE what we're supposed to do, go COMPLETED, and | |
| 2116 it doesn't matter what the multi_done() returned! */ | |
| 2117 multistate(data, CURLM_STATE_COMPLETED); | |
| 2118 break; | |
| 2119 | |
| 2120 case CURLM_STATE_COMPLETED: | |
| 2121 break; | |
| 2122 | |
| 2123 case CURLM_STATE_MSGSENT: | |
| 2124 data->result = result; | |
| 2125 return CURLM_OK; /* do nothing */ | |
| 2126 | |
| 2127 default: | |
| 2128 return CURLM_INTERNAL_ERROR; | |
| 2129 } | |
| 2130 statemachine_end: | |
| 2131 | |
| 2132 if(data->mstate < CURLM_STATE_COMPLETED) { | |
| 2133 if(result) { | |
| 2134 /* | |
| 2135 * If an error was returned, and we aren't in completed state now, | |
| 2136 * then we go to completed and consider this transfer aborted. | |
| 2137 */ | |
| 2138 | |
| 2139 /* NOTE: no attempt to disconnect connections must be made | |
| 2140 in the case blocks above - cleanup happens only here */ | |
| 2141 | |
| 2142 /* Check if we can move pending requests to send pipe */ | |
| 2143 process_pending_handles(multi); /* connection */ | |
| 2144 | |
| 2145 if(data->conn) { | |
| 2146 if(stream_error) { | |
| 2147 /* Don't attempt to send data over a connection that timed out */ | |
| 2148 bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; | |
| 2149 struct connectdata *conn = data->conn; | |
| 2150 | |
| 2151 /* This is where we make sure that the conn pointer is reset. | |
| 2152 We don't have to do this in every case block above where a | |
| 2153 failure is detected */ | |
| 2154 detach_connnection(data); | |
| 2155 | |
| 2156 /* disconnect properly */ | |
| 2157 Curl_disconnect(data, conn, dead_connection); | |
| 2158 } | |
| 2159 } | |
| 2160 else if(data->mstate == CURLM_STATE_CONNECT) { | |
| 2161 /* Curl_connect() failed */ | |
| 2162 (void)Curl_posttransfer(data); | |
| 2163 } | |
| 2164 | |
| 2165 multistate(data, CURLM_STATE_COMPLETED); | |
| 2166 rc = CURLM_CALL_MULTI_PERFORM; | |
| 2167 } | |
| 2168 /* if there's still a connection to use, call the progress function */ | |
| 2169 else if(data->conn && Curl_pgrsUpdate(data->conn)) { | |
| 2170 /* aborted due to progress callback return code must close the | |
| 2171 connection */ | |
| 2172 result = CURLE_ABORTED_BY_CALLBACK; | |
| 2173 streamclose(data->conn, "Aborted by callback"); | |
| 2174 | |
| 2175 /* if not yet in DONE state, go there, otherwise COMPLETED */ | |
| 2176 multistate(data, (data->mstate < CURLM_STATE_DONE)? | |
| 2177 CURLM_STATE_DONE: CURLM_STATE_COMPLETED); | |
| 2178 rc = CURLM_CALL_MULTI_PERFORM; | |
| 2179 } | |
| 2180 } | |
| 2181 | |
| 2182 if(CURLM_STATE_COMPLETED == data->mstate) { | |
| 2183 if(data->set.fmultidone) { | |
| 2184 /* signal via callback instead */ | |
| 2185 data->set.fmultidone(data, result); | |
| 2186 } | |
| 2187 else { | |
| 2188 /* now fill in the Curl_message with this info */ | |
| 2189 msg = &data->msg; | |
| 2190 | |
| 2191 msg->extmsg.msg = CURLMSG_DONE; | |
| 2192 msg->extmsg.easy_handle = data; | |
| 2193 msg->extmsg.data.result = result; | |
| 2194 | |
| 2195 rc = multi_addmsg(multi, msg); | |
| 2196 DEBUGASSERT(!data->conn); | |
| 2197 } | |
| 2198 multistate(data, CURLM_STATE_MSGSENT); | |
| 2199 } | |
| 2200 } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE)); | |
| 2201 | |
| 2202 data->result = result; | |
| 2203 return rc; | |
| 2204 } | |
| 2205 | |
| 2206 | |
| 2207 CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) | |
| 2208 { | |
| 2209 struct Curl_easy *data; | |
| 2210 CURLMcode returncode = CURLM_OK; | |
| 2211 struct Curl_tree *t; | |
| 2212 struct curltime now = Curl_now(); | |
| 2213 | |
| 2214 if(!GOOD_MULTI_HANDLE(multi)) | |
| 2215 return CURLM_BAD_HANDLE; | |
| 2216 | |
| 2217 if(multi->in_callback) | |
| 2218 return CURLM_RECURSIVE_API_CALL; | |
| 2219 | |
| 2220 data = multi->easyp; | |
| 2221 while(data) { | |
| 2222 CURLMcode result; | |
| 2223 SIGPIPE_VARIABLE(pipe_st); | |
| 2224 | |
| 2225 sigpipe_ignore(data, &pipe_st); | |
| 2226 result = multi_runsingle(multi, now, data); | |
| 2227 sigpipe_restore(&pipe_st); | |
| 2228 | |
| 2229 if(result) | |
| 2230 returncode = result; | |
| 2231 | |
| 2232 data = data->next; /* operate on next handle */ | |
| 2233 } | |
| 2234 | |
| 2235 /* | |
| 2236 * Simply remove all expired timers from the splay since handles are dealt | |
| 2237 * with unconditionally by this function and curl_multi_timeout() requires | |
| 2238 * that already passed/handled expire times are removed from the splay. | |
| 2239 * | |
| 2240 * It is important that the 'now' value is set at the entry of this function | |
| 2241 * and not for the current time as it may have ticked a little while since | |
| 2242 * then and then we risk this loop to remove timers that actually have not | |
| 2243 * been handled! | |
| 2244 */ | |
| 2245 do { | |
| 2246 multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); | |
| 2247 if(t) | |
| 2248 /* the removed may have another timeout in queue */ | |
| 2249 (void)add_next_timeout(now, multi, t->payload); | |
| 2250 | |
| 2251 } while(t); | |
| 2252 | |
| 2253 *running_handles = multi->num_alive; | |
| 2254 | |
| 2255 if(CURLM_OK >= returncode) | |
| 2256 Curl_update_timer(multi); | |
| 2257 | |
| 2258 return returncode; | |
| 2259 } | |
| 2260 | |
| 2261 CURLMcode curl_multi_cleanup(struct Curl_multi *multi) | |
| 2262 { | |
| 2263 struct Curl_easy *data; | |
| 2264 struct Curl_easy *nextdata; | |
| 2265 | |
| 2266 if(GOOD_MULTI_HANDLE(multi)) { | |
| 2267 if(multi->in_callback) | |
| 2268 return CURLM_RECURSIVE_API_CALL; | |
| 2269 | |
| 2270 multi->type = 0; /* not good anymore */ | |
| 2271 | |
| 2272 /* Firsrt remove all remaining easy handles */ | |
| 2273 data = multi->easyp; | |
| 2274 while(data) { | |
| 2275 nextdata = data->next; | |
| 2276 if(!data->state.done && data->conn) | |
| 2277 /* if DONE was never called for this handle */ | |
| 2278 (void)multi_done(data, CURLE_OK, TRUE); | |
| 2279 if(data->dns.hostcachetype == HCACHE_MULTI) { | |
| 2280 /* clear out the usage of the shared DNS cache */ | |
| 2281 Curl_hostcache_clean(data, data->dns.hostcache); | |
| 2282 data->dns.hostcache = NULL; | |
| 2283 data->dns.hostcachetype = HCACHE_NONE; | |
| 2284 } | |
| 2285 | |
| 2286 /* Clear the pointer to the connection cache */ | |
| 2287 data->state.conn_cache = NULL; | |
| 2288 data->multi = NULL; /* clear the association */ | |
| 2289 | |
| 2290 #ifdef USE_LIBPSL | |
| 2291 if(data->psl == &multi->psl) | |
| 2292 data->psl = NULL; | |
| 2293 #endif | |
| 2294 | |
| 2295 data = nextdata; | |
| 2296 } | |
| 2297 | |
| 2298 /* Close all the connections in the connection cache */ | |
| 2299 Curl_conncache_close_all_connections(&multi->conn_cache); | |
| 2300 | |
| 2301 Curl_hash_destroy(&multi->sockhash); | |
| 2302 Curl_conncache_destroy(&multi->conn_cache); | |
| 2303 Curl_llist_destroy(&multi->msglist, NULL); | |
| 2304 Curl_llist_destroy(&multi->pending, NULL); | |
| 2305 | |
| 2306 Curl_hash_destroy(&multi->hostcache); | |
| 2307 Curl_psl_destroy(&multi->psl); | |
| 2308 free(multi); | |
| 2309 | |
| 2310 return CURLM_OK; | |
| 2311 } | |
| 2312 return CURLM_BAD_HANDLE; | |
| 2313 } | |
| 2314 | |
| 2315 /* | |
| 2316 * curl_multi_info_read() | |
| 2317 * | |
| 2318 * This function is the primary way for a multi/multi_socket application to | |
| 2319 * figure out if a transfer has ended. We MUST make this function as fast as | |
| 2320 * possible as it will be polled frequently and we MUST NOT scan any lists in | |
| 2321 * here to figure out things. We must scale fine to thousands of handles and | |
| 2322 * beyond. The current design is fully O(1). | |
| 2323 */ | |
| 2324 | |
| 2325 CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue) | |
| 2326 { | |
| 2327 struct Curl_message *msg; | |
| 2328 | |
| 2329 *msgs_in_queue = 0; /* default to none */ | |
| 2330 | |
| 2331 if(GOOD_MULTI_HANDLE(multi) && | |
| 2332 !multi->in_callback && | |
| 2333 Curl_llist_count(&multi->msglist)) { | |
| 2334 /* there is one or more messages in the list */ | |
| 2335 struct curl_llist_element *e; | |
| 2336 | |
| 2337 /* extract the head of the list to return */ | |
| 2338 e = multi->msglist.head; | |
| 2339 | |
| 2340 msg = e->ptr; | |
| 2341 | |
| 2342 /* remove the extracted entry */ | |
| 2343 Curl_llist_remove(&multi->msglist, e, NULL); | |
| 2344 | |
| 2345 *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist)); | |
| 2346 | |
| 2347 return &msg->extmsg; | |
| 2348 } | |
| 2349 return NULL; | |
| 2350 } | |
| 2351 | |
| 2352 /* | |
| 2353 * singlesocket() checks what sockets we deal with and their "action state" | |
| 2354 * and if we have a different state in any of those sockets from last time we | |
| 2355 * call the callback accordingly. | |
| 2356 */ | |
| 2357 static CURLMcode singlesocket(struct Curl_multi *multi, | |
| 2358 struct Curl_easy *data) | |
| 2359 { | |
| 2360 curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; | |
| 2361 int i; | |
| 2362 struct Curl_sh_entry *entry; | |
| 2363 curl_socket_t s; | |
| 2364 int num; | |
| 2365 unsigned int curraction; | |
| 2366 int actions[MAX_SOCKSPEREASYHANDLE]; | |
| 2367 | |
| 2368 for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) | |
| 2369 socks[i] = CURL_SOCKET_BAD; | |
| 2370 | |
| 2371 /* Fill in the 'current' struct with the state as it is now: what sockets to | |
| 2372 supervise and for what actions */ | |
| 2373 curraction = multi_getsock(data, socks); | |
| 2374 | |
| 2375 /* We have 0 .. N sockets already and we get to know about the 0 .. M | |
| 2376 sockets we should have from now on. Detect the differences, remove no | |
| 2377 longer supervised ones and add new ones */ | |
| 2378 | |
| 2379 /* walk over the sockets we got right now */ | |
| 2380 for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) && | |
| 2381 (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))); | |
| 2382 i++) { | |
| 2383 unsigned int action = CURL_POLL_NONE; | |
| 2384 unsigned int prevaction = 0; | |
| 2385 unsigned int comboaction; | |
| 2386 bool sincebefore = FALSE; | |
| 2387 | |
| 2388 s = socks[i]; | |
| 2389 | |
| 2390 /* get it from the hash */ | |
| 2391 entry = sh_getentry(&multi->sockhash, s); | |
| 2392 | |
| 2393 if(curraction & GETSOCK_READSOCK(i)) | |
| 2394 action |= CURL_POLL_IN; | |
| 2395 if(curraction & GETSOCK_WRITESOCK(i)) | |
| 2396 action |= CURL_POLL_OUT; | |
| 2397 | |
| 2398 actions[i] = action; | |
| 2399 if(entry) { | |
| 2400 /* check if new for this transfer */ | |
| 2401 int j; | |
| 2402 for(j = 0; j< data->numsocks; j++) { | |
| 2403 if(s == data->sockets[j]) { | |
| 2404 prevaction = data->actions[j]; | |
| 2405 sincebefore = TRUE; | |
| 2406 break; | |
| 2407 } | |
| 2408 } | |
| 2409 } | |
| 2410 else { | |
| 2411 /* this is a socket we didn't have before, add it to the hash! */ | |
| 2412 entry = sh_addentry(&multi->sockhash, s); | |
| 2413 if(!entry) | |
| 2414 /* fatal */ | |
| 2415 return CURLM_OUT_OF_MEMORY; | |
| 2416 } | |
| 2417 if(sincebefore && (prevaction != action)) { | |
| 2418 /* Socket was used already, but different action now */ | |
| 2419 if(prevaction & CURL_POLL_IN) | |
| 2420 entry->readers--; | |
| 2421 if(prevaction & CURL_POLL_OUT) | |
| 2422 entry->writers--; | |
| 2423 if(action & CURL_POLL_IN) | |
| 2424 entry->readers++; | |
| 2425 if(action & CURL_POLL_OUT) | |
| 2426 entry->writers++; | |
| 2427 } | |
| 2428 else if(!sincebefore) { | |
| 2429 /* a new user */ | |
| 2430 entry->users++; | |
| 2431 if(action & CURL_POLL_IN) | |
| 2432 entry->readers++; | |
| 2433 if(action & CURL_POLL_OUT) | |
| 2434 entry->writers++; | |
| 2435 | |
| 2436 /* add 'data' to the transfer hash on this socket! */ | |
| 2437 if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */ | |
| 2438 sizeof(struct Curl_easy *), data)) | |
| 2439 return CURLM_OUT_OF_MEMORY; | |
| 2440 } | |
| 2441 | |
| 2442 comboaction = (entry->writers? CURL_POLL_OUT : 0) | | |
| 2443 (entry->readers ? CURL_POLL_IN : 0); | |
| 2444 | |
| 2445 /* socket existed before and has the same action set as before */ | |
| 2446 if(sincebefore && (entry->action == comboaction)) | |
| 2447 /* same, continue */ | |
| 2448 continue; | |
| 2449 | |
| 2450 if(multi->socket_cb) | |
| 2451 multi->socket_cb(data, s, comboaction, multi->socket_userp, | |
| 2452 entry->socketp); | |
| 2453 | |
| 2454 entry->action = comboaction; /* store the current action state */ | |
| 2455 } | |
| 2456 | |
| 2457 num = i; /* number of sockets */ | |
| 2458 | |
| 2459 /* when we've walked over all the sockets we should have right now, we must | |
| 2460 make sure to detect sockets that are removed */ | |
| 2461 for(i = 0; i< data->numsocks; i++) { | |
| 2462 int j; | |
| 2463 bool stillused = FALSE; | |
| 2464 s = data->sockets[i]; | |
| 2465 for(j = 0; j < num; j++) { | |
| 2466 if(s == socks[j]) { | |
| 2467 /* this is still supervised */ | |
| 2468 stillused = TRUE; | |
| 2469 break; | |
| 2470 } | |
| 2471 } | |
| 2472 if(stillused) | |
| 2473 continue; | |
| 2474 | |
| 2475 entry = sh_getentry(&multi->sockhash, s); | |
| 2476 /* if this is NULL here, the socket has been closed and notified so | |
| 2477 already by Curl_multi_closed() */ | |
| 2478 if(entry) { | |
| 2479 int oldactions = data->actions[i]; | |
| 2480 /* this socket has been removed. Decrease user count */ | |
| 2481 entry->users--; | |
| 2482 if(oldactions & CURL_POLL_OUT) | |
| 2483 entry->writers--; | |
| 2484 if(oldactions & CURL_POLL_IN) | |
| 2485 entry->readers--; | |
| 2486 if(!entry->users) { | |
| 2487 if(multi->socket_cb) | |
| 2488 multi->socket_cb(data, s, CURL_POLL_REMOVE, | |
| 2489 multi->socket_userp, | |
| 2490 entry->socketp); | |
| 2491 sh_delentry(entry, &multi->sockhash, s); | |
| 2492 } | |
| 2493 else { | |
| 2494 /* still users, but remove this handle as a user of this socket */ | |
| 2495 if(Curl_hash_delete(&entry->transfers, (char *)&data, | |
| 2496 sizeof(struct Curl_easy *))) { | |
| 2497 DEBUGASSERT(NULL); | |
| 2498 } | |
| 2499 } | |
| 2500 } | |
| 2501 } /* for loop over numsocks */ | |
| 2502 | |
| 2503 memcpy(data->sockets, socks, num*sizeof(curl_socket_t)); | |
| 2504 memcpy(data->actions, actions, num*sizeof(int)); | |
| 2505 data->numsocks = num; | |
| 2506 return CURLM_OK; | |
| 2507 } | |
| 2508 | |
| 2509 void Curl_updatesocket(struct Curl_easy *data) | |
| 2510 { | |
| 2511 singlesocket(data->multi, data); | |
| 2512 } | |
| 2513 | |
| 2514 | |
| 2515 /* | |
| 2516 * Curl_multi_closed() | |
| 2517 * | |
| 2518 * Used by the connect code to tell the multi_socket code that one of the | |
| 2519 * sockets we were using is about to be closed. This function will then | |
| 2520 * remove it from the sockethash for this handle to make the multi_socket API | |
| 2521 * behave properly, especially for the case when libcurl will create another | |
| 2522 * socket again and it gets the same file descriptor number. | |
| 2523 */ | |
| 2524 | |
| 2525 void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s) | |
| 2526 { | |
| 2527 if(data) { | |
| 2528 /* if there's still an easy handle associated with this connection */ | |
| 2529 struct Curl_multi *multi = data->multi; | |
| 2530 if(multi) { | |
| 2531 /* this is set if this connection is part of a handle that is added to | |
| 2532 a multi handle, and only then this is necessary */ | |
| 2533 struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); | |
| 2534 | |
| 2535 if(entry) { | |
| 2536 if(multi->socket_cb) | |
| 2537 multi->socket_cb(data, s, CURL_POLL_REMOVE, | |
| 2538 multi->socket_userp, | |
| 2539 entry->socketp); | |
| 2540 | |
| 2541 /* now remove it from the socket hash */ | |
| 2542 sh_delentry(entry, &multi->sockhash, s); | |
| 2543 } | |
| 2544 } | |
| 2545 } | |
| 2546 } | |
| 2547 | |
| 2548 /* | |
| 2549 * add_next_timeout() | |
| 2550 * | |
| 2551 * Each Curl_easy has a list of timeouts. The add_next_timeout() is called | |
| 2552 * when it has just been removed from the splay tree because the timeout has | |
| 2553 * expired. This function is then to advance in the list to pick the next | |
| 2554 * timeout to use (skip the already expired ones) and add this node back to | |
| 2555 * the splay tree again. | |
| 2556 * | |
| 2557 * The splay tree only has each sessionhandle as a single node and the nearest | |
| 2558 * timeout is used to sort it on. | |
| 2559 */ | |
| 2560 static CURLMcode add_next_timeout(struct curltime now, | |
| 2561 struct Curl_multi *multi, | |
| 2562 struct Curl_easy *d) | |
| 2563 { | |
| 2564 struct curltime *tv = &d->state.expiretime; | |
| 2565 struct curl_llist *list = &d->state.timeoutlist; | |
| 2566 struct curl_llist_element *e; | |
| 2567 struct time_node *node = NULL; | |
| 2568 | |
| 2569 /* move over the timeout list for this specific handle and remove all | |
| 2570 timeouts that are now passed tense and store the next pending | |
| 2571 timeout in *tv */ | |
| 2572 for(e = list->head; e;) { | |
| 2573 struct curl_llist_element *n = e->next; | |
| 2574 timediff_t diff; | |
| 2575 node = (struct time_node *)e->ptr; | |
| 2576 diff = Curl_timediff(node->time, now); | |
| 2577 if(diff <= 0) | |
| 2578 /* remove outdated entry */ | |
| 2579 Curl_llist_remove(list, e, NULL); | |
| 2580 else | |
| 2581 /* the list is sorted so get out on the first mismatch */ | |
| 2582 break; | |
| 2583 e = n; | |
| 2584 } | |
| 2585 e = list->head; | |
| 2586 if(!e) { | |
| 2587 /* clear the expire times within the handles that we remove from the | |
| 2588 splay tree */ | |
| 2589 tv->tv_sec = 0; | |
| 2590 tv->tv_usec = 0; | |
| 2591 } | |
| 2592 else { | |
| 2593 /* copy the first entry to 'tv' */ | |
| 2594 memcpy(tv, &node->time, sizeof(*tv)); | |
| 2595 | |
| 2596 /* Insert this node again into the splay. Keep the timer in the list in | |
| 2597 case we need to recompute future timers. */ | |
| 2598 multi->timetree = Curl_splayinsert(*tv, multi->timetree, | |
| 2599 &d->state.timenode); | |
| 2600 } | |
| 2601 return CURLM_OK; | |
| 2602 } | |
| 2603 | |
| 2604 static CURLMcode multi_socket(struct Curl_multi *multi, | |
| 2605 bool checkall, | |
| 2606 curl_socket_t s, | |
| 2607 int ev_bitmask, | |
| 2608 int *running_handles) | |
| 2609 { | |
| 2610 CURLMcode result = CURLM_OK; | |
| 2611 struct Curl_easy *data = NULL; | |
| 2612 struct Curl_tree *t; | |
| 2613 struct curltime now = Curl_now(); | |
| 2614 | |
| 2615 if(checkall) { | |
| 2616 /* *perform() deals with running_handles on its own */ | |
| 2617 result = curl_multi_perform(multi, running_handles); | |
| 2618 | |
| 2619 /* walk through each easy handle and do the socket state change magic | |
| 2620 and callbacks */ | |
| 2621 if(result != CURLM_BAD_HANDLE) { | |
| 2622 data = multi->easyp; | |
| 2623 while(data && !result) { | |
| 2624 result = singlesocket(multi, data); | |
| 2625 data = data->next; | |
| 2626 } | |
| 2627 } | |
| 2628 | |
| 2629 /* or should we fall-through and do the timer-based stuff? */ | |
| 2630 return result; | |
| 2631 } | |
| 2632 if(s != CURL_SOCKET_TIMEOUT) { | |
| 2633 struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); | |
| 2634 | |
| 2635 if(!entry) | |
| 2636 /* Unmatched socket, we can't act on it but we ignore this fact. In | |
| 2637 real-world tests it has been proved that libevent can in fact give | |
| 2638 the application actions even though the socket was just previously | |
| 2639 asked to get removed, so thus we better survive stray socket actions | |
| 2640 and just move on. */ | |
| 2641 ; | |
| 2642 else { | |
| 2643 struct curl_hash_iterator iter; | |
| 2644 struct curl_hash_element *he; | |
| 2645 | |
| 2646 /* the socket can be shared by many transfers, iterate */ | |
| 2647 Curl_hash_start_iterate(&entry->transfers, &iter); | |
| 2648 for(he = Curl_hash_next_element(&iter); he; | |
| 2649 he = Curl_hash_next_element(&iter)) { | |
| 2650 data = (struct Curl_easy *)he->ptr; | |
| 2651 DEBUGASSERT(data); | |
| 2652 DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER); | |
| 2653 | |
| 2654 if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK)) | |
| 2655 /* set socket event bitmask if they're not locked */ | |
| 2656 data->conn->cselect_bits = ev_bitmask; | |
| 2657 | |
| 2658 Curl_expire(data, 0, EXPIRE_RUN_NOW); | |
| 2659 } | |
| 2660 | |
| 2661 /* Now we fall-through and do the timer-based stuff, since we don't want | |
| 2662 to force the user to have to deal with timeouts as long as at least | |
| 2663 one connection in fact has traffic. */ | |
| 2664 | |
| 2665 data = NULL; /* set data to NULL again to avoid calling | |
| 2666 multi_runsingle() in case there's no need to */ | |
| 2667 now = Curl_now(); /* get a newer time since the multi_runsingle() loop | |
| 2668 may have taken some time */ | |
| 2669 } | |
| 2670 } | |
| 2671 else { | |
| 2672 /* Asked to run due to time-out. Clear the 'lastcall' variable to force | |
| 2673 Curl_update_timer() to trigger a callback to the app again even if the | |
| 2674 same timeout is still the one to run after this call. That handles the | |
| 2675 case when the application asks libcurl to run the timeout | |
| 2676 prematurely. */ | |
| 2677 memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); | |
| 2678 } | |
| 2679 | |
| 2680 /* | |
| 2681 * The loop following here will go on as long as there are expire-times left | |
| 2682 * to process in the splay and 'data' will be re-assigned for every expired | |
| 2683 * handle we deal with. | |
| 2684 */ | |
| 2685 do { | |
| 2686 /* the first loop lap 'data' can be NULL */ | |
| 2687 if(data) { | |
| 2688 SIGPIPE_VARIABLE(pipe_st); | |
| 2689 | |
| 2690 sigpipe_ignore(data, &pipe_st); | |
| 2691 result = multi_runsingle(multi, now, data); | |
| 2692 sigpipe_restore(&pipe_st); | |
| 2693 | |
| 2694 if(CURLM_OK >= result) { | |
| 2695 /* get the socket(s) and check if the state has been changed since | |
| 2696 last */ | |
| 2697 result = singlesocket(multi, data); | |
| 2698 if(result) | |
| 2699 return result; | |
| 2700 } | |
| 2701 } | |
| 2702 | |
| 2703 /* Check if there's one (more) expired timer to deal with! This function | |
| 2704 extracts a matching node if there is one */ | |
| 2705 | |
| 2706 multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); | |
| 2707 if(t) { | |
| 2708 data = t->payload; /* assign this for next loop */ | |
| 2709 (void)add_next_timeout(now, multi, t->payload); | |
| 2710 } | |
| 2711 | |
| 2712 } while(t); | |
| 2713 | |
| 2714 *running_handles = multi->num_alive; | |
| 2715 return result; | |
| 2716 } | |
| 2717 | |
| 2718 #undef curl_multi_setopt | |
| 2719 CURLMcode curl_multi_setopt(struct Curl_multi *multi, | |
| 2720 CURLMoption option, ...) | |
| 2721 { | |
| 2722 CURLMcode res = CURLM_OK; | |
| 2723 va_list param; | |
| 2724 | |
| 2725 if(!GOOD_MULTI_HANDLE(multi)) | |
| 2726 return CURLM_BAD_HANDLE; | |
| 2727 | |
| 2728 if(multi->in_callback) | |
| 2729 return CURLM_RECURSIVE_API_CALL; | |
| 2730 | |
| 2731 va_start(param, option); | |
| 2732 | |
| 2733 switch(option) { | |
| 2734 case CURLMOPT_SOCKETFUNCTION: | |
| 2735 multi->socket_cb = va_arg(param, curl_socket_callback); | |
| 2736 break; | |
| 2737 case CURLMOPT_SOCKETDATA: | |
| 2738 multi->socket_userp = va_arg(param, void *); | |
| 2739 break; | |
| 2740 case CURLMOPT_PUSHFUNCTION: | |
| 2741 multi->push_cb = va_arg(param, curl_push_callback); | |
| 2742 break; | |
| 2743 case CURLMOPT_PUSHDATA: | |
| 2744 multi->push_userp = va_arg(param, void *); | |
| 2745 break; | |
| 2746 case CURLMOPT_PIPELINING: | |
| 2747 multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX; | |
| 2748 break; | |
| 2749 case CURLMOPT_TIMERFUNCTION: | |
| 2750 multi->timer_cb = va_arg(param, curl_multi_timer_callback); | |
| 2751 break; | |
| 2752 case CURLMOPT_TIMERDATA: | |
| 2753 multi->timer_userp = va_arg(param, void *); | |
| 2754 break; | |
| 2755 case CURLMOPT_MAXCONNECTS: | |
| 2756 multi->maxconnects = va_arg(param, long); | |
| 2757 break; | |
| 2758 case CURLMOPT_MAX_HOST_CONNECTIONS: | |
| 2759 multi->max_host_connections = va_arg(param, long); | |
| 2760 break; | |
| 2761 case CURLMOPT_MAX_TOTAL_CONNECTIONS: | |
| 2762 multi->max_total_connections = va_arg(param, long); | |
| 2763 break; | |
| 2764 /* options formerly used for pipelining */ | |
| 2765 case CURLMOPT_MAX_PIPELINE_LENGTH: | |
| 2766 break; | |
| 2767 case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: | |
| 2768 break; | |
| 2769 case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: | |
| 2770 break; | |
| 2771 case CURLMOPT_PIPELINING_SITE_BL: | |
| 2772 break; | |
| 2773 case CURLMOPT_PIPELINING_SERVER_BL: | |
| 2774 break; | |
| 2775 default: | |
| 2776 res = CURLM_UNKNOWN_OPTION; | |
| 2777 break; | |
| 2778 } | |
| 2779 va_end(param); | |
| 2780 return res; | |
| 2781 } | |
| 2782 | |
| 2783 /* we define curl_multi_socket() in the public multi.h header */ | |
| 2784 #undef curl_multi_socket | |
| 2785 | |
| 2786 CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s, | |
| 2787 int *running_handles) | |
| 2788 { | |
| 2789 CURLMcode result; | |
| 2790 if(multi->in_callback) | |
| 2791 return CURLM_RECURSIVE_API_CALL; | |
| 2792 result = multi_socket(multi, FALSE, s, 0, running_handles); | |
| 2793 if(CURLM_OK >= result) | |
| 2794 Curl_update_timer(multi); | |
| 2795 return result; | |
| 2796 } | |
| 2797 | |
| 2798 CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s, | |
| 2799 int ev_bitmask, int *running_handles) | |
| 2800 { | |
| 2801 CURLMcode result; | |
| 2802 if(multi->in_callback) | |
| 2803 return CURLM_RECURSIVE_API_CALL; | |
| 2804 result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles); | |
| 2805 if(CURLM_OK >= result) | |
| 2806 Curl_update_timer(multi); | |
| 2807 return result; | |
| 2808 } | |
| 2809 | |
| 2810 CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles) | |
| 2811 | |
| 2812 { | |
| 2813 CURLMcode result; | |
| 2814 if(multi->in_callback) | |
| 2815 return CURLM_RECURSIVE_API_CALL; | |
| 2816 result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); | |
| 2817 if(CURLM_OK >= result) | |
| 2818 Curl_update_timer(multi); | |
| 2819 return result; | |
| 2820 } | |
| 2821 | |
| 2822 static CURLMcode multi_timeout(struct Curl_multi *multi, | |
| 2823 long *timeout_ms) | |
| 2824 { | |
| 2825 static struct curltime tv_zero = {0, 0}; | |
| 2826 | |
| 2827 if(multi->timetree) { | |
| 2828 /* we have a tree of expire times */ | |
| 2829 struct curltime now = Curl_now(); | |
| 2830 | |
| 2831 /* splay the lowest to the bottom */ | |
| 2832 multi->timetree = Curl_splay(tv_zero, multi->timetree); | |
| 2833 | |
| 2834 if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) { | |
| 2835 /* some time left before expiration */ | |
| 2836 timediff_t diff = Curl_timediff(multi->timetree->key, now); | |
| 2837 if(diff <= 0) | |
| 2838 /* | |
| 2839 * Since we only provide millisecond resolution on the returned value | |
| 2840 * and the diff might be less than one millisecond here, we don't | |
| 2841 * return zero as that may cause short bursts of busyloops on fast | |
| 2842 * processors while the diff is still present but less than one | |
| 2843 * millisecond! instead we return 1 until the time is ripe. | |
| 2844 */ | |
| 2845 *timeout_ms = 1; | |
| 2846 else | |
| 2847 /* this should be safe even on 64 bit archs, as we don't use that | |
| 2848 overly long timeouts */ | |
| 2849 *timeout_ms = (long)diff; | |
| 2850 } | |
| 2851 else | |
| 2852 /* 0 means immediately */ | |
| 2853 *timeout_ms = 0; | |
| 2854 } | |
| 2855 else | |
| 2856 *timeout_ms = -1; | |
| 2857 | |
| 2858 return CURLM_OK; | |
| 2859 } | |
| 2860 | |
| 2861 CURLMcode curl_multi_timeout(struct Curl_multi *multi, | |
| 2862 long *timeout_ms) | |
| 2863 { | |
| 2864 /* First, make some basic checks that the CURLM handle is a good handle */ | |
| 2865 if(!GOOD_MULTI_HANDLE(multi)) | |
| 2866 return CURLM_BAD_HANDLE; | |
| 2867 | |
| 2868 if(multi->in_callback) | |
| 2869 return CURLM_RECURSIVE_API_CALL; | |
| 2870 | |
| 2871 return multi_timeout(multi, timeout_ms); | |
| 2872 } | |
| 2873 | |
| 2874 /* | |
| 2875 * Tell the application it should update its timers, if it subscribes to the | |
| 2876 * update timer callback. | |
| 2877 */ | |
| 2878 void Curl_update_timer(struct Curl_multi *multi) | |
| 2879 { | |
| 2880 long timeout_ms; | |
| 2881 | |
| 2882 if(!multi->timer_cb) | |
| 2883 return; | |
| 2884 if(multi_timeout(multi, &timeout_ms)) { | |
| 2885 return; | |
| 2886 } | |
| 2887 if(timeout_ms < 0) { | |
| 2888 static const struct curltime none = {0, 0}; | |
| 2889 if(Curl_splaycomparekeys(none, multi->timer_lastcall)) { | |
| 2890 multi->timer_lastcall = none; | |
| 2891 /* there's no timeout now but there was one previously, tell the app to | |
| 2892 disable it */ | |
| 2893 multi->timer_cb(multi, -1, multi->timer_userp); | |
| 2894 return; | |
| 2895 } | |
| 2896 return; | |
| 2897 } | |
| 2898 | |
| 2899 /* When multi_timeout() is done, multi->timetree points to the node with the | |
| 2900 * timeout we got the (relative) time-out time for. We can thus easily check | |
| 2901 * if this is the same (fixed) time as we got in a previous call and then | |
| 2902 * avoid calling the callback again. */ | |
| 2903 if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0) | |
| 2904 return; | |
| 2905 | |
| 2906 multi->timer_lastcall = multi->timetree->key; | |
| 2907 | |
| 2908 multi->timer_cb(multi, timeout_ms, multi->timer_userp); | |
| 2909 } | |
| 2910 | |
| 2911 /* | |
| 2912 * multi_deltimeout() | |
| 2913 * | |
| 2914 * Remove a given timestamp from the list of timeouts. | |
| 2915 */ | |
| 2916 static void | |
| 2917 multi_deltimeout(struct Curl_easy *data, expire_id eid) | |
| 2918 { | |
| 2919 struct curl_llist_element *e; | |
| 2920 struct curl_llist *timeoutlist = &data->state.timeoutlist; | |
| 2921 /* find and remove the specific node from the list */ | |
| 2922 for(e = timeoutlist->head; e; e = e->next) { | |
| 2923 struct time_node *n = (struct time_node *)e->ptr; | |
| 2924 if(n->eid == eid) { | |
| 2925 Curl_llist_remove(timeoutlist, e, NULL); | |
| 2926 return; | |
| 2927 } | |
| 2928 } | |
| 2929 } | |
| 2930 | |
| 2931 /* | |
| 2932 * multi_addtimeout() | |
| 2933 * | |
| 2934 * Add a timestamp to the list of timeouts. Keep the list sorted so that head | |
| 2935 * of list is always the timeout nearest in time. | |
| 2936 * | |
| 2937 */ | |
| 2938 static CURLMcode | |
| 2939 multi_addtimeout(struct Curl_easy *data, | |
| 2940 struct curltime *stamp, | |
| 2941 expire_id eid) | |
| 2942 { | |
| 2943 struct curl_llist_element *e; | |
| 2944 struct time_node *node; | |
| 2945 struct curl_llist_element *prev = NULL; | |
| 2946 size_t n; | |
| 2947 struct curl_llist *timeoutlist = &data->state.timeoutlist; | |
| 2948 | |
| 2949 node = &data->state.expires[eid]; | |
| 2950 | |
| 2951 /* copy the timestamp and id */ | |
| 2952 memcpy(&node->time, stamp, sizeof(*stamp)); | |
| 2953 node->eid = eid; /* also marks it as in use */ | |
| 2954 | |
| 2955 n = Curl_llist_count(timeoutlist); | |
| 2956 if(n) { | |
| 2957 /* find the correct spot in the list */ | |
| 2958 for(e = timeoutlist->head; e; e = e->next) { | |
| 2959 struct time_node *check = (struct time_node *)e->ptr; | |
| 2960 timediff_t diff = Curl_timediff(check->time, node->time); | |
| 2961 if(diff > 0) | |
| 2962 break; | |
| 2963 prev = e; | |
| 2964 } | |
| 2965 | |
| 2966 } | |
| 2967 /* else | |
| 2968 this is the first timeout on the list */ | |
| 2969 | |
| 2970 Curl_llist_insert_next(timeoutlist, prev, node, &node->list); | |
| 2971 return CURLM_OK; | |
| 2972 } | |
| 2973 | |
| 2974 /* | |
| 2975 * Curl_expire() | |
| 2976 * | |
| 2977 * given a number of milliseconds from now to use to set the 'act before | |
| 2978 * this'-time for the transfer, to be extracted by curl_multi_timeout() | |
| 2979 * | |
| 2980 * The timeout will be added to a queue of timeouts if it defines a moment in | |
| 2981 * time that is later than the current head of queue. | |
| 2982 * | |
| 2983 * Expire replaces a former timeout using the same id if already set. | |
| 2984 */ | |
| 2985 void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) | |
| 2986 { | |
| 2987 struct Curl_multi *multi = data->multi; | |
| 2988 struct curltime *nowp = &data->state.expiretime; | |
| 2989 struct curltime set; | |
| 2990 | |
| 2991 /* this is only interesting while there is still an associated multi struct | |
| 2992 remaining! */ | |
| 2993 if(!multi) | |
| 2994 return; | |
| 2995 | |
| 2996 DEBUGASSERT(id < EXPIRE_LAST); | |
| 2997 | |
| 2998 set = Curl_now(); | |
| 2999 set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bit conversion */ | |
| 3000 set.tv_usec += (unsigned int)(milli%1000)*1000; | |
| 3001 | |
| 3002 if(set.tv_usec >= 1000000) { | |
| 3003 set.tv_sec++; | |
| 3004 set.tv_usec -= 1000000; | |
| 3005 } | |
| 3006 | |
| 3007 /* Remove any timer with the same id just in case. */ | |
| 3008 multi_deltimeout(data, id); | |
| 3009 | |
| 3010 /* Add it to the timer list. It must stay in the list until it has expired | |
| 3011 in case we need to recompute the minimum timer later. */ | |
| 3012 multi_addtimeout(data, &set, id); | |
| 3013 | |
| 3014 if(nowp->tv_sec || nowp->tv_usec) { | |
| 3015 /* This means that the struct is added as a node in the splay tree. | |
| 3016 Compare if the new time is earlier, and only remove-old/add-new if it | |
| 3017 is. */ | |
| 3018 timediff_t diff = Curl_timediff(set, *nowp); | |
| 3019 int rc; | |
| 3020 | |
| 3021 if(diff > 0) { | |
| 3022 /* The current splay tree entry is sooner than this new expiry time. | |
| 3023 We don't need to update our splay tree entry. */ | |
| 3024 return; | |
| 3025 } | |
| 3026 | |
| 3027 /* Since this is an updated time, we must remove the previous entry from | |
| 3028 the splay tree first and then re-add the new value */ | |
| 3029 rc = Curl_splayremovebyaddr(multi->timetree, | |
| 3030 &data->state.timenode, | |
| 3031 &multi->timetree); | |
| 3032 if(rc) | |
| 3033 infof(data, "Internal error removing splay node = %d\n", rc); | |
| 3034 } | |
| 3035 | |
| 3036 /* Indicate that we are in the splay tree and insert the new timer expiry | |
| 3037 value since it is our local minimum. */ | |
| 3038 *nowp = set; | |
| 3039 data->state.timenode.payload = data; | |
| 3040 multi->timetree = Curl_splayinsert(*nowp, multi->timetree, | |
| 3041 &data->state.timenode); | |
| 3042 } | |
| 3043 | |
| 3044 /* | |
| 3045 * Curl_expire_done() | |
| 3046 * | |
| 3047 * Removes the expire timer. Marks it as done. | |
| 3048 * | |
| 3049 */ | |
| 3050 void Curl_expire_done(struct Curl_easy *data, expire_id id) | |
| 3051 { | |
| 3052 /* remove the timer, if there */ | |
| 3053 multi_deltimeout(data, id); | |
| 3054 } | |
| 3055 | |
| 3056 /* | |
| 3057 * Curl_expire_clear() | |
| 3058 * | |
| 3059 * Clear ALL timeout values for this handle. | |
| 3060 */ | |
| 3061 void Curl_expire_clear(struct Curl_easy *data) | |
| 3062 { | |
| 3063 struct Curl_multi *multi = data->multi; | |
| 3064 struct curltime *nowp = &data->state.expiretime; | |
| 3065 | |
| 3066 /* this is only interesting while there is still an associated multi struct | |
| 3067 remaining! */ | |
| 3068 if(!multi) | |
| 3069 return; | |
| 3070 | |
| 3071 if(nowp->tv_sec || nowp->tv_usec) { | |
| 3072 /* Since this is an cleared time, we must remove the previous entry from | |
| 3073 the splay tree */ | |
| 3074 struct curl_llist *list = &data->state.timeoutlist; | |
| 3075 int rc; | |
| 3076 | |
| 3077 rc = Curl_splayremovebyaddr(multi->timetree, | |
| 3078 &data->state.timenode, | |
| 3079 &multi->timetree); | |
| 3080 if(rc) | |
| 3081 infof(data, "Internal error clearing splay node = %d\n", rc); | |
| 3082 | |
| 3083 /* flush the timeout list too */ | |
| 3084 while(list->size > 0) { | |
| 3085 Curl_llist_remove(list, list->tail, NULL); | |
| 3086 } | |
| 3087 | |
| 3088 #ifdef DEBUGBUILD | |
| 3089 infof(data, "Expire cleared (transfer %p)\n", data); | |
| 3090 #endif | |
| 3091 nowp->tv_sec = 0; | |
| 3092 nowp->tv_usec = 0; | |
| 3093 } | |
| 3094 } | |
| 3095 | |
| 3096 | |
| 3097 | |
| 3098 | |
| 3099 CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s, | |
| 3100 void *hashp) | |
| 3101 { | |
| 3102 struct Curl_sh_entry *there = NULL; | |
| 3103 | |
| 3104 if(multi->in_callback) | |
| 3105 return CURLM_RECURSIVE_API_CALL; | |
| 3106 | |
| 3107 there = sh_getentry(&multi->sockhash, s); | |
| 3108 | |
| 3109 if(!there) | |
| 3110 return CURLM_BAD_SOCKET; | |
| 3111 | |
| 3112 there->socketp = hashp; | |
| 3113 | |
| 3114 return CURLM_OK; | |
| 3115 } | |
| 3116 | |
| 3117 size_t Curl_multi_max_host_connections(struct Curl_multi *multi) | |
| 3118 { | |
| 3119 return multi ? multi->max_host_connections : 0; | |
| 3120 } | |
| 3121 | |
| 3122 size_t Curl_multi_max_total_connections(struct Curl_multi *multi) | |
| 3123 { | |
| 3124 return multi ? multi->max_total_connections : 0; | |
| 3125 } | |
| 3126 | |
| 3127 /* | |
| 3128 * When information about a connection has appeared, call this! | |
| 3129 */ | |
| 3130 | |
| 3131 void Curl_multiuse_state(struct connectdata *conn, | |
| 3132 int bundlestate) /* use BUNDLE_* defines */ | |
| 3133 { | |
| 3134 DEBUGASSERT(conn); | |
| 3135 DEBUGASSERT(conn->bundle); | |
| 3136 DEBUGASSERT(conn->data); | |
| 3137 DEBUGASSERT(conn->data->multi); | |
| 3138 | |
| 3139 conn->bundle->multiuse = bundlestate; | |
| 3140 process_pending_handles(conn->data->multi); | |
| 3141 } | |
| 3142 | |
| 3143 static void process_pending_handles(struct Curl_multi *multi) | |
| 3144 { | |
| 3145 struct curl_llist_element *e = multi->pending.head; | |
| 3146 if(e) { | |
| 3147 struct Curl_easy *data = e->ptr; | |
| 3148 | |
| 3149 DEBUGASSERT(data->mstate == CURLM_STATE_CONNECT_PEND); | |
| 3150 | |
| 3151 multistate(data, CURLM_STATE_CONNECT); | |
| 3152 | |
| 3153 /* Remove this node from the list */ | |
| 3154 Curl_llist_remove(&multi->pending, e, NULL); | |
| 3155 | |
| 3156 /* Make sure that the handle will be processed soonish. */ | |
| 3157 Curl_expire(data, 0, EXPIRE_RUN_NOW); | |
| 3158 | |
| 3159 /* mark this as having been in the pending queue */ | |
| 3160 data->state.previouslypending = TRUE; | |
| 3161 } | |
| 3162 } | |
| 3163 | |
| 3164 void Curl_set_in_callback(struct Curl_easy *data, bool value) | |
| 3165 { | |
| 3166 /* might get called when there is no data pointer! */ | |
| 3167 if(data) { | |
| 3168 if(data->multi_easy) | |
| 3169 data->multi_easy->in_callback = value; | |
| 3170 else if(data->multi) | |
| 3171 data->multi->in_callback = value; | |
| 3172 } | |
| 3173 } | |
| 3174 | |
| 3175 bool Curl_is_in_callback(struct Curl_easy *easy) | |
| 3176 { | |
| 3177 return ((easy->multi && easy->multi->in_callback) || | |
| 3178 (easy->multi_easy && easy->multi_easy->in_callback)); | |
| 3179 } | |
| 3180 | |
| 3181 #ifdef DEBUGBUILD | |
| 3182 void Curl_multi_dump(struct Curl_multi *multi) | |
| 3183 { | |
| 3184 struct Curl_easy *data; | |
| 3185 int i; | |
| 3186 fprintf(stderr, "* Multi status: %d handles, %d alive\n", | |
| 3187 multi->num_easy, multi->num_alive); | |
| 3188 for(data = multi->easyp; data; data = data->next) { | |
| 3189 if(data->mstate < CURLM_STATE_COMPLETED) { | |
| 3190 /* only display handles that are not completed */ | |
| 3191 fprintf(stderr, "handle %p, state %s, %d sockets\n", | |
| 3192 (void *)data, | |
| 3193 statename[data->mstate], data->numsocks); | |
| 3194 for(i = 0; i < data->numsocks; i++) { | |
| 3195 curl_socket_t s = data->sockets[i]; | |
| 3196 struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); | |
| 3197 | |
| 3198 fprintf(stderr, "%d ", (int)s); | |
| 3199 if(!entry) { | |
| 3200 fprintf(stderr, "INTERNAL CONFUSION\n"); | |
| 3201 continue; | |
| 3202 } | |
| 3203 fprintf(stderr, "[%s %s] ", | |
| 3204 (entry->action&CURL_POLL_IN)?"RECVING":"", | |
| 3205 (entry->action&CURL_POLL_OUT)?"SENDING":""); | |
| 3206 } | |
| 3207 if(data->numsocks) | |
| 3208 fprintf(stderr, "\n"); | |
| 3209 } | |
| 3210 } | |
| 3211 } | |
| 3212 #endif |
