1
1
use std:: sync:: Arc ;
2
2
3
+ use derive_more:: { Display , Error } ;
3
4
use hyper:: StatusCode ;
4
5
use log:: error;
5
6
use reqwest:: Response ;
@@ -8,10 +9,37 @@ use serde::{Deserialize, Serialize};
8
9
use super :: api:: { Client , ConnectionInfo } ;
9
10
use crate :: config:: Configuration ;
10
11
use crate :: databases:: database:: Database ;
11
- use crate :: errors:: ServiceError ;
12
12
use crate :: models:: tracker_key:: TrackerKey ;
13
13
use crate :: models:: user:: UserId ;
14
14
15
+ #[ derive( Debug , Display , PartialEq , Eq , Error ) ]
16
+ #[ allow( dead_code) ]
17
+ pub enum TrackerAPIError {
18
+ #[ display( fmt = "Error with tracker connection." ) ]
19
+ TrackerOffline ,
20
+
21
+ #[ display( fmt = "Could not whitelist torrent." ) ]
22
+ AddToWhitelistError ,
23
+
24
+ #[ display( fmt = "Could not remove torrent from whitelist." ) ]
25
+ RemoveFromWhitelistError ,
26
+
27
+ #[ display( fmt = "Could not retrieve a new user key." ) ]
28
+ RetrieveUserKeyError ,
29
+
30
+ #[ display( fmt = "Could not save the newly generated user key into the database." ) ]
31
+ CannotSaveUserKey ,
32
+
33
+ #[ display( fmt = "Torrent not found." ) ]
34
+ TorrentNotFound ,
35
+
36
+ #[ display( fmt = "Expected body in tracker response, received empty body." ) ]
37
+ MissingResponseBody ,
38
+
39
+ #[ display( fmt = "Expected body in tracker response, received empty body." ) ]
40
+ FailedToParseTrackerResponse { body : String } ,
41
+ }
42
+
15
43
#[ derive( Debug , Serialize , Deserialize , PartialEq ) ]
16
44
pub struct TorrentInfo {
17
45
pub info_hash : String ,
@@ -69,18 +97,18 @@ impl Service {
69
97
///
70
98
/// Will return an error if the HTTP request failed (for example if the
71
99
/// tracker API is offline) or if the tracker API returned an error.
72
- pub async fn whitelist_info_hash ( & self , info_hash : String ) -> Result < ( ) , ServiceError > {
100
+ pub async fn whitelist_info_hash ( & self , info_hash : String ) -> Result < ( ) , TrackerAPIError > {
73
101
let response = self . api_client . whitelist_torrent ( & info_hash) . await ;
74
102
75
103
match response {
76
104
Ok ( response) => {
77
105
if response. status ( ) . is_success ( ) {
78
106
Ok ( ( ) )
79
107
} else {
80
- Err ( ServiceError :: WhitelistingError )
108
+ Err ( TrackerAPIError :: AddToWhitelistError )
81
109
}
82
110
}
83
- Err ( _) => Err ( ServiceError :: TrackerOffline ) ,
111
+ Err ( _) => Err ( TrackerAPIError :: TrackerOffline ) ,
84
112
}
85
113
}
86
114
@@ -90,18 +118,18 @@ impl Service {
90
118
///
91
119
/// Will return an error if the HTTP request failed (for example if the
92
120
/// tracker API is offline) or if the tracker API returned an error.
93
- pub async fn remove_info_hash_from_whitelist ( & self , info_hash : String ) -> Result < ( ) , ServiceError > {
121
+ pub async fn remove_info_hash_from_whitelist ( & self , info_hash : String ) -> Result < ( ) , TrackerAPIError > {
94
122
let response = self . api_client . remove_torrent_from_whitelist ( & info_hash) . await ;
95
123
96
124
match response {
97
125
Ok ( response) => {
98
126
if response. status ( ) . is_success ( ) {
99
127
Ok ( ( ) )
100
128
} else {
101
- Err ( ServiceError :: InternalServerError )
129
+ Err ( TrackerAPIError :: RemoveFromWhitelistError )
102
130
}
103
131
}
104
- Err ( _) => Err ( ServiceError :: InternalServerError ) ,
132
+ Err ( _) => Err ( TrackerAPIError :: TrackerOffline ) ,
105
133
}
106
134
}
107
135
@@ -116,14 +144,14 @@ impl Service {
116
144
///
117
145
/// Will return an error if the HTTP request to get generated a new
118
146
/// user tracker key failed.
119
- pub async fn get_personal_announce_url ( & self , user_id : UserId ) -> Result < String , ServiceError > {
147
+ pub async fn get_personal_announce_url ( & self , user_id : UserId ) -> Result < String , TrackerAPIError > {
120
148
let tracker_key = self . database . get_user_tracker_key ( user_id) . await ;
121
149
122
150
match tracker_key {
123
- Some ( v ) => Ok ( self . announce_url_with_key ( & v ) ) ,
151
+ Some ( tracker_key ) => Ok ( self . announce_url_with_key ( & tracker_key ) ) ,
124
152
None => match self . retrieve_new_tracker_key ( user_id) . await {
125
- Ok ( v ) => Ok ( self . announce_url_with_key ( & v ) ) ,
126
- Err ( _) => Err ( ServiceError :: TrackerOffline ) ,
153
+ Ok ( new_tracker_key ) => Ok ( self . announce_url_with_key ( & new_tracker_key ) ) ,
154
+ Err ( _) => Err ( TrackerAPIError :: TrackerOffline ) ,
127
155
} ,
128
156
}
129
157
}
@@ -134,14 +162,19 @@ impl Service {
134
162
///
135
163
/// Will return an error if the HTTP request to get torrent info fails or
136
164
/// if the response cannot be parsed.
137
- pub async fn get_torrent_info ( & self , info_hash : & str ) -> Result < TorrentInfo , ServiceError > {
138
- let response = self
139
- . api_client
140
- . get_torrent_info ( info_hash)
141
- . await
142
- . map_err ( |_| ServiceError :: InternalServerError ) ?;
143
-
144
- map_torrent_info_response ( response) . await
165
+ pub async fn get_torrent_info ( & self , info_hash : & str ) -> Result < TorrentInfo , TrackerAPIError > {
166
+ let response = self . api_client . get_torrent_info ( info_hash) . await ;
167
+
168
+ match response {
169
+ Ok ( response) => {
170
+ if response. status ( ) . is_success ( ) {
171
+ map_torrent_info_response ( response) . await
172
+ } else {
173
+ Err ( TrackerAPIError :: RemoveFromWhitelistError )
174
+ }
175
+ }
176
+ Err ( _) => Err ( TrackerAPIError :: TrackerOffline ) ,
177
+ }
145
178
}
146
179
147
180
/// It builds the announce url appending the user tracker key.
@@ -150,51 +183,59 @@ impl Service {
150
183
format ! ( "{}/{}" , self . tracker_url, tracker_key. key)
151
184
}
152
185
153
- /// Issue a new tracker key from tracker and save it in database,
154
- /// tied to a user
155
- async fn retrieve_new_tracker_key ( & self , user_id : i64 ) -> Result < TrackerKey , ServiceError > {
156
- // Request new tracker key from tracker
157
- let response = self
158
- . api_client
159
- . retrieve_new_tracker_key ( self . token_valid_seconds )
160
- . await
161
- . map_err ( |_| ServiceError :: InternalServerError ) ?;
162
-
163
- // Parse tracker key from response
164
- let tracker_key = response
165
- . json :: < TrackerKey > ( )
166
- . await
167
- . map_err ( |_| ServiceError :: InternalServerError ) ?;
168
-
169
- // Add tracker key to database (tied to a user)
170
- self . database . add_tracker_key ( user_id, & tracker_key) . await ?;
171
-
172
- // return tracker key
173
- Ok ( tracker_key)
186
+ /// Issue a new tracker key from tracker.
187
+ async fn retrieve_new_tracker_key ( & self , user_id : i64 ) -> Result < TrackerKey , TrackerAPIError > {
188
+ let response = self . api_client . retrieve_new_tracker_key ( self . token_valid_seconds ) . await ;
189
+
190
+ match response {
191
+ Ok ( response) => {
192
+ if response. status ( ) . is_success ( ) {
193
+ let body = response. text ( ) . await . map_err ( |_| {
194
+ error ! ( "Tracker API response without body" ) ;
195
+ TrackerAPIError :: MissingResponseBody
196
+ } ) ?;
197
+
198
+ // Parse tracker key from response
199
+ let tracker_key =
200
+ serde_json:: from_str ( & body) . map_err ( |_| TrackerAPIError :: FailedToParseTrackerResponse { body } ) ?;
201
+
202
+ // Add tracker key to database (tied to a user)
203
+ self . database
204
+ . add_tracker_key ( user_id, & tracker_key)
205
+ . await
206
+ . map_err ( |_| TrackerAPIError :: CannotSaveUserKey ) ?;
207
+
208
+ Ok ( tracker_key)
209
+ } else {
210
+ Err ( TrackerAPIError :: RetrieveUserKeyError )
211
+ }
212
+ }
213
+ Err ( _) => Err ( TrackerAPIError :: TrackerOffline ) ,
214
+ }
174
215
}
175
216
}
176
217
177
- async fn map_torrent_info_response ( response : Response ) -> Result < TorrentInfo , ServiceError > {
218
+ async fn map_torrent_info_response ( response : Response ) -> Result < TorrentInfo , TrackerAPIError > {
178
219
if response. status ( ) == StatusCode :: NOT_FOUND {
179
- return Err ( ServiceError :: TorrentNotFound ) ;
220
+ return Err ( TrackerAPIError :: TorrentNotFound ) ;
180
221
}
181
222
182
223
let body = response. text ( ) . await . map_err ( |_| {
183
224
error ! ( "Tracker API response without body" ) ;
184
- ServiceError :: InternalServerError
225
+ TrackerAPIError :: MissingResponseBody
185
226
} ) ?;
186
227
187
228
if body == "\" torrent not known\" " {
188
229
// todo: temporary fix. the service should return a 404 (StatusCode::NOT_FOUND).
189
- return Err ( ServiceError :: TorrentNotFound ) ;
230
+ return Err ( TrackerAPIError :: TorrentNotFound ) ;
190
231
}
191
232
192
233
serde_json:: from_str ( & body) . map_err ( |e| {
193
234
error ! (
194
235
"Failed to parse torrent info from tracker response. Body: {}, Error: {}" ,
195
236
body, e
196
237
) ;
197
- ServiceError :: InternalServerError
238
+ TrackerAPIError :: FailedToParseTrackerResponse { body }
198
239
} )
199
240
}
200
241
@@ -204,16 +245,15 @@ mod tests {
204
245
mod getting_the_torrent_info_from_the_tracker {
205
246
use hyper:: { Response , StatusCode } ;
206
247
207
- use crate :: errors:: ServiceError ;
208
- use crate :: tracker:: service:: { map_torrent_info_response, TorrentInfo } ;
248
+ use crate :: tracker:: service:: { map_torrent_info_response, TorrentInfo , TrackerAPIError } ;
209
249
210
250
#[ tokio:: test]
211
251
async fn it_should_return_a_torrent_not_found_response_when_the_tracker_returns_the_current_torrent_not_known_response ( ) {
212
252
let tracker_response = Response :: new ( "\" torrent not known\" " ) ;
213
253
214
254
let result = map_torrent_info_response ( tracker_response. into ( ) ) . await . unwrap_err ( ) ;
215
255
216
- assert_eq ! ( result, ServiceError :: TorrentNotFound ) ;
256
+ assert_eq ! ( result, TrackerAPIError :: TorrentNotFound ) ;
217
257
}
218
258
219
259
#[ tokio:: test]
@@ -225,7 +265,7 @@ mod tests {
225
265
226
266
let result = map_torrent_info_response ( tracker_response. into ( ) ) . await . unwrap_err ( ) ;
227
267
228
- assert_eq ! ( result, ServiceError :: TorrentNotFound ) ;
268
+ assert_eq ! ( result, TrackerAPIError :: TorrentNotFound ) ;
229
269
}
230
270
231
271
#[ tokio:: test]
@@ -266,9 +306,14 @@ mod tests {
266
306
267
307
let tracker_response = Response :: new ( invalid_json_body_for_torrent_info) ;
268
308
269
- let result = map_torrent_info_response ( tracker_response. into ( ) ) . await . unwrap_err ( ) ;
309
+ let err = map_torrent_info_response ( tracker_response. into ( ) ) . await . unwrap_err ( ) ;
270
310
271
- assert_eq ! ( result, ServiceError :: InternalServerError ) ;
311
+ assert_eq ! (
312
+ err,
313
+ TrackerAPIError :: FailedToParseTrackerResponse {
314
+ body: invalid_json_body_for_torrent_info. to_string( )
315
+ }
316
+ ) ;
272
317
}
273
318
}
274
319
}
0 commit comments