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