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