iota_storage/object_store/http/
s3.rs1use std::{fmt, sync::Arc};
6
7use anyhow::Result;
8use async_trait::async_trait;
9use bytes::Bytes;
10use object_store::{GetResult, path::Path};
11use percent_encoding::{PercentEncode, utf8_percent_encode};
12use reqwest::{Client, ClientBuilder};
13
14use crate::object_store::{
15 ObjectStoreGetExt,
16 http::{DEFAULT_USER_AGENT, STRICT_PATH_ENCODE_SET, get},
17};
18
19#[derive(Debug)]
20pub(crate) struct S3Client {
21 endpoint: String,
22 client: Client,
23}
24
25impl S3Client {
26 pub fn new(endpoint: &str) -> Result<Self> {
27 let mut builder = ClientBuilder::new();
28 builder = builder
29 .user_agent(DEFAULT_USER_AGENT)
30 .pool_idle_timeout(None);
31 let client = builder.https_only(false).build()?;
32
33 Ok(Self {
34 endpoint: endpoint.to_string(),
35 client,
36 })
37 }
38 async fn get(&self, location: &Path) -> Result<GetResult> {
39 let url = self.path_url(location);
40 get(&url, "s3", location, &self.client).await
41 }
42 fn path_url(&self, path: &Path) -> String {
43 format!("{}/{}", self.endpoint, Self::encode_path(path))
44 }
45 fn encode_path(path: &Path) -> PercentEncode<'_> {
46 utf8_percent_encode(path.as_ref(), &STRICT_PATH_ENCODE_SET)
47 }
48}
49
50#[derive(Debug)]
52pub struct AmazonS3 {
53 client: Arc<S3Client>,
54}
55
56impl AmazonS3 {
57 pub fn new(endpoint: &str) -> Result<Self> {
58 let s3_client = S3Client::new(endpoint)?;
59 Ok(AmazonS3 {
60 client: Arc::new(s3_client),
61 })
62 }
63}
64
65impl fmt::Display for AmazonS3 {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 write!(f, "s3:{}", self.client.endpoint)
68 }
69}
70
71#[async_trait]
72impl ObjectStoreGetExt for AmazonS3 {
73 async fn get_bytes(&self, location: &Path) -> Result<Bytes> {
74 let result = self.client.get(location).await?;
75 let bytes = result.bytes().await?;
76 Ok(bytes)
77 }
78}