azure_storage/
clients.rs

1use crate::{
2    authorization::{AuthorizationPolicy, StorageCredentialsInner},
3    shared_access_signature::account_sas::{
4        AccountSasPermissions, AccountSasResource, AccountSasResourceType,
5        AccountSharedAccessSignature,
6    },
7    StorageCredentials,
8};
9use azure_core::{
10    date,
11    error::{Error, ErrorKind},
12    headers::*,
13    Body, ClientOptions, Method, Pipeline, Request, Url,
14};
15use std::{ops::Deref, sync::Arc};
16use time::OffsetDateTime;
17
18/// The well-known account used by Azurite and the legacy Azure Storage Emulator.
19/// <https://docs.microsoft.com/azure/storage/common/storage-use-azurite#well-known-storage-account-and-key>
20pub const EMULATOR_ACCOUNT: &str = "devstoreaccount1";
21
22/// The well-known account key used by Azurite and the legacy Azure Storage Emulator.
23/// <https://docs.microsoft.com/azure/storage/common/storage-use-azurite#well-known-storage-account-and-key>
24pub const EMULATOR_ACCOUNT_KEY: &str =
25    "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
26
27const AZURE_VERSION: HeaderValue = HeaderValue::from_static("2022-11-02");
28
29#[derive(Debug, Clone, Copy)]
30pub enum ServiceType {
31    Blob,
32    Queue,
33    // File,
34    Table,
35    DataLake,
36}
37
38impl ServiceType {
39    pub fn subdomain(&self) -> &str {
40        match self {
41            ServiceType::Blob => "blob",
42            ServiceType::Queue => "queue",
43            ServiceType::Table => "table",
44            ServiceType::DataLake => "dfs",
45        }
46    }
47}
48
49pub async fn shared_access_signature(
50    storage_credentials: &StorageCredentials,
51    resource: AccountSasResource,
52    resource_type: AccountSasResourceType,
53    expiry: OffsetDateTime,
54    permissions: AccountSasPermissions,
55) -> Result<AccountSharedAccessSignature, Error> {
56    let creds = storage_credentials.0.read().await;
57    let StorageCredentialsInner::Key(account, key) = creds.deref() else {
58        return Err(Error::message(
59            ErrorKind::Credential,
60            "Shared access signature generation - SAS can be generated with access_key clients",
61        ));
62    };
63
64    Ok(AccountSharedAccessSignature::new(
65        account.clone(),
66        key.clone(),
67        resource,
68        resource_type,
69        expiry,
70        permissions,
71    ))
72}
73
74pub fn finalize_request(
75    url: Url,
76    method: Method,
77    headers: Headers,
78    request_body: Option<Body>,
79) -> Result<Request, Error> {
80    let dt = OffsetDateTime::now_utc();
81    let time = date::to_rfc1123(&dt);
82    let mut request = Request::new(url, method);
83    for (k, v) in headers {
84        request.insert_header(k, v);
85    }
86    // let's add content length to avoid "chunking" errors.
87    match request_body {
88        Some(ref b) => request.insert_header(CONTENT_LENGTH, b.len().to_string()),
89        None => request.insert_header(CONTENT_LENGTH, "0"),
90    };
91    request.insert_header(MS_DATE, time);
92    request.insert_header(VERSION, AZURE_VERSION);
93    if let Some(request_body) = request_body {
94        request.set_body(request_body);
95    } else {
96        request.set_body(azure_core::EMPTY_BODY);
97    };
98    Ok(request)
99}
100
101/// Create a Pipeline from `ClientOptions`
102pub fn new_pipeline_from_options(
103    options: ClientOptions,
104    credentials: StorageCredentials,
105) -> Pipeline {
106    let auth_policy: Arc<dyn azure_core::Policy> = Arc::new(AuthorizationPolicy::new(credentials));
107
108    // The `AuthorizationPolicy` must be the **last** retry policy.
109    // Policies can change the url and/or the headers, and the `AuthorizationPolicy`
110    // must be able to inspect them or the resulting token will be invalid.
111    let per_retry_policies = vec![
112        Arc::new(options.timeout.clone()) as Arc<dyn azure_core::Policy>,
113        auth_policy,
114    ];
115
116    Pipeline::new(
117        option_env!("CARGO_PKG_NAME"),
118        option_env!("CARGO_PKG_VERSION"),
119        options,
120        Vec::new(),
121        per_retry_policies,
122    )
123}