1use super::{UploadPolicy, UploadPolicyBuilder};
2use anyhow::{Error as AnyError, Result as AnyResult};
3use auto_impl::auto_impl;
4use dyn_clonable::clonable;
5use once_cell::sync::OnceCell;
6use qiniu_credential::{AccessKey, CredentialProvider};
7use qiniu_utils::{base64, BucketName, ObjectName};
8use std::{
9 borrow::Cow,
10 convert::Infallible,
11 fmt::{self, Debug, Display},
12 io::Error as IoError,
13 ops::{Deref, DerefMut},
14 str::FromStr,
15 sync::{Arc, RwLock},
16 time::{Duration, Instant},
17};
18use thiserror::Error;
19
20#[cfg(feature = "async")]
21use {
22 futures::lock::Mutex as AsyncMutex,
23 std::{future::Future, pin::Pin},
24};
25
26#[clonable]
30#[auto_impl(&, &mut, Box, Rc, Arc)]
31pub trait UploadTokenProvider: Clone + Debug + Sync + Send {
32 fn access_key(&self, opts: GetAccessKeyOptions) -> ParseResult<GotAccessKey>;
36
37 #[inline]
39 #[cfg(feature = "async")]
40 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
41 fn async_access_key(&self, opts: GetAccessKeyOptions) -> AsyncParseResult<'_, GotAccessKey> {
42 Box::pin(async move { self.access_key(opts) })
43 }
44
45 fn policy(&self, opts: GetPolicyOptions) -> ParseResult<GotUploadPolicy>;
49
50 #[inline]
52 #[cfg(feature = "async")]
53 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
54 fn async_policy(&self, opts: GetPolicyOptions) -> AsyncParseResult<'_, GotUploadPolicy> {
55 Box::pin(async move { self.policy(opts) })
56 }
57
58 fn to_token_string(&self, opts: ToStringOptions) -> ToStringResult<Cow<'_, str>>;
62
63 #[inline]
65 #[cfg(feature = "async")]
66 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
67 fn async_to_token_string(&self, opts: ToStringOptions) -> AsyncToStringResult<'_, Cow<'_, str>> {
68 Box::pin(async move { self.to_token_string(opts) })
69 }
70}
71
72#[derive(Copy, Clone, Debug, Default)]
74pub struct GetAccessKeyOptions {}
75
76impl GetAccessKeyOptions {}
77
78#[derive(Copy, Clone, Debug, Default)]
80pub struct GetPolicyOptions {}
81
82impl GetPolicyOptions {}
83
84#[derive(Copy, Clone, Debug, Default)]
86pub struct ToStringOptions {}
87
88impl ToStringOptions {}
89
90#[derive(Debug)]
94pub struct GotAccessKey(AccessKey);
95
96impl From<GotAccessKey> for AccessKey {
97 #[inline]
98 fn from(result: GotAccessKey) -> Self {
99 result.into_access_key()
100 }
101}
102
103impl From<AccessKey> for GotAccessKey {
104 #[inline]
105 fn from(result: AccessKey) -> Self {
106 Self(result)
107 }
108}
109
110impl GotAccessKey {
111 #[inline]
113 pub fn access_key(&self) -> &AccessKey {
114 &self.0
115 }
116
117 #[inline]
119 pub fn access_key_mut(&mut self) -> &mut AccessKey {
120 &mut self.0
121 }
122
123 #[inline]
125 pub fn into_access_key(self) -> AccessKey {
126 self.0
127 }
128}
129
130impl Deref for GotAccessKey {
131 type Target = AccessKey;
132
133 #[inline]
134 fn deref(&self) -> &Self::Target {
135 &self.0
136 }
137}
138
139impl DerefMut for GotAccessKey {
140 #[inline]
141 fn deref_mut(&mut self) -> &mut Self::Target {
142 &mut self.0
143 }
144}
145
146#[derive(Debug, Clone)]
150pub struct GotUploadPolicy<'a>(Cow<'a, UploadPolicy>);
151
152impl<'a> From<GotUploadPolicy<'a>> for Cow<'a, UploadPolicy> {
153 #[inline]
154 fn from(result: GotUploadPolicy<'a>) -> Self {
155 result.0
156 }
157}
158
159impl From<GotUploadPolicy<'_>> for UploadPolicy {
160 #[inline]
161 fn from(result: GotUploadPolicy<'_>) -> Self {
162 result.into_upload_policy()
163 }
164}
165
166impl<'a> From<Cow<'a, UploadPolicy>> for GotUploadPolicy<'a> {
167 #[inline]
168 fn from(policy: Cow<'a, UploadPolicy>) -> Self {
169 Self(policy)
170 }
171}
172
173impl<'a> From<&'a UploadPolicy> for GotUploadPolicy<'a> {
174 #[inline]
175 fn from(policy: &'a UploadPolicy) -> Self {
176 Self::from(Cow::Borrowed(policy))
177 }
178}
179
180impl From<UploadPolicy> for GotUploadPolicy<'_> {
181 #[inline]
182 fn from(policy: UploadPolicy) -> Self {
183 Self::from(Cow::Owned(policy))
184 }
185}
186
187impl GotUploadPolicy<'_> {
188 #[inline]
190 pub fn upload_policy(&self) -> &UploadPolicy {
191 &self.0
192 }
193
194 #[inline]
196 pub fn upload_policy_mut(&mut self) -> &mut UploadPolicy {
197 self.0.to_mut()
198 }
199
200 #[inline]
202 pub fn into_upload_policy(self) -> UploadPolicy {
203 self.0.into_owned()
204 }
205}
206
207impl Deref for GotUploadPolicy<'_> {
208 type Target = UploadPolicy;
209
210 #[inline]
211 fn deref(&self) -> &Self::Target {
212 &self.0
213 }
214}
215
216impl DerefMut for GotUploadPolicy<'_> {
217 #[inline]
218 fn deref_mut(&mut self) -> &mut Self::Target {
219 self.0.to_mut()
220 }
221}
222
223pub trait UploadTokenProviderExt: UploadTokenProvider {
227 fn bucket_name(&self, opts: GetPolicyOptions) -> ParseResult<BucketName> {
231 self.policy(opts).and_then(|policy| {
232 policy
233 .bucket()
234 .map_or(Err(ParseError::InvalidUploadTokenFormat), |bucket_name| {
235 Ok(BucketName::from(bucket_name))
236 })
237 })
238 }
239
240 #[cfg(feature = "async")]
242 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
243 fn async_bucket_name(&self, opts: GetPolicyOptions) -> AsyncParseResult<'_, BucketName> {
244 Box::pin(async move {
245 self.async_policy(opts).await.and_then(|policy| {
246 policy
247 .bucket()
248 .map_or(Err(ParseError::InvalidUploadTokenFormat), |bucket_name| {
249 Ok(BucketName::from(bucket_name))
250 })
251 })
252 })
253 }
254}
255
256impl<T: UploadTokenProvider> UploadTokenProviderExt for T {}
257
258#[derive(Clone)]
262pub struct StaticUploadTokenProvider {
263 upload_token: Box<str>,
264 policy: OnceCell<UploadPolicy>,
265 access_key: OnceCell<AccessKey>,
266}
267
268impl StaticUploadTokenProvider {
269 pub fn new(upload_token: impl Into<String>) -> Self {
271 Self {
272 upload_token: upload_token.into().into_boxed_str(),
273 policy: OnceCell::new(),
274 access_key: OnceCell::new(),
275 }
276 }
277
278 #[inline]
280 pub fn into_string(self) -> String {
281 self.upload_token.into_string()
282 }
283
284 pub(super) fn set_policy(&self, policy: UploadPolicy) {
285 self.policy.set(policy).ok();
286 }
287
288 pub(super) fn set_access_key(&self, access_key: AccessKey) {
289 self.access_key.set(access_key).ok();
290 }
291}
292
293impl Debug for StaticUploadTokenProvider {
294 #[inline]
295 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
296 f.debug_struct("StaticUploadTokenProvider")
297 .field("upload_token", &self.upload_token)
298 .finish()
299 }
300}
301
302impl Display for StaticUploadTokenProvider {
303 #[inline]
304 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
305 Display::fmt(&self.upload_token, f)
306 }
307}
308
309impl UploadTokenProvider for StaticUploadTokenProvider {
310 fn access_key(&self, _opts: GetAccessKeyOptions) -> ParseResult<GotAccessKey> {
311 self.access_key
312 .get_or_try_init(|| {
313 self.upload_token
314 .find(':')
315 .map(|i| self.upload_token.split_at(i).0.to_owned().into())
316 .ok_or(ParseError::InvalidUploadTokenFormat)
317 })
318 .map(|access_key| access_key.to_owned())
319 .map(GotAccessKey::from)
320 }
321
322 fn policy(&self, _opts: GetPolicyOptions) -> ParseResult<GotUploadPolicy<'_>> {
323 self.policy
324 .get_or_try_init(|| {
325 let encoded_policy = self
326 .upload_token
327 .splitn(3, ':')
328 .last()
329 .ok_or(ParseError::InvalidUploadTokenFormat)?;
330 let decoded_policy =
331 base64::decode(encoded_policy.as_bytes()).map_err(ParseError::Base64DecodeError)?;
332 UploadPolicy::from_json(decoded_policy).map_err(ParseError::JsonDecodeError)
333 })
334 .map(Cow::Borrowed)
335 .map(GotUploadPolicy::from)
336 }
337
338 #[inline]
339 fn to_token_string(&self, _opts: ToStringOptions) -> ToStringResult<Cow<'_, str>> {
340 Ok(Cow::Borrowed(self.upload_token.as_ref()))
341 }
342}
343
344impl<T: Into<String>> From<T> for StaticUploadTokenProvider {
345 #[inline]
346 fn from(s: T) -> Self {
347 Self::new(s.into())
348 }
349}
350
351impl FromStr for StaticUploadTokenProvider {
352 type Err = Infallible;
353
354 #[inline]
355 fn from_str(s: &str) -> Result<Self, Self::Err> {
356 Ok(Self::new(s))
357 }
358}
359
360#[derive(Debug, Clone)]
364pub struct FromUploadPolicy<C: Clone> {
365 upload_policy: UploadPolicy,
366 credential: C,
367}
368
369impl<C: Clone> FromUploadPolicy<C> {
370 #[inline]
372 pub fn new(upload_policy: UploadPolicy, credential: C) -> Self {
373 Self {
374 upload_policy,
375 credential,
376 }
377 }
378
379 #[inline]
381 pub fn split(self) -> (UploadPolicy, C) {
382 (self.upload_policy, self.credential)
383 }
384}
385
386impl<C: CredentialProvider + Clone> UploadTokenProvider for FromUploadPolicy<C> {
387 fn access_key(&self, _opts: GetAccessKeyOptions) -> ParseResult<GotAccessKey> {
388 Ok(self
389 .credential
390 .get(Default::default())?
391 .into_credential()
392 .split()
393 .0
394 .into())
395 }
396
397 #[inline]
398 fn policy(&self, _opts: GetPolicyOptions) -> ParseResult<GotUploadPolicy<'_>> {
399 Ok(Cow::Borrowed(&self.upload_policy).into())
400 }
401
402 fn to_token_string(&self, _opts: ToStringOptions) -> ToStringResult<Cow<'_, str>> {
403 Ok(Cow::Owned(
404 self.credential
405 .get(Default::default())?
406 .sign_with_data(self.upload_policy.as_json().as_bytes()),
407 ))
408 }
409}
410
411type OnPolicyGeneratedCallback = Arc<dyn Fn(&mut UploadPolicyBuilder) -> AnyResult<()> + Sync + Send + 'static>;
412
413#[derive(Clone)]
417pub struct BucketUploadTokenProvider<C: Clone> {
418 bucket: BucketName,
419 upload_token_lifetime: Duration,
420 credential: C,
421 on_policy_generated: Option<OnPolicyGeneratedCallback>,
422}
423
424impl<C: Clone> BucketUploadTokenProvider<C> {
425 #[inline]
427 pub fn new(bucket: impl Into<BucketName>, upload_token_lifetime: Duration, credential: C) -> Self {
428 Self::builder(bucket, upload_token_lifetime, credential).build()
429 }
430
431 #[inline]
433 pub fn builder(
434 bucket: impl Into<BucketName>,
435 upload_token_lifetime: Duration,
436 credential: C,
437 ) -> BucketUploadTokenProviderBuilder<C> {
438 BucketUploadTokenProviderBuilder {
439 inner: Self {
440 bucket: bucket.into(),
441 upload_token_lifetime,
442 credential,
443 on_policy_generated: None,
444 },
445 }
446 }
447
448 fn make_policy(&self) -> AnyResult<UploadPolicy> {
449 let mut builder =
450 UploadPolicyBuilder::new_policy_for_bucket(self.bucket.to_string(), self.upload_token_lifetime);
451 if let Some(on_policy_generated) = self.on_policy_generated.as_ref() {
452 on_policy_generated(&mut builder)?;
453 }
454 Ok(builder.build())
455 }
456}
457
458impl<C: Clone> Debug for BucketUploadTokenProvider<C> {
459 #[inline]
460 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
461 f.debug_struct("BucketUploadTokenProvider")
462 .field("bucket", &self.bucket)
463 .field("upload_token_lifetime", &self.upload_token_lifetime)
464 .finish()
465 }
466}
467
468impl<C: CredentialProvider + Clone> UploadTokenProvider for BucketUploadTokenProvider<C> {
469 #[inline]
470 fn access_key(&self, _opts: GetAccessKeyOptions) -> ParseResult<GotAccessKey> {
471 Ok(self
472 .credential
473 .get(Default::default())?
474 .into_credential()
475 .split()
476 .0
477 .into())
478 }
479
480 fn policy(&self, _opts: GetPolicyOptions) -> ParseResult<GotUploadPolicy<'_>> {
481 Ok(self.make_policy()?.into())
482 }
483
484 fn to_token_string(&self, _opts: ToStringOptions) -> ToStringResult<Cow<'_, str>> {
485 Ok(Cow::Owned(
486 self.credential
487 .get(Default::default())?
488 .sign_with_data(self.make_policy()?.as_json().as_bytes()),
489 ))
490 }
491}
492
493#[derive(Clone)]
495pub struct BucketUploadTokenProviderBuilder<C: Clone> {
496 inner: BucketUploadTokenProvider<C>,
497}
498
499impl<C: Clone> BucketUploadTokenProviderBuilder<C> {
500 #[inline]
502 #[must_use]
503 pub fn on_policy_generated(
504 mut self,
505 callback: impl Fn(&mut UploadPolicyBuilder) -> AnyResult<()> + Sync + Send + 'static,
506 ) -> Self {
507 self.inner.on_policy_generated = Some(Arc::new(callback));
508 self
509 }
510
511 #[inline]
513 pub fn build(self) -> BucketUploadTokenProvider<C> {
514 self.inner
515 }
516}
517
518impl<C: Clone> Debug for BucketUploadTokenProviderBuilder<C> {
519 #[inline]
520 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
521 f.debug_struct("BucketUploadTokenProviderBuilder")
522 .field("bucket", &self.inner.bucket)
523 .field("upload_token_lifetime", &self.inner.upload_token_lifetime)
524 .finish()
525 }
526}
527
528#[derive(Clone)]
532pub struct ObjectUploadTokenProvider<C: Clone> {
533 bucket: BucketName,
534 object: ObjectName,
535 upload_token_lifetime: Duration,
536 credential: C,
537 on_policy_generated: Option<OnPolicyGeneratedCallback>,
538}
539
540impl<C: Clone> ObjectUploadTokenProvider<C> {
541 #[inline]
543 pub fn new(
544 bucket: impl Into<BucketName>,
545 object: impl Into<ObjectName>,
546 upload_token_lifetime: Duration,
547 credential: C,
548 ) -> Self {
549 Self::builder(bucket, object, upload_token_lifetime, credential).build()
550 }
551
552 #[inline]
554 pub fn builder(
555 bucket: impl Into<BucketName>,
556 object: impl Into<ObjectName>,
557 upload_token_lifetime: Duration,
558 credential: C,
559 ) -> ObjectUploadTokenProviderBuilder<C> {
560 ObjectUploadTokenProviderBuilder {
561 inner: Self {
562 bucket: bucket.into(),
563 object: object.into(),
564 upload_token_lifetime,
565 credential,
566 on_policy_generated: None,
567 },
568 }
569 }
570
571 fn make_policy(&self) -> AnyResult<UploadPolicy> {
572 let mut builder = UploadPolicyBuilder::new_policy_for_object(
573 self.bucket.to_string(),
574 self.object.to_string(),
575 self.upload_token_lifetime,
576 );
577 if let Some(on_policy_generated) = self.on_policy_generated.as_ref() {
578 on_policy_generated(&mut builder)?;
579 }
580 Ok(builder.build())
581 }
582}
583
584impl<C: Clone> Debug for ObjectUploadTokenProvider<C> {
585 #[inline]
586 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
587 f.debug_struct("ObjectUploadTokenProvider")
588 .field("bucket", &self.bucket)
589 .field("object", &self.object)
590 .field("upload_token_lifetime", &self.upload_token_lifetime)
591 .finish()
592 }
593}
594
595impl<C: CredentialProvider + Clone> UploadTokenProvider for ObjectUploadTokenProvider<C> {
596 fn access_key(&self, _opts: GetAccessKeyOptions) -> ParseResult<GotAccessKey> {
597 Ok(self
598 .credential
599 .get(Default::default())?
600 .into_credential()
601 .split()
602 .0
603 .into())
604 }
605
606 fn policy(&self, _opts: GetPolicyOptions) -> ParseResult<GotUploadPolicy<'_>> {
607 Ok(self.make_policy()?.into())
608 }
609
610 fn to_token_string(&self, _opts: ToStringOptions) -> ToStringResult<Cow<'_, str>> {
611 Ok(Cow::Owned(
612 self.credential
613 .get(Default::default())?
614 .sign_with_data(self.make_policy()?.as_json().as_bytes()),
615 ))
616 }
617}
618
619#[derive(Clone)]
621pub struct ObjectUploadTokenProviderBuilder<C: Clone> {
622 inner: ObjectUploadTokenProvider<C>,
623}
624
625impl<C: Clone> ObjectUploadTokenProviderBuilder<C> {
626 #[inline]
628 #[must_use]
629 pub fn on_policy_generated(
630 mut self,
631 callback: impl Fn(&mut UploadPolicyBuilder) -> AnyResult<()> + Sync + Send + 'static,
632 ) -> Self {
633 self.inner.on_policy_generated = Some(Arc::new(callback));
634 self
635 }
636
637 #[inline]
639 pub fn build(self) -> ObjectUploadTokenProvider<C> {
640 self.inner
641 }
642}
643
644impl<C: Clone> Debug for ObjectUploadTokenProviderBuilder<C> {
645 #[inline]
646 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
647 f.debug_struct("ObjectUploadTokenProviderBuilder")
648 .field("bucket", &self.inner.bucket)
649 .field("object", &self.inner.object)
650 .field("upload_token_lifetime", &self.inner.upload_token_lifetime)
651 .finish()
652 }
653}
654
655#[derive(Debug)]
656struct Cache<T> {
657 cached_at: Instant,
658 value: T,
659}
660
661#[derive(Debug, Default, Clone)]
662struct SyncCache(Arc<SyncCacheInner>);
663
664#[derive(Debug, Default)]
665struct SyncCacheInner {
666 access_key: RwLock<Option<Cache<AccessKey>>>,
667 upload_policy: RwLock<Option<Cache<UploadPolicy>>>,
668 upload_token: RwLock<Option<Cache<String>>>,
669}
670
671#[cfg(feature = "async")]
672#[derive(Debug, Default, Clone)]
673struct AsyncCache(Arc<AsyncCacheInner>);
674
675#[cfg(feature = "async")]
676#[derive(Debug, Default)]
677struct AsyncCacheInner {
678 access_key: AsyncMutex<Option<Cache<AccessKey>>>,
679 upload_policy: AsyncMutex<Option<Cache<UploadPolicy>>>,
680 upload_token: AsyncMutex<Option<Cache<String>>>,
681}
682
683#[derive(Debug, Clone)]
687pub struct CachedUploadTokenProvider<P: Clone> {
688 inner_provider: P,
689 cache_lifetime: Duration,
690 sync_cache: SyncCache,
691
692 #[cfg(feature = "async")]
693 async_cache: AsyncCache,
694}
695
696impl<P: Clone> CachedUploadTokenProvider<P> {
697 #[inline]
699 pub fn new(inner_provider: P, cache_lifetime: Duration) -> Self {
700 Self {
701 inner_provider,
702 cache_lifetime,
703 sync_cache: Default::default(),
704
705 #[cfg(feature = "async")]
706 async_cache: Default::default(),
707 }
708 }
709}
710
711macro_rules! sync_method {
712 ($provider:expr, $cache_field:ident, $opts_field:ident, $opts_type:ty, $method_name:ident, $return_type:ty) => {{
713 let guard = $provider.sync_cache.0.$cache_field.read().unwrap();
714 return if let Some(cache) = &*guard {
715 if cache.cached_at.elapsed() < $provider.cache_lifetime {
716 Ok(cache.value.to_owned().into())
717 } else {
718 drop(guard);
719 update_cache(&$provider, $opts_field)
720 }
721 } else {
722 drop(guard);
723 update_cache(&$provider, $opts_field)
724 };
725
726 #[allow(unused_lifetimes)]
727 fn update_cache(
728 provider: &CachedUploadTokenProvider<impl UploadTokenProvider + Clone>,
729 opts: $opts_type,
730 ) -> $return_type {
731 let mut guard = provider.sync_cache.0.$cache_field.write().unwrap();
732 if let Some(cache) = &*guard {
733 if cache.cached_at.elapsed() < provider.cache_lifetime {
734 return Ok(cache.value.to_owned().into());
735 }
736 }
737 match provider.inner_provider.$method_name(opts) {
738 Ok(value) => {
739 *guard = Some(Cache {
740 cached_at: Instant::now(),
741 value: value.to_owned().into(),
742 });
743 Ok(value)
744 }
745 Err(err) => Err(err),
746 }
747 }
748 }};
749}
750
751#[cfg(feature = "async")]
752macro_rules! async_method {
753 ($provider:expr, $cache_field:ident, $opts_field:ident, $method_name:ident) => {{
754 Box::pin(async move {
755 let mut cache = $provider.async_cache.0.$cache_field.lock().await;
756 if let Some(cache) = &*cache {
757 if cache.cached_at.elapsed() < $provider.cache_lifetime {
758 return Ok(cache.value.to_owned().into());
759 }
760 }
761 match $provider.inner_provider.$method_name($opts_field).await {
762 Ok(value) => {
763 *cache = Some(Cache {
764 cached_at: Instant::now(),
765 value: value.to_owned().into(),
766 });
767 Ok(value)
768 }
769 Err(err) => Err(err),
770 }
771 })
772 }};
773}
774
775impl<P: UploadTokenProvider + Clone> UploadTokenProvider for CachedUploadTokenProvider<P> {
776 fn access_key(&self, opts: GetAccessKeyOptions) -> ParseResult<GotAccessKey> {
777 sync_method!(
778 self,
779 access_key,
780 opts,
781 GetAccessKeyOptions,
782 access_key,
783 ParseResult<GotAccessKey>
784 )
785 }
786
787 fn policy(&self, opts: GetPolicyOptions) -> ParseResult<GotUploadPolicy<'_>> {
788 sync_method!(
789 self,
790 upload_policy,
791 opts,
792 GetPolicyOptions,
793 policy,
794 ParseResult<GotUploadPolicy<'_>>
795 )
796 }
797
798 fn to_token_string(&self, opts: ToStringOptions) -> ToStringResult<Cow<'_, str>> {
799 sync_method!(
800 self,
801 upload_token,
802 opts,
803 ToStringOptions,
804 to_token_string,
805 ToStringResult<Cow<str>>
806 )
807 }
808
809 #[cfg(feature = "async")]
810 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
811 fn async_access_key(&self, opts: GetAccessKeyOptions) -> AsyncParseResult<'_, GotAccessKey> {
812 async_method!(self, access_key, opts, async_access_key)
813 }
814
815 #[cfg(feature = "async")]
816 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
817 fn async_policy(&self, opts: GetPolicyOptions) -> AsyncParseResult<'_, GotUploadPolicy<'_>> {
818 async_method!(self, upload_policy, opts, async_policy)
819 }
820
821 #[cfg(feature = "async")]
822 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
823 fn async_to_token_string(&self, opts: ToStringOptions) -> AsyncToStringResult<'_, Cow<'_, str>> {
824 async_method!(self, upload_token, opts, async_to_token_string)
825 }
826}
827
828#[derive(Error, Debug)]
830#[non_exhaustive]
831pub enum ParseError {
832 #[error("Invalid upload token format")]
834 InvalidUploadTokenFormat,
835 #[error("Base64 decode error: {0}")]
837 Base64DecodeError(#[from] base64::DecodeError),
838 #[error("JSON decode error: {0}")]
840 JsonDecodeError(#[from] serde_json::Error),
841 #[error("Credential get error: {0}")]
843 CredentialGetError(#[from] IoError),
844 #[error("on_policy_generated callback error: {0}")]
846 CallbackError(#[from] AnyError),
847}
848
849pub type ParseResult<T> = Result<T, ParseError>;
851
852#[derive(Error, Debug)]
854#[non_exhaustive]
855pub enum ToStringError {
856 #[error("Credential get error: {0}")]
858 CredentialGetError(#[from] IoError),
859 #[error("Generate Upload Policy Callback error: {0}")]
861 CallbackError(#[from] AnyError),
862}
863
864pub type ToStringResult<T> = Result<T, ToStringError>;
866
867#[cfg(feature = "async")]
868type AsyncParseResult<'a, T> = Pin<Box<dyn Future<Output = ParseResult<T>> + 'a + Send>>;
869
870#[cfg(feature = "async")]
871type AsyncToStringResult<'a, T> = Pin<Box<dyn Future<Output = ToStringResult<T>> + 'a + Send>>;
872
873#[cfg(test)]
874mod tests {
875 use super::{super::UploadPolicyBuilder, *};
876 use async_std as _;
877 use qiniu_credential::Credential;
878 use std::{boxed::Box, error::Error, result::Result};
879 use structopt as _;
880
881 #[test]
882 fn test_build_upload_token_from_upload_policy() -> Result<(), Box<dyn Error>> {
883 let policy =
884 UploadPolicyBuilder::new_policy_for_object("test_bucket", "test:file", Duration::from_secs(3600)).build();
885 let provider = FromUploadPolicy::new(policy, get_credential());
886 let token = provider.to_token_string(Default::default())?;
887 assert!(token.starts_with(get_credential().get(Default::default())?.access_key().as_str()));
888 let token: StaticUploadTokenProvider = token.parse()?;
889 let policy = token.policy(Default::default())?;
890 assert_eq!(policy.bucket(), Some("test_bucket"));
891 assert_eq!(policy.key(), Some("test:file"));
892 Ok(())
893 }
894
895 #[test]
896 fn test_build_upload_token_for_bucket() -> Result<(), Box<dyn Error>> {
897 let provider = BucketUploadTokenProvider::builder("test_bucket", Duration::from_secs(3600), get_credential())
898 .on_policy_generated(|policy| {
899 policy.return_body("{\"key\":$(key)}");
900 Ok(())
901 })
902 .build();
903
904 let token = provider.to_token_string(Default::default())?;
905 assert!(token.starts_with(get_credential().get(Default::default())?.access_key().as_str()));
906
907 let policy = provider.policy(Default::default())?;
908 assert_eq!(policy.bucket(), Some("test_bucket"));
909 assert_eq!(policy.key(), None);
910 assert_eq!(policy.return_body(), Some("{\"key\":$(key)}"));
911
912 Ok(())
913 }
914
915 #[cfg(feature = "async")]
916 mod async_test {
917 use super::*;
918
919 #[async_std::test]
920 async fn test_async_build_upload_token_from_upload_policy() -> Result<(), Box<dyn Error>> {
921 let policy =
922 UploadPolicyBuilder::new_policy_for_object("test_bucket", "test:file", Duration::from_secs(3600))
923 .build();
924 let provider = FromUploadPolicy::new(policy, get_credential());
925 let token = provider.async_to_token_string(Default::default()).await?;
926 assert!(token.starts_with(
927 get_credential()
928 .async_get(Default::default())
929 .await?
930 .access_key()
931 .as_str()
932 ));
933 let token: StaticUploadTokenProvider = token.parse()?;
934 let get_policy_from_size_options = Default::default();
935 let policy = token.async_policy(get_policy_from_size_options).await?;
936 assert_eq!(policy.bucket(), Some("test_bucket"));
937 assert_eq!(policy.key(), Some("test:file"));
938 Ok(())
939 }
940 }
941
942 fn get_credential() -> Credential {
943 Credential::new("abcdefghklmnopq", "1234567890")
944 }
945}