Skip to main content

rusty_cat/azure-blob-direct/
download.rs

1use reqwest::header::{HeaderValue, ACCEPT, RANGE};
2
3use super::constants::DEFAULT_RANGE_ACCEPT;
4use super::signing::{apply_signed_headers, header_value};
5use crate::{BreakpointDownload, DownloadHeadCtx, DownloadRangeGetCtx, MeowError, TransferTask};
6
7/// Azure Blob direct range download protocol using SharedKey authentication.
8#[derive(Clone)]
9pub struct AzureBlobDirectDownload {
10    account_name: String,
11    account_key_b64: String,
12}
13
14impl AzureBlobDirectDownload {
15    pub fn new(account_name: impl Into<String>, account_key_b64: impl Into<String>) -> Self {
16        Self {
17            account_name: account_name.into(),
18            account_key_b64: account_key_b64.into(),
19        }
20    }
21}
22
23impl BreakpointDownload for AzureBlobDirectDownload {
24    fn resume_identity(&self, task: &TransferTask) -> Result<Option<Vec<u8>>, MeowError> {
25        let mut headers = task.headers().clone();
26        if !headers.contains_key(ACCEPT) {
27            headers.insert(ACCEPT, HeaderValue::from_static(DEFAULT_RANGE_ACCEPT));
28        }
29        headers.insert(
30            super::constants::HEADER_MS_VERSION,
31            HeaderValue::from_static(super::constants::MS_VERSION),
32        );
33        let mut context = crate::http_breakpoint::canonical_resume_headers(headers);
34        context.extend_from_slice(b"rusty-cat/azure-blob-direct/v1\0");
35        crate::http_breakpoint::append_resume_identity_field(
36            &mut context,
37            self.account_name.as_bytes(),
38        );
39        Ok(Some(context))
40    }
41
42    fn merge_head_headers(&self, ctx: DownloadHeadCtx<'_>) -> Result<(), MeowError> {
43        apply_signed_headers(
44            ctx.task.url(),
45            "HEAD",
46            ctx.base,
47            self.account_name.as_str(),
48            self.account_key_b64.as_str(),
49        )
50    }
51
52    fn merge_range_get_headers(&self, ctx: DownloadRangeGetCtx<'_>) -> Result<(), MeowError> {
53        ctx.base.insert(RANGE, header_value(ctx.range_value)?);
54        if !ctx.base.contains_key(ACCEPT) {
55            ctx.base
56                .insert(ACCEPT, HeaderValue::from_static(DEFAULT_RANGE_ACCEPT));
57        }
58        apply_signed_headers(
59            ctx.task.url(),
60            "GET",
61            ctx.base,
62            self.account_name.as_str(),
63            self.account_key_b64.as_str(),
64        )
65    }
66}