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