Skip to main content

cloud_sdk/
transport.rs

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