Browse the docs

silk.http.transport.

The byte transport under an HTTP connection: plain bytes or TLS.

When to use#

silk.http.exchange reads and writes through the HttpTransport service. Use plain to use a byte duplex, such as a TCP socket, as the transport. Use secured to use a TLS connection. Implement HttpTransport yourself for a custom transport, for example in tests.

Details#

plain, secured, and borrow borrow a provider. They do not allocate or do I/O. The caller keeps ownership of the provider.

HttpTransport.readSome and HttpTransport.writeSome check the counts of the provider. A count of zero, or more than the buffer, closes the transport and fails. An empty buffer returns zero at once, and does not call the provider.

Each operation gets an absolute deadline, and passes it to the provider unchanged.

Errors#

TransportError keeps the original error: Plain for a byte duplex error, Secured for a TLS error.

Gotchas#

  • close closes the provider: the socket for plain, the TLS connection for secured. It does not flush. A second call does nothing.

  • After close, each read, write, or flush fails with a Closed error.

Examples#

Write and read over a memory duplex#

silk
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.io.byte_duplex { ReadTransfer }
import silk.collections.bytes { Bytes }
import silk.core.effect { Effect }
import silk.http.transport { HttpTransport, TransportError, plain }
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

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 ()
  }
}

// Writes a ping, reads the reply, and closes the transport.
effect fn exchange() -> i32
  ! TransportError | OutOfMemoryError
  ? &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random {
  let written = run HttpTransport.writeSome(b"ping", Option<Instant>.none())
  if written != 4 {
    return 1
  }
  run HttpTransport.flush(Option<Instant>.none())
  let mut reply: [u8; 8] = [0; 8]
  let count = match run HttpTransport.readSome(&mut reply, Option<Instant>.none()) {
    ReadTransfer.Data { count } => count
    ReadTransfer.End => {
      return 1
    }
  }
  if count != 4 || reply[0] != 112 {
    return 1
  }
  run HttpTransport.close()

  // A second close does nothing.
  run HttpTransport.close()
  return 0
}

effect fn program() -> i32
  ! TransportError | OutOfMemoryError
  ? &mut MonotonicClock | &mut Allocator | &mut Random {
  let reply = run Bytes.copy(b"pong")
  let mut reads = List<MemoryReadEvent>.make()
  run List.append(
    &mut reads,
    MemoryReadEvent.Data { readyAt: SystemClock.make(0, 0), bytes: move reply }
  )
  let mut writes = List<MemoryWriteEvent>.make()
  run List.append(
    &mut writes,
    MemoryWriteEvent {
      readyAt: SystemClock.make(0, 0),
      action: MemoryWriteAction.Accept { count: 64 }
    },
  )
  let mut memory = run MemoryByteDuplex.make(move reads, move writes, 64, 8, Option<i32>.none())
  let mut transport = plain(&mut memory)
  let result = run exchange() |> Effect.provideMut<HttpTransport>(&mut transport)
  drop transport
  if MemoryByteDuplex.closeAttempts(&memory) != 1 {
    return 1
  }
  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 HttpTransport with import silk.http.transport { HttpTransport }.

Public declarations: 8.

TransportError#

silk
pub union TransportError

The error of a transport operation, with its original cause: Plain for a byte duplex error, Secured for a TLS error.

Plain#

silk
Plain

The byte duplex failed.

Field error#

silk
pub error: ByteIoError

The error of the byte duplex.

Secured#

silk
Secured

The TLS connection failed.

Field error#

silk
pub error: ConnectionError

The error of the TLS connection.

Implementation Debug for TransportError#

silk
impl Debug for TransportError

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 Plain { ... }.

HttpTransport#

silk
pub service HttpTransport

Reads and writes the bytes of an HTTP connection.

Call HttpTransport.readSome and HttpTransport.writeSome, not the raw operations. They check the counts of the provider: a count of zero, or more than the buffer, closes the transport and fails. Each operation gets an absolute deadline, and passes it to the provider unchanged. Use plain for a byte duplex, such as a TCP socket, or secured for a TLS connection.

Gotchas#

close closes the provider: the socket for plain, the TLS connection for secured. It does not flush. After close, each read, write, or flush fails with a Closed error.

Operation readSomeRaw#

silk
unsafe effect fn readSomeRaw(output: &mut [u8], deadline: Option<Instant>) -> ReadTransfer ! TransportError | OutOfMemoryError ? &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random

Reads bytes from the provider into output. It is unsafe because it does not check the count. Use HttpTransport.readSome.

Operation writeSomeRaw#

silk
unsafe effect fn writeSomeRaw(input: &[u8], deadline: Option<Instant>) -> usize ! TransportError | OutOfMemoryError ? &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random

Writes bytes from input to the provider. It is unsafe because it does not check the count. Use HttpTransport.writeSome.

Operation flush#

silk
effect fn flush(deadline: Option<Instant>) -> () ! TransportError | OutOfMemoryError ? &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random

Sends all bytes that writes accepted to the provider.

Operation close#

silk
effect fn close() -> () ! TransportError ? &mut HttpTransport

Closes the transport without waiting. A second call does nothing. It does not flush. After it, each read, write, or flush fails with a Closed error.

Implementation HttpTransport#

silk
impl HttpTransport

Associated function readSome#

silk
pub effect fn readSome(output: &mut [u8], deadline: Option<Instant>) -> ReadTransfer
  ! TransportError | OutOfMemoryError
  ? &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random

Reads at least one byte into the start of output, or returns End.

If output is empty, it returns zero bytes at once. If the provider reports zero bytes, or more than output.length, the transport is closed and the read fails.

Associated function writeSome#

silk
pub effect fn writeSome(input: &[u8], deadline: Option<Instant>) -> usize
  ! TransportError | OutOfMemoryError
  ? &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random

Writes at least one byte from the start of input, and returns the count.

The count can be less than input.length. If input is empty, it returns zero at once. If the provider reports zero, or more than input.length, the transport is closed and the write fails.

Plain#

silk
pub struct Plain<'provider, P>

A transport over a byte duplex. Make it with plain.

Secured#

silk
pub struct Secured<'provider, P>

A transport over a TLS connection. Make it with secured.

plain#

silk
pub fn plain<'provider, P>(provider: &'provider mut P) -> Plain<'provider, P>

Uses the byte duplex provider as a transport. It does not allocate or do I/O. The caller keeps ownership of provider. Closing the transport closes the byte duplex.

secured#

silk
pub fn secured<'provider, P>(provider: &'provider mut OwnedConnection<P>) -> Secured<'provider, P>

Uses the TLS connection provider as a transport. It does not allocate or do I/O. The caller keeps ownership of provider. Closing the transport closes the TLS connection.

Implementation Plain<'provider, P>#

silk
impl<'provider, P> Plain<'provider, P>

Implementation HttpTransport for Plain<'provider, P>#

silk
impl<'provider, P> HttpTransport for Plain<'provider, P>
where
  &mut P provides &ByteDuplex from &mut ByteDuplex,
  &mut P provides &ByteDuplex from &mut ByteDuplex | &mut MonotonicClock,
  &mut P provides &ByteDuplex from &mut ByteDuplex | &mut MonotonicClock | &mut Allocator | &mut Random

Operation readSomeRaw#

silk
readSomeRaw: Plain.read

Operation writeSomeRaw#

silk
writeSomeRaw: Plain.write

Operation flush#

silk
flush: Plain.flushOutput

Operation close#

silk
close: Plain.closeLease

Implementation Secured<'provider, P>#

silk
impl<'provider, P> Secured<'provider, P>

Implementation HttpTransport for Secured<'provider, P>#

silk
impl<'provider, P> HttpTransport for Secured<'provider, P>
where
  &mut P provides &ByteDuplex from &mut ByteDuplex,
  &mut P provides &ByteDuplex from &mut ByteDuplex | &mut MonotonicClock,
  &mut P provides &ByteDuplex from &mut ByteDuplex | &mut MonotonicClock | &mut Allocator | &mut Random

Operation readSomeRaw#

silk
readSomeRaw: Secured.read

Operation writeSomeRaw#

silk
writeSomeRaw: Secured.write

Operation flush#

silk
flush: Secured.flushOutput

Operation close#

silk
close: Secured.closeLease

Loan#

silk
pub struct Loan<'provider, P>

A transport that borrows another transport. Make it with borrow.

Closing the loan closes the transport that it borrows.

borrow#

silk
pub fn borrow<'provider, P>(provider: &'provider mut P) -> Loan<'provider, P>

Borrows the transport provider as a Loan. It does not allocate or do I/O.

Implementation Loan<'provider, P>#

silk
impl<'provider, P> Loan<'provider, P>

Implementation HttpTransport for Loan<'provider, P>#

silk
impl<'provider, P> HttpTransport for Loan<'provider, P>
where
  &mut P provides &HttpTransport from &mut HttpTransport,
  &mut P provides &HttpTransport from &mut HttpTransport | &mut MonotonicClock | &mut Allocator | &mut Random

Operation readSomeRaw#

silk
readSomeRaw: Loan.read

Operation writeSomeRaw#

silk
writeSomeRaw: Loan.write

Operation flush#

silk
flush: Loan.flushOutput

Operation close#

silk
close: Loan.closeLease

Implementation ByteDuplex for Loan<'provider, P>#

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

Operation readSomeRaw#

silk
readSomeRaw: Loan.readBytes

Operation writeSomeRaw#

silk
writeSomeRaw: Loan.writeBytes

Operation writeSomeVectoredRaw#

silk
writeSomeVectoredRaw: Loan.writeBytesVectored

Operation flushRaw#

silk
flushRaw: Loan.flushBytes

Operation shutdownWriteRaw#

silk
shutdownWriteRaw: Loan.shutdownBytes

Operation closeRaw#

silk
closeRaw: Loan.closeBytes

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