-
Notifications
You must be signed in to change notification settings - Fork 48
Closed
Copy link
Labels
Code Cleanup / RefactoringTidying and Making NeatTidying and Making Neat
Description
Parent issue: #1270
In the HTTP and UDP trackers authentication is done at the delivery layer. Since it's not handled by external frameworks we can move it to the http-tracker-core
and udp-tracker-core
. That way that code can be decoupled from the framework. For example, if we migrate the HTTP tracker from Axum web framework to ActixWeb we don't need to implement authentication again for ActixcWeb.
NOTICE: The UDP tracker is always public. "Authentication" is the validation of the connection cookie.
There are some comment with the prefix // todo: move authentication
indicating where to make the refactor.
HTTP Tracker
Announce
#[allow(clippy::too_many_arguments)]
async fn handle_announce(
core_config: &Arc<Core>,
announce_handler: &Arc<AnnounceHandler>,
authentication_service: &Arc<AuthenticationService>,
whitelist_authorization: &Arc<whitelist::authorization::WhitelistAuthorization>,
opt_http_stats_event_sender: &Arc<Option<Box<dyn http_tracker_core::statistics::event::sender::Sender>>>,
announce_request: &Announce,
client_ip_sources: &ClientIpSources,
maybe_key: Option<Key>,
) -> Result<AnnounceData, responses::error::Error> {
// todo: move authentication inside `http_tracker_core::services::announce::handle_announce`
// Authentication
if core_config.private {
match maybe_key {
Some(key) => match authentication_service.authenticate(&key).await {
Ok(()) => (),
Err(error) => return Err(map_auth_error_to_error_response(&error)),
},
None => {
return Err(responses::error::Error::from(auth::Error::MissingAuthKey {
location: Location::caller(),
}))
}
}
}
http_tracker_core::services::announce::handle_announce(
&core_config.clone(),
&announce_handler.clone(),
&authentication_service.clone(),
&whitelist_authorization.clone(),
&opt_http_stats_event_sender.clone(),
announce_request,
client_ip_sources,
)
.await
}
Scrape
#[allow(clippy::too_many_arguments)]
async fn handle_scrape(
core_config: &Arc<Core>,
scrape_handler: &Arc<ScrapeHandler>,
authentication_service: &Arc<AuthenticationService>,
opt_http_stats_event_sender: &Arc<Option<Box<dyn http_tracker_core::statistics::event::sender::Sender>>>,
scrape_request: &Scrape,
client_ip_sources: &ClientIpSources,
maybe_key: Option<Key>,
) -> Result<ScrapeData, responses::error::Error> {
// todo: move authentication inside `http_tracker_core::services::scrape::handle_scrape`
// Authentication
let return_fake_scrape_data = if core_config.private {
match maybe_key {
Some(key) => match authentication_service.authenticate(&key).await {
Ok(()) => false,
Err(_error) => true,
},
None => true,
}
} else {
false
};
http_tracker_core::services::scrape::handle_scrape(
core_config,
scrape_handler,
opt_http_stats_event_sender,
scrape_request,
client_ip_sources,
return_fake_scrape_data,
)
.await
}
UDP Tracker
Announce
/// It handles the `Announce` request. Refer to [`Announce`](crate::servers::udp#announce)
/// request for more information.
///
/// # Errors
///
/// If a error happens in the `handle_announce` function, it will just return the `ServerError`.
#[allow(clippy::too_many_arguments)]
#[instrument(fields(transaction_id, connection_id, info_hash), skip(announce_handler, whitelist_authorization, opt_udp_stats_event_sender), ret(level = Level::TRACE))]
pub async fn handle_announce(
remote_addr: SocketAddr,
request: &AnnounceRequest,
core_config: &Arc<Core>,
announce_handler: &Arc<AnnounceHandler>,
whitelist_authorization: &Arc<whitelist::authorization::WhitelistAuthorization>,
opt_udp_stats_event_sender: &Arc<Option<Box<dyn udp_tracker_core::statistics::event::sender::Sender>>>,
cookie_valid_range: Range<f64>,
) -> Result<Response, (Error, TransactionId)> {
tracing::Span::current()
.record("transaction_id", request.transaction_id.0.to_string())
.record("connection_id", request.connection_id.0.to_string())
.record("info_hash", InfoHash::from_bytes(&request.info_hash.0).to_hex_string());
tracing::trace!("handle announce");
// todo: move authentication to `udp_tracker_core::services::announce::handle_announce`
check(
&request.connection_id,
gen_remote_fingerprint(&remote_addr),
cookie_valid_range,
)
.map_err(|e| (e, request.transaction_id))?;
let response = udp_tracker_core::services::announce::handle_announce(
remote_addr,
request,
announce_handler,
whitelist_authorization,
opt_udp_stats_event_sender,
)
.await
.map_err(|e| Error::TrackerError {
source: (Arc::new(e) as Arc<dyn std::error::Error + Send + Sync>).into(),
})
.map_err(|e| (e, request.transaction_id))?;
// todo: extract `build_response` function.
// ...
}
Scrape
/// It handles the `Scrape` request. Refer to [`Scrape`](crate::servers::udp#scrape)
/// request for more information.
///
/// # Errors
///
/// This function does not ever return an error.
#[instrument(fields(transaction_id, connection_id), skip(scrape_handler, opt_udp_stats_event_sender), ret(level = Level::TRACE))]
pub async fn handle_scrape(
remote_addr: SocketAddr,
request: &ScrapeRequest,
scrape_handler: &Arc<ScrapeHandler>,
opt_udp_stats_event_sender: &Arc<Option<Box<dyn udp_tracker_core::statistics::event::sender::Sender>>>,
cookie_valid_range: Range<f64>,
) -> Result<Response, (Error, TransactionId)> {
tracing::Span::current()
.record("transaction_id", request.transaction_id.0.to_string())
.record("connection_id", request.connection_id.0.to_string());
tracing::trace!("handle scrape");
// todo: move authentication to `udp_tracker_core::services::scrape::handle_scrape`
check(
&request.connection_id,
gen_remote_fingerprint(&remote_addr),
cookie_valid_range,
)
.map_err(|e| (e, request.transaction_id))?;
let scrape_data =
udp_tracker_core::services::scrape::handle_scrape(remote_addr, request, scrape_handler, opt_udp_stats_event_sender)
.await
.map_err(|e| Error::TrackerError {
source: (Arc::new(e) as Arc<dyn std::error::Error + Send + Sync>).into(),
})
.map_err(|e| (e, request.transaction_id))?;
// todo: extract `build_response` function.
let mut torrent_stats: Vec<TorrentScrapeStatistics> = Vec::new();
for file in &scrape_data.files {
let swarm_metadata = file.1;
#[allow(clippy::cast_possible_truncation)]
let scrape_entry = {
TorrentScrapeStatistics {
seeders: NumberOfPeers(I32::new(i64::from(swarm_metadata.complete) as i32)),
completed: NumberOfDownloads(I32::new(i64::from(swarm_metadata.downloaded) as i32)),
leechers: NumberOfPeers(I32::new(i64::from(swarm_metadata.incomplete) as i32)),
}
};
torrent_stats.push(scrape_entry);
}
let response = ScrapeResponse {
transaction_id: request.transaction_id,
torrent_stats,
};
Ok(Response::from(response))
}
Metadata
Metadata
Assignees
Labels
Code Cleanup / RefactoringTidying and Making NeatTidying and Making Neat