@@ -2,7 +2,7 @@ use std::cmp;
2
2
use std:: collections:: HashMap ;
3
3
use std:: fs:: File ;
4
4
use std:: io:: Read ;
5
- use std:: sync:: Arc ;
5
+ use std:: sync:: { Arc , Once } ;
6
6
7
7
use disk:: fs:: NativeFileSystem ;
8
8
use disk:: fs_cache:: FileHandleCache ;
@@ -16,8 +16,7 @@ use futures::lock::Mutex;
16
16
use futures:: { stream, SinkExt as _, StreamExt as _} ;
17
17
use handshake:: transports:: TcpTransport ;
18
18
use handshake:: {
19
- Extensions , Handshaker , HandshakerBuilder , HandshakerConfig , HandshakerSink , HandshakerStream , InitiateMessage , PeerId ,
20
- Protocol ,
19
+ Extensions , Handshaker , HandshakerBuilder , HandshakerConfig , HandshakerStream , InitiateMessage , PeerId , Protocol ,
21
20
} ;
22
21
use metainfo:: { Info , Metainfo } ;
23
22
use peer:: messages:: { BitFieldMessage , HaveMessage , PeerWireProtocolMessage , PieceMessage , RequestMessage } ;
@@ -31,10 +30,13 @@ use tokio::signal;
31
30
use tokio:: task:: JoinSet ;
32
31
use tokio_util:: bytes:: BytesMut ;
33
32
use tokio_util:: codec:: { Decoder , Framed } ;
33
+ use tracing:: level_filters:: LevelFilter ;
34
34
35
35
// Maximum number of requests that can be in flight at once.
36
36
const MAX_PENDING_BLOCKS : usize = 50 ;
37
37
38
+ pub static INIT : Once = Once :: new ( ) ;
39
+
38
40
// Enum to store our selection state updates
39
41
#[ allow( dead_code) ]
40
42
#[ derive( Debug ) ]
@@ -59,8 +61,30 @@ enum Downloader {
59
61
Interrupted ,
60
62
}
61
63
64
+ enum Setup {
65
+ Finished ( ( NativeDiskManager , PeerManager , TcpHandshaker ) , JoinSet < ( ) > ) ,
66
+ Interrupted ,
67
+ }
68
+
69
+ pub fn tracing_stdout_init ( filter : LevelFilter ) {
70
+ let builder = tracing_subscriber:: fmt ( ) . with_max_level ( filter) . with_ansi ( true ) ;
71
+
72
+ builder. pretty ( ) . with_file ( true ) . init ( ) ;
73
+
74
+ tracing:: info!( "Logging initialized" ) ;
75
+ }
76
+
77
+ async fn ctrl_c ( ) {
78
+ signal:: ctrl_c ( ) . await . expect ( "failed to listen for event" ) ;
79
+ println ! ( "Ctrl-C received, shutting down..." ) ;
80
+ }
81
+
62
82
#[ tokio:: main]
63
83
async fn main ( ) {
84
+ INIT . call_once ( || {
85
+ tracing_stdout_init ( LevelFilter :: TRACE ) ;
86
+ } ) ;
87
+
64
88
// Parse command-line arguments
65
89
let matched_arguments = parse_arguments ( ) ;
66
90
let ( torrent_file_path, download_directory, peer_address) = extract_arguments ( & matched_arguments) ;
@@ -71,28 +95,51 @@ async fn main() {
71
95
// Create a JoinSet to manage background tasks
72
96
let tasks = Arc :: new ( Mutex :: new ( JoinSet :: new ( ) ) ) ;
73
97
74
- let ctrl_c = async {
75
- signal:: ctrl_c ( ) . await . expect ( "failed to listen for event" ) ;
76
- println ! ( "Ctrl-C received, shutting down..." ) ;
98
+ // Setup the managers.
99
+ let setup = setup ( download_directory) ;
100
+
101
+ // Await either the completion of the setup or the Ctrl-C signal
102
+ let setup = tokio:: select! {
103
+ setup = setup => Setup :: Finished ( setup. 0 , setup. 1 ) ,
104
+ ( ) = ctrl_c( ) => Setup :: Interrupted ,
105
+ } ;
106
+
107
+ let ( managers, mut handshaker_tasks) = match setup {
108
+ Setup :: Finished ( managers, handshaker_tasks) => ( managers, handshaker_tasks) ,
109
+ Setup :: Interrupted => {
110
+ tracing:: warn!( "setup was canceled..." ) ;
111
+ return ;
112
+ }
77
113
} ;
78
114
79
- let downloader = downloader ( tasks. clone ( ) , download_directory , peer_address, metainfo, info_hash) ;
115
+ let downloader = downloader ( tasks. clone ( ) , managers , peer_address, metainfo, info_hash) ;
80
116
81
- // Await either the completion of all tasks or the Ctrl-C signal
117
+ // Await either the completion of the downloader or the Ctrl-C signal
82
118
let status = tokio:: select! {
83
119
( ) = downloader => Downloader :: Finished ,
84
- ( ) = ctrl_c => Downloader :: Interrupted ,
120
+ ( ) = ctrl_c( ) => Downloader :: Interrupted ,
85
121
} ;
86
122
87
123
match status {
88
124
Downloader :: Finished => {
89
- while let Some ( result) = tasks . lock ( ) . await . join_next ( ) . await {
125
+ while let Some ( result) = handshaker_tasks . try_join_next ( ) {
90
126
if let Err ( e) = result {
91
127
eprintln ! ( "Task failed: {e:?}" ) ;
92
128
}
93
129
}
130
+ handshaker_tasks. shutdown ( ) . await ;
131
+
132
+ while let Some ( result) = tasks. lock ( ) . await . try_join_next ( ) {
133
+ if let Err ( e) = result {
134
+ eprintln ! ( "Task failed: {e:?}" ) ;
135
+ }
136
+ }
137
+ tasks. lock ( ) . await . shutdown ( ) . await ;
138
+ }
139
+ Downloader :: Interrupted => {
140
+ handshaker_tasks. shutdown ( ) . await ;
141
+ tasks. lock ( ) . await . shutdown ( ) . await ;
94
142
}
95
- Downloader :: Interrupted => tasks. lock ( ) . await . shutdown ( ) . await ,
96
143
}
97
144
}
98
145
@@ -142,21 +189,36 @@ fn load_and_parse_torrent_file(torrent_file_path: &str) -> (Metainfo, InfoHash)
142
189
( metainfo, info_hash)
143
190
}
144
191
192
+ async fn setup ( download_directory : String ) -> ( ( NativeDiskManager , PeerManager , TcpHandshaker ) , JoinSet < ( ) > ) {
193
+ // Setup disk manager for handling file operations
194
+ let disk_manager = setup_disk_manager ( & download_directory) ;
195
+
196
+ // Setup peer manager for managing peer communication
197
+ let peer_manager = setup_peer_manager ( ) ;
198
+
199
+ // Setup handshaker for managing peer connections
200
+ let ( handshaker, handshaker_tasks) = setup_handshaker ( ) . await ;
201
+
202
+ ( ( disk_manager, peer_manager, handshaker) , handshaker_tasks)
203
+ }
204
+
145
205
async fn downloader (
146
206
tasks : Arc < Mutex < JoinSet < ( ) > > > ,
147
- download_directory : String ,
207
+ managers : ( NativeDiskManager , PeerManager , TcpHandshaker ) ,
148
208
peer_address : String ,
149
209
metainfo : Metainfo ,
150
210
info_hash : InfoHash ,
151
211
) {
212
+ let ( disk_manager, peer_manager, handshaker) = managers;
213
+
152
214
// Setup disk manager for handling file operations
153
- let ( mut disk_manager_sender, disk_manager_receiver) = setup_disk_manager ( & download_directory ) ;
215
+ let ( mut disk_manager_sender, disk_manager_receiver) = disk_manager . into_parts ( ) ;
154
216
155
217
// Setup peer manager for managing peer communication
156
- let ( peer_manager_sender, peer_manager_receiver) = setup_peer_manager ( ) ;
218
+ let ( peer_manager_sender, peer_manager_receiver) = peer_manager . into_parts ( ) ;
157
219
158
220
// Setup handshaker for managing peer connections
159
- let ( mut handshaker_sender, handshaker_receiver) = setup_handshaker ( ) . await ;
221
+ let ( mut handshaker_sender, handshaker_receiver) = handshaker . into_parts ( ) ;
160
222
161
223
// Handle new incoming connections
162
224
tasks
@@ -231,44 +293,39 @@ async fn downloader(
231
293
. await ;
232
294
}
233
295
234
- fn setup_disk_manager ( download_directory : & str ) -> ( DiskManagerSink < FileHandleCache < NativeFileSystem > > , DiskManagerStream ) {
296
+ type NativeDiskManager = DiskManager < FileHandleCache < NativeFileSystem > > ;
297
+
298
+ fn setup_disk_manager ( download_directory : & str ) -> DiskManager < FileHandleCache < NativeFileSystem > > {
235
299
let filesystem = FileHandleCache :: new ( NativeFileSystem :: with_directory ( download_directory) , 100 ) ;
236
- let disk_manager: DiskManager < FileHandleCache < NativeFileSystem > > = DiskManagerBuilder :: new ( )
300
+
301
+ DiskManagerBuilder :: new ( )
237
302
. with_sink_buffer_capacity ( 1 )
238
303
. with_stream_buffer_capacity ( 0 )
239
- . build ( Arc :: new ( filesystem) ) ;
240
-
241
- disk_manager. into_parts ( )
304
+ . build ( Arc :: new ( filesystem) )
242
305
}
243
306
244
- async fn setup_handshaker ( ) -> ( HandshakerSink , HandshakerStream < TcpStream > ) {
245
- let handshaker: Handshaker < TcpStream > = HandshakerBuilder :: new ( )
307
+ type TcpHandshaker = Handshaker < TcpStream > ;
308
+
309
+ async fn setup_handshaker ( ) -> ( TcpHandshaker , JoinSet < ( ) > ) {
310
+ HandshakerBuilder :: new ( )
246
311
. with_peer_id ( PeerId :: from_hash ( "-BI0000-000000000000" . as_bytes ( ) ) . unwrap ( ) )
247
312
. with_config ( HandshakerConfig :: default ( ) . with_wait_buffer_size ( 0 ) . with_done_buffer_size ( 0 ) )
248
313
. build ( TcpTransport )
249
314
. await
250
- . unwrap ( ) ;
251
-
252
- handshaker. into_parts ( )
315
+ . unwrap ( )
253
316
}
254
317
318
+ type PeerManager = peer:: PeerManager <
319
+ Framed < TcpStream , PeerProtocolCodec < PeerWireProtocol < NullProtocol > > > ,
320
+ PeerWireProtocolMessage < NullProtocol > ,
321
+ > ;
322
+
255
323
#[ allow( clippy:: type_complexity) ]
256
- fn setup_peer_manager ( ) -> (
257
- PeerManagerSink < Framed < TcpStream , PeerProtocolCodec < PeerWireProtocol < NullProtocol > > > , PeerWireProtocolMessage < NullProtocol > > ,
258
- PeerManagerStream <
259
- Framed < TcpStream , PeerProtocolCodec < PeerWireProtocol < NullProtocol > > > ,
260
- PeerWireProtocolMessage < NullProtocol > ,
261
- > ,
262
- ) {
263
- let peer_manager: peer:: PeerManager <
264
- Framed < TcpStream , PeerProtocolCodec < PeerWireProtocol < NullProtocol > > > ,
265
- PeerWireProtocolMessage < NullProtocol > ,
266
- > = PeerManagerBuilder :: new ( )
324
+ fn setup_peer_manager ( ) -> PeerManager {
325
+ PeerManagerBuilder :: new ( )
267
326
. with_sink_buffer_capacity ( 0 )
268
327
. with_stream_buffer_capacity ( 0 )
269
- . build ( ) ;
270
-
271
- peer_manager. into_parts ( )
328
+ . build ( )
272
329
}
273
330
274
331
async fn handle_new_connections (
0 commit comments