• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "progress.h"
33 #include "multiif.h"
34 #include "sendf.h"
35 #include "conncache.h"
36 #include "share.h"
37 #include "sigpipe.h"
38 #include "connect.h"
39 #include "strcase.h"
40 
41 /* The last 3 #include files should be in this order */
42 #include "curl_printf.h"
43 #include "curl_memory.h"
44 #include "memdebug.h"
45 
46 #define HASHKEY_SIZE 128
47 
bundle_create(struct connectbundle ** bundlep)48 static CURLcode bundle_create(struct connectbundle **bundlep)
49 {
50   DEBUGASSERT(*bundlep == NULL);
51   *bundlep = malloc(sizeof(struct connectbundle));
52   if(!*bundlep)
53     return CURLE_OUT_OF_MEMORY;
54 
55   (*bundlep)->num_connections = 0;
56   (*bundlep)->multiuse = BUNDLE_UNKNOWN;
57 
58   Curl_llist_init(&(*bundlep)->conn_list, NULL);
59   return CURLE_OK;
60 }
61 
bundle_destroy(struct connectbundle * bundle)62 static void bundle_destroy(struct connectbundle *bundle)
63 {
64   free(bundle);
65 }
66 
67 /* Add a connection to a bundle */
bundle_add_conn(struct connectbundle * bundle,struct connectdata * conn)68 static void bundle_add_conn(struct connectbundle *bundle,
69                             struct connectdata *conn)
70 {
71   Curl_llist_insert_next(&bundle->conn_list, bundle->conn_list.tail, conn,
72                          &conn->bundle_node);
73   conn->bundle = bundle;
74   bundle->num_connections++;
75 }
76 
77 /* Remove a connection from a bundle */
bundle_remove_conn(struct connectbundle * bundle,struct connectdata * conn)78 static int bundle_remove_conn(struct connectbundle *bundle,
79                               struct connectdata *conn)
80 {
81   struct Curl_llist_element *curr;
82 
83   curr = bundle->conn_list.head;
84   while(curr) {
85     if(curr->ptr == conn) {
86       Curl_llist_remove(&bundle->conn_list, curr, NULL);
87       bundle->num_connections--;
88       conn->bundle = NULL;
89       return 1; /* we removed a handle */
90     }
91     curr = curr->next;
92   }
93   DEBUGASSERT(0);
94   return 0;
95 }
96 
free_bundle_hash_entry(void * freethis)97 static void free_bundle_hash_entry(void *freethis)
98 {
99   struct connectbundle *b = (struct connectbundle *) freethis;
100 
101   bundle_destroy(b);
102 }
103 
Curl_conncache_init(struct conncache * connc,int size)104 int Curl_conncache_init(struct conncache *connc, int size)
105 {
106   /* allocate a new easy handle to use when closing cached connections */
107   connc->closure_handle = curl_easy_init();
108   if(!connc->closure_handle)
109     return 1; /* bad */
110   connc->closure_handle->state.internal = true;
111 
112   Curl_hash_init(&connc->hash, size, Curl_hash_str,
113                  Curl_str_key_compare, free_bundle_hash_entry);
114   connc->closure_handle->state.conn_cache = connc;
115 
116   return 0; /* good */
117 }
118 
Curl_conncache_destroy(struct conncache * connc)119 void Curl_conncache_destroy(struct conncache *connc)
120 {
121   if(connc)
122     Curl_hash_destroy(&connc->hash);
123 }
124 
125 /* creates a key to find a bundle for this connection */
hashkey(struct connectdata * conn,char * buf,size_t len)126 static void hashkey(struct connectdata *conn, char *buf, size_t len)
127 {
128   const char *hostname;
129   long port = conn->remote_port;
130   DEBUGASSERT(len >= HASHKEY_SIZE);
131 #ifndef CURL_DISABLE_PROXY
132   if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
133     hostname = conn->http_proxy.host.name;
134     port = conn->port;
135   }
136   else
137 #endif
138     if(conn->bits.conn_to_host)
139       hostname = conn->conn_to_host.name;
140   else
141     hostname = conn->host.name;
142 
143   /* put the numbers first so that the hostname gets cut off if too long */
144 #ifdef ENABLE_IPV6
145   msnprintf(buf, len, "%u/%ld/%s", conn->scope_id, port, hostname);
146 #else
147   msnprintf(buf, len, "%ld/%s", port, hostname);
148 #endif
149   Curl_strntolower(buf, buf, len);
150 }
151 
152 /* Returns number of connections currently held in the connection cache.
153    Locks/unlocks the cache itself!
154 */
Curl_conncache_size(struct Curl_easy * data)155 size_t Curl_conncache_size(struct Curl_easy *data)
156 {
157   size_t num;
158   CONNCACHE_LOCK(data);
159   num = data->state.conn_cache->num_conn;
160   CONNCACHE_UNLOCK(data);
161   return num;
162 }
163 
164 /* Look up the bundle with all the connections to the same host this
165    connectdata struct is setup to use.
166 
167    **NOTE**: When it returns, it holds the connection cache lock! */
168 struct connectbundle *
Curl_conncache_find_bundle(struct Curl_easy * data,struct connectdata * conn,struct conncache * connc)169 Curl_conncache_find_bundle(struct Curl_easy *data,
170                            struct connectdata *conn,
171                            struct conncache *connc)
172 {
173   struct connectbundle *bundle = NULL;
174   CONNCACHE_LOCK(data);
175   if(connc) {
176     char key[HASHKEY_SIZE];
177     hashkey(conn, key, sizeof(key));
178     bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
179   }
180 
181   return bundle;
182 }
183 
conncache_add_bundle(struct conncache * connc,char * key,struct connectbundle * bundle)184 static void *conncache_add_bundle(struct conncache *connc,
185                                   char *key,
186                                   struct connectbundle *bundle)
187 {
188   return Curl_hash_add(&connc->hash, key, strlen(key), bundle);
189 }
190 
conncache_remove_bundle(struct conncache * connc,struct connectbundle * bundle)191 static void conncache_remove_bundle(struct conncache *connc,
192                                     struct connectbundle *bundle)
193 {
194   struct Curl_hash_iterator iter;
195   struct Curl_hash_element *he;
196 
197   if(!connc)
198     return;
199 
200   Curl_hash_start_iterate(&connc->hash, &iter);
201 
202   he = Curl_hash_next_element(&iter);
203   while(he) {
204     if(he->ptr == bundle) {
205       /* The bundle is destroyed by the hash destructor function,
206          free_bundle_hash_entry() */
207       Curl_hash_delete(&connc->hash, he->key, he->key_len);
208       return;
209     }
210 
211     he = Curl_hash_next_element(&iter);
212   }
213 }
214 
Curl_conncache_add_conn(struct Curl_easy * data)215 CURLcode Curl_conncache_add_conn(struct Curl_easy *data)
216 {
217   CURLcode result = CURLE_OK;
218   struct connectbundle *bundle = NULL;
219   struct connectdata *conn = data->conn;
220   struct conncache *connc = data->state.conn_cache;
221   DEBUGASSERT(conn);
222 
223   /* *find_bundle() locks the connection cache */
224   bundle = Curl_conncache_find_bundle(data, conn, data->state.conn_cache);
225   if(!bundle) {
226     char key[HASHKEY_SIZE];
227 
228     result = bundle_create(&bundle);
229     if(result) {
230       goto unlock;
231     }
232 
233     hashkey(conn, key, sizeof(key));
234 
235     if(!conncache_add_bundle(data->state.conn_cache, key, bundle)) {
236       bundle_destroy(bundle);
237       result = CURLE_OUT_OF_MEMORY;
238       goto unlock;
239     }
240   }
241 
242   bundle_add_conn(bundle, conn);
243   conn->connection_id = connc->next_connection_id++;
244   connc->num_conn++;
245 
246   DEBUGF(infof(data, "Added connection %" CURL_FORMAT_CURL_OFF_T ". "
247                "The cache now contains %zu members",
248                conn->connection_id, connc->num_conn));
249 
250 unlock:
251   CONNCACHE_UNLOCK(data);
252 
253   return result;
254 }
255 
256 /*
257  * Removes the connectdata object from the connection cache, but the transfer
258  * still owns this connection.
259  *
260  * Pass TRUE/FALSE in the 'lock' argument depending on if the parent function
261  * already holds the lock or not.
262  */
Curl_conncache_remove_conn(struct Curl_easy * data,struct connectdata * conn,bool lock)263 void Curl_conncache_remove_conn(struct Curl_easy *data,
264                                 struct connectdata *conn, bool lock)
265 {
266   struct connectbundle *bundle = conn->bundle;
267   struct conncache *connc = data->state.conn_cache;
268 
269   /* The bundle pointer can be NULL, since this function can be called
270      due to a failed connection attempt, before being added to a bundle */
271   if(bundle) {
272     if(lock) {
273       CONNCACHE_LOCK(data);
274     }
275     bundle_remove_conn(bundle, conn);
276     if(bundle->num_connections == 0)
277       conncache_remove_bundle(connc, bundle);
278     conn->bundle = NULL; /* removed from it */
279     if(connc) {
280       connc->num_conn--;
281       DEBUGF(infof(data, "The cache now contains %zu members",
282                    connc->num_conn));
283     }
284     if(lock) {
285       CONNCACHE_UNLOCK(data);
286     }
287   }
288 }
289 
290 /* This function iterates the entire connection cache and calls the function
291    func() with the connection pointer as the first argument and the supplied
292    'param' argument as the other.
293 
294    The conncache lock is still held when the callback is called. It needs it,
295    so that it can safely continue traversing the lists once the callback
296    returns.
297 
298    Returns 1 if the loop was aborted due to the callback's return code.
299 
300    Return 0 from func() to continue the loop, return 1 to abort it.
301  */
Curl_conncache_foreach(struct Curl_easy * data,struct conncache * connc,void * param,int (* func)(struct Curl_easy * data,struct connectdata * conn,void * param))302 bool Curl_conncache_foreach(struct Curl_easy *data,
303                             struct conncache *connc,
304                             void *param,
305                             int (*func)(struct Curl_easy *data,
306                                         struct connectdata *conn, void *param))
307 {
308   struct Curl_hash_iterator iter;
309   struct Curl_llist_element *curr;
310   struct Curl_hash_element *he;
311 
312   if(!connc)
313     return FALSE;
314 
315   CONNCACHE_LOCK(data);
316   Curl_hash_start_iterate(&connc->hash, &iter);
317 
318   he = Curl_hash_next_element(&iter);
319   while(he) {
320     struct connectbundle *bundle;
321 
322     bundle = he->ptr;
323     he = Curl_hash_next_element(&iter);
324 
325     curr = bundle->conn_list.head;
326     while(curr) {
327       /* Yes, we need to update curr before calling func(), because func()
328          might decide to remove the connection */
329       struct connectdata *conn = curr->ptr;
330       curr = curr->next;
331 
332       if(1 == func(data, conn, param)) {
333         CONNCACHE_UNLOCK(data);
334         return TRUE;
335       }
336     }
337   }
338   CONNCACHE_UNLOCK(data);
339   return FALSE;
340 }
341 
342 /* Return the first connection found in the cache. Used when closing all
343    connections.
344 
345    NOTE: no locking is done here as this is presumably only done when cleaning
346    up a cache!
347 */
348 static struct connectdata *
conncache_find_first_connection(struct conncache * connc)349 conncache_find_first_connection(struct conncache *connc)
350 {
351   struct Curl_hash_iterator iter;
352   struct Curl_hash_element *he;
353   struct connectbundle *bundle;
354 
355   Curl_hash_start_iterate(&connc->hash, &iter);
356 
357   he = Curl_hash_next_element(&iter);
358   while(he) {
359     struct Curl_llist_element *curr;
360     bundle = he->ptr;
361 
362     curr = bundle->conn_list.head;
363     if(curr) {
364       return curr->ptr;
365     }
366 
367     he = Curl_hash_next_element(&iter);
368   }
369 
370   return NULL;
371 }
372 
373 /*
374  * Give ownership of a connection back to the connection cache. Might
375  * disconnect the oldest existing in there to make space.
376  *
377  * Return TRUE if stored, FALSE if closed.
378  */
Curl_conncache_return_conn(struct Curl_easy * data,struct connectdata * conn)379 bool Curl_conncache_return_conn(struct Curl_easy *data,
380                                 struct connectdata *conn)
381 {
382   unsigned int maxconnects = !data->multi->maxconnects ?
383     data->multi->num_easy * 4: data->multi->maxconnects;
384   struct connectdata *conn_candidate = NULL;
385 
386   conn->lastused = Curl_now(); /* it was used up until now */
387   if(maxconnects && Curl_conncache_size(data) > maxconnects) {
388     infof(data, "Connection cache is full, closing the oldest one");
389 
390     conn_candidate = Curl_conncache_extract_oldest(data);
391     if(conn_candidate) {
392       /* Use the closure handle for this disconnect so that anything that
393          happens during the disconnect is not stored and associated with the
394          'data' handle which already just finished a transfer and it is
395          important that details from this (unrelated) disconnect does not
396          taint meta-data in the data handle. */
397       struct conncache *connc = data->state.conn_cache;
398       connc->closure_handle->state.buffer = data->state.buffer;
399       connc->closure_handle->set.buffer_size = data->set.buffer_size;
400       Curl_disconnect(connc->closure_handle, conn_candidate,
401                       /* dead_connection */ FALSE);
402     }
403   }
404 
405   return (conn_candidate == conn) ? FALSE : TRUE;
406 
407 }
408 
409 /*
410  * This function finds the connection in the connection bundle that has been
411  * unused for the longest time.
412  *
413  * Does not lock the connection cache!
414  *
415  * Returns the pointer to the oldest idle connection, or NULL if none was
416  * found.
417  */
418 struct connectdata *
Curl_conncache_extract_bundle(struct Curl_easy * data,struct connectbundle * bundle)419 Curl_conncache_extract_bundle(struct Curl_easy *data,
420                               struct connectbundle *bundle)
421 {
422   struct Curl_llist_element *curr;
423   timediff_t highscore = -1;
424   timediff_t score;
425   struct curltime now;
426   struct connectdata *conn_candidate = NULL;
427   struct connectdata *conn;
428 
429   (void)data;
430 
431   now = Curl_now();
432 
433   curr = bundle->conn_list.head;
434   while(curr) {
435     conn = curr->ptr;
436 
437     if(!CONN_INUSE(conn)) {
438       /* Set higher score for the age passed since the connection was used */
439       score = Curl_timediff(now, conn->lastused);
440 
441       if(score > highscore) {
442         highscore = score;
443         conn_candidate = conn;
444       }
445     }
446     curr = curr->next;
447   }
448   if(conn_candidate) {
449     /* remove it to prevent another thread from nicking it */
450     bundle_remove_conn(bundle, conn_candidate);
451     data->state.conn_cache->num_conn--;
452     DEBUGF(infof(data, "The cache now contains %zu members",
453                  data->state.conn_cache->num_conn));
454   }
455 
456   return conn_candidate;
457 }
458 
459 /*
460  * This function finds the connection in the connection cache that has been
461  * unused for the longest time and extracts that from the bundle.
462  *
463  * Returns the pointer to the connection, or NULL if none was found.
464  */
465 struct connectdata *
Curl_conncache_extract_oldest(struct Curl_easy * data)466 Curl_conncache_extract_oldest(struct Curl_easy *data)
467 {
468   struct conncache *connc = data->state.conn_cache;
469   struct Curl_hash_iterator iter;
470   struct Curl_llist_element *curr;
471   struct Curl_hash_element *he;
472   timediff_t highscore =- 1;
473   timediff_t score;
474   struct curltime now;
475   struct connectdata *conn_candidate = NULL;
476   struct connectbundle *bundle;
477   struct connectbundle *bundle_candidate = NULL;
478 
479   now = Curl_now();
480 
481   CONNCACHE_LOCK(data);
482   Curl_hash_start_iterate(&connc->hash, &iter);
483 
484   he = Curl_hash_next_element(&iter);
485   while(he) {
486     struct connectdata *conn;
487 
488     bundle = he->ptr;
489 
490     curr = bundle->conn_list.head;
491     while(curr) {
492       conn = curr->ptr;
493 
494       if(!CONN_INUSE(conn) && !conn->bits.close &&
495          !conn->connect_only) {
496         /* Set higher score for the age passed since the connection was used */
497         score = Curl_timediff(now, conn->lastused);
498 
499         if(score > highscore) {
500           highscore = score;
501           conn_candidate = conn;
502           bundle_candidate = bundle;
503         }
504       }
505       curr = curr->next;
506     }
507 
508     he = Curl_hash_next_element(&iter);
509   }
510   if(conn_candidate) {
511     /* remove it to prevent another thread from nicking it */
512     bundle_remove_conn(bundle_candidate, conn_candidate);
513     connc->num_conn--;
514     DEBUGF(infof(data, "The cache now contains %zu members",
515                  connc->num_conn));
516   }
517   CONNCACHE_UNLOCK(data);
518 
519   return conn_candidate;
520 }
521 
Curl_conncache_close_all_connections(struct conncache * connc)522 void Curl_conncache_close_all_connections(struct conncache *connc)
523 {
524   struct connectdata *conn;
525   char buffer[READBUFFER_MIN + 1];
526   SIGPIPE_VARIABLE(pipe_st);
527   if(!connc->closure_handle)
528     return;
529   connc->closure_handle->state.buffer = buffer;
530   connc->closure_handle->set.buffer_size = READBUFFER_MIN;
531 
532   conn = conncache_find_first_connection(connc);
533   while(conn) {
534     sigpipe_ignore(connc->closure_handle, &pipe_st);
535     /* This will remove the connection from the cache */
536     connclose(conn, "kill all");
537     Curl_conncache_remove_conn(connc->closure_handle, conn, TRUE);
538     Curl_disconnect(connc->closure_handle, conn, FALSE);
539     sigpipe_restore(&pipe_st);
540 
541     conn = conncache_find_first_connection(connc);
542   }
543 
544   connc->closure_handle->state.buffer = NULL;
545   sigpipe_ignore(connc->closure_handle, &pipe_st);
546 
547   Curl_hostcache_clean(connc->closure_handle,
548                        connc->closure_handle->dns.hostcache);
549   Curl_close(&connc->closure_handle);
550   sigpipe_restore(&pipe_st);
551 }
552 
553 #if 0
554 /* Useful for debugging the connection cache */
555 void Curl_conncache_print(struct conncache *connc)
556 {
557   struct Curl_hash_iterator iter;
558   struct Curl_llist_element *curr;
559   struct Curl_hash_element *he;
560 
561   if(!connc)
562     return;
563 
564   fprintf(stderr, "=Bundle cache=\n");
565 
566   Curl_hash_start_iterate(connc->hash, &iter);
567 
568   he = Curl_hash_next_element(&iter);
569   while(he) {
570     struct connectbundle *bundle;
571     struct connectdata *conn;
572 
573     bundle = he->ptr;
574 
575     fprintf(stderr, "%s -", he->key);
576     curr = bundle->conn_list->head;
577     while(curr) {
578       conn = curr->ptr;
579 
580       fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
581       curr = curr->next;
582     }
583     fprintf(stderr, "\n");
584 
585     he = Curl_hash_next_element(&iter);
586   }
587 }
588 #endif
589