194 lines
6.2 KiB
Rust
194 lines
6.2 KiB
Rust
use super::{
|
|
Downloader, FtpConfigModel, FtpRequestModel, FtpResponseModel, HttpConnector, HttpRequestModel,
|
|
HttpResponseModel, HttpTransferTaskModel, HttpsConnector, SftpConfigModel, SftpRequestModel,
|
|
SftpResponseModel, TransportError,
|
|
};
|
|
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
/// Placeholder HTTP connector that always reports that no connector is configured.
|
|
pub struct NullHttpConnector;
|
|
|
|
impl HttpConnector for NullHttpConnector {
|
|
fn connect_http(
|
|
&self,
|
|
request: &HttpRequestModel,
|
|
) -> Result<HttpResponseModel, TransportError> {
|
|
Err(TransportError {
|
|
kind: crate::transport::TransportErrorKind::NotConnected,
|
|
message: format!("no http connector configured for {}", request.url),
|
|
source: None,
|
|
context: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
/// Placeholder HTTPS connector that always reports that no connector is configured.
|
|
pub struct NullHttpsConnector;
|
|
|
|
impl HttpsConnector for NullHttpsConnector {
|
|
fn connect_https(
|
|
&self,
|
|
request: &HttpRequestModel,
|
|
) -> Result<HttpResponseModel, TransportError> {
|
|
Err(TransportError {
|
|
kind: crate::transport::TransportErrorKind::NotConnected,
|
|
message: format!("no https connector configured for {}", request.url),
|
|
source: None,
|
|
context: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
/// Downloader wrapper that routes HTTP and HTTPS requests through connector implementations.
|
|
pub struct ConnectorBackedDownloader<HC, HSC> {
|
|
/// Connector used for plain HTTP requests.
|
|
http: HC,
|
|
/// Connector used for HTTPS requests.
|
|
https: HSC,
|
|
}
|
|
|
|
impl<HC, HSC> ConnectorBackedDownloader<HC, HSC> {
|
|
#[must_use]
|
|
/// Builds a downloader from plain HTTP and HTTPS connector implementations.
|
|
pub const fn new(http: HC, https: HSC) -> Self {
|
|
Self { http, https }
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
/// Downloader that only supports HTTP(S) transfers.
|
|
pub struct HttpOnlyDownloader {
|
|
/// Connector-backed executor used by the HTTP-only wrapper.
|
|
inner: ConnectorBackedDownloader<NullHttpConnector, NullHttpsConnector>,
|
|
}
|
|
|
|
impl HttpOnlyDownloader {
|
|
#[must_use]
|
|
/// Builds the HTTP-only downloader.
|
|
pub fn new() -> Self {
|
|
Self {
|
|
inner: ConnectorBackedDownloader::new(NullHttpConnector, NullHttpsConnector),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Downloader for HttpOnlyDownloader {
|
|
fn start_http_transfer(
|
|
&self,
|
|
task: &HttpTransferTaskModel,
|
|
) -> Result<HttpResponseModel, TransportError> {
|
|
self.inner.start_http_transfer(task)
|
|
}
|
|
|
|
fn start_ftp_transfer(
|
|
&self,
|
|
_config: &FtpConfigModel,
|
|
_request: &FtpRequestModel,
|
|
) -> Result<FtpResponseModel, TransportError> {
|
|
Err(TransportError {
|
|
kind: crate::transport::TransportErrorKind::UnsupportedScheme,
|
|
message: "ftp transfer is not supported by the HTTP-only downloader".to_owned(),
|
|
source: None,
|
|
context: None,
|
|
})
|
|
}
|
|
|
|
fn start_sftp_transfer(
|
|
&self,
|
|
_config: &SftpConfigModel,
|
|
_request: &SftpRequestModel,
|
|
) -> Result<SftpResponseModel, TransportError> {
|
|
Err(TransportError {
|
|
kind: crate::transport::TransportErrorKind::UnsupportedScheme,
|
|
message: "sftp transfer is not supported by the HTTP-only downloader".to_owned(),
|
|
source: None,
|
|
context: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<HC, HSC> Downloader for ConnectorBackedDownloader<HC, HSC>
|
|
where
|
|
HC: HttpConnector,
|
|
HSC: HttpsConnector,
|
|
{
|
|
fn start_http_transfer(
|
|
&self,
|
|
task: &HttpTransferTaskModel,
|
|
) -> Result<HttpResponseModel, TransportError> {
|
|
match request_scheme(&task.request.url) {
|
|
Some("http") => self
|
|
.http
|
|
.connect_http(&task.request)
|
|
.map(|response| normalize_http_response_for_execution(task, response)),
|
|
Some("https") => self
|
|
.https
|
|
.connect_https(&task.request)
|
|
.map(|response| normalize_http_response_for_execution(task, response)),
|
|
Some(scheme) => Err(TransportError {
|
|
kind: crate::transport::TransportErrorKind::UnsupportedScheme,
|
|
message: format!("unsupported http transfer scheme: {scheme}"),
|
|
source: None,
|
|
context: None,
|
|
}),
|
|
None => Err(TransportError {
|
|
kind: crate::transport::TransportErrorKind::ProtocolViolation,
|
|
message: format!("request url has no scheme: {}", task.request.url),
|
|
source: None,
|
|
context: None,
|
|
}),
|
|
}
|
|
}
|
|
|
|
fn start_ftp_transfer(
|
|
&self,
|
|
_config: &FtpConfigModel,
|
|
_request: &FtpRequestModel,
|
|
) -> Result<FtpResponseModel, TransportError> {
|
|
Err(TransportError {
|
|
kind: crate::transport::TransportErrorKind::UnsupportedScheme,
|
|
message: "ftp transfer is not implemented".to_owned(),
|
|
source: None,
|
|
context: None,
|
|
})
|
|
}
|
|
|
|
fn start_sftp_transfer(
|
|
&self,
|
|
_config: &SftpConfigModel,
|
|
_request: &SftpRequestModel,
|
|
) -> Result<SftpResponseModel, TransportError> {
|
|
Err(TransportError {
|
|
kind: crate::transport::TransportErrorKind::UnsupportedScheme,
|
|
message: "sftp transfer is not implemented".to_owned(),
|
|
source: None,
|
|
context: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Injects transfer-task metadata such as checksum hooks into one HTTP response model.
|
|
pub(super) fn normalize_http_response_for_execution(
|
|
task: &HttpTransferTaskModel,
|
|
mut response: HttpResponseModel,
|
|
) -> HttpResponseModel {
|
|
if response.checksum.is_none()
|
|
&& let Some(checksum) = task
|
|
.checksum_hook
|
|
.as_ref()
|
|
.filter(|checksum| checksum.enabled)
|
|
.map(|checksum| checksum.spec.clone())
|
|
{
|
|
response.checksum = Some(checksum);
|
|
}
|
|
|
|
response
|
|
}
|
|
|
|
/// Extracts the lowercase URI scheme prefix from one request URL when present.
|
|
pub(super) fn request_scheme(url: &str) -> Option<&str> {
|
|
url.split_once("://").map(|(scheme, _)| scheme)
|
|
}
|