1
1
//! The "Server" side of the client. Uses the `ClientConnManager`.
2
2
// Based on tailscale/derp/derp_server.go
3
3
4
- use std:: { collections:: HashSet , sync:: Arc } ;
4
+ use std:: {
5
+ collections:: HashSet ,
6
+ sync:: {
7
+ atomic:: { AtomicU64 , Ordering } ,
8
+ Arc ,
9
+ } ,
10
+ } ;
5
11
6
12
use anyhow:: { bail, Result } ;
7
13
use bytes:: Bytes ;
@@ -24,6 +30,8 @@ struct Inner {
24
30
clients : DashMap < NodeId , Client > ,
25
31
/// Map of which client has sent where
26
32
sent_to : DashMap < NodeId , HashSet < NodeId > > ,
33
+ /// Connection ID Counter
34
+ next_connection_id : AtomicU64 ,
27
35
}
28
36
29
37
impl Clients {
@@ -41,9 +49,10 @@ impl Clients {
41
49
/// Builds the client handler and starts the read & write loops for the connection.
42
50
pub async fn register ( & self , client_config : Config ) {
43
51
let node_id = client_config. node_id ;
52
+ let connection_id = self . get_connection_id ( ) ;
44
53
trace ! ( remote_node = node_id. fmt_short( ) , "registering client" ) ;
45
54
46
- let client = Client :: new ( client_config, self ) ;
55
+ let client = Client :: new ( client_config, connection_id , self ) ;
47
56
if let Some ( old_client) = self . 0 . clients . insert ( node_id, client) {
48
57
debug ! (
49
58
remote_node = node_id. fmt_short( ) ,
@@ -53,20 +62,27 @@ impl Clients {
53
62
}
54
63
}
55
64
65
+ fn get_connection_id ( & self ) -> u64 {
66
+ self . 0 . next_connection_id . fetch_add ( 1 , Ordering :: Relaxed )
67
+ }
68
+
56
69
/// Removes the client from the map of clients, & sends a notification
57
70
/// to each client that peers has sent data to, to let them know that
58
71
/// peer is gone from the network.
59
72
///
60
- /// Explicitly drops the reference to the client to avoid deadlock.
61
- async fn unregister < ' a > (
62
- & self ,
63
- client : dashmap:: mapref:: one:: Ref < ' a , iroh_base:: PublicKey , Client > ,
64
- node_id : NodeId ,
65
- ) {
66
- drop ( client) ; // avoid deadlock
67
- trace ! ( node_id = node_id. fmt_short( ) , "unregistering client" ) ;
68
-
69
- if let Some ( ( _, client) ) = self . 0 . clients . remove ( & node_id) {
73
+ /// Must be passed a matching connection_id.
74
+ pub ( super ) async fn unregister < ' a > ( & self , connection_id : u64 , node_id : NodeId ) {
75
+ trace ! (
76
+ node_id = node_id. fmt_short( ) ,
77
+ connection_id,
78
+ "unregistering client"
79
+ ) ;
80
+
81
+ if let Some ( ( _, client) ) = self
82
+ . 0
83
+ . clients
84
+ . remove_if ( & node_id, |_, c| c. connection_id ( ) == connection_id)
85
+ {
70
86
if let Some ( ( _, sent_to) ) = self . 0 . sent_to . remove ( & node_id) {
71
87
for key in sent_to {
72
88
match client. try_send_peer_gone ( key) {
@@ -91,7 +107,7 @@ impl Clients {
91
107
}
92
108
93
109
/// Attempt to send a packet to client with [`NodeId`] `dst`.
94
- pub ( super ) async fn send_packet ( & self , dst : NodeId , data : Bytes , src : NodeId ) -> Result < ( ) > {
110
+ pub ( super ) fn send_packet ( & self , dst : NodeId , data : Bytes , src : NodeId ) -> Result < ( ) > {
95
111
let Some ( client) = self . 0 . clients . get ( & dst) else {
96
112
debug ! ( dst = dst. fmt_short( ) , "no connected client, dropped packet" ) ;
97
113
inc ! ( Metrics , send_packets_dropped) ;
@@ -115,19 +131,14 @@ impl Clients {
115
131
dst = dst. fmt_short( ) ,
116
132
"can no longer write to client, dropping message and pruning connection"
117
133
) ;
118
- self . unregister ( client , dst ) . await ;
134
+ client . start_shutdown ( ) ;
119
135
bail ! ( "failed to send message: gone" ) ;
120
136
}
121
137
}
122
138
}
123
139
124
140
/// Attempt to send a disco packet to client with [`NodeId`] `dst`.
125
- pub ( super ) async fn send_disco_packet (
126
- & self ,
127
- dst : NodeId ,
128
- data : Bytes ,
129
- src : NodeId ,
130
- ) -> Result < ( ) > {
141
+ pub ( super ) fn send_disco_packet ( & self , dst : NodeId , data : Bytes , src : NodeId ) -> Result < ( ) > {
131
142
let Some ( client) = self . 0 . clients . get ( & dst) else {
132
143
debug ! (
133
144
dst = dst. fmt_short( ) ,
@@ -154,7 +165,7 @@ impl Clients {
154
165
dst = dst. fmt_short( ) ,
155
166
"can no longer write to client, dropping disco message and pruning connection"
156
167
) ;
157
- self . unregister ( client , dst ) . await ;
168
+ client . start_shutdown ( ) ;
158
169
bail ! ( "failed to send message: gone" ) ;
159
170
}
160
171
}
@@ -205,9 +216,7 @@ mod tests {
205
216
206
217
// send packet
207
218
let data = b"hello world!" ;
208
- clients
209
- . send_packet ( a_key, Bytes :: from ( & data[ ..] ) , b_key)
210
- . await ?;
219
+ clients. send_packet ( a_key, Bytes :: from ( & data[ ..] ) , b_key) ?;
211
220
let frame = recv_frame ( FrameType :: RecvPacket , & mut a_rw) . await ?;
212
221
assert_eq ! (
213
222
frame,
@@ -218,9 +227,7 @@ mod tests {
218
227
) ;
219
228
220
229
// send disco packet
221
- clients
222
- . send_disco_packet ( a_key, Bytes :: from ( & data[ ..] ) , b_key)
223
- . await ?;
230
+ clients. send_disco_packet ( a_key, Bytes :: from ( & data[ ..] ) , b_key) ?;
224
231
let frame = recv_frame ( FrameType :: RecvPacket , & mut a_rw) . await ?;
225
232
assert_eq ! (
226
233
frame,
@@ -230,13 +237,23 @@ mod tests {
230
237
}
231
238
) ;
232
239
233
- let client = clients . 0 . clients . get ( & a_key ) . unwrap ( ) ;
234
-
235
- // send peer_gone. Also, tests that we do not get a deadlock
236
- // when unregistering.
237
- clients . unregister ( client , a_key ) . await ;
240
+ {
241
+ let client = clients . 0 . clients . get ( & a_key ) . unwrap ( ) ;
242
+ // shutdown client a, this should trigger the removal from the clients list
243
+ client . start_shutdown ( ) ;
244
+ }
238
245
239
- assert ! ( !clients. 0 . clients. contains_key( & a_key) ) ;
246
+ // need to wait a moment for the removal to be processed
247
+ let c = clients. clone ( ) ;
248
+ tokio:: time:: timeout ( Duration :: from_secs ( 1 ) , async move {
249
+ loop {
250
+ if !c. 0 . clients . contains_key ( & a_key) {
251
+ break ;
252
+ }
253
+ tokio:: time:: sleep ( Duration :: from_millis ( 100 ) ) . await ;
254
+ }
255
+ } )
256
+ . await ?;
240
257
clients. shutdown ( ) . await ;
241
258
242
259
Ok ( ( ) )
0 commit comments