pub mod multipart;
pub mod request_signer;
pub mod url_signer;
pub use url_signer::build_signer;
use crate::error::ProxyError;
use crate::maybe_send::{MaybeSend, MaybeSync};
use crate::route_handler::ForwardRequest;
use crate::types::{BackendType, BucketConfig};
use bytes::Bytes;
use http::HeaderMap;
use object_store::aws::AmazonS3Builder;
use object_store::list::PaginatedListStore;
use object_store::signer::Signer;
use std::future::Future;
use std::sync::Arc;
#[cfg(feature = "azure")]
use object_store::azure::MicrosoftAzureBuilder;
#[cfg(feature = "gcp")]
use object_store::gcp::GoogleCloudStorageBuilder;
pub trait ProxyBackend: Clone + MaybeSend + MaybeSync + 'static {
type ResponseBody: MaybeSend + 'static;
type Body: MaybeSend + 'static;
fn forward(
&self,
request: ForwardRequest,
body: Self::Body,
) -> impl Future<Output = Result<ForwardResponse<Self::ResponseBody>, ProxyError>> + MaybeSend;
fn create_paginated_store(
&self,
config: &BucketConfig,
) -> Result<Box<dyn PaginatedListStore>, ProxyError>;
fn create_signer(&self, config: &BucketConfig) -> Result<Arc<dyn Signer>, ProxyError>;
fn send_raw(
&self,
method: http::Method,
url: String,
headers: HeaderMap,
body: Bytes,
) -> impl Future<Output = Result<RawResponse, ProxyError>> + MaybeSend;
}
pub struct RawResponse {
pub status: u16,
pub headers: HeaderMap,
pub body: Bytes,
}
pub struct ForwardResponse<S> {
pub status: u16,
pub headers: HeaderMap,
pub body: S,
pub content_length: Option<u64>,
}
pub enum StoreBuilder {
S3(AmazonS3Builder),
#[cfg(feature = "azure")]
Azure(MicrosoftAzureBuilder),
#[cfg(feature = "gcp")]
Gcs(GoogleCloudStorageBuilder),
}
impl StoreBuilder {
pub fn build(self) -> Result<Box<dyn PaginatedListStore>, ProxyError> {
match self {
StoreBuilder::S3(b) => Ok(Box::new(b.build().map_err(|e| {
ProxyError::ConfigError(format!("failed to build S3 paginated store: {}", e))
})?)),
#[cfg(feature = "azure")]
StoreBuilder::Azure(b) => Ok(Box::new(b.build().map_err(|e| {
ProxyError::ConfigError(format!("failed to build Azure paginated store: {}", e))
})?)),
#[cfg(feature = "gcp")]
StoreBuilder::Gcs(b) => Ok(Box::new(b.build().map_err(|e| {
ProxyError::ConfigError(format!("failed to build GCS paginated store: {}", e))
})?)),
}
}
pub fn build_signer(self) -> Result<Arc<dyn Signer>, ProxyError> {
match self {
StoreBuilder::S3(b) => Ok(Arc::new(b.build().map_err(|e| {
ProxyError::ConfigError(format!("failed to build S3 signer: {}", e))
})?)),
#[cfg(feature = "azure")]
StoreBuilder::Azure(b) => Ok(Arc::new(b.build().map_err(|e| {
ProxyError::ConfigError(format!("failed to build Azure signer: {}", e))
})?)),
#[cfg(feature = "gcp")]
StoreBuilder::Gcs(b) => Ok(Arc::new(b.build().map_err(|e| {
ProxyError::ConfigError(format!("failed to build GCS signer: {}", e))
})?)),
}
}
}
pub fn create_builder(config: &BucketConfig) -> Result<StoreBuilder, ProxyError> {
let backend_type = config.parsed_backend_type().ok_or_else(|| {
ProxyError::ConfigError(format!(
"unsupported backend_type: '{}'",
config.backend_type
))
})?;
match backend_type {
BackendType::S3 => {
let mut b = AmazonS3Builder::new();
for (k, v) in &config.backend_options {
if let Ok(key) = k.parse() {
b = b.with_config(key, v);
}
}
Ok(StoreBuilder::S3(b))
}
#[cfg(feature = "azure")]
BackendType::Azure => {
let mut b = MicrosoftAzureBuilder::new();
for (k, v) in &config.backend_options {
if let Ok(key) = k.parse() {
b = b.with_config(key, v);
}
}
Ok(StoreBuilder::Azure(b))
}
#[cfg(not(feature = "azure"))]
BackendType::Azure => Err(ProxyError::ConfigError(
"Azure backend support not enabled (requires 'azure' feature)".into(),
)),
#[cfg(feature = "gcp")]
BackendType::Gcs => {
let mut b = GoogleCloudStorageBuilder::new();
for (k, v) in &config.backend_options {
if let Ok(key) = k.parse() {
b = b.with_config(key, v);
}
}
Ok(StoreBuilder::Gcs(b))
}
#[cfg(not(feature = "gcp"))]
BackendType::Gcs => Err(ProxyError::ConfigError(
"GCS backend support not enabled (requires 'gcp' feature)".into(),
)),
}
}