iota_storage/object_store/http/
gcs.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::{NON_ALPHANUMERIC, percent_encode, utf8_percent_encode};
12use reqwest::{Client, ClientBuilder};
13
14use crate::object_store::{
15    ObjectStoreGetExt,
16    http::{DEFAULT_USER_AGENT, get},
17};
18
19#[derive(Debug)]
20struct GoogleCloudStorageClient {
21    client: Client,
22    bucket_name_encoded: String,
23}
24
25impl GoogleCloudStorageClient {
26    pub fn new(bucket: &str) -> Result<Self> {
27        let mut builder = ClientBuilder::new().pool_idle_timeout(None);
28        builder = builder.user_agent(DEFAULT_USER_AGENT);
29        let client = builder.https_only(false).build()?;
30        let bucket_name_encoded = percent_encode(bucket.as_bytes(), NON_ALPHANUMERIC).to_string();
31
32        Ok(Self {
33            client,
34            bucket_name_encoded,
35        })
36    }
37
38    async fn get(&self, path: &Path) -> Result<GetResult> {
39        let url = self.object_url(path);
40        get(&url, "gcs", path, &self.client).await
41    }
42
43    fn object_url(&self, path: &Path) -> String {
44        let encoded = utf8_percent_encode(path.as_ref(), NON_ALPHANUMERIC);
45        format!(
46            "https://storage.googleapis.com/{}/{}",
47            self.bucket_name_encoded, encoded
48        )
49    }
50}
51
52/// Interface for [Google Cloud Storage](https://cloud.google.com/storage/).
53#[derive(Debug)]
54pub struct GoogleCloudStorage {
55    client: Arc<GoogleCloudStorageClient>,
56}
57
58impl GoogleCloudStorage {
59    pub fn new(bucket: &str) -> Result<Self> {
60        let gcs_client = GoogleCloudStorageClient::new(bucket)?;
61        Ok(GoogleCloudStorage {
62            client: Arc::new(gcs_client),
63        })
64    }
65}
66
67impl fmt::Display for GoogleCloudStorage {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        write!(f, "gcs:{}", self.client.bucket_name_encoded)
70    }
71}
72
73#[async_trait]
74impl ObjectStoreGetExt for GoogleCloudStorage {
75    async fn get_bytes(&self, location: &Path) -> Result<Bytes> {
76        let result = self.client.get(location).await?;
77        let bytes = result.bytes().await?;
78        Ok(bytes)
79    }
80}