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