silk.http.exchange.
Run HTTP/1 requests over a connection step by step: send the head, stream the body, and read the response.
When to use#
Use this module to stream a request or a response body, or to reuse a connection. Make a
Connection around a transport with makeOwned, withOwned, or withConnected, or
let withRoute connect through a proxy route. Then call withExchange for each request.
Most programs use silk.http.client or silk.http.fetch, which call this module.
Details#
A connection runs one exchange at a time. In the callback of withExchange, call the steps in
order:
sendsends the head of aPreparedRequestfromsilk.http.request.writeSomesends the body, andfinishRequestends it. CallfinishRequestalso when there is no body.receiveorreceiveFinalreads a response head. ThenreadSomereads the body, orbeginContentandreadContentSomedecode it.finishResponseends the response. The connection can then run the next exchange.
Exchange.phase tells which step is next. A deadline in RequestOptions covers the whole
exchange, and is never restarted.
Errors#
ExchangeErrorkeeps the error from the lower module: the head parser, the body framing, content decoding, or the transport. A step out of order fails withInvalidState.OutOfMemoryErrorif theAllocatorfails.
Gotchas#
If the callback fails or returns before the response is complete, the connection closes and is not used again. Nothing is flushed, drained, or sent again for you. A 4xx or 5xx status is not an error.
Examples#
Send a GET request and read the body#
This example uses a transport with a fixed response in place of a network connection.
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.io.byte_duplex { ByteDuplex, ReadTransfer }
import silk.collections.bytes { Bytes }
import silk.core.effect { Effect }
import silk.http.message { Header, Method, ValueError, Version }
import silk.http.exchange { Connection, Exchange, ExchangeError, Limits, RequestOptions }
import silk.http.exchange as Client
import silk.http.headers { Headers, Limits as ValueLimits }
import silk.http.origin { Origin, OriginError }
import silk.http.request { BodyMode, HeaderPolicy, PreparedRequest, RequestError, fromUri }
import silk.http.transport { HttpTransport, TransportError }
import silk.collections.list { List }
import silk.io.memory_byte_duplex {
MemoryByteDuplex,
MemoryReadEvent,
MemoryWriteAction,
MemoryWriteEvent
}
import silk.system.monotonic_clock { MonotonicClock }
import silk.core.option { Option }
import silk.system.random { Random }
import silk.core.result { Result }
import silk.system.system_clock { Instant, SystemClock }
import silk.num.u64
import silk.uri.uri { Uri }
import silk.uri.reference { ParseError }
// A transport that answers with fixed bytes and accepts all writes.
struct ScriptedTransport {
memory: MemoryByteDuplex,
}
impl HttpTransport for ScriptedTransport {
effect fn readSomeRaw(
self: &mut Self,
output: &mut [u8],
deadline: Option<Instant>
) -> ReadTransfer
! TransportError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random {
let attempted = run Effect.result(ByteDuplex.readSome(&mut output, move deadline))
|> Effect.provideMut<ByteDuplex>(&mut self.memory)
return match move attempted {
Result.Success { value } => move value
Result.Failure { error } => {
fail TransportError.Plain { error: move error }
}
}
}
effect fn writeSomeRaw(self: &mut Self, input: &[u8], deadline: Option<Instant>) -> usize
! TransportError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random {
let attempted = run Effect.result(ByteDuplex.writeSome(input, move deadline))
|> Effect.provideMut<ByteDuplex>(&mut self.memory)
return match move attempted {
Result.Success { value } => value
Result.Failure { error } => {
fail TransportError.Plain { error: move error }
}
}
}
effect fn flush(self: &mut Self, deadline: Option<Instant>) -> ()
! TransportError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random {
drop deadline
return ()
}
effect fn close(self: &mut Self) -> ()
! TransportError {
return ()
}
}
struct FixedClock {}
impl MonotonicClock for FixedClock {
effect fn now(self: &mut Self) -> Instant {
return SystemClock.make(0, 0)
}
effect fn getResolution(self: &mut Self) -> u64 {
return u64.toU64(1)
}
effect fn waitUntil(self: &mut Self, when: Instant) -> () {
drop when
return ()
}
effect fn waitFor(self: &mut Self, howLong: u64) -> () {
drop howLong
return ()
}
}
struct ZeroRandom {}
impl Random for ZeroRandom {
effect fn fillBytes(self: &mut Self, output: &mut [u8]) -> () {
drop output
return ()
}
}
fn valueLimits() -> ValueLimits {
return Limits.defaults().head.values
}
// Sends the request and reads the body of the response.
effect<'call> fn readBody<'call, 'exchange: 'call>(
exchange: &'call mut Exchange<'exchange, ScriptedTransport>,
) -> i32
! ExchangeError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random {
run Client.send(&mut exchange.*)
// The request has no body, so it ends with no trailers.
let entries: [Header<'static>; 0] = []
let trailers = match Headers.make(&entries, valueLimits()) {
Result<Headers, ValueError>.Success { value } => value
Result<Headers, ValueError>.Failure { error: _ } => {
return 1
}
}
run Client.finishRequest(&mut exchange.*, &trailers)
let status = run Client.receiveFinal(&mut exchange.*)
if status != 200 {
return 1
}
let mut body: [u8; 16] = [0; 16]
let count = run Client.readSome(&mut exchange.*, &mut body)
if count != 5 || body[0] != 104 {
return 1
}
run Client.finishResponse(&mut exchange.*)
return 0
}
effect fn program() -> i32
! ExchangeError | RequestError | OutOfMemoryError
? &mut Allocator | &mut MonotonicClock | &mut Random {
let uri = match Uri.parse("http://example.com/") {
Result<Uri, ParseError>.Success { value } => value
Result<Uri, ParseError>.Failure { error: _ } => {
return 1
}
}
let origin = match Origin.fromUri(&uri) {
Result<Origin, OriginError>.Success { value } => value
Result<Origin, OriginError>.Failure { error: _ } => {
return 1
}
}
let entries: [Header<'static>; 0] = []
let headers = match Headers.make(&entries, valueLimits()) {
Result<Headers, ValueError>.Success { value } => value
Result<Headers, ValueError>.Failure { error: _ } => {
return 1
}
}
let policy = HeaderPolicy.defaults()
let request = run fromUri(
&uri,
Version.Http11,
Method.get(),
&headers,
&policy,
BodyMode.Empty,
false,
valueLimits(),
4096,
128,
)
let response = run Bytes.copy(b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello")
let mut reads = List<MemoryReadEvent>.make()
run List.append(
&mut reads,
MemoryReadEvent.Data { readyAt: SystemClock.make(0, 0), bytes: move response }
)
let mut writes = List<MemoryWriteEvent>.make()
run List.append(
&mut writes,
MemoryWriteEvent {
readyAt: SystemClock.make(0, 0),
action: MemoryWriteAction.Accept { count: 4096 }
},
)
let memory = run MemoryByteDuplex.make(move reads, move writes, 4096, 4, Option<i32>.none())
let mut connection = run Client.makeOwned(
ScriptedTransport { memory: move memory },
origin,
Version.Http11,
Limits.defaults(),
Option<Instant>.none(),
)
let result = run Client.withExchange(
&mut connection,
&request,
RequestOptions.defaults(),
readBody
)
run Client.close(&mut connection)
return result
}
effect fn failed<E>(error: E) -> i32 {
drop error
return 1
}
pub fn main() -> i32 {
let mut allocator = Allocator.systemAllocatorProvider()
let mut clock = FixedClock {}
let mut random = ZeroRandom {}
return run Effect.catchAll(program(), failed) |> Effect.provideMut<Random>(&mut random)
|> Effect.provideMut<MonotonicClock>(&mut clock)
|> Effect.provideMut<Allocator>(&mut allocator)
}Import as Exchange with import silk.http.exchange { Exchange }.
Public declarations: 44.
ConnectionPhase#
pub enum ConnectionPhaseThe state of a Connection.
Ready#
ReadyThe connection can start a new exchange.
Active#
ActiveAn exchange is running.
Closed#
ClosedThe connection cannot run more exchanges.
ExchangePhase#
pub enum ExchangePhaseThe next step of an Exchange.
Prepared#
PreparedCall send.
AwaitContinue#
AwaitContinueThe request has Expect: 100-continue. Call receive to wait for 100 Continue or an early
final response.
Upload#
UploadCall writeSome for the body, then finishRequest.
AwaitResponse#
AwaitResponseCall receive or receiveFinal.
Response#
ResponseRead the body with readSome or readContentSome, then call finishResponse.
Tunnel#
TunnelA CONNECT succeeded. Call withTunnel.
Finished#
FinishedThe request and the response are complete.
Aborted#
AbortedThe exchange stopped. No more steps are possible.
ContinuePolicy#
pub union ContinuePolicyWhether the client waits for 100 Continue before it sends the body.
Disabled#
DisabledSend the whole body before the response is read.
Require100#
Require100Wait for 100 Continue until a deadline. If it does not come, fail with ContinueTimeout. The
request needs Expect: 100-continue.
Field deadline#
pub deadline: InstantThe deadline on the MonotonicClock. The deadline of the exchange also applies.
RequestOptions#
pub struct RequestOptionsThe deadlines of one exchange. They are never restarted between steps.
Field deadline#
pub deadline: Option<Instant>The deadline of the whole exchange on the MonotonicClock, or None for no deadline.
Field continuePolicy#
pub continuePolicy: ContinuePolicyWhether to wait for 100 Continue. It must match the PreparedRequest.
Limits#
pub struct LimitsThe limits of a connection: buffers, heads, bodies, and request counts. Start from
Limits.defaults.
Field head#
pub head: HeadLimitsThe limits for response heads.
Field requestBody#
pub requestBody: BodyLimitsThe limits for request bodies.
Field responseBody#
pub responseBody: BodyLimitsThe limits for response bodies.
Field readCapacity#
pub readCapacity: usizeThe size of the read buffer. It must not be 0.
Field writeCapacity#
pub writeCapacity: usizeThe size of the write buffer for framing. It must not be 0.
Field maxRequests#
pub maxRequests: usizeThe maximum number of requests on one connection.
Field maxInformationalResponses#
pub maxInformationalResponses: usizeThe maximum number of 1xx responses in one exchange. The default is 16.
Field maxInformationalWireBytes#
pub maxInformationalWireBytes: u64The maximum total size of the 1xx responses in one exchange. The default is 65536.
Field maxDiscardWireBytes#
pub maxDiscardWireBytes: u64The maximum bytes that one discard or drain call reads. It is at most 1048576.
Implementation Copy for Limits#
impl Copy for LimitsImplementation Limits#
impl LimitsAssociated function defaults#
pub fn defaults() -> LimitsReturns the default limits: 16 MiB bodies, 256 KiB heads with field values up to 64 KiB, and
128 requests for each connection. Servers send long values: GitHub's release downloads
answer with a Content-Security-Policy of over 9 KiB.
Implementation RequestOptions#
impl RequestOptionsAssociated function defaults#
pub fn defaults() -> RequestOptionsReturns options with no deadline that do not wait for 100 Continue.
ExchangeError#
pub union ExchangeErrorThe error from a connection or an exchange.
Details#
It keeps the error from the lower module: the head parser, the body framing, content decoding,
or the transport. A step out of order fails with InvalidState.
Content#
ContentContent decoding failed.
Field error#
pub error: ContentError<'static>The error from silk.http.content.
Head#
HeadA response head is not valid, or the head limits are not valid.
Field error#
pub error: ParseErrorThe error from silk.http.head.
Body#
BodyThe body framing failed.
Field error#
pub error: BodyErrorThe error from silk.http.body.
Transport#
TransportThe transport failed.
Field error#
pub error: TransportErrorThe error from silk.http.transport.
Output#
OutputThe transport failed during a write. progress tells what was already accepted and sent.
Field error#
pub error: TransportErrorThe error from silk.http.transport.
Field progress#
pub progress: WriteProgressThe bytes accepted and sent by the call before the error.
OutputTimeout#
OutputTimeoutThe deadline passed during a write.
Field progress#
pub progress: WriteProgressThe bytes accepted and sent by the call before the deadline.
OutputAllocation#
OutputAllocationThe transport could not allocate during a write.
Field error#
pub error: OutOfMemoryErrorThe allocation error.
Field progress#
pub progress: WriteProgressThe bytes accepted and sent by the call before the error.
InvalidState#
InvalidStateThe step is not valid in the current phase. Nothing was sent or read.
InvalidRequest#
InvalidRequestThe request does not fit the connection, for example another version or another server, or its body framing does not fit the options.
InvalidLimits#
InvalidLimitsA buffer size in the limits is 0.
LimitExceeded#
LimitExceededToo many 1xx responses, or too many requests on the connection.
TruncatedHead#
TruncatedHeadThe connection ended before a complete response head.
UnsupportedUpgrade#
UnsupportedUpgradeThe server sent 101 Switching Protocols, which this module does not support.
ContinueTimeout#
ContinueTimeoutThe deadline of Require100 passed without 100 Continue.
Timeout#
TimeoutThe deadline of the exchange passed.
Implementation Debug for ExchangeError#
impl Debug for ExchangeErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant and its fields, such as Content { ... }.
validateLimits#
pub fn validateLimits(limits: &Limits) -> Result<(), ExchangeError>Checks limits without allocation or I/O.
Details#
The buffer sizes must not be 0, and maxDiscardWireBytes must be at most 1048576. The head
limits must pass validateLimits of silk.http.head. That error is returned in Head.
DiscardOutcome#
pub enum DiscardOutcomeThe result of a discard or a drain.
Completed#
CompletedThe whole response was read within the limit.
CapReached#
CapReachedThe limit was reached before the end of the response. The connection cannot be used again.
ReuseEligibility#
pub union ReuseEligibilityWhether a connection can go back to a pool. See reuseEligibility.
Eligible#
EligibleThe connection can go back to a pool.
NotReady#
NotReadyThe last exchange did not complete.
NotOwned#
NotOwnedThe connection does not own its transport, so it cannot close it later.
RequestLimit#
RequestLimitThe connection has reached maxRequests.
RetainedInput#
RetainedInputThe server sent bytes after the end of the response.
Field bytes#
pub bytes: usizeThe number of extra bytes.
Implementation Copy for ReuseEligibility#
impl Copy for ReuseEligibilityWriteProgress#
pub struct WriteProgressThe progress of one write call.
Field consumed#
pub consumed: usizeThe bytes of your input that the call accepted.
Field written#
pub written: usizeThe framed bytes that the call sent to the transport.
Field totalPayload#
pub totalPayload: u64The total body bytes accepted so far.
Field totalWire#
pub totalWire: u64The total framed bytes sent so far.
Implementation Copy for WriteProgress#
impl Copy for WriteProgressImplementation Debug for WriteProgress#
impl Debug for WriteProgressOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the fields, as WriteProgress { consumed: ..., written: ..., ... }.
Connection#
pub struct Connection<P>An HTTP/1 connection over the transport P. It keeps its buffers and the bytes that it read
after the last response.
When to use#
Use it to stream a request or a response body, or to reuse a connection. Make one with
makeOwned, withOwned, or withConnected, or let withRoute connect through a proxy
route. Then call withExchange for each request. Most programs use silk.http.client or
silk.http.fetch instead.
Details#
A connection runs one exchange at a time. In the callback of withExchange, call the steps in
order: send, then writeSome and finishRequest, then receive or receiveFinal
and readSome or readContentSome, then finishResponse.
Gotchas#
If an exchange callback fails or returns before the response is complete, the connection closes and is not used again. Nothing is flushed, drained, or sent again for you.
Implementation Connection<P>#
impl<P> Connection<P>Method phase#
pub fn phase(self: &Self) -> ConnectionPhaseReturns the state of the connection.
Method unread#
pub fn unread(self: &Self) -> usizeReturns the number of bytes that were read after the last response.
reuseEligibility#
pub fn reuseEligibility<P>(connection: &Connection<P>) -> ReuseEligibilityReturns whether the connection can go back to a pool.
Eligible needs a Ready connection that owns its transport, has requests left, and has no
extra bytes. Other values tell the first check that failed.
TransportContext#
pub struct TransportContext<'view, P>A shared view of the transport of a connection, to inspect its route.
Field transport#
pub transport: &'view PThe transport.
Implementation Connection<P>#
impl<P> Connection<P>Method transportContext#
pub fn transportContext<'view>(self: &'view Self) -> TransportContext<'view, P>Returns a shared view of the transport.
Exchange#
pub struct Exchange<'exchange, P>One request and its response on a connection. You get it in the callback of withExchange.
Implementation Exchange<'exchange, P>#
impl<'exchange, P> Exchange<'exchange, P>Method phase#
pub fn phase(self: &Self) -> ExchangePhaseReturns the next step.
Method head#
pub effect fn head<'head>(self: &'head Self) -> ResponseHead<'head> ! ExchangeErrorReturns the last response head that was received. Copy it with ResponseHead.copy to keep it.
ConnectionHandler#
pub interface ConnectionHandler<P, A, E, ?R>The code that uses a connection in withOwned or withConnected. It cannot keep the
connection.
Operation handle#
effect<'call> fn handle<'call>(handler: Self, connection: &'call mut Connection<P>) -> A ! E ? R where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random,
&mut P provides &HttpTransport from &mut HttpTransportUses the connection. The connection is closed or kept after this call returns.
RouteProtocol#
pub enum RouteProtocolThe HTTP version, and the ALPN that TLS sends, for a route.
Http10#
Http10Use HTTP/1.0, and send no ALPN.
OptionalHttp11#
OptionalHttp11Use HTTP/1.1. The TLS server can choose no ALPN protocol.
RequiredHttp11#
RequiredHttp11Use HTTP/1.1. The TLS server must choose http/1.1.
RouteSettings#
pub struct RouteSettingsThe settings for withRoute.
Field http#
pub http: LimitsThe limits of the connection.
Field request#
pub request: HeaderLimitsThe value limits for the CONNECT request to a proxy.
Field maxRequestHeadBytes#
pub maxRequestHeadBytes: usizeThe maximum size of the CONNECT head.
Field maxProxyCredentialBytes#
pub maxProxyCredentialBytes: usizeThe maximum size of the proxy credentials in the CONNECT head.
Field protocol#
pub protocol: RouteProtocolThe HTTP version and ALPN.
Field tls#
pub tls: ClientLimitsThe limits of the TLS client.
Field handshakeDurationNanoseconds#
pub handshakeDurationNanoseconds: u64The maximum duration of the TLS handshake, in nanoseconds.
Implementation Copy for RouteSettings#
impl Copy for RouteSettingsAcquiredRouteContext#
pub service AcquiredRouteContext<P, A, E, ?R>The service that a RouteClient calls with the transport that it opened.
Operation use#
effect<'env> fn use<'env>(provider: P) -> A ! E ? R | &mut AcquiredRouteContext<P, A, E, R> where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random,
&mut P provides &HttpTransport from &mut HttpTransport,
&mut P provides &ByteDuplex from &mut ByteDuplex | &mut MonotonicClock | &mut Allocator | &mut Random,
&mut P provides &ByteDuplex from &mut ByteDuplex,
R in Without<R, ByteDuplex>,
R in Without<R, HttpTransport>Uses provider, the transport to the server that the route selected.
RouteClient#
pub interface RouteClient<P, A, E, AcquisitionError, ?AcquisitionRequirements, ?R>Opens transports for withRoute: a direct connection or a connection to a proxy.
Operation settings#
fn settings(client: &Self) -> RouteSettingsReturns the HTTP and TLS settings. No default is added.
Operation acquire#
effect<'env> fn acquire<'env>(client: Self, peer: Origin, deadline: Option<Instant>) -> A ! E | AcquisitionError ? AcquisitionRequirements | R | &mut AcquiredRouteContext<
P,
A,
E,
R,
> where R in Without<R, ByteDuplex>, R in Without<R, HttpTransport>Opens a transport to peer before deadline, and gives it to AcquiredRouteContext.use.
RouteTransport#
pub struct RouteTransport<'transport, 'provider: 'transport, 'tunnel: 'provider, P>The transport that withRoute gives to your handler.
RouteHandler#
pub interface RouteHandler<'configuration, P, A, E, ?R>The code that uses the connection of withRoute.
Operation handle#
effect<'call> fn handle<'call, 'transport: 'call, 'provider: 'transport, 'tunnel: 'provider>(
handler: Self,
route: Route<'configuration>,
connection: &'call mut Connection<RouteTransport<'transport, 'provider, 'tunnel, P>>,
) -> A ! E ? R where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random,
&mut P provides &HttpTransport from &mut HttpTransport,
&mut P provides &ByteDuplex from &mut ByteDuplex | &mut MonotonicClock | &mut Allocator | &mut Random,
&mut P provides &ByteDuplex from &mut ByteDuplexUses the connection after it is open, and after TLS checked the origin.
Implementation RouteTransport<'transport, 'provider, 'tunnel, P>#
impl<'transport, 'provider: 'transport, 'tunnel: 'provider, P> RouteTransport<
'transport,
'provider,
'tunnel,
P,
>Implementation HttpTransport for RouteTransport<'transport, 'provider, 'tunnel, P>#
impl<'transport, 'provider: 'transport, 'tunnel: 'provider, P> HttpTransport for RouteTransport<
'transport,
'provider,
'tunnel,
P,
>
where
&mut P provides &HttpTransport from &mut HttpTransport,
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random,
&mut P provides &ByteDuplex from &mut ByteDuplex,
&mut P provides &ByteDuplex from &mut ByteDuplex | &mut MonotonicClock | &mut Allocator | &mut RandomOperation readSomeRaw#
readSomeRaw: RouteTransport.readOperation writeSomeRaw#
writeSomeRaw: RouteTransport.writeOperation flush#
flush: RouteTransport.flushOutputOperation close#
close: RouteTransport.closeTransportwithRoute#
pub effect<'env> fn withRoute<
'configuration,
P,
A,
E,
AcquisitionError,
?R,
?AcquisitionRequirements,
H: RouteHandler<'configuration, P, A, E ? R> + 'env,
C: RouteClient<P, A, E | ProxyError | ExchangeError | Tls.ConnectionError | IdentityError | OutOfMemoryError, AcquisitionError, AcquisitionRequirements ? R | &mut Allocator | &mut MonotonicClock | &mut SystemClock | &mut Random> + 'env,
'env,
>(
client: C,
route: Route<'configuration>,
preparedTrust: Option<TrustSnapshot>,
deadline: Option<Instant>,
handler: H,
) -> A
! E | AcquisitionError | ProxyError | ExchangeError | Tls.ConnectionError | IdentityError | OutOfMemoryError
? R | AcquisitionRequirements | &mut Allocator | &mut MonotonicClock | &mut SystemClock | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random,
&mut P provides &HttpTransport from &mut HttpTransport,
&mut P provides &ByteDuplex from &mut ByteDuplex | &mut MonotonicClock | &mut Allocator | &mut Random,
&mut P provides &ByteDuplex from &mut ByteDuplex,
R in Without<R, ByteDuplex>,
R in Without<R, HttpTransport>Opens a connection for route, gives it to handler, and closes it.
Details#
The trust for TLS is checked before the connection opens. A Forward route uses the plain
connection to the proxy. A Tunnel route sends CONNECT, then does TLS with the origin through
the tunnel. One deadline covers all steps.
makeOwned#
pub effect<'env> fn makeOwned<P: 'env, 'env>(
transport: P,
origin: Origin,
version: Version,
limits: Limits,
overallDeadline: Option<Instant>,
) -> Connection<P>
! ExchangeError | OutOfMemoryError
? &mut Allocator
where &mut P provides &HttpTransport from &mut HttpTransportReturns a connection that owns transport, so that you can keep it for more exchanges. If
allocation fails, transport is closed.
withOwned#
pub effect<'env> fn withOwned<P: 'env, A, E, ?R, H: ConnectionHandler<P, A, E ? R> + 'env, 'env>(
transport: P,
origin: Origin,
version: Version,
limits: Limits,
overallDeadline: Option<Instant>,
handler: H,
) -> A
! E | ExchangeError | OutOfMemoryError
? R | &mut Allocator
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random,
&mut P provides &HttpTransport from &mut HttpTransportMakes a connection that owns transport, gives it to handler, and closes it when handler
returns or fails.
withConnected#
pub effect<'env> fn withConnected<'env, P, A, E, ?R, H: ConnectionHandler<Loan<'env, P>, A, E ? R>>(
transport: &'env mut P,
origin: Origin,
version: Version,
limits: Limits,
overallDeadline: Option<Instant>,
handler: H,
) -> A
! E | ExchangeError | OutOfMemoryError
? R | &mut Allocator
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random,
&mut P provides &HttpTransport from &mut HttpTransportGives a connection over a borrowed transport to handler. The connection closes when
handler returns, but you still own transport.
close#
pub effect fn close<P>(connection: &mut Connection<P>) -> ()
! ExchangeError
where &mut P provides &HttpTransport from &mut HttpTransportCloses the connection and its transport, without a flush or a drain. A second call does nothing.
withExchange#
pub effect fn withExchange<'exchange, 'callback, P, A, E, ?R>(
connection: &'exchange mut Connection<P>,
request: &PreparedRequest,
requestedOptions: RequestOptions,
callback: for<'call, 'exchangeView: 'call> once fn<'callback>(
&'call mut Exchange<'exchangeView, P>,
) -> once Effect<'call & 'callback; A ! E ? R>,
) -> A ! E | ExchangeError | OutOfMemoryError ? R | &mut AllocatorRuns one exchange of request on the connection, in callback.
In callback, call the steps in order:
sendsends the head of aPreparedRequestfromsilk.http.request.writeSomesends the body, andfinishRequestends it. CallfinishRequestalso when there is no body.receiveorreceiveFinalreads a response head. ThenreadSomereads the body, orbeginContentandreadContentSomedecode it.finishResponseends the response. The connection can then run the next exchange.
Exchange.phase tells which step is next. A step out of order fails with InvalidState. A
deadline in RequestOptions covers the whole exchange, and is never restarted.
Gotchas#
If the callback fails, or returns before the response is complete, the connection closes and cannot run another exchange. Nothing is flushed, drained, or sent again for you. A 4xx or 5xx status is not an error.
send#
pub effect fn send<'exchange, P>(exchange: &mut Exchange<'exchange, P>) -> ()
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomSends the request head. With Require100, it also flushes, so that the server can answer before
the body.
writeSome#
pub effect fn writeSome<'exchange, P>(exchange: &mut Exchange<'exchange, P>, input: &[u8]) -> WriteProgress
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomSends a prefix of input as body, and returns the progress. On an error, the error keeps the
progress.
finishRequest#
pub effect fn finishRequest<'exchange, P>(exchange: &mut Exchange<'exchange, P>, trailers: &Headers) -> ()
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomEnds the request body with trailers, and flushes. Call it also when there is no body. Then
read the response.
receive#
pub effect fn receive<'exchange, P>(exchange: &mut Exchange<'exchange, P>) -> u16
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomReads one response head, and returns its status. A 1xx status is also returned. A 4xx or 5xx status is not an error.
receiveFinal#
pub effect fn receiveFinal<'exchange, P>(exchange: &mut Exchange<'exchange, P>) -> u16
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomReads response heads until one is not 1xx, and returns its status.
Details#
It does not copy the head and does not read the body. Get the head with Exchange.head.
readSome#
pub effect fn readSome<'exchange, P>(exchange: &mut Exchange<'exchange, P>, output: &mut [u8]) -> usize
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomReads body bytes into output, and returns how many. It returns 0 at the end of the body. It
never reads past the end of the response.
discardRemainingAtMost#
pub effect fn discardRemainingAtMost<'exchange, P>(
exchange: &mut Exchange<'exchange, P>,
maxWireBytes: u64,
deadline: Option<Instant>,
) -> DiscardOutcome
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomReads and drops the rest of the response body, up to maxWireBytes bytes on the wire.
When to use#
Use it when you can continue if the limit is reached.
Details#
The limit is the smaller of maxWireBytes and Limits.maxDiscardWireBytes, and counts the body
and its framing. deadline cannot be later than the deadline of the exchange. After
Completed, call finishResponse. After CapReached, the connection cannot be used again.
Gotchas#
Bad framing, a read error, the deadline, and the allocator still fail with their errors.
drainAndFinishAtMost#
pub effect fn drainAndFinishAtMost<'exchange, P>(
exchange: &mut Exchange<'exchange, P>,
maxWireBytes: u64,
deadline: Instant,
) -> DiscardOutcome
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomReads and drops the rest of the response, then calls finishResponse.
When to use#
Use it when the response must complete so that the connection can be used again. A good limit is 65536 bytes. The maximum is 1048576.
Details#
The limit is the smaller of maxWireBytes and Limits.maxDiscardWireBytes, and counts the
body, the framing, and the trailers. deadline cannot be later than the deadline of the
exchange. If a content decoder was selected, it is checked too.
Gotchas#
After CapReached, the response is not complete. Errors keep their original types.
discardRemaining#
pub effect fn discardRemaining<'exchange, P>(
exchange: &mut Exchange<'exchange, P>,
maxWireBytes: u64,
) -> ()
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomReads and drops the rest of the response body, up to the smaller of maxWireBytes and the limit
of the client. On an error or at the limit, the connection cannot be used again.
trailers#
pub effect fn trailers<'head, 'exchange, P>(exchange: &'head Exchange<'exchange, P>) -> Option<
Trailers<'head>,
> ! ExchangeErrorReturns the trailers of a complete chunked response, or None.
finishResponse#
pub effect fn finishResponse<'exchange, P>(exchange: &mut Exchange<'exchange, P>) -> ()
! ExchangeErrorEnds the response. The body must be complete. This function does not read the rest of the body for you.
abort#
pub fn abort<'exchange, P>(exchange: &mut Exchange<'exchange, P>) -> ()Stops the exchange. The connection cannot be used again. Nothing is sent or read.
Tunnel#
pub struct Tunnel<'tunnel, P>The connection after a successful CONNECT. Reads first return the bytes that came after the
response head.
TransferredTunnel#
pub struct TransferredTunnel<'tunnel, P>A tunnel that owns the transport of the connection.
Details#
Reads first return the bytes that came after the response head. The deadline of the exchange also applies. Closing it closes the transport one time.
withTunnel#
pub effect fn withTunnel<'tunnel, 'exchange: 'tunnel, 'callback, P, A, E, ?R>(
exchange: &'tunnel mut Exchange<'exchange, P>,
callback: for<'call, 'tunnelView: 'call> once fn<'callback>(&'call mut Tunnel<'tunnelView, P>) -> once Effect<'call & 'callback; A ! E ? R>,
) -> A ! E | ExchangeError ? RGives the tunnel of a successful CONNECT to callback. The connection cannot run HTTP again.
Implementation Tunnel<'tunnel, P>#
impl<'tunnel, P> Tunnel<'tunnel, P>Method transferByteDuplex#
pub effect fn transferByteDuplex<'transfer, 'callback, A, E, ?CallbackRequirements>(
self: &'transfer mut Self,
callback: for<'call, 'duplexView: 'call> once fn<'callback>(
&'call mut TransferredTunnel<'duplexView, P>,
) -> once Effect<'call & 'callback; A ! E ? CallbackRequirements>,
) -> A
! E | ExchangeError
? CallbackRequirements
where
&mut P provides &ByteDuplex from &mut ByteDuplex,
CallbackRequirements in Without<CallbackRequirements, ByteDuplex>Gives the tunnel to callback as a byte duplex, for example for TLS.
Details#
This does not allocate. The duplex closes after the callback returns, fails, or is cancelled.
Gotchas#
A second call returns InvalidState.
Method readSome#
pub effect fn readSome(self: &mut Self, output: &mut [u8], requestedDeadline: Option<Instant>) -> ReadTransfer
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomReads bytes. It returns the bytes that came after the response head first.
Method writeSome#
pub effect fn writeSome(self: &mut Self, input: &[u8], requestedDeadline: Option<Instant>) -> usize
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomWrites a prefix of input, and returns its length.
Implementation TransferredTunnel<'tunnel, P>#
impl<'tunnel, P> TransferredTunnel<'tunnel, P>Implementation ByteDuplex for TransferredTunnel<'tunnel, P>#
impl<'tunnel, P> ByteDuplex for TransferredTunnel<'tunnel, P>
where
&mut P provides &ByteDuplex from &mut ByteDuplex,
&mut P provides &ByteDuplex from &mut ByteDuplex | &mut MonotonicClockOperation readSomeRaw#
readSomeRaw: TransferredTunnel.readOperation writeSomeRaw#
writeSomeRaw: TransferredTunnel.writeOperation writeSomeVectoredRaw#
writeSomeVectoredRaw: TransferredTunnel.writeVectoredOperation flushRaw#
flushRaw: TransferredTunnel.flushOutputOperation shutdownWriteRaw#
shutdownWriteRaw: TransferredTunnel.shutdownOutputOperation closeRaw#
closeRaw: TransferredTunnel.closeTransportbeginContent#
pub effect fn beginContent<'head, 'exchange: 'head, P>(
exchange: &'head mut Exchange<'exchange, P>,
mode: ContentMode,
limits: ContentLimits,
) -> AppliedPlan
! ExchangeError | ContentError<'head> | OutOfMemoryError
? &mut Allocator | &mut MonotonicClockSelects how to decode the response body, from its head and mode, before you read it.
Details#
Handle a planning error before you use the exchange again, because the error borrows the head. The result tells which decoding was selected.
readContentSome#
pub effect fn readContentSome<'exchange, P>(
exchange: &mut Exchange<'exchange, P>,
output: &mut [u8],
) -> ContentProgress
! ExchangeError | OutOfMemoryError
? &mut MonotonicClock | &mut Allocator | &mut Random
where
&mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut RandomReads decoded body bytes into output. The bytes are final only after the content and the
framing complete.
Category: HTTP.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.