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 { 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 { 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 { /// Connector used for plain HTTP requests. http: HC, /// Connector used for HTTPS requests. https: HSC, } impl ConnectorBackedDownloader { #[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, } 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 { self.inner.start_http_transfer(task) } fn start_ftp_transfer( &self, _config: &FtpConfigModel, _request: &FtpRequestModel, ) -> Result { 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 { 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 Downloader for ConnectorBackedDownloader where HC: HttpConnector, HSC: HttpsConnector, { fn start_http_transfer( &self, task: &HttpTransferTaskModel, ) -> Result { 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 { 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 { 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) }