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