1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//! why do we need connection pooling?
//! ==================================
//!
//! let's first understand the types of connection we make over the network.
//!
//! every running ftn app opens one "connection" with the relay at startup. if the connection is
//! lost, it tries to reconnect. in `iroh` terms, this is called `iroh::Endpoint`. this is a
//! connection with the relay, and it is a long-lived connection. this is still not a
//! `iroh::Connection`, which is the connection we want to pool, so at startup we do not have
//! any `iroh::Connection`, but `iroh::Endpoint`. which is a different kind of connection, and it
//! must remain open for the lifetime of the app.
//!
//! let's call this the "jane's side". let's say jane has a friend, bob, and bob wants to connect to
//! the fastn server that is running on the jane's computer, listening at the loopback address.
//! bob cannot directly connect to it, so we need iroh.
//!
//! so when bob tries to access jane's fastn, bob starts an endpoint instance, and issues the
//! `endpoint.connect(jane_identity)` call. this creates two `iroh::Connection` instances, one on
//! bob's side and the other on the jane's side.
//!
//! in `iroh` world, once a connection is established, either party can initiate a stream, but the
//! other party must accept the stream. in our case, bob's side initiates the stream, and the
//! alice's side accepts the stream.
//!
//! let's recap the control flow. bob wants to access alice's fastn, so bob opens
//! https://<alice-id>.localhost.direct on their browser. localhost.direct distributes their
//! wildcard domain certificate[1], and maps *.localhost.direct to 127.0.0.1, so the request from bob's
//! browser lands on their own machine, to port 443, where bob's ftn-http-proxy is running. bob's
//! ftn-http-proxy gets the `alice-id` and is the main actor here, it gets an HTTP request, and for
//! that it request it creates an endpoint, initiates a connection, and creates a bidirectional
//! stream. on the stream it then writes the HTTP request, and waits for the response from the
//! alice's side, and converts it back as an HTTP response and send it to the browser.
//!
//! now creating the endpoint, and the connection takes time, of the order of a second or so. doing
//! that on every incoming HTTP request will make our system very slow. so we want to reuse the
//! existing connection. creating a bidirectional stream on existing connection is an inexpensive
//! operation, so we want to keep the connection alive, and reuse it to handle multiple HTTP requests.
//!
//! we also do not want to keep the connection open forever, bob may stop browsing alice's site, and
//! alice may have many friends, and they may all want to interact with her fastn server. so if we
//! leave the connection open forever, alice's machine will slowly get overwhelmed with connections.
//!
//! [1]: https://get.localhost.direct
//!
//! why did we pick bb8?
//! ====================
//!
//! when picking `bb8,` I evaluated various crates for connection pooling, namely `r2d2`, `bb8`,
//! `deadpool`. `r2d2` is the most popular, but it is not async, and we need async. `deadpool` is
//! async, but it does not cleanly support idle timeout and max connections, it does have facilities
//! through which you can implement them, but it is not as clean as `bb8`. `bb8` is a fork of `r2d2`
//! that is async, and it has a clean API for idle timeout and max connections.
use WrapErr;
pub async