rusty_cat/aliyun-oss-direct/
download.rs1use reqwest::header::{HeaderMap, ACCEPT, RANGE};
2use reqwest::Url;
3
4use super::constants::DEFAULT_RANGE_ACCEPT;
5use super::signing::{header_value, param_error, signed_headers};
6use crate::{BreakpointDownload, DownloadHeadCtx, DownloadRangeGetCtx, MeowError, TransferTask};
7
8#[derive(Clone)]
10pub struct AliOssDirectDownload {
11 bucket: String,
12 access_key_id: String,
13 access_key_secret: String,
14 region: String,
15}
16
17impl AliOssDirectDownload {
18 pub fn new(
19 bucket: impl Into<String>,
20 access_key_id: impl Into<String>,
21 access_key_secret: impl Into<String>,
22 region: impl Into<String>,
23 ) -> Self {
24 Self {
25 bucket: bucket.into(),
26 access_key_id: access_key_id.into(),
27 access_key_secret: access_key_secret.into(),
28 region: region.into(),
29 }
30 }
31
32 fn object_canonical_uri_from_task_url(&self, task: &TransferTask) -> Result<String, MeowError> {
33 let url = Url::parse(task.url()).map_err(param_error)?;
34 Ok(format!("/{}{}", self.bucket, url.path()))
35 }
36
37 fn apply_signed_headers(
38 &self,
39 task: &TransferTask,
40 method: &str,
41 base: &mut HeaderMap,
42 ) -> Result<(), MeowError> {
43 let headers = signed_headers(
44 method,
45 self.object_canonical_uri_from_task_url(task)?.as_str(),
46 None,
47 &[],
48 None,
49 self.access_key_id.as_str(),
50 self.access_key_secret.as_str(),
51 self.region.as_str(),
52 )?;
53 for (k, v) in headers {
54 if let Some(k) = k {
55 base.insert(k, v);
56 }
57 }
58 Ok(())
59 }
60}
61
62impl BreakpointDownload for AliOssDirectDownload {
63 fn resume_identity(&self, task: &TransferTask) -> Result<Option<Vec<u8>>, MeowError> {
64 let mut headers = task.headers().clone();
65 if !headers.contains_key(ACCEPT) {
66 headers.insert(ACCEPT, header_value(DEFAULT_RANGE_ACCEPT)?);
67 }
68 let mut context = crate::http_breakpoint::canonical_resume_headers(headers);
69 context.extend_from_slice(b"rusty-cat/aliyun-oss-direct/v1\0");
70 crate::http_breakpoint::append_resume_identity_field(&mut context, self.bucket.as_bytes());
71 crate::http_breakpoint::append_resume_identity_field(
72 &mut context,
73 self.access_key_id.as_bytes(),
74 );
75 crate::http_breakpoint::append_resume_identity_field(&mut context, self.region.as_bytes());
76 Ok(Some(context))
77 }
78
79 fn merge_head_headers(&self, ctx: DownloadHeadCtx<'_>) -> Result<(), MeowError> {
80 self.apply_signed_headers(ctx.task, "HEAD", ctx.base)
81 }
82
83 fn merge_range_get_headers(&self, ctx: DownloadRangeGetCtx<'_>) -> Result<(), MeowError> {
84 ctx.base.insert(RANGE, header_value(ctx.range_value)?);
85 if !ctx.base.contains_key(ACCEPT) {
86 ctx.base.insert(ACCEPT, header_value(DEFAULT_RANGE_ACCEPT)?);
87 }
88 self.apply_signed_headers(ctx.task, "GET", ctx.base)
89 }
90}