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