source: UsbWattMeter/trunk/curl-7.47.1/lib/multi.c@ 164

Last change on this file since 164 was 164, checked in by coas-nagasima, 6 years ago

TOPPERS/ECNLサンプルアプリ「USB充電器電力計」を追加

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