iota_storage/object_store/http/
s3.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use 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/// Interface for [Amazon S3](https://aws.amazon.com/s3/).
51#[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}