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