Skip to main content

aws_runtime/content_encoding/
sign.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use aws_sigv4::http_request::SigningError;
7use aws_smithy_runtime_api::http::Headers;
8use aws_smithy_types::config_bag::{Storable, StoreReplace};
9use bytes::Bytes;
10use std::sync::{mpsc, Arc, Mutex};
11
12// Trait for signing chunks and trailers
13//
14// Trait methods take `&mut self`` because they keep track of running signature as they sign each chunk.
15pub(crate) trait SignChunk: std::fmt::Debug {
16    fn chunk_signature(&mut self, chunk: &Bytes) -> Result<String, SigningError>;
17
18    fn trailer_signature(&mut self, trailing_headers: &Headers) -> Result<String, SigningError>;
19}
20
21/// Deferred chunk signer that allows a signer to be wired up later.
22///
23/// Signing chunks and trailers occurs after HTTP request signing and requires
24/// signing context from the initial HTTP signature operation.
25///
26/// This signer establishes an MPSC channel with a sender placed in the request
27/// configuration. The HTTP signer implementation retrieves the sender from the
28/// config and sends an actual signing implementation with the required context.
29#[derive(Clone, Debug)]
30#[allow(clippy::type_complexity)]
31pub struct DeferredSigner {
32    // The outer `Arc` enables cloning `DeferredSigner`, making `AwsChunkedBody` retryable.
33    // The inner trait objects are boxed to enable calling mutable trait methods.
34    signer: Arc<Mutex<Option<Box<dyn SignChunk + Send + Sync>>>>,
35    rx: Arc<Mutex<Option<mpsc::Receiver<Box<dyn SignChunk + Send + Sync>>>>>,
36}
37
38impl Storable for DeferredSigner {
39    type Storer = StoreReplace<Self>;
40}
41
42impl DeferredSigner {
43    /// Create a new `DeferredSigner` and its associated sender.
44    pub fn new() -> (Self, DeferredSignerSender) {
45        let (tx, rx) = mpsc::channel();
46        (
47            Self {
48                signer: Default::default(),
49                rx: Arc::new(Mutex::new(Some(rx))),
50            },
51            DeferredSignerSender { tx: Mutex::new(tx) },
52        )
53    }
54
55    /// Create an empty `DeferredSigner`, typically used as a placeholder for `std::mem::replace`
56    pub fn empty() -> Self {
57        Self {
58            rx: Default::default(),
59            signer: Default::default(),
60        }
61    }
62
63    fn acquire(&self) -> Box<dyn SignChunk + Send + Sync> {
64        let mut rx = self.rx.lock().unwrap();
65        rx.take()
66            .and_then(|receiver| receiver.try_recv().ok())
67            .expect("signer should be available")
68    }
69}
70
71/// A sender placed in the config bag to wire up a signer for signing chunks and trailers.
72#[derive(Debug)]
73pub struct DeferredSignerSender {
74    tx: Mutex<mpsc::Sender<Box<dyn SignChunk + Send + Sync>>>,
75}
76
77impl DeferredSignerSender {
78    pub(crate) fn send(
79        &self,
80        signer: Box<dyn SignChunk + Send + Sync>,
81    ) -> Result<(), mpsc::SendError<Box<dyn SignChunk + Send + Sync>>> {
82        self.tx.lock().unwrap().send(signer)
83    }
84}
85
86impl Storable for DeferredSignerSender {
87    type Storer = StoreReplace<Self>;
88}
89
90impl SignChunk for DeferredSigner {
91    fn chunk_signature(&mut self, chunk: &Bytes) -> Result<String, SigningError> {
92        let mut signer = self.signer.lock().unwrap();
93        let signer = signer.get_or_insert_with(|| self.acquire());
94        signer.chunk_signature(chunk)
95    }
96
97    fn trailer_signature(&mut self, trailing_headers: &Headers) -> Result<String, SigningError> {
98        let mut signer = self.signer.lock().unwrap();
99        let signer = signer.get_or_insert_with(|| self.acquire());
100        signer.trailer_signature(trailing_headers)
101    }
102}