Skip to main content

objectstore_service/
resumable.rs

1//! Types supporting authenticated Resumable Upload Session tokens.
2//!
3//! Storage backends represent their opaque upload state as a [`BackendToken`]. At the service
4//! boundary, [`Session`] combines that state with service-specific fields, and
5//! [`crate::encryption::Cipher`] protects the serialized token before it is returned to the server.
6//! After authentication, the service passes the structured [`Session`] to the backend.
7//!
8//! ```text
9//! Storage backend       | objectstore-service                          | objectstore-server             |
10//! BackendToken <------->| Session --------------- Cipher ------------->| EncryptedSessionToken          |
11//! opaque backend state  | { ObjectId, upload_length, BackendToken }    | b64url encoded opaque envelope |
12//! ```
13
14use std::num::NonZeroU64;
15
16use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
17
18use crate::id::ObjectId;
19
20pub use objectstore_types::resumable::{
21    SessionToken as EncryptedSessionToken, UploadOffset, UploadProgress,
22};
23
24/// Opaque session state encoded and decoded by a storage backend.
25pub type BackendToken = String;
26
27/// Identifies a resumable upload and carries its declared length.
28///
29/// The service encrypts this value before returning it to clients and authenticates it before
30/// passing it to backend continuation operations.
31#[derive(Clone, Debug, Deserialize, Serialize)]
32pub struct Session {
33    /// The `ObjectID` this upload is tied to to.
34    #[serde(
35        serialize_with = "serialize_object_id",
36        deserialize_with = "deserialize_object_id"
37    )]
38    pub object_id: ObjectId,
39    /// Total length of the upload in bytes.
40    pub upload_length: NonZeroU64,
41    /// Opaque session state belonging to the receiving backend.
42    pub backend_token: BackendToken,
43}
44
45fn serialize_object_id<S>(id: &ObjectId, serializer: S) -> std::result::Result<S::Ok, S::Error>
46where
47    S: Serializer,
48{
49    serializer.collect_str(&id.as_storage_path())
50}
51
52fn deserialize_object_id<'de, D>(deserializer: D) -> std::result::Result<ObjectId, D::Error>
53where
54    D: Deserializer<'de>,
55{
56    let path = String::deserialize(deserializer)?;
57    ObjectId::from_storage_path(&path)
58        .ok_or_else(|| de::Error::custom("invalid object storage path"))
59}