use std::fmt::Debug;
use http::Request;
use http::Response;
use http::header;
use http::header::IF_MATCH;
use http::header::IF_MODIFIED_SINCE;
use http::header::IF_NONE_MATCH;
use http::header::IF_UNMODIFIED_SINCE;
use opendal_core::raw::*;
use opendal_core::*;
pub struct HttpCore {
pub info: ServiceInfo,
pub capability: Capability,
pub endpoint: String,
pub root: String,
pub authorization: Option<String>,
}
impl Debug for HttpCore {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HttpCore")
.field("endpoint", &self.endpoint)
.field("root", &self.root)
.finish_non_exhaustive()
}
}
impl HttpCore {
pub fn has_authorization(&self) -> bool {
self.authorization.is_some()
}
pub fn http_get_request(
&self,
path: &str,
range: BytesRange,
args: &OpRead,
) -> Result<Request<Buffer>> {
let p = build_rooted_abs_path(&self.root, path);
let url = format!("{}{}", self.endpoint, percent_encode_path(&p));
let mut req = Request::get(&url);
if let Some(if_match) = args.if_match() {
req = req.header(IF_MATCH, if_match);
}
if let Some(if_none_match) = args.if_none_match() {
req = req.header(IF_NONE_MATCH, if_none_match);
}
if let Some(if_modified_since) = args.if_modified_since() {
req = req.header(IF_MODIFIED_SINCE, if_modified_since.format_http_date());
}
if let Some(if_unmodified_since) = args.if_unmodified_since() {
req = req.header(IF_UNMODIFIED_SINCE, if_unmodified_since.format_http_date());
}
if let Some(auth) = &self.authorization {
req = req.header(header::AUTHORIZATION, auth.clone())
}
if !range.is_full() {
req = req.header(header::RANGE, range.to_header());
}
let req = req
.extension(Operation::Read)
.extension(ServiceOperation("Get"));
req.body(Buffer::new()).map_err(new_request_build_error)
}
pub async fn http_get(
&self,
ctx: &OperationContext,
path: &str,
range: BytesRange,
args: &OpRead,
) -> Result<Response<HttpBody>> {
let req = self.http_get_request(path, range, args)?;
ctx.http_transport().fetch(req).await
}
pub fn http_head_request(&self, path: &str, args: &OpStat) -> Result<Request<Buffer>> {
let p = build_rooted_abs_path(&self.root, path);
let url = format!("{}{}", self.endpoint, percent_encode_path(&p));
let mut req = Request::head(&url);
if let Some(if_match) = args.if_match() {
req = req.header(IF_MATCH, if_match);
}
if let Some(if_none_match) = args.if_none_match() {
req = req.header(IF_NONE_MATCH, if_none_match);
}
if let Some(if_modified_since) = args.if_modified_since() {
req = req.header(IF_MODIFIED_SINCE, if_modified_since.format_http_date());
}
if let Some(if_unmodified_since) = args.if_unmodified_since() {
req = req.header(IF_UNMODIFIED_SINCE, if_unmodified_since.format_http_date());
}
if let Some(auth) = &self.authorization {
req = req.header(header::AUTHORIZATION, auth.clone())
}
let req = req
.extension(Operation::Stat)
.extension(ServiceOperation("Head"));
req.body(Buffer::new()).map_err(new_request_build_error)
}
pub async fn http_head(
&self,
ctx: &OperationContext,
path: &str,
args: &OpStat,
) -> Result<Response<Buffer>> {
let req = self.http_head_request(path, args)?;
ctx.http_transport().send(req).await
}
}
mod error {
use http::Response;
use http::StatusCode;
use opendal_core::raw::*;
use opendal_core::*;
pub(crate) fn parse_error(resp: Response<Buffer>) -> Error {
let (parts, body) = resp.into_parts();
let bs = body.to_bytes();
let (kind, retryable) = match parts.status {
StatusCode::NOT_FOUND => (ErrorKind::NotFound, false),
StatusCode::FORBIDDEN => (ErrorKind::PermissionDenied, false),
StatusCode::PRECONDITION_FAILED | StatusCode::NOT_MODIFIED => {
(ErrorKind::ConditionNotMatch, false)
}
StatusCode::INTERNAL_SERVER_ERROR
| StatusCode::BAD_GATEWAY
| StatusCode::SERVICE_UNAVAILABLE
| StatusCode::GATEWAY_TIMEOUT => (ErrorKind::Unexpected, true),
_ => (ErrorKind::Unexpected, false),
};
let message = String::from_utf8_lossy(&bs);
let mut err = Error::new(kind, message);
err = with_error_response_context(err, parts);
if retryable {
err = err.set_temporary();
}
err
}
}
pub(super) use error::*;