Skip to main content

cloud_sdk/
transport.rs

1//! Provider-neutral blocking and asynchronous transport contracts.
2
3mod asynchronous;
4mod content_type;
5mod endpoint;
6mod header;
7mod request_target;
8mod response;
9
10pub use asynchronous::AsyncTransport;
11pub use content_type::{
12    ContentType, ContentTypeError, MAX_CONTENT_TYPE_BYTES, MediaType, ResponseContentType,
13};
14pub use endpoint::{
15    AcknowledgedCustomEndpoint, BoundTransport, CustomEndpointAcknowledgement, EndpointIdentity,
16    EndpointIdentityError, EndpointPolicy, EndpointPolicyError, EndpointPolicyKind, EndpointScheme,
17    MAX_ENDPOINT_BASE_PATH_BYTES, MAX_ENDPOINT_HOST_BYTES, MAX_ENDPOINT_REGION_BYTES,
18    MAX_OFFICIAL_ENDPOINTS, RegionEndpoint,
19};
20pub use header::{
21    HeaderError, HeaderName, HeaderSensitivity, HeaderValue, MAX_HEADER_NAME_BYTES,
22    MAX_HEADER_VALUE_BYTES, MAX_REQUEST_HEADER_BYTES, MAX_REQUEST_HEADERS,
23    MAX_RESPONSE_HEADER_BYTES, MAX_RESPONSE_HEADERS, RequestHeader, RequestHeaders, ResponseHeader,
24    ResponseHeaders,
25};
26pub use request_target::{
27    CanonicalQuery, FormQuery, MAX_REQUEST_TARGET_BYTES, QueryPair, QueryPairs, RequestPath,
28    RequestPathError, RequestQuery, RequestTarget, RequestTargetError, StructuredQueryError,
29};
30pub use response::{
31    ResponseBuffer, ResponseMetadata, ResponseWriter, ResponseWriterError, TransportResponse,
32};
33
34use core::fmt;
35
36use crate::Method;
37
38/// Explicit cleanup contract for caller-owned response storage.
39///
40/// Prepared execution invokes this for the complete supplied buffer before
41/// endpoint verification or response-capacity admission. Production
42/// implementations must use a cleanup primitive that cannot be removed as a
43/// dead store. This remains separate from [`BlockingTransport`] and
44/// [`AsyncTransport`] so direct transport implementations cannot silently
45/// acquire a weaker cleanup promise.
46pub trait ResponseStorageSanitizer {
47    /// Clears the complete caller-owned response buffer.
48    fn sanitize_response_storage(&self, response_storage: &mut [u8]);
49}
50
51/// Provider-neutral request passed to a blocking transport.
52#[derive(Clone, Copy)]
53pub struct TransportRequest<'a> {
54    method: Method,
55    target: RequestTarget<'a>,
56    body: &'a [u8],
57    headers: RequestHeaders<'a>,
58}
59
60impl<'a> TransportRequest<'a> {
61    /// Creates a bodyless request.
62    #[must_use]
63    pub const fn new(method: Method, target: RequestTarget<'a>) -> Self {
64        Self {
65            method,
66            target,
67            body: &[],
68            headers: RequestHeaders::EMPTY,
69        }
70    }
71
72    /// Adds a borrowed request body.
73    #[must_use]
74    pub const fn with_body(mut self, body: &'a [u8]) -> Self {
75        self.body = body;
76        self
77    }
78
79    /// Adds a complete validated request-header block.
80    #[must_use]
81    pub const fn with_headers(mut self, headers: RequestHeaders<'a>) -> Self {
82        self.headers = headers;
83        self
84    }
85
86    /// Returns the HTTP method.
87    #[must_use]
88    pub const fn method(self) -> Method {
89        self.method
90    }
91
92    /// Returns the validated origin-form target.
93    #[must_use]
94    pub const fn target(self) -> RequestTarget<'a> {
95        self.target
96    }
97
98    /// Returns the borrowed body bytes.
99    #[must_use]
100    pub const fn body(self) -> &'a [u8] {
101        self.body
102    }
103
104    /// Returns the complete ordered request-header block.
105    #[must_use]
106    pub const fn headers(self) -> RequestHeaders<'a> {
107        self.headers
108    }
109}
110
111impl fmt::Debug for TransportRequest<'_> {
112    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
113        formatter
114            .debug_struct("TransportRequest")
115            .field("method", &self.method)
116            .field("target", &self.target)
117            .field("body", &"[redacted]")
118            .field("headers", &self.headers)
119            .finish()
120    }
121}
122
123/// Valid HTTP response status code.
124#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
125pub struct StatusCode(u16);
126
127impl StatusCode {
128    /// `200 OK`.
129    pub const OK: Self = Self(200);
130    /// `201 Created`.
131    pub const CREATED: Self = Self(201);
132    /// `202 Accepted`.
133    pub const ACCEPTED: Self = Self(202);
134    /// `204 No Content`.
135    pub const NO_CONTENT: Self = Self(204);
136    /// `429 Too Many Requests`.
137    pub const TOO_MANY_REQUESTS: Self = Self(429);
138
139    /// Creates a status code in the HTTP `100..=599` range.
140    #[must_use]
141    pub const fn new(value: u16) -> Option<Self> {
142        if value < 100 || value > 599 {
143            return None;
144        }
145        Some(Self(value))
146    }
147
148    /// Returns the numeric status code.
149    #[must_use]
150    pub const fn get(self) -> u16 {
151        self.0
152    }
153
154    /// Reports whether this is a success status.
155    #[must_use]
156    pub const fn is_success(self) -> bool {
157        self.0 >= 200 && self.0 <= 299
158    }
159
160    /// Reports whether this is a client or server error status.
161    #[must_use]
162    pub const fn is_error(self) -> bool {
163        self.0 >= 400
164    }
165}
166
167/// Synchronous transport over caller-owned request and response buffers.
168///
169/// Authentication, base URLs, headers, timeouts, TLS, and retry policy belong
170/// to adapters and are intentionally outside this minimal contract.
171/// The shared receiver does not itself promise concurrency: callers may issue
172/// overlapping requests only when the concrete implementation satisfies their
173/// required [`Sync`] and [`Send`] bounds. Sequential implementations may use
174/// safe interior mutability without becoming `Sync`.
175pub trait BlockingTransport {
176    /// Transport-specific failure.
177    type Error;
178
179    /// Sends one request and writes the response body into the caller buffer.
180    fn send(
181        &self,
182        request: TransportRequest<'_>,
183        response: &mut ResponseWriter<'_>,
184    ) -> Result<(), Self::Error>;
185}
186
187#[cfg(test)]
188mod tests;