///|
/// A wire request, response description and per-request options.
pub struct RequestPlan[T, M] {
priv raw : @http.Request
priv response_as : ResponsePlan[T, M]
priv body_factory : (() -> Bytes)?
priv timeout : Int32?
priv idle : Int32?
priv body_limit : Int32?
priv redirects : Bool?
priv hops : Int32?
}
///|
/// A request whose response is buffered before it is returned.
pub type ClientRequest[T] = RequestPlan[T, Buffered]
///|
/// A request whose response may run a scoped stream consumer.
pub type StreamRequest[T] = RequestPlan[T, Streaming]
///|
/// Pairs a wire request with its response interpretation.
pub fn[T, M] RequestPlan::new(
raw : @http.Request,
response_as : ResponsePlan[T, M],
) -> RequestPlan[T, M] {
{
raw,
response_as,
body_factory: None,
timeout: None,
idle: None,
body_limit: None,
redirects: None,
hops: None,
}
}
///|
/// Replaces the interpretation, including its lifetime marker.
pub fn[T, U, M, N] RequestPlan::response(
self : RequestPlan[T, M],
response_as : ResponsePlan[U, N],
) -> RequestPlan[U, N] {
{
raw: self.raw,
response_as,
body_factory: self.body_factory,
timeout: self.timeout,
idle: self.idle,
body_limit: self.body_limit,
redirects: self.redirects,
hops: self.hops,
}
}
///|
/// Supplies bytes freshly built after admission on each attempt.
pub fn[T, M] RequestPlan::body_fn(
self : RequestPlan[T, M],
factory : () -> Bytes,
) -> RequestPlan[T, M] {
{ ..self, body_factory: Some(factory), }
}
///|
/// Adds a wire header to a copy of the request.
pub fn[T, M] RequestPlan::header(
self : RequestPlan[T, M],
name : String,
value : String,
) -> RequestPlan[T, M] {
{ ..self, raw: self.raw.header(name, value), }
}
///|
/// Sets a bearer Authorization header on a copy.
pub fn[T, M] RequestPlan::bearer(
self : RequestPlan[T, M],
token : String,
) -> RequestPlan[T, M] {
{ ..self, raw: self.raw.bearer(token), }
}
///|
/// Sets a replayable byte body on a copy.
pub fn[T, M] RequestPlan::body_bytes(
self : RequestPlan[T, M],
bytes : Bytes,
) -> RequestPlan[T, M] {
{ ..self, raw: self.raw.body_bytes(bytes), body_factory: None, }
}
///|
/// Sets a JSON body on a copy.
pub fn[T, M] RequestPlan::body_json(
self : RequestPlan[T, M],
value : Json,
) -> RequestPlan[T, M] {
{ ..self, raw: self.raw.body_json(value), body_factory: None, }
}
///|
/// Overrides the Client's attempt deadline for this request. It is sent as
/// `SendOptions.timeout_ms`, shortened by what is left of a total deadline.
pub fn[T, M] RequestPlan::timeout_ms(
self : RequestPlan[T, M],
ms : Int32,
) -> RequestPlan[T, M] {
{ ..self, timeout: Some(ms), }
}
///|
/// Overrides the Client's per-read deadline for a body sent as
/// `Stream(idle_ms)`.
pub fn[T, M] RequestPlan::read_idle_ms(
self : RequestPlan[T, M],
ms : Int32,
) -> RequestPlan[T, M] {
{ ..self, idle: Some(ms), }
}
///|
/// Overrides the Client's body limit for this request: the decoded bytes a
/// buffered reader keeps, and the wire bytes sent as `Exchange(max_bytes)`.
pub fn[T, M] RequestPlan::max_body_bytes(
self : RequestPlan[T, M],
bytes : Int32,
) -> RequestPlan[T, M] {
{ ..self, body_limit: Some(bytes), }
}
///|
/// Adds typed local metadata to a copy.
pub fn[T, A, M] RequestPlan::attribute(
self : RequestPlan[T, M],
key : @http.AttributeKey[A],
value : A,
) -> RequestPlan[T, M] {
{ ..self, raw: self.raw.attribute(key, value), }
}
///|
/// Returns the wire request before client defaults are applied.
pub fn[T, M] RequestPlan::raw(self : RequestPlan[T, M]) -> @http.Request {
self.raw
}
///|
/// The request with this plan's redirect override as the `redirect_limit`
/// attribute, which `FollowRedirects` reads.
fn[T, M] RequestPlan::wire(self : RequestPlan[T, M]) -> @http.Request {
match (self.redirects, self.hops) {
(Some(false), _) => self.raw.attribute(redirect_limit, 0)
(_, Some(max)) => self.raw.attribute(redirect_limit, max.max(0))
_ => self.raw
}
}
///|
fn[T, M] RequestPlan::limits(
self : RequestPlan[T, M],
defaults : Limits,
) -> Limits {
{
timeout_ms: match self.timeout {
Some(ms) => Some(ms)
None => defaults.timeout_ms
},
read_idle_ms: match self.idle {
Some(ms) => Some(ms)
None => defaults.read_idle_ms
},
max_body_bytes: self.body_limit.unwrap_or(defaults.max_body_bytes),
}
}
///|
/// Turns redirect following off for this request (`false` sets the
/// `redirect_limit` attribute to 0), or, with `true`, undoes an earlier
/// `follow_redirects(false)` on this plan so the `FollowRedirects`
/// configuration applies again.
pub fn[T, M] RequestPlan::follow_redirects(
self : RequestPlan[T, M],
enabled : Bool,
) -> RequestPlan[T, M] {
{ ..self, redirects: Some(enabled), }
}
///|
/// Overrides the redirect-hop limit of `FollowRedirects` for this request,
/// through the `redirect_limit` attribute. A limit below 1 follows nothing.
pub fn[T, M] RequestPlan::max_redirects(
self : RequestPlan[T, M],
max : Int32,
) -> RequestPlan[T, M] {
{ ..self, hops: Some(max), }
}
///|
/// Sends through the Client's single executor, with an optional typed policy.
pub async fn[T, M : ResponseMode] RequestPlan::send(
self : RequestPlan[T, M],
client : Client,
decide_retry? : (AttemptResult[T], RetryContext) -> Decision,
) -> Reply[T] {
client.send(self, decide_retry?)
}
///|
/// A decoded response, preserving the actual HTTP and request metadata.
pub(all) struct Reply[T] {
status : Int32
headers : @http.Headers
body : T
request : @http.RequestHead
attributes : @http.Attributes
}
///|
/// An explicit local admission bucket; absent metadata selects "default".
pub let rate_limit_bucket : @http.AttributeKey[String] = @http.AttributeKey::new(
"rate_limit_bucket",
)
///|
/// Whether the request bypasses global admission limits.
pub let global_exempt : @http.AttributeKey[Bool] = @http.AttributeKey::new(
"global_exempt",
)
///|
/// The endpoint operation identifier, independent of admission buckets.
pub let operation_id : @http.AttributeKey[String] = @http.AttributeKey::new(
"operation_id",
)
///|
/// How many redirect hops this request may follow, overriding the
/// configuration of a redirect-following wrapper (`RedirectConfig.max_redirects`
/// of `FollowRedirects`), which reads it from the request's attributes. 0 (or
/// less) follows no redirect and returns the 3xx response as it is.
/// `RequestPlan::follow_redirects` and `max_redirects` set it.
pub let redirect_limit : @http.AttributeKey[Int32] = @http.AttributeKey::new(
"redirect_limit",
)
///|
/// Creates a GET request with the default Result-valued string reader.
pub fn get(url : String) -> ClientRequest[Result[String, ResponseFailure]] {
request(@http.Request::get(url))
}
///|
/// Creates a POST request with the default Result-valued string reader.
pub fn post(url : String) -> ClientRequest[Result[String, ResponseFailure]] {
request(@http.Request::post(url))
}