xref: /curl/lib/conncache.c (revision fde2143d)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
9  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
10  *
11  * This software is licensed as described in the file COPYING, which
12  * you should have received as part of this distribution. The terms
13  * are also available at https://curl.se/docs/copyright.html.
14  *
15  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16  * copies of the Software, and permit persons to whom the Software is
17  * furnished to do so, under the terms of the COPYING file.
18  *
19  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20  * KIND, either express or implied.
21  *
22  * SPDX-License-Identifier: curl
23  *
24  ***************************************************************************/
25 
26 #include "curl_setup.h"
27 
28 #include <curl/curl.h>
29 
30 #include "urldata.h"
31 #include "url.h"
32 #include "cfilters.h"
33 #include "progress.h"
34 #include "multiif.h"
35 #include "sendf.h"
36 #include "conncache.h"
37 #include "http_negotiate.h"
38 #include "http_ntlm.h"
39 #include "share.h"
40 #include "sigpipe.h"
41 #include "connect.h"
42 #include "select.h"
43 #include "strcase.h"
44 
45 /* The last 3 #include files should be in this order */
46 #include "curl_printf.h"
47 #include "curl_memory.h"
48 #include "memdebug.h"
49 
50 #define HASHKEY_SIZE 128
51 
52 static void connc_discard_conn(struct conncache *connc,
53                                struct Curl_easy *last_data,
54                                struct connectdata *conn,
55                                bool aborted);
56 static void connc_disconnect(struct Curl_easy *data,
57                              struct connectdata *conn,
58                              struct conncache *connc,
59                              bool do_shutdown);
60 static void connc_run_conn_shutdown(struct Curl_easy *data,
61                                     struct connectdata *conn,
62                                     bool *done);
63 static void connc_run_conn_shutdown_handler(struct Curl_easy *data,
64                                             struct connectdata *conn);
65 static CURLcode connc_update_shutdown_ev(struct Curl_multi *multi,
66                                          struct Curl_easy *data,
67                                          struct connectdata *conn);
68 static void connc_shutdown_all(struct conncache *connc, int timeout_ms);
69 
bundle_create(struct connectbundle ** bundlep)70 static CURLcode bundle_create(struct connectbundle **bundlep)
71 {
72   DEBUGASSERT(*bundlep == NULL);
73   *bundlep = malloc(sizeof(struct connectbundle));
74   if(!*bundlep)
75     return CURLE_OUT_OF_MEMORY;
76 
77   (*bundlep)->num_connections = 0;
78   (*bundlep)->multiuse = BUNDLE_UNKNOWN;
79 
80   Curl_llist_init(&(*bundlep)->conn_list, NULL);
81   return CURLE_OK;
82 }
83 
bundle_destroy(struct connectbundle * bundle)84 static void bundle_destroy(struct connectbundle *bundle)
85 {
86   free(bundle);
87 }
88 
89 /* Add a connection to a bundle */
bundle_add_conn(struct connectbundle * bundle,struct connectdata * conn)90 static void bundle_add_conn(struct connectbundle *bundle,
91                             struct connectdata *conn)
92 {
93   Curl_llist_append(&bundle->conn_list, conn, &conn->bundle_node);
94   conn->bundle = bundle;
95   bundle->num_connections++;
96 }
97 
98 /* Remove a connection from a bundle */
bundle_remove_conn(struct connectbundle * bundle,struct connectdata * conn)99 static int bundle_remove_conn(struct connectbundle *bundle,
100                               struct connectdata *conn)
101 {
102   struct Curl_llist_element *curr;
103 
104   curr = bundle->conn_list.head;
105   while(curr) {
106     if(curr->ptr == conn) {
107       Curl_llist_remove(&bundle->conn_list, curr, NULL);
108       bundle->num_connections--;
109       conn->bundle = NULL;
110       return 1; /* we removed a handle */
111     }
112     curr = curr->next;
113   }
114   DEBUGASSERT(0);
115   return 0;
116 }
117 
free_bundle_hash_entry(void * freethis)118 static void free_bundle_hash_entry(void *freethis)
119 {
120   struct connectbundle *b = (struct connectbundle *) freethis;
121 
122   bundle_destroy(b);
123 }
124 
Curl_conncache_init(struct conncache * connc,struct Curl_multi * multi,size_t size)125 int Curl_conncache_init(struct conncache *connc,
126                         struct Curl_multi *multi, size_t size)
127 {
128   /* allocate a new easy handle to use when closing cached connections */
129   connc->closure_handle = curl_easy_init();
130   if(!connc->closure_handle)
131     return 1; /* bad */
132   connc->closure_handle->state.internal = true;
133  #ifdef DEBUGBUILD
134   if(getenv("CURL_DEBUG"))
135     connc->closure_handle->set.verbose = true;
136 #endif
137 
138   Curl_hash_init(&connc->hash, size, Curl_hash_str,
139                  Curl_str_key_compare, free_bundle_hash_entry);
140   connc->closure_handle->state.conn_cache = connc;
141   connc->multi = multi;
142   Curl_llist_init(&connc->shutdowns.conn_list, NULL);
143 
144   return 0; /* good */
145 }
146 
Curl_conncache_destroy(struct conncache * connc)147 void Curl_conncache_destroy(struct conncache *connc)
148 {
149   if(connc) {
150     Curl_hash_destroy(&connc->hash);
151     connc->multi = NULL;
152     DEBUGASSERT(!Curl_llist_count(&connc->shutdowns.conn_list));
153   }
154 }
155 
156 /* creates a key to find a bundle for this connection */
hashkey(struct connectdata * conn,char * buf,size_t len)157 static void hashkey(struct connectdata *conn, char *buf, size_t len)
158 {
159   const char *hostname;
160   long port = conn->remote_port;
161   DEBUGASSERT(len >= HASHKEY_SIZE);
162 #ifndef CURL_DISABLE_PROXY
163   if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
164     hostname = conn->http_proxy.host.name;
165     port = conn->primary.remote_port;
166   }
167   else
168 #endif
169     if(conn->bits.conn_to_host)
170       hostname = conn->conn_to_host.name;
171   else
172     hostname = conn->host.name;
173 
174   /* put the numbers first so that the hostname gets cut off if too long */
175 #ifdef USE_IPV6
176   msnprintf(buf, len, "%u/%ld/%s", conn->scope_id, port, hostname);
177 #else
178   msnprintf(buf, len, "%ld/%s", port, hostname);
179 #endif
180   Curl_strntolower(buf, buf, len);
181 }
182 
183 /* Returns number of connections currently held in the connection cache.
184    Locks/unlocks the cache itself!
185 */
Curl_conncache_size(struct Curl_easy * data)186 size_t Curl_conncache_size(struct Curl_easy *data)
187 {
188   size_t num;
189   CONNCACHE_LOCK(data);
190   num = data->state.conn_cache->num_conn;
191   CONNCACHE_UNLOCK(data);
192   return num;
193 }
194 
195 /* Look up the bundle with all the connections to the same host this
196    connectdata struct is setup to use.
197 
198    **NOTE**: When it returns, it holds the connection cache lock! */
199 struct connectbundle *
Curl_conncache_find_bundle(struct Curl_easy * data,struct connectdata * conn,struct conncache * connc)200 Curl_conncache_find_bundle(struct Curl_easy *data,
201                            struct connectdata *conn,
202                            struct conncache *connc)
203 {
204   struct connectbundle *bundle = NULL;
205   CONNCACHE_LOCK(data);
206   if(connc) {
207     char key[HASHKEY_SIZE];
208     hashkey(conn, key, sizeof(key));
209     bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
210   }
211 
212   return bundle;
213 }
214 
connc_add_bundle(struct conncache * connc,char * key,struct connectbundle * bundle)215 static void *connc_add_bundle(struct conncache *connc,
216                               char *key, struct connectbundle *bundle)
217 {
218   return Curl_hash_add(&connc->hash, key, strlen(key), bundle);
219 }
220 
connc_remove_bundle(struct conncache * connc,struct connectbundle * bundle)221 static void connc_remove_bundle(struct conncache *connc,
222                                 struct connectbundle *bundle)
223 {
224   struct Curl_hash_iterator iter;
225   struct Curl_hash_element *he;
226 
227   if(!connc)
228     return;
229 
230   Curl_hash_start_iterate(&connc->hash, &iter);
231 
232   he = Curl_hash_next_element(&iter);
233   while(he) {
234     if(he->ptr == bundle) {
235       /* The bundle is destroyed by the hash destructor function,
236          free_bundle_hash_entry() */
237       Curl_hash_delete(&connc->hash, he->key, he->key_len);
238       return;
239     }
240 
241     he = Curl_hash_next_element(&iter);
242   }
243 }
244 
Curl_conncache_add_conn(struct Curl_easy * data)245 CURLcode Curl_conncache_add_conn(struct Curl_easy *data)
246 {
247   CURLcode result = CURLE_OK;
248   struct connectbundle *bundle = NULL;
249   struct connectdata *conn = data->conn;
250   struct conncache *connc = data->state.conn_cache;
251   DEBUGASSERT(conn);
252 
253   /* *find_bundle() locks the connection cache */
254   bundle = Curl_conncache_find_bundle(data, conn, data->state.conn_cache);
255   if(!bundle) {
256     char key[HASHKEY_SIZE];
257 
258     result = bundle_create(&bundle);
259     if(result) {
260       goto unlock;
261     }
262 
263     hashkey(conn, key, sizeof(key));
264 
265     if(!connc_add_bundle(data->state.conn_cache, key, bundle)) {
266       bundle_destroy(bundle);
267       result = CURLE_OUT_OF_MEMORY;
268       goto unlock;
269     }
270   }
271 
272   bundle_add_conn(bundle, conn);
273   conn->connection_id = connc->next_connection_id++;
274   connc->num_conn++;
275 
276   DEBUGF(infof(data, "Added connection %" CURL_FORMAT_CURL_OFF_T ". "
277                "The cache now contains %zu members",
278                conn->connection_id, connc->num_conn));
279 
280 unlock:
281   CONNCACHE_UNLOCK(data);
282 
283   return result;
284 }
285 
connc_remove_conn(struct conncache * connc,struct connectdata * conn)286 static void connc_remove_conn(struct conncache *connc,
287                               struct connectdata *conn)
288 {
289   struct connectbundle *bundle = conn->bundle;
290 
291   /* The bundle pointer can be NULL, since this function can be called
292      due to a failed connection attempt, before being added to a bundle */
293   if(bundle) {
294     bundle_remove_conn(bundle, conn);
295     if(connc && bundle->num_connections == 0)
296       connc_remove_bundle(connc, bundle);
297     conn->bundle = NULL; /* removed from it */
298     if(connc)
299       connc->num_conn--;
300   }
301 }
302 
303 /*
304  * Removes the connectdata object from the connection cache, but the transfer
305  * still owns this connection.
306  *
307  * Pass TRUE/FALSE in the 'lock' argument depending on if the parent function
308  * already holds the lock or not.
309  */
Curl_conncache_remove_conn(struct Curl_easy * data,struct connectdata * conn,bool lock)310 void Curl_conncache_remove_conn(struct Curl_easy *data,
311                                 struct connectdata *conn, bool lock)
312 {
313   struct conncache *connc = data->state.conn_cache;
314 
315   if(lock)
316     CONNCACHE_LOCK(data);
317   connc_remove_conn(connc, conn);
318   if(lock)
319     CONNCACHE_UNLOCK(data);
320   if(connc)
321     DEBUGF(infof(data, "The cache now contains %zu members",
322                  connc->num_conn));
323 }
324 
325 /* This function iterates the entire connection cache and calls the function
326    func() with the connection pointer as the first argument and the supplied
327    'param' argument as the other.
328 
329    The conncache lock is still held when the callback is called. It needs it,
330    so that it can safely continue traversing the lists once the callback
331    returns.
332 
333    Returns 1 if the loop was aborted due to the callback's return code.
334 
335    Return 0 from func() to continue the loop, return 1 to abort it.
336  */
Curl_conncache_foreach(struct Curl_easy * data,struct conncache * connc,void * param,int (* func)(struct Curl_easy * data,struct connectdata * conn,void * param))337 bool Curl_conncache_foreach(struct Curl_easy *data,
338                             struct conncache *connc,
339                             void *param,
340                             int (*func)(struct Curl_easy *data,
341                                         struct connectdata *conn, void *param))
342 {
343   struct Curl_hash_iterator iter;
344   struct Curl_llist_element *curr;
345   struct Curl_hash_element *he;
346 
347   if(!connc)
348     return FALSE;
349 
350   CONNCACHE_LOCK(data);
351   Curl_hash_start_iterate(&connc->hash, &iter);
352 
353   he = Curl_hash_next_element(&iter);
354   while(he) {
355     struct connectbundle *bundle;
356 
357     bundle = he->ptr;
358     he = Curl_hash_next_element(&iter);
359 
360     curr = bundle->conn_list.head;
361     while(curr) {
362       /* Yes, we need to update curr before calling func(), because func()
363          might decide to remove the connection */
364       struct connectdata *conn = curr->ptr;
365       curr = curr->next;
366 
367       if(1 == func(data, conn, param)) {
368         CONNCACHE_UNLOCK(data);
369         return TRUE;
370       }
371     }
372   }
373   CONNCACHE_UNLOCK(data);
374   return FALSE;
375 }
376 
377 /* Return the first connection found in the cache. Used when closing all
378    connections.
379 
380    NOTE: no locking is done here as this is presumably only done when cleaning
381    up a cache!
382 */
383 static struct connectdata *
connc_find_first_connection(struct conncache * connc)384 connc_find_first_connection(struct conncache *connc)
385 {
386   struct Curl_hash_iterator iter;
387   struct Curl_hash_element *he;
388   struct connectbundle *bundle;
389 
390   Curl_hash_start_iterate(&connc->hash, &iter);
391 
392   he = Curl_hash_next_element(&iter);
393   while(he) {
394     struct Curl_llist_element *curr;
395     bundle = he->ptr;
396 
397     curr = bundle->conn_list.head;
398     if(curr) {
399       return curr->ptr;
400     }
401 
402     he = Curl_hash_next_element(&iter);
403   }
404 
405   return NULL;
406 }
407 
408 /*
409  * Give ownership of a connection back to the connection cache. Might
410  * disconnect the oldest existing in there to make space.
411  *
412  * Return TRUE if stored, FALSE if closed.
413  */
Curl_conncache_return_conn(struct Curl_easy * data,struct connectdata * conn)414 bool Curl_conncache_return_conn(struct Curl_easy *data,
415                                 struct connectdata *conn)
416 {
417   unsigned int maxconnects = !data->multi->maxconnects ?
418     data->multi->num_easy * 4: data->multi->maxconnects;
419   struct connectdata *conn_candidate = NULL;
420 
421   conn->lastused = Curl_now(); /* it was used up until now */
422   if(maxconnects && Curl_conncache_size(data) > maxconnects) {
423     infof(data, "Connection cache is full, closing the oldest one");
424 
425     conn_candidate = Curl_conncache_extract_oldest(data);
426     if(conn_candidate) {
427       /* Use the closure handle for this disconnect so that anything that
428          happens during the disconnect is not stored and associated with the
429          'data' handle which already just finished a transfer and it is
430          important that details from this (unrelated) disconnect does not
431          taint meta-data in the data handle. */
432       struct conncache *connc = data->state.conn_cache;
433       connc_disconnect(NULL, conn_candidate, connc, TRUE);
434     }
435   }
436 
437   return (conn_candidate == conn) ? FALSE : TRUE;
438 
439 }
440 
441 /*
442  * This function finds the connection in the connection bundle that has been
443  * unused for the longest time.
444  *
445  * Does not lock the connection cache!
446  *
447  * Returns the pointer to the oldest idle connection, or NULL if none was
448  * found.
449  */
450 struct connectdata *
Curl_conncache_extract_bundle(struct Curl_easy * data,struct connectbundle * bundle)451 Curl_conncache_extract_bundle(struct Curl_easy *data,
452                               struct connectbundle *bundle)
453 {
454   struct Curl_llist_element *curr;
455   timediff_t highscore = -1;
456   timediff_t score;
457   struct curltime now;
458   struct connectdata *conn_candidate = NULL;
459   struct connectdata *conn;
460 
461   (void)data;
462 
463   now = Curl_now();
464 
465   curr = bundle->conn_list.head;
466   while(curr) {
467     conn = curr->ptr;
468 
469     if(!CONN_INUSE(conn)) {
470       /* Set higher score for the age passed since the connection was used */
471       score = Curl_timediff(now, conn->lastused);
472 
473       if(score > highscore) {
474         highscore = score;
475         conn_candidate = conn;
476       }
477     }
478     curr = curr->next;
479   }
480   if(conn_candidate) {
481     /* remove it to prevent another thread from nicking it */
482     bundle_remove_conn(bundle, conn_candidate);
483     data->state.conn_cache->num_conn--;
484     DEBUGF(infof(data, "The cache now contains %zu members",
485                  data->state.conn_cache->num_conn));
486   }
487 
488   return conn_candidate;
489 }
490 
491 /*
492  * This function finds the connection in the connection cache that has been
493  * unused for the longest time and extracts that from the bundle.
494  *
495  * Returns the pointer to the connection, or NULL if none was found.
496  */
497 struct connectdata *
Curl_conncache_extract_oldest(struct Curl_easy * data)498 Curl_conncache_extract_oldest(struct Curl_easy *data)
499 {
500   struct conncache *connc = data->state.conn_cache;
501   struct Curl_hash_iterator iter;
502   struct Curl_llist_element *curr;
503   struct Curl_hash_element *he;
504   timediff_t highscore =- 1;
505   timediff_t score;
506   struct curltime now;
507   struct connectdata *conn_candidate = NULL;
508   struct connectbundle *bundle;
509   struct connectbundle *bundle_candidate = NULL;
510 
511   now = Curl_now();
512 
513   CONNCACHE_LOCK(data);
514   Curl_hash_start_iterate(&connc->hash, &iter);
515 
516   he = Curl_hash_next_element(&iter);
517   while(he) {
518     struct connectdata *conn;
519 
520     bundle = he->ptr;
521 
522     curr = bundle->conn_list.head;
523     while(curr) {
524       conn = curr->ptr;
525 
526       if(!CONN_INUSE(conn) && !conn->bits.close &&
527          !conn->connect_only) {
528         /* Set higher score for the age passed since the connection was used */
529         score = Curl_timediff(now, conn->lastused);
530 
531         if(score > highscore) {
532           highscore = score;
533           conn_candidate = conn;
534           bundle_candidate = bundle;
535         }
536       }
537       curr = curr->next;
538     }
539 
540     he = Curl_hash_next_element(&iter);
541   }
542   if(conn_candidate) {
543     /* remove it to prevent another thread from nicking it */
544     bundle_remove_conn(bundle_candidate, conn_candidate);
545     connc->num_conn--;
546     DEBUGF(infof(data, "The cache now contains %zu members",
547                  connc->num_conn));
548   }
549   CONNCACHE_UNLOCK(data);
550 
551   return conn_candidate;
552 }
553 
connc_shutdown_discard_all(struct conncache * connc)554 static void connc_shutdown_discard_all(struct conncache *connc)
555 {
556   struct Curl_llist_element *e = connc->shutdowns.conn_list.head;
557   struct connectdata *conn;
558 
559   if(!e)
560     return;
561 
562   DEBUGF(infof(connc->closure_handle, "conncache_shutdown_discard_all"));
563   DEBUGASSERT(!connc->shutdowns.iter_locked);
564   connc->shutdowns.iter_locked = TRUE;
565   while(e) {
566     conn = e->ptr;
567     Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL);
568     DEBUGF(infof(connc->closure_handle, "discard connection #%"
569                  CURL_FORMAT_CURL_OFF_T, conn->connection_id));
570     connc_disconnect(NULL, conn, connc, FALSE);
571     e = connc->shutdowns.conn_list.head;
572   }
573   connc->shutdowns.iter_locked = FALSE;
574 }
575 
connc_close_all(struct conncache * connc)576 static void connc_close_all(struct conncache *connc)
577 {
578   struct Curl_easy *data = connc->closure_handle;
579   struct connectdata *conn;
580   int timeout_ms = 0;
581   SIGPIPE_VARIABLE(pipe_st);
582 
583   if(!data)
584     return;
585 
586   /* Move all connections to the shutdown list */
587   conn = connc_find_first_connection(connc);
588   while(conn) {
589     connc_remove_conn(connc, conn);
590     sigpipe_ignore(data, &pipe_st);
591     /* This will remove the connection from the cache */
592     connclose(conn, "kill all");
593     Curl_conncache_remove_conn(connc->closure_handle, conn, TRUE);
594     connc_discard_conn(connc, connc->closure_handle, conn, FALSE);
595     sigpipe_restore(&pipe_st);
596 
597     conn = connc_find_first_connection(connc);
598   }
599 
600     /* Just for testing, run graceful shutdown */
601 #ifdef DEBUGBUILD
602   {
603     char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
604     if(p) {
605       long l = strtol(p, NULL, 10);
606       if(l > 0 && l < INT_MAX)
607         timeout_ms = (int)l;
608     }
609   }
610 #endif
611   connc_shutdown_all(connc, timeout_ms);
612 
613   /* discard all connections in the shutdown list */
614   connc_shutdown_discard_all(connc);
615 
616   sigpipe_ignore(data, &pipe_st);
617   Curl_hostcache_clean(data, data->dns.hostcache);
618   Curl_close(&data);
619   sigpipe_restore(&pipe_st);
620 }
621 
Curl_conncache_close_all_connections(struct conncache * connc)622 void Curl_conncache_close_all_connections(struct conncache *connc)
623 {
624   connc_close_all(connc);
625 }
626 
connc_shutdown_discard_oldest(struct conncache * connc)627 static void connc_shutdown_discard_oldest(struct conncache *connc)
628 {
629   struct Curl_llist_element *e;
630   struct connectdata *conn;
631   SIGPIPE_VARIABLE(pipe_st);
632 
633   DEBUGASSERT(!connc->shutdowns.iter_locked);
634   if(connc->shutdowns.iter_locked)
635     return;
636 
637   e = connc->shutdowns.conn_list.head;
638   if(e) {
639     conn = e->ptr;
640     Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL);
641     sigpipe_ignore(connc->closure_handle, &pipe_st);
642     connc_disconnect(NULL, conn, connc, FALSE);
643     sigpipe_restore(&pipe_st);
644   }
645 }
646 
connc_discard_conn(struct conncache * connc,struct Curl_easy * last_data,struct connectdata * conn,bool aborted)647 static void connc_discard_conn(struct conncache *connc,
648                                struct Curl_easy *last_data,
649                                struct connectdata *conn,
650                                bool aborted)
651 {
652   /* `last_data`, if present, is the transfer that last worked with
653    * the connection. It is present when the connection is being shut down
654    * via `Curl_conncache_discard_conn()`, e.g. when the transfer failed
655    * or does not allow connection reuse.
656    * Using the original handle is necessary for shutting down the protocol
657    * handler belonging to the connection. Protocols like 'file:' rely on
658    * being invoked to clean up their allocations in the easy handle.
659    * When a connection comes from the cache, the transfer is no longer
660    * there and we use the cache's own closure handle.
661    */
662   struct Curl_easy *data = last_data? last_data : connc->closure_handle;
663   bool done = FALSE;
664 
665   DEBUGASSERT(data);
666   DEBUGASSERT(connc);
667   DEBUGASSERT(!conn->bundle);
668 
669   /*
670    * If this connection isn't marked to force-close, leave it open if there
671    * are other users of it
672    */
673   if(CONN_INUSE(conn) && !aborted) {
674     DEBUGF(infof(data, "[CCACHE] not discarding #%" CURL_FORMAT_CURL_OFF_T
675                        " still in use by %zu transfers", conn->connection_id,
676                        CONN_INUSE(conn)));
677     return;
678   }
679 
680   /* treat the connection as aborted in CONNECT_ONLY situations, we do
681    * not know what the APP did with it. */
682   if(conn->connect_only)
683     aborted = TRUE;
684   conn->bits.aborted = aborted;
685 
686   /* We do not shutdown dead connections. The term 'dead' can be misleading
687    * here, as we also mark errored connections/transfers as 'dead'.
688    * If we do a shutdown for an aborted transfer, the server might think
689    * it was successful otherwise (for example an ftps: upload). This is
690    * not what we want. */
691   if(aborted)
692     done = TRUE;
693   if(!done) {
694     /* Attempt to shutdown the connection right away. */
695     Curl_attach_connection(data, conn);
696     connc_run_conn_shutdown(data, conn, &done);
697     DEBUGF(infof(data, "[CCACHE] shutdown #%" CURL_FORMAT_CURL_OFF_T
698                        ", done=%d",conn->connection_id, done));
699     Curl_detach_connection(data);
700   }
701 
702   if(done) {
703     connc_disconnect(data, conn, connc, FALSE);
704     return;
705   }
706 
707   DEBUGASSERT(!connc->shutdowns.iter_locked);
708   if(connc->shutdowns.iter_locked) {
709     DEBUGF(infof(data, "[CCACHE] discarding #%" CURL_FORMAT_CURL_OFF_T
710                        ", list locked", conn->connection_id));
711     connc_disconnect(data, conn, connc, FALSE);
712     return;
713   }
714 
715   /* Add the connection to our shutdown list for non-blocking shutdown
716    * during multi processing. */
717   if(data->multi && data->multi->max_shutdown_connections > 0 &&
718      (data->multi->max_shutdown_connections >=
719       (long)Curl_llist_count(&connc->shutdowns.conn_list))) {
720     DEBUGF(infof(data, "[CCACHE] discarding oldest shutdown connection "
721                        "due to limit of %ld",
722                        data->multi->max_shutdown_connections));
723     connc_shutdown_discard_oldest(connc);
724   }
725 
726   if(data->multi && data->multi->socket_cb) {
727     DEBUGASSERT(connc == &data->multi->conn_cache);
728     if(connc_update_shutdown_ev(data->multi, data, conn)) {
729       DEBUGF(infof(data, "[CCACHE] update events for shutdown failed, "
730                          "discarding #%" CURL_FORMAT_CURL_OFF_T,
731                          conn->connection_id));
732       connc_disconnect(data, conn, connc, FALSE);
733       return;
734     }
735   }
736 
737   Curl_llist_append(&connc->shutdowns.conn_list, conn, &conn->bundle_node);
738   DEBUGF(infof(data, "[CCACHE] added #%" CURL_FORMAT_CURL_OFF_T
739                      " to shutdown list of length %zu", conn->connection_id,
740                      Curl_llist_count(&connc->shutdowns.conn_list)));
741 
742   /* Forget what this transfer last polled, the connection is ours now.
743    * If we do not clear this, the event handling for `data` will tell
744    * the callback to remove the connection socket after we return here. */
745   memset(&data->last_poll, 0, sizeof(data->last_poll));
746 }
747 
Curl_conncache_disconnect(struct Curl_easy * data,struct connectdata * conn,bool aborted)748 void Curl_conncache_disconnect(struct Curl_easy *data,
749                                struct connectdata *conn,
750                                bool aborted)
751 {
752   DEBUGASSERT(data);
753   /* Connection must no longer be in and connection cache */
754   DEBUGASSERT(!conn->bundle);
755 
756   if(data->multi) {
757     /* Add it to the multi's conncache for shutdown handling */
758     infof(data, "%s connection #%" CURL_FORMAT_CURL_OFF_T,
759           aborted? "closing" : "shutting down", conn->connection_id);
760     connc_discard_conn(&data->multi->conn_cache, data, conn, aborted);
761   }
762   else {
763     /* No multi available. Make a best-effort shutdown + close */
764     infof(data, "closing connection #%" CURL_FORMAT_CURL_OFF_T,
765           conn->connection_id);
766     DEBUGASSERT(!conn->bundle);
767     connc_run_conn_shutdown_handler(data, conn);
768     connc_disconnect(data, conn, NULL, !aborted);
769   }
770 }
771 
connc_run_conn_shutdown_handler(struct Curl_easy * data,struct connectdata * conn)772 static void connc_run_conn_shutdown_handler(struct Curl_easy *data,
773                                             struct connectdata *conn)
774 {
775   if(!conn->bits.shutdown_handler) {
776     if(conn->dns_entry) {
777       Curl_resolv_unlock(data, conn->dns_entry);
778       conn->dns_entry = NULL;
779     }
780 
781     /* Cleanup NTLM connection-related data */
782     Curl_http_auth_cleanup_ntlm(conn);
783 
784     /* Cleanup NEGOTIATE connection-related data */
785     Curl_http_auth_cleanup_negotiate(conn);
786 
787     if(conn->handler && conn->handler->disconnect) {
788       /* This is set if protocol-specific cleanups should be made */
789       DEBUGF(infof(data, "connection #%" CURL_FORMAT_CURL_OFF_T
790                    ", shutdown protocol handler (aborted=%d)",
791                    conn->connection_id, conn->bits.aborted));
792       conn->handler->disconnect(data, conn, conn->bits.aborted);
793     }
794 
795     /* possible left-overs from the async name resolvers */
796     Curl_resolver_cancel(data);
797 
798     conn->bits.shutdown_handler = TRUE;
799   }
800 }
801 
connc_run_conn_shutdown(struct Curl_easy * data,struct connectdata * conn,bool * done)802 static void connc_run_conn_shutdown(struct Curl_easy *data,
803                                     struct connectdata *conn,
804                                     bool *done)
805 {
806   CURLcode r1, r2;
807   bool done1, done2;
808 
809   /* We expect to be attached when called */
810   DEBUGASSERT(data->conn == conn);
811 
812   connc_run_conn_shutdown_handler(data, conn);
813 
814   if(conn->bits.shutdown_filters) {
815     *done = TRUE;
816     return;
817   }
818 
819   if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
820     r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
821   else {
822     r1 = CURLE_OK;
823     done1 = TRUE;
824   }
825 
826   if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
827     r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
828   else {
829     r2 = CURLE_OK;
830     done2 = TRUE;
831   }
832 
833   /* we are done when any failed or both report success */
834   *done = (r1 || r2 || (done1 && done2));
835   if(*done)
836     conn->bits.shutdown_filters = TRUE;
837 }
838 
Curl_conncache_add_pollfds(struct conncache * connc,struct curl_pollfds * cpfds)839 CURLcode Curl_conncache_add_pollfds(struct conncache *connc,
840                                     struct curl_pollfds *cpfds)
841 {
842   CURLcode result = CURLE_OK;
843 
844   DEBUGASSERT(!connc->shutdowns.iter_locked);
845   connc->shutdowns.iter_locked = TRUE;
846   if(connc->shutdowns.conn_list.head) {
847     struct Curl_llist_element *e;
848     struct easy_pollset ps;
849     struct connectdata *conn;
850 
851     for(e = connc->shutdowns.conn_list.head; e; e = e->next) {
852       conn = e->ptr;
853       memset(&ps, 0, sizeof(ps));
854       Curl_attach_connection(connc->closure_handle, conn);
855       Curl_conn_adjust_pollset(connc->closure_handle, &ps);
856       Curl_detach_connection(connc->closure_handle);
857 
858       result = Curl_pollfds_add_ps(cpfds, &ps);
859       if(result) {
860         Curl_pollfds_cleanup(cpfds);
861         goto out;
862       }
863     }
864   }
865 out:
866   connc->shutdowns.iter_locked = FALSE;
867   return result;
868 }
869 
Curl_conncache_add_waitfds(struct conncache * connc,struct curl_waitfds * cwfds)870 CURLcode Curl_conncache_add_waitfds(struct conncache *connc,
871                                     struct curl_waitfds *cwfds)
872 {
873   CURLcode result = CURLE_OK;
874 
875   DEBUGASSERT(!connc->shutdowns.iter_locked);
876   connc->shutdowns.iter_locked = TRUE;
877   if(connc->shutdowns.conn_list.head) {
878     struct Curl_llist_element *e;
879     struct easy_pollset ps;
880     struct connectdata *conn;
881 
882     for(e = connc->shutdowns.conn_list.head; e; e = e->next) {
883       conn = e->ptr;
884       memset(&ps, 0, sizeof(ps));
885       Curl_attach_connection(connc->closure_handle, conn);
886       Curl_conn_adjust_pollset(connc->closure_handle, &ps);
887       Curl_detach_connection(connc->closure_handle);
888 
889       result = Curl_waitfds_add_ps(cwfds, &ps);
890       if(result)
891         goto out;
892     }
893   }
894 out:
895   connc->shutdowns.iter_locked = FALSE;
896   return result;
897 }
898 
connc_perform(struct conncache * connc)899 static void connc_perform(struct conncache *connc)
900 {
901   struct Curl_easy *data = connc->closure_handle;
902   struct Curl_llist_element *e = connc->shutdowns.conn_list.head;
903   struct Curl_llist_element *enext;
904   struct connectdata *conn;
905   bool done;
906 
907   if(!e)
908     return;
909 
910   DEBUGASSERT(data);
911   DEBUGASSERT(!connc->shutdowns.iter_locked);
912   DEBUGF(infof(data, "[CCACHE] perform, %zu connections being shutdown",
913                Curl_llist_count(&connc->shutdowns.conn_list)));
914   connc->shutdowns.iter_locked = TRUE;
915   while(e) {
916     enext = e->next;
917     conn = e->ptr;
918     Curl_attach_connection(data, conn);
919     connc_run_conn_shutdown(data, conn, &done);
920     DEBUGF(infof(data, "[CCACHE] shutdown #%" CURL_FORMAT_CURL_OFF_T
921                  ", done=%d", conn->connection_id, done));
922     Curl_detach_connection(data);
923     if(done) {
924       Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL);
925       connc_disconnect(NULL, conn, connc, FALSE);
926     }
927     e = enext;
928   }
929   connc->shutdowns.iter_locked = FALSE;
930 }
931 
Curl_conncache_multi_perform(struct Curl_multi * multi)932 void Curl_conncache_multi_perform(struct Curl_multi *multi)
933 {
934   connc_perform(&multi->conn_cache);
935 }
936 
937 
938 /*
939  * Disconnects the given connection. Note the connection may not be the
940  * primary connection, like when freeing room in the connection cache or
941  * killing of a dead old connection.
942  *
943  * A connection needs an easy handle when closing down. We support this passed
944  * in separately since the connection to get closed here is often already
945  * disassociated from an easy handle.
946  *
947  * This function MUST NOT reset state in the Curl_easy struct if that
948  * isn't strictly bound to the life-time of *this* particular connection.
949  *
950  */
connc_disconnect(struct Curl_easy * data,struct connectdata * conn,struct conncache * connc,bool do_shutdown)951 static void connc_disconnect(struct Curl_easy *data,
952                              struct connectdata *conn,
953                              struct conncache *connc,
954                              bool do_shutdown)
955 {
956   bool done;
957 
958   /* there must be a connection to close */
959   DEBUGASSERT(conn);
960   /* it must be removed from the connection cache */
961   DEBUGASSERT(!conn->bundle);
962   /* there must be an associated transfer */
963   DEBUGASSERT(data || connc);
964   if(!data)
965     data = connc->closure_handle;
966 
967   /* the transfer must be detached from the connection */
968   DEBUGASSERT(data && !data->conn);
969 
970   if(connc && connc->multi && connc->multi->socket_cb) {
971     unsigned int i;
972     for(i = 0; i < 2; ++i) {
973       if(CURL_SOCKET_BAD == conn->sock[i])
974         continue;
975       /* remove all connection's sockets from event handling */
976       connc->multi->in_callback = TRUE;
977       connc->multi->socket_cb(data, conn->sock[i], CURL_POLL_REMOVE,
978                               connc->multi->socket_userp, NULL);
979       connc->multi->in_callback = FALSE;
980     }
981   }
982 
983   Curl_attach_connection(data, conn);
984 
985   connc_run_conn_shutdown_handler(data, conn);
986   if(do_shutdown) {
987     /* Make a last attempt to shutdown handlers and filters, if
988      * not done so already. */
989     connc_run_conn_shutdown(data, conn, &done);
990   }
991 
992   if(connc)
993     DEBUGF(infof(data, "[CCACHE] closing #%" CURL_FORMAT_CURL_OFF_T,
994                  conn->connection_id));
995   else
996     DEBUGF(infof(data, "closing connection #%" CURL_FORMAT_CURL_OFF_T,
997                  conn->connection_id));
998   Curl_conn_close(data, SECONDARYSOCKET);
999   Curl_conn_close(data, FIRSTSOCKET);
1000   Curl_detach_connection(data);
1001 
1002   Curl_conn_free(data, conn);
1003 }
1004 
1005 
connc_update_shutdown_ev(struct Curl_multi * multi,struct Curl_easy * data,struct connectdata * conn)1006 static CURLcode connc_update_shutdown_ev(struct Curl_multi *multi,
1007                                          struct Curl_easy *data,
1008                                          struct connectdata *conn)
1009 {
1010   struct easy_pollset ps;
1011   unsigned int i;
1012   int rc;
1013 
1014   DEBUGASSERT(data);
1015   DEBUGASSERT(multi);
1016   DEBUGASSERT(multi->socket_cb);
1017 
1018   memset(&ps, 0, sizeof(ps));
1019   Curl_attach_connection(data, conn);
1020   Curl_conn_adjust_pollset(data, &ps);
1021   Curl_detach_connection(data);
1022 
1023   if(!ps.num)
1024     return CURLE_FAILED_INIT;
1025 
1026   for(i = 0; i < ps.num; ++i) {
1027     DEBUGF(infof(data, "[CCACHE] set socket=%" CURL_FORMAT_SOCKET_T
1028                  " events=%d on #%" CURL_FORMAT_CURL_OFF_T,
1029                  ps.sockets[i], ps.actions[i], conn->connection_id));
1030     multi->in_callback = TRUE;
1031     rc = multi->socket_cb(data, ps.sockets[i], ps.actions[i],
1032                           multi->socket_userp, NULL);
1033     multi->in_callback = FALSE;
1034     if(rc == -1)
1035       return CURLE_FAILED_INIT;
1036   }
1037 
1038   return CURLE_OK;
1039 }
1040 
Curl_conncache_multi_socket(struct Curl_multi * multi,curl_socket_t s,int ev_bitmask)1041 void Curl_conncache_multi_socket(struct Curl_multi *multi,
1042                                  curl_socket_t s, int ev_bitmask)
1043 {
1044   struct conncache *connc = &multi->conn_cache;
1045   struct Curl_easy *data = connc->closure_handle;
1046   struct Curl_llist_element *e = connc->shutdowns.conn_list.head;
1047   struct connectdata *conn;
1048   bool done;
1049 
1050   (void)ev_bitmask;
1051   DEBUGASSERT(multi->socket_cb);
1052   if(!e)
1053     return;
1054 
1055   connc->shutdowns.iter_locked = TRUE;
1056   while(e) {
1057     conn = e->ptr;
1058     if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
1059       Curl_attach_connection(data, conn);
1060       connc_run_conn_shutdown(data, conn, &done);
1061       DEBUGF(infof(data, "[CCACHE] shutdown #%" CURL_FORMAT_CURL_OFF_T
1062                    ", done=%d", conn->connection_id, done));
1063       Curl_detach_connection(data);
1064       if(done || connc_update_shutdown_ev(multi, data, conn)) {
1065         Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL);
1066         connc_disconnect(NULL, conn, connc, FALSE);
1067       }
1068       break;
1069     }
1070     e = e->next;
1071   }
1072   connc->shutdowns.iter_locked = FALSE;
1073 }
1074 
Curl_conncache_multi_close_all(struct Curl_multi * multi)1075 void Curl_conncache_multi_close_all(struct Curl_multi *multi)
1076 {
1077   connc_close_all(&multi->conn_cache);
1078 }
1079 
1080 
1081 #define NUM_POLLS_ON_STACK 10
1082 
connc_shutdown_wait(struct conncache * connc,int timeout_ms)1083 static CURLcode connc_shutdown_wait(struct conncache *connc, int timeout_ms)
1084 {
1085   struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
1086   struct curl_pollfds cpfds;
1087   CURLcode result;
1088 
1089   Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
1090 
1091   result = Curl_conncache_add_pollfds(connc, &cpfds);
1092   if(result)
1093     goto out;
1094 
1095   Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
1096 
1097 out:
1098   Curl_pollfds_cleanup(&cpfds);
1099   return result;
1100 }
1101 
connc_shutdown_all(struct conncache * connc,int timeout_ms)1102 static void connc_shutdown_all(struct conncache *connc, int timeout_ms)
1103 {
1104   struct Curl_easy *data = connc->closure_handle;
1105   struct connectdata *conn;
1106   struct curltime started = Curl_now();
1107 
1108   if(!data)
1109     return;
1110   (void)data;
1111 
1112   DEBUGF(infof(data, "conncache shutdown all"));
1113 
1114   /* Move all connections into the shutdown queue */
1115   conn = connc_find_first_connection(connc);
1116   while(conn) {
1117     /* This will remove the connection from the cache */
1118     DEBUGF(infof(data, "moving connection %" CURL_FORMAT_CURL_OFF_T
1119                  " to shutdown queue", conn->connection_id));
1120     connc_remove_conn(connc, conn);
1121     connc_discard_conn(connc, NULL, conn, FALSE);
1122     conn = connc_find_first_connection(connc);
1123   }
1124 
1125   DEBUGASSERT(!connc->shutdowns.iter_locked);
1126   while(connc->shutdowns.conn_list.head) {
1127     timediff_t timespent;
1128     int remain_ms;
1129 
1130     connc_perform(connc);
1131 
1132     if(!connc->shutdowns.conn_list.head) {
1133       DEBUGF(infof(data, "conncache shutdown ok"));
1134       break;
1135     }
1136 
1137     /* wait for activity, timeout or "nothing" */
1138     timespent = Curl_timediff(Curl_now(), started);
1139     if(timespent >= (timediff_t)timeout_ms) {
1140       DEBUGF(infof(data, "conncache shutdown %s",
1141                    (timeout_ms > 0)? "timeout" : "best effort done"));
1142       break;
1143     }
1144 
1145     remain_ms = timeout_ms - (int)timespent;
1146     if(connc_shutdown_wait(connc, remain_ms)) {
1147       DEBUGF(infof(data, "conncache shutdown all, abort"));
1148       break;
1149     }
1150   }
1151 
1152   /* Due to errors/timeout, we might come here without being full ydone. */
1153   connc_shutdown_discard_all(connc);
1154 }
1155 
1156 #if 0
1157 /* Useful for debugging the connection cache */
1158 void Curl_conncache_print(struct conncache *connc)
1159 {
1160   struct Curl_hash_iterator iter;
1161   struct Curl_llist_element *curr;
1162   struct Curl_hash_element *he;
1163 
1164   if(!connc)
1165     return;
1166 
1167   fprintf(stderr, "=Bundle cache=\n");
1168 
1169   Curl_hash_start_iterate(connc->hash, &iter);
1170 
1171   he = Curl_hash_next_element(&iter);
1172   while(he) {
1173     struct connectbundle *bundle;
1174     struct connectdata *conn;
1175 
1176     bundle = he->ptr;
1177 
1178     fprintf(stderr, "%s -", he->key);
1179     curr = bundle->conn_list->head;
1180     while(curr) {
1181       conn = curr->ptr;
1182 
1183       fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
1184       curr = curr->next;
1185     }
1186     fprintf(stderr, "\n");
1187 
1188     he = Curl_hash_next_element(&iter);
1189   }
1190 }
1191 #endif
1192