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