Browse the docs

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:

  1. send sends the head of a PreparedRequest from silk.http.request.

  2. writeSome sends the body, and finishRequest ends it. Call finishRequest also when there is no body.

  3. receive or receiveFinal reads a response head. Then readSome reads the body, or beginContent and readContentSome decode it.

  4. finishResponse ends 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#

  • ExchangeError 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.

  • OutOfMemoryError if the Allocator fails.

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.

silk
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#

silk
pub enum ConnectionPhase

The state of a Connection.

Ready#

silk
Ready

The connection can start a new exchange.

Active#

silk
Active

An exchange is running.

Closed#

silk
Closed

The connection cannot run more exchanges.

ExchangePhase#

silk
pub enum ExchangePhase

The next step of an Exchange.

Prepared#

silk
Prepared

Call send.

AwaitContinue#

silk
AwaitContinue

The request has Expect: 100-continue. Call receive to wait for 100 Continue or an early final response.

Upload#

silk
Upload

Call writeSome for the body, then finishRequest.

AwaitResponse#

silk
AwaitResponse

Call receive or receiveFinal.

Response#

silk
Response

Read the body with readSome or readContentSome, then call finishResponse.

Tunnel#

silk
Tunnel

A CONNECT succeeded. Call withTunnel.

Finished#

silk
Finished

The request and the response are complete.

Aborted#

silk
Aborted

The exchange stopped. No more steps are possible.

ContinuePolicy#

silk
pub union ContinuePolicy

Whether the client waits for 100 Continue before it sends the body.

Disabled#

silk
Disabled

Send the whole body before the response is read.

Require100#

silk
Require100

Wait for 100 Continue until a deadline. If it does not come, fail with ContinueTimeout. The request needs Expect: 100-continue.

Field deadline#

silk
pub deadline: Instant

The deadline on the MonotonicClock. The deadline of the exchange also applies.

RequestOptions#

silk
pub struct RequestOptions

The deadlines of one exchange. They are never restarted between steps.

Field deadline#

silk
pub deadline: Option<Instant>

The deadline of the whole exchange on the MonotonicClock, or None for no deadline.

Field continuePolicy#

silk
pub continuePolicy: ContinuePolicy

Whether to wait for 100 Continue. It must match the PreparedRequest.

Limits#

silk
pub struct Limits

The limits of a connection: buffers, heads, bodies, and request counts. Start from Limits.defaults.

Field head#

silk
pub head: HeadLimits

The limits for response heads.

Field requestBody#

silk
pub requestBody: BodyLimits

The limits for request bodies.

Field responseBody#

silk
pub responseBody: BodyLimits

The limits for response bodies.

Field readCapacity#

silk
pub readCapacity: usize

The size of the read buffer. It must not be 0.

Field writeCapacity#

silk
pub writeCapacity: usize

The size of the write buffer for framing. It must not be 0.

Field maxRequests#

silk
pub maxRequests: usize

The maximum number of requests on one connection.

Field maxInformationalResponses#

silk
pub maxInformationalResponses: usize

The maximum number of 1xx responses in one exchange. The default is 16.

Field maxInformationalWireBytes#

silk
pub maxInformationalWireBytes: u64

The maximum total size of the 1xx responses in one exchange. The default is 65536.

Field maxDiscardWireBytes#

silk
pub maxDiscardWireBytes: u64

The maximum bytes that one discard or drain call reads. It is at most 1048576.

Implementation Copy for Limits#

silk
impl Copy for Limits

Implementation Limits#

silk
impl Limits

Associated function defaults#

silk
pub fn defaults() -> Limits

Returns 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#

silk
impl RequestOptions

Associated function defaults#

silk
pub fn defaults() -> RequestOptions

Returns options with no deadline that do not wait for 100 Continue.

ExchangeError#

silk
pub union ExchangeError

The 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#

silk
Content

Content decoding failed.

Field error#

silk
pub error: ContentError<'static>

The error from silk.http.content.

silk
Head

A response head is not valid, or the head limits are not valid.

Field error#

silk
pub error: ParseError

The error from silk.http.head.

Body#

silk
Body

The body framing failed.

Field error#

silk
pub error: BodyError

The error from silk.http.body.

Transport#

silk
Transport

The transport failed.

Field error#

silk
pub error: TransportError

The error from silk.http.transport.

Output#

silk
Output

The transport failed during a write. progress tells what was already accepted and sent.

Field error#

silk
pub error: TransportError

The error from silk.http.transport.

Field progress#

silk
pub progress: WriteProgress

The bytes accepted and sent by the call before the error.

OutputTimeout#

silk
OutputTimeout

The deadline passed during a write.

Field progress#

silk
pub progress: WriteProgress

The bytes accepted and sent by the call before the deadline.

OutputAllocation#

silk
OutputAllocation

The transport could not allocate during a write.

Field error#

silk
pub error: OutOfMemoryError

The allocation error.

Field progress#

silk
pub progress: WriteProgress

The bytes accepted and sent by the call before the error.

InvalidState#

silk
InvalidState

The step is not valid in the current phase. Nothing was sent or read.

InvalidRequest#

silk
InvalidRequest

The request does not fit the connection, for example another version or another server, or its body framing does not fit the options.

InvalidLimits#

silk
InvalidLimits

A buffer size in the limits is 0.

LimitExceeded#

silk
LimitExceeded

Too many 1xx responses, or too many requests on the connection.

TruncatedHead#

silk
TruncatedHead

The connection ended before a complete response head.

UnsupportedUpgrade#

silk
UnsupportedUpgrade

The server sent 101 Switching Protocols, which this module does not support.

ContinueTimeout#

silk
ContinueTimeout

The deadline of Require100 passed without 100 Continue.

Timeout#

silk
Timeout

The deadline of the exchange passed.

Implementation Debug for ExchangeError#

silk
impl Debug for ExchangeError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant and its fields, such as Content { ... }.

validateLimits#

silk
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#

silk
pub enum DiscardOutcome

The result of a discard or a drain.

Completed#

silk
Completed

The whole response was read within the limit.

CapReached#

silk
CapReached

The limit was reached before the end of the response. The connection cannot be used again.

ReuseEligibility#

silk
pub union ReuseEligibility

Whether a connection can go back to a pool. See reuseEligibility.

Eligible#

silk
Eligible

The connection can go back to a pool.

NotReady#

silk
NotReady

The last exchange did not complete.

NotOwned#

silk
NotOwned

The connection does not own its transport, so it cannot close it later.

RequestLimit#

silk
RequestLimit

The connection has reached maxRequests.

RetainedInput#

silk
RetainedInput

The server sent bytes after the end of the response.

Field bytes#

silk
pub bytes: usize

The number of extra bytes.

Implementation Copy for ReuseEligibility#

silk
impl Copy for ReuseEligibility

WriteProgress#

silk
pub struct WriteProgress

The progress of one write call.

Field consumed#

silk
pub consumed: usize

The bytes of your input that the call accepted.

Field written#

silk
pub written: usize

The framed bytes that the call sent to the transport.

Field totalPayload#

silk
pub totalPayload: u64

The total body bytes accepted so far.

Field totalWire#

silk
pub totalWire: u64

The total framed bytes sent so far.

Implementation Copy for WriteProgress#

silk
impl Copy for WriteProgress

Implementation Debug for WriteProgress#

silk
impl Debug for WriteProgress

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the fields, as WriteProgress { consumed: ..., written: ..., ... }.

Connection#

silk
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>#

silk
impl<P> Connection<P>

Method phase#

silk
pub fn phase(self: &Self) -> ConnectionPhase

Returns the state of the connection.

Method unread#

silk
pub fn unread(self: &Self) -> usize

Returns the number of bytes that were read after the last response.

reuseEligibility#

silk
pub fn reuseEligibility<P>(connection: &Connection<P>) -> ReuseEligibility

Returns 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#

silk
pub struct TransportContext<'view, P>

A shared view of the transport of a connection, to inspect its route.

Field transport#

silk
pub transport: &'view P

The transport.

Implementation Connection<P>#

silk
impl<P> Connection<P>

Method transportContext#

silk
pub fn transportContext<'view>(self: &'view Self) -> TransportContext<'view, P>

Returns a shared view of the transport.

Exchange#

silk
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>#

silk
impl<'exchange, P> Exchange<'exchange, P>

Method phase#

silk
pub fn phase(self: &Self) -> ExchangePhase

Returns the next step.

Method head#

silk
pub effect fn head<'head>(self: &'head Self) -> ResponseHead<'head> ! ExchangeError

Returns the last response head that was received. Copy it with ResponseHead.copy to keep it.

ConnectionHandler#

silk
pub interface ConnectionHandler<P, A, E, ?R>

The code that uses a connection in withOwned or withConnected. It cannot keep the connection.

Operation handle#

silk
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 HttpTransport

Uses the connection. The connection is closed or kept after this call returns.

RouteProtocol#

silk
pub enum RouteProtocol

The HTTP version, and the ALPN that TLS sends, for a route.

Http10#

silk
Http10

Use HTTP/1.0, and send no ALPN.

OptionalHttp11#

silk
OptionalHttp11

Use HTTP/1.1. The TLS server can choose no ALPN protocol.

RequiredHttp11#

silk
RequiredHttp11

Use HTTP/1.1. The TLS server must choose http/1.1.

RouteSettings#

silk
pub struct RouteSettings

The settings for withRoute.

Field http#

silk
pub http: Limits

The limits of the connection.

Field request#

silk
pub request: HeaderLimits

The value limits for the CONNECT request to a proxy.

Field maxRequestHeadBytes#

silk
pub maxRequestHeadBytes: usize

The maximum size of the CONNECT head.

Field maxProxyCredentialBytes#

silk
pub maxProxyCredentialBytes: usize

The maximum size of the proxy credentials in the CONNECT head.

Field protocol#

silk
pub protocol: RouteProtocol

The HTTP version and ALPN.

Field tls#

silk
pub tls: ClientLimits

The limits of the TLS client.

Field handshakeDurationNanoseconds#

silk
pub handshakeDurationNanoseconds: u64

The maximum duration of the TLS handshake, in nanoseconds.

Implementation Copy for RouteSettings#

silk
impl Copy for RouteSettings

AcquiredRouteContext#

silk
pub service AcquiredRouteContext<P, A, E, ?R>

The service that a RouteClient calls with the transport that it opened.

Operation use#

silk
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#

silk
pub interface RouteClient<P, A, E, AcquisitionError, ?AcquisitionRequirements, ?R>

Opens transports for withRoute: a direct connection or a connection to a proxy.

Operation settings#

silk
fn settings(client: &Self) -> RouteSettings

Returns the HTTP and TLS settings. No default is added.

Operation acquire#

silk
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#

silk
pub struct RouteTransport<'transport, 'provider: 'transport, 'tunnel: 'provider, P>

The transport that withRoute gives to your handler.

RouteHandler#

silk
pub interface RouteHandler<'configuration, P, A, E, ?R>

The code that uses the connection of withRoute.

Operation handle#

silk
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 ByteDuplex

Uses the connection after it is open, and after TLS checked the origin.

Implementation RouteTransport<'transport, 'provider, 'tunnel, P>#

silk
impl<'transport, 'provider: 'transport, 'tunnel: 'provider, P> RouteTransport<
  'transport,
  'provider,
  'tunnel,
  P,
>

Implementation HttpTransport for RouteTransport<'transport, 'provider, 'tunnel, P>#

silk
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 Random

Operation readSomeRaw#

silk
readSomeRaw: RouteTransport.read

Operation writeSomeRaw#

silk
writeSomeRaw: RouteTransport.write

Operation flush#

silk
flush: RouteTransport.flushOutput

Operation close#

silk
close: RouteTransport.closeTransport

withRoute#

silk
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#

silk
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 HttpTransport

Returns a connection that owns transport, so that you can keep it for more exchanges. If allocation fails, transport is closed.

withOwned#

silk
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 HttpTransport

Makes a connection that owns transport, gives it to handler, and closes it when handler returns or fails.

withConnected#

silk
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 HttpTransport

Gives a connection over a borrowed transport to handler. The connection closes when handler returns, but you still own transport.

close#

silk
pub effect fn close<P>(connection: &mut Connection<P>) -> ()
! ExchangeError
where &mut P provides &HttpTransport from &mut HttpTransport

Closes the connection and its transport, without a flush or a drain. A second call does nothing.

withExchange#

silk
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 Allocator

Runs one exchange of request on the connection, in callback.

In callback, call the steps in order:

  1. send sends the head of a PreparedRequest from silk.http.request.

  2. writeSome sends the body, and finishRequest ends it. Call finishRequest also when there is no body.

  3. receive or receiveFinal reads a response head. Then readSome reads the body, or beginContent and readContentSome decode it.

  4. finishResponse ends 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#

silk
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 Random

Sends the request head. With Require100, it also flushes, so that the server can answer before the body.

writeSome#

silk
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 Random

Sends a prefix of input as body, and returns the progress. On an error, the error keeps the progress.

finishRequest#

silk
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 Random

Ends the request body with trailers, and flushes. Call it also when there is no body. Then read the response.

receive#

silk
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 Random

Reads one response head, and returns its status. A 1xx status is also returned. A 4xx or 5xx status is not an error.

receiveFinal#

silk
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 Random

Reads 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#

silk
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 Random

Reads 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#

silk
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 Random

Reads 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#

silk
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 Random

Reads 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#

silk
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 Random

Reads 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#

silk
pub effect fn trailers<'head, 'exchange, P>(exchange: &'head Exchange<'exchange, P>) -> Option<
  Trailers<'head>,
> ! ExchangeError

Returns the trailers of a complete chunked response, or None.

finishResponse#

silk
pub effect fn finishResponse<'exchange, P>(exchange: &mut Exchange<'exchange, P>) -> ()
! ExchangeError

Ends the response. The body must be complete. This function does not read the rest of the body for you.

abort#

silk
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#

silk
pub struct Tunnel<'tunnel, P>

The connection after a successful CONNECT. Reads first return the bytes that came after the response head.

TransferredTunnel#

silk
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#

silk
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 ? R

Gives the tunnel of a successful CONNECT to callback. The connection cannot run HTTP again.

Implementation Tunnel<'tunnel, P>#

silk
impl<'tunnel, P> Tunnel<'tunnel, P>

Method transferByteDuplex#

silk
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#

silk
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 Random

Reads bytes. It returns the bytes that came after the response head first.

Method writeSome#

silk
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 Random

Writes a prefix of input, and returns its length.

Implementation TransferredTunnel<'tunnel, P>#

silk
impl<'tunnel, P> TransferredTunnel<'tunnel, P>

Implementation ByteDuplex for TransferredTunnel<'tunnel, P>#

silk
impl<'tunnel, P> ByteDuplex for TransferredTunnel<'tunnel, P>
where
  &mut P provides &ByteDuplex from &mut ByteDuplex,
  &mut P provides &ByteDuplex from &mut ByteDuplex | &mut MonotonicClock

Operation readSomeRaw#

silk
readSomeRaw: TransferredTunnel.read

Operation writeSomeRaw#

silk
writeSomeRaw: TransferredTunnel.write

Operation writeSomeVectoredRaw#

silk
writeSomeVectoredRaw: TransferredTunnel.writeVectored

Operation flushRaw#

silk
flushRaw: TransferredTunnel.flushOutput

Operation shutdownWriteRaw#

silk
shutdownWriteRaw: TransferredTunnel.shutdownOutput

Operation closeRaw#

silk
closeRaw: TransferredTunnel.closeTransport

beginContent#

silk
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 MonotonicClock

Selects 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#

silk
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 Random

Reads 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.

View the source of this page