1
1
use std:: net:: SocketAddr ;
2
+ use std:: str:: FromStr ;
2
3
use std:: sync:: Arc ;
3
4
use std:: time:: Duration ;
4
5
5
6
use colored:: Colorize ;
6
- use reqwest:: { Client , Url } ;
7
+ use reqwest:: { Client as HttpClient , Url } ;
7
8
8
9
use super :: config:: Configuration ;
9
10
use super :: console:: Console ;
10
11
use crate :: console:: clients:: checker:: printer:: Printer ;
12
+ use crate :: shared:: bit_torrent:: info_hash:: InfoHash ;
13
+ use crate :: shared:: bit_torrent:: tracker:: http:: client:: requests:: announce:: QueryBuilder ;
14
+ use crate :: shared:: bit_torrent:: tracker:: http:: client:: responses:: announce:: Announce ;
15
+ use crate :: shared:: bit_torrent:: tracker:: http:: client:: responses:: scrape;
16
+ use crate :: shared:: bit_torrent:: tracker:: http:: client:: { requests, Client } ;
11
17
12
18
pub struct Service {
13
19
pub ( crate ) config : Arc < Configuration > ,
@@ -19,7 +25,7 @@ pub type CheckResult = Result<(), CheckError>;
19
25
#[ derive( Debug ) ]
20
26
pub enum CheckError {
21
27
UdpError ,
22
- HttpError ,
28
+ HttpError { url : Url } ,
23
29
HealthCheckError { url : Url } ,
24
30
}
25
31
@@ -30,10 +36,15 @@ impl Service {
30
36
pub async fn run_checks ( & self ) -> Vec < CheckResult > {
31
37
self . console . println ( "Running checks for trackers ..." ) ;
32
38
39
+ let mut check_results = vec ! [ ] ;
40
+
33
41
self . check_udp_trackers ( ) ;
34
- self . check_http_trackers ( ) ;
35
42
36
- self . run_health_checks ( ) . await
43
+ self . check_http_trackers ( & mut check_results) . await ;
44
+
45
+ self . run_health_checks ( & mut check_results) . await ;
46
+
47
+ check_results
37
48
}
38
49
39
50
fn check_udp_trackers ( & self ) {
@@ -44,63 +55,130 @@ impl Service {
44
55
}
45
56
}
46
57
47
- fn check_http_trackers ( & self ) {
58
+ async fn check_http_trackers ( & self , check_results : & mut Vec < CheckResult > ) {
48
59
self . console . println ( "HTTP trackers ..." ) ;
49
60
50
61
for http_tracker in & self . config . http_trackers {
51
- self . check_http_tracker ( http_tracker) ;
62
+ let colored_tracker_url = http_tracker. to_string ( ) . yellow ( ) ;
63
+
64
+ match self . check_http_announce ( http_tracker) . await {
65
+ Ok ( ( ) ) => {
66
+ check_results. push ( Ok ( ( ) ) ) ;
67
+ self . console
68
+ . println ( & format ! ( "{} - Announce at {} is OK" , "✓" . green( ) , colored_tracker_url) ) ;
69
+ }
70
+ Err ( err) => {
71
+ check_results. push ( Err ( err) ) ;
72
+ self . console
73
+ . println ( & format ! ( "{} - Announce at {} is failing" , "✗" . red( ) , colored_tracker_url) ) ;
74
+ }
75
+ }
76
+
77
+ match self . check_http_scrape ( http_tracker) . await {
78
+ Ok ( ( ) ) => {
79
+ check_results. push ( Ok ( ( ) ) ) ;
80
+ self . console
81
+ . println ( & format ! ( "{} - Scrape at {} is OK" , "✓" . green( ) , colored_tracker_url) ) ;
82
+ }
83
+ Err ( err) => {
84
+ check_results. push ( Err ( err) ) ;
85
+ self . console
86
+ . println ( & format ! ( "{} - Scrape at {} is failing" , "✗" . red( ) , colored_tracker_url) ) ;
87
+ }
88
+ }
52
89
}
53
90
}
54
91
55
- async fn run_health_checks ( & self ) -> Vec < CheckResult > {
92
+ async fn run_health_checks ( & self , check_results : & mut Vec < CheckResult > ) {
56
93
self . console . println ( "Health checks ..." ) ;
57
94
58
- let mut check_results = vec ! [ ] ;
59
-
60
95
for health_check_url in & self . config . health_checks {
61
96
match self . run_health_check ( health_check_url. clone ( ) ) . await {
62
97
Ok ( ( ) ) => check_results. push ( Ok ( ( ) ) ) ,
63
98
Err ( err) => check_results. push ( Err ( err) ) ,
64
99
}
65
100
}
66
-
67
- check_results
68
101
}
69
102
70
103
fn check_udp_tracker ( & self , address : & SocketAddr ) {
71
104
// todo:
72
105
// - Make announce request
73
106
// - Make scrape request
74
- self . console
75
- . println ( & format ! ( "{} - UDP tracker at {:?} is OK (TODO)" , "✓" . green( ) , address) ) ;
107
+
108
+ let colored_address = address. to_string ( ) . yellow ( ) ;
109
+
110
+ self . console . println ( & format ! (
111
+ "{} - UDP tracker at udp://{} is OK ({})" ,
112
+ "✓" . green( ) ,
113
+ colored_address,
114
+ "TODO" . red( ) ,
115
+ ) ) ;
76
116
}
77
117
78
- fn check_http_tracker ( & self , url : & Url ) {
79
- // todo:
80
- // - Make announce request
81
- // - Make scrape request
82
- self . console
83
- . println ( & format ! ( "{} - HTTP tracker at {} is OK (TODO)" , "✓" . green( ) , url) ) ;
118
+ async fn check_http_announce ( & self , url : & Url ) -> Result < ( ) , CheckError > {
119
+ let info_hash_str = "9c38422213e30bff212b30c360d26f9a02136422" . to_string ( ) ; // # DevSkim: ignore DS173237
120
+ let info_hash = InfoHash :: from_str ( & info_hash_str) . expect ( "a valid info-hash is required" ) ;
121
+
122
+ let response = Client :: new ( url. clone ( ) )
123
+ . announce ( & QueryBuilder :: with_default_values ( ) . with_info_hash ( & info_hash) . query ( ) )
124
+ . await ;
125
+
126
+ if let Ok ( body) = response. bytes ( ) . await {
127
+ if let Ok ( _announce_response) = serde_bencode:: from_bytes :: < Announce > ( & body) {
128
+ Ok ( ( ) )
129
+ } else {
130
+ Err ( CheckError :: HttpError { url : url. clone ( ) } )
131
+ }
132
+ } else {
133
+ Err ( CheckError :: HttpError { url : url. clone ( ) } )
134
+ }
135
+ }
136
+
137
+ async fn check_http_scrape ( & self , url : & Url ) -> Result < ( ) , CheckError > {
138
+ let info_hashes: Vec < String > = vec ! [ "9c38422213e30bff212b30c360d26f9a02136422" . to_string( ) ] ; // # DevSkim: ignore DS173237
139
+ let query = requests:: scrape:: Query :: try_from ( info_hashes) . expect ( "a valid array of info-hashes is required" ) ;
140
+
141
+ let response = Client :: new ( url. clone ( ) ) . scrape ( & query) . await ;
142
+
143
+ if let Ok ( body) = response. bytes ( ) . await {
144
+ if let Ok ( _scrape_response) = scrape:: Response :: try_from_bencoded ( & body) {
145
+ Ok ( ( ) )
146
+ } else {
147
+ Err ( CheckError :: HttpError { url : url. clone ( ) } )
148
+ }
149
+ } else {
150
+ Err ( CheckError :: HttpError { url : url. clone ( ) } )
151
+ }
84
152
}
85
153
86
154
async fn run_health_check ( & self , url : Url ) -> Result < ( ) , CheckError > {
87
- let client = Client :: builder ( ) . timeout ( Duration :: from_secs ( 5 ) ) . build ( ) . unwrap ( ) ;
155
+ let client = HttpClient :: builder ( ) . timeout ( Duration :: from_secs ( 5 ) ) . build ( ) . unwrap ( ) ;
156
+
157
+ let colored_url = url. to_string ( ) . yellow ( ) ;
88
158
89
159
match client. get ( url. clone ( ) ) . send ( ) . await {
90
160
Ok ( response) => {
91
161
if response. status ( ) . is_success ( ) {
92
162
self . console
93
- . println ( & format ! ( "{} - Health API at {} is OK" , "✓" . green( ) , url ) ) ;
163
+ . println ( & format ! ( "{} - Health API at {} is OK" , "✓" . green( ) , colored_url ) ) ;
94
164
Ok ( ( ) )
95
165
} else {
96
- self . console
97
- . eprintln ( & format ! ( "{} - Health API at {} failing: {:?}" , "✗" . red( ) , url, response) ) ;
166
+ self . console . eprintln ( & format ! (
167
+ "{} - Health API at {} is failing: {:?}" ,
168
+ "✗" . red( ) ,
169
+ colored_url,
170
+ response
171
+ ) ) ;
98
172
Err ( CheckError :: HealthCheckError { url } )
99
173
}
100
174
}
101
175
Err ( err) => {
102
- self . console
103
- . eprintln ( & format ! ( "{} - Health API at {} failing: {:?}" , "✗" . red( ) , url, err) ) ;
176
+ self . console . eprintln ( & format ! (
177
+ "{} - Health API at {} is failing: {:?}" ,
178
+ "✗" . red( ) ,
179
+ colored_url,
180
+ err
181
+ ) ) ;
104
182
Err ( CheckError :: HealthCheckError { url } )
105
183
}
106
184
}
0 commit comments