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