-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge #185: Axum HTTP tracker:
announce
request in public mode
da638d6 docs(http): fix extractor docs (Jose Celano) 30918da refactor(http): [#184] move extractor to extractor mod (Jose Celano) 99dbbe4 refactor(http): [#184] extract announce service in Axum tracker (Jose Celano) 02e2516 feat(http): [#184] Axum extractor for peer IP (Jose Celano) 74ed592 feat(http): [#184] added optional params to announce req in Axum implementation (Jose Celano) b1612f6 test(http): improve tests (Jose Celano) 3eb7475 feat(http): [#184] normal (non-compact) announce response in axum tracker (Jose Celano) 42bd313 feat: [#184] calculate remote client ip depending on whether the tracker is running on reverse proxy or not (Jose Celano) 8318057 feat: [#184] add dependency: axum-client-ip (Jose Celano) f327dcf fix(http): [#184] bencoded error responses for announce request (Jose Celano) d0c8eb0 refactor(http): reorganize mods (Jose Celano) 03024e2 refactor(http): extract function to get client IP on reverse proxy (Jose Celano) Pull request description: It implements the `announce` request handler in the Axum HTTP tracker for `public` mode. - [x] Normal (non-compact) response with only mandatory params - [x] Optional params for `announce` request - [x] Return bencoded error when remote client IP cannot be obtained from the `X-Forwarded-For` header on reverse proxy mode. See commented tests. - [x] Refactor: extract service for handler body. Out of this PR scope: - [ ] Capture unexpected handler errors and convert them into bencoded generic HTTP tracker response errors. Moved to [parent issue](#160). - [ ] Compact response. **UPDATE**: 16/02/2023 Top commit has no ACKs. Tree-SHA512: 76f77ed87f64ff6a488090e9013d92cbc516135a770206408a38bcba1c57ceb01154cd87b7169ed01ee12791c12d5e991ab59c4e53ba7c164a146292d6a94677
- Loading branch information
Showing
33 changed files
with
1,504 additions
and
379 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
45 changes: 45 additions & 0 deletions
45
src/http/axum_implementation/extractors/announce_request.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
use std::panic::Location; | ||
|
||
use axum::async_trait; | ||
use axum::extract::FromRequestParts; | ||
use axum::http::request::Parts; | ||
use axum::response::{IntoResponse, Response}; | ||
|
||
use crate::http::axum_implementation::query::Query; | ||
use crate::http::axum_implementation::requests::announce::{Announce, ParseAnnounceQueryError}; | ||
use crate::http::axum_implementation::responses; | ||
|
||
pub struct ExtractRequest(pub Announce); | ||
|
||
#[async_trait] | ||
impl<S> FromRequestParts<S> for ExtractRequest | ||
where | ||
S: Send + Sync, | ||
{ | ||
type Rejection = Response; | ||
|
||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> { | ||
let raw_query = parts.uri.query(); | ||
|
||
if raw_query.is_none() { | ||
return Err(responses::error::Error::from(ParseAnnounceQueryError::MissingParams { | ||
location: Location::caller(), | ||
}) | ||
.into_response()); | ||
} | ||
|
||
let query = raw_query.unwrap().parse::<Query>(); | ||
|
||
if let Err(error) = query { | ||
return Err(responses::error::Error::from(error).into_response()); | ||
} | ||
|
||
let announce_request = Announce::try_from(query.unwrap()); | ||
|
||
if let Err(error) = announce_request { | ||
return Err(responses::error::Error::from(error).into_response()); | ||
} | ||
|
||
Ok(ExtractRequest(announce_request.unwrap())) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pub mod announce_request; | ||
pub mod peer_ip; | ||
pub mod remote_client_ip; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
use std::net::IpAddr; | ||
use std::panic::Location; | ||
|
||
use axum::response::{IntoResponse, Response}; | ||
use thiserror::Error; | ||
|
||
use super::remote_client_ip::RemoteClientIp; | ||
use crate::http::axum_implementation::responses; | ||
|
||
#[derive(Error, Debug)] | ||
pub enum ResolutionError { | ||
#[error("missing the right most X-Forwarded-For IP (mandatory on reverse proxy tracker configuration) in {location}")] | ||
MissingRightMostXForwardedForIp { location: &'static Location<'static> }, | ||
#[error("cannot get the client IP from the connection info in {location}")] | ||
MissingClientIp { location: &'static Location<'static> }, | ||
} | ||
|
||
impl From<ResolutionError> for responses::error::Error { | ||
fn from(err: ResolutionError) -> Self { | ||
responses::error::Error { | ||
failure_reason: format!("{err}"), | ||
} | ||
} | ||
} | ||
|
||
/// It resolves the peer IP. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Will return an error if the peer IP cannot be obtained according to the configuration. | ||
/// For example, if the IP is extracted from an HTTP header which is missing in the request. | ||
pub fn assign_ip_address_to_peer(on_reverse_proxy: bool, remote_client_ip: &RemoteClientIp) -> Result<IpAddr, Response> { | ||
if on_reverse_proxy { | ||
if let Some(ip) = remote_client_ip.right_most_x_forwarded_for { | ||
Ok(ip) | ||
} else { | ||
Err( | ||
responses::error::Error::from(ResolutionError::MissingRightMostXForwardedForIp { | ||
location: Location::caller(), | ||
}) | ||
.into_response(), | ||
) | ||
} | ||
} else if let Some(ip) = remote_client_ip.connection_info_ip { | ||
Ok(ip) | ||
} else { | ||
Err(responses::error::Error::from(ResolutionError::MissingClientIp { | ||
location: Location::caller(), | ||
}) | ||
.into_response()) | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
src/http/axum_implementation/extractors/remote_client_ip.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
use std::net::{IpAddr, SocketAddr}; | ||
|
||
use axum::async_trait; | ||
use axum::extract::{ConnectInfo, FromRequestParts}; | ||
use axum::http::request::Parts; | ||
use axum::response::Response; | ||
use axum_client_ip::RightmostXForwardedFor; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
/// Given this request chain: | ||
/// | ||
/// client <-> http proxy 1 <-> http proxy 2 <-> server | ||
/// ip: 126.0.0.1 ip: 126.0.0.2 ip: 126.0.0.3 ip: 126.0.0.4 | ||
/// X-Forwarded-For: 126.0.0.1 X-Forwarded-For: 126.0.0.1,126.0.0.2 | ||
/// | ||
/// This extractor extracts these values from the HTTP headers and connection info. | ||
/// | ||
/// `right_most_x_forwarded_for` = 126.0.0.2 | ||
/// `connection_info_ip` = 126.0.0.3 | ||
/// | ||
/// More info about inner extractors :<https://github.com/imbolc/axum-client-ip> | ||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] | ||
pub struct RemoteClientIp { | ||
pub right_most_x_forwarded_for: Option<IpAddr>, | ||
pub connection_info_ip: Option<IpAddr>, | ||
} | ||
|
||
#[async_trait] | ||
impl<S> FromRequestParts<S> for RemoteClientIp | ||
where | ||
S: Send + Sync, | ||
{ | ||
type Rejection = Response; | ||
|
||
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> { | ||
let right_most_x_forwarded_for = match RightmostXForwardedFor::from_request_parts(parts, state).await { | ||
Ok(right_most_x_forwarded_for) => Some(right_most_x_forwarded_for.0), | ||
Err(_) => None, | ||
}; | ||
|
||
let connection_info_ip = match ConnectInfo::<SocketAddr>::from_request_parts(parts, state).await { | ||
Ok(connection_info_socket_addr) => Some(connection_info_socket_addr.0.ip()), | ||
Err(_) => None, | ||
}; | ||
|
||
Ok(RemoteClientIp { | ||
right_most_x_forwarded_for, | ||
connection_info_ip, | ||
}) | ||
} | ||
} |
Oops, something went wrong.