aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'crates/recluse/src/proxy.rs')
-rw-r--r--crates/recluse/src/proxy.rs78+78 −0
1 files changed, 78 insertions, 0 deletions
diff --git a/crates/recluse/src/proxy.rs b/crates/recluse/src/proxy.rs
new file mode 100644
--- /dev/null
+++ b/crates/recluse/src/proxy.rs
@@ -0,0 +1,78 @@
+// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+use bytes::Bytes;
+use http_body_util::{BodyExt, Empty};
+use hyper::{Request, http};
+use hyper_tls::HttpsConnector;
+use hyper_util::client::legacy::{Client, connect::HttpConnector};
+use hyper_util::rt::TokioExecutor;
+use tower::ServiceExt;
+use tower_http::follow_redirect::FollowRedirect;
+use url::Url;
+
+use repos::{BackendError, BackendNetwork};
+
+#[derive(Clone)]
+pub struct DownloadRequest {
+ pub url: Url,
+}
+
+#[derive(Clone)]
+pub struct File {
+ pub bytes: Bytes,
+}
+
+pub struct ProxyService {
+ client: FollowRedirect<Client<HttpsConnector<HttpConnector>, Empty<Bytes>>>,
+}
+
+impl ProxyService {
+ pub fn new() -> Self {
+ let https = HttpsConnector::new();
+ let client = Client::builder(TokioExecutor::new()).build(https);
+ Self {
+ client: FollowRedirect::new(client),
+ }
+ }
+
+ pub async fn fetch(&self, request: DownloadRequest) -> Result<File, http::StatusCode> {
+ let request = Request::builder()
+ .method(http::Method::GET)
+ .uri(request.url.as_str())
+ .header(http::header::USER_AGENT, "recluse/0.1")
+ .body(Empty::<Bytes>::new())
+ .unwrap();
+
+ let response = self
+ .client
+ .clone()
+ .oneshot(request)
+ .await
+ .map_err(|_| http::StatusCode::GATEWAY_TIMEOUT)?;
+
+ let (parts, body) = response.into_parts();
+ let status = parts.status;
+ if !status.is_success() {
+ return Err(status);
+ }
+
+ let bytes = body
+ .collect()
+ .await
+ .map_err(|_| http::StatusCode::GATEWAY_TIMEOUT)?
+ .to_bytes();
+
+ Ok(File { bytes })
+ }
+}
+
+#[async_trait::async_trait]
+impl BackendNetwork for ProxyService {
+ async fn http_get(&self, url: &url::Url) -> Result<bytes::Bytes, BackendError> {
+ self.fetch(DownloadRequest { url: url.clone() })
+ .await
+ .map(|f| f.bytes)
+ .map_err(|e| BackendError::Network(e.to_string()))
+ }
+}