Browse the docs

silk.http.request.

Check and write the head of an HTTP request before it is sent.

When to use#

Use fromUri or prepare to make a PreparedRequest for a direct connection to an origin. silk.http.exchange sends it. Use prepareForward and prepareConnect for a route from silk.http.proxy. Most programs use silk.http.client or silk.http.fetch, which call this module.

Details#

The HeaderPolicy adds Host, User-Agent, Accept, and Authorization. The BodyMode adds Content-Length or Transfer-Encoding. You cannot also give these fields, or Expect and Connection, in headers. Your other fields keep their order. The prepared request owns a copy of all its bytes. Nothing is sent and nothing is resolved here.

Errors#

  • RequestError tells which check failed. Nothing was sent.

  • OutOfMemoryError if the Allocator fails.

Gotchas#

By default, Basic credentials fail over plain HTTP with InsecureBasic. Set basicSecurity to AllowInsecureBasic only when you accept that the password is sent as plain text.

Examples#

Prepare a GET request#

silk
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.core.effect { Effect }
import silk.http.message { Header, Method, ValueError, Version }
import silk.http.headers { Headers, Limits }
import silk.http.request { BodyMode, HeaderPolicy, PreparedRequest, RequestError, fromUri }
import silk.core.result { Result }
import silk.uri.uri { Uri }
import silk.uri.reference { ParseError }
import silk.num.usize

fn limits() -> Limits {
  return Limits {
    maxMethodBytes: 64,
    maxTargetBytes: 8192,
    maxNameBytes: 256,
    maxValueBytes: 8192,
    maxFields: 32,
    maxFieldBytes: 8192,
    maxOwnedBytes: 16384,
  }
}

fn check(actual: &[u8], expected: &[u8]) -> i32 {
  let mut i = usize.ZERO
  while i < actual.length && i < expected.length {
    if actual[i] != expected[i] {
      return 1
    }
    i = i + usize.ONE
  }
  if actual.length != expected.length {
    return 1
  }
  return 0
}

effect fn program() -> i32
  ! RequestError | OutOfMemoryError {
  let mut allocator = Allocator.systemAllocatorProvider()
  let uri = match Uri.parse("http://example.com/a?b#frag") {
    Result<Uri, ParseError>.Success { value } => value
    Result<Uri, ParseError>.Failure { error: _ } => {
      return 1
    }
  }
  let entries: [Header<'static>; 0] = []
  let headers = match Headers.make(&entries, limits()) {
    Result<Headers, ValueError>.Success { value } => value
    Result<Headers, ValueError>.Failure { error: _ } => {
      return 1
    }
  }
  let policy = HeaderPolicy.defaults()
  let prepared = run fromUri(
    &uri,
    Version.Http11,
    Method.get(),
    &headers,
    &policy,
    BodyMode.Empty,
    false,
    limits(),
    4096,
    256
  )
    |> Effect.provideMut<Allocator>(&mut allocator)

  // The fragment is not sent, and the policy adds Host, User-Agent, and Accept.
  return check(
    PreparedRequest.bytes(&prepared),
    b"GET /a?b HTTP/1.1\r\nHost: example.com\r\nUser-Agent: silk-http/1\r\nAccept: */*\r\n\r\n"
  )
}

effect fn failed<E>(error: E) -> i32 {
  drop error
  return 1
}

pub fn main() -> i32 {
  return run Effect.catchAll(program(), failed)
}

Import as HttpRequest with import silk.http.request as HttpRequest.

Public declarations: 11.

BodyMode#

silk
pub union BodyMode

How the request body is framed. It adds Content-Length or Transfer-Encoding, so you cannot also give these fields in headers.

Empty#

silk
Empty

There is no body. No framing field is sent.

KnownLength#

silk
KnownLength

The body has a known length. Content-Length is sent.

Field length#

silk
pub length: u64

The length of the body, in bytes.

Chunked#

silk
Chunked

The body is sent in chunks, with trailers. Transfer-Encoding: chunked is sent. Only for HTTP/1.1.

Implementation Copy for BodyMode#

silk
impl Copy for BodyMode

HeaderControl#

silk
pub union HeaderControl<'value>

What to do with a field that the policy makes.

Default#

silk
Default

Send the default value: for Host, the authority of the origin; for User-Agent, silk-http/1; for Accept, */*; for Authorization, no field.

Omit#

silk
Omit

Do not send the field. HTTP/1.1 cannot omit Host.

Value#

silk
Value

Send this value.

Field value#

silk
pub value: &'value [u8]

The field value, without the spaces around it.

Implementation Copy for HeaderControl<'value>#

silk
impl<'value> Copy for HeaderControl<'value>

Authorization#

silk
pub union Authorization<'value>

The Authorization field to send.

Absent#

silk
Absent

Send no Authorization field.

Value#

silk
Value

Send this value, such as Bearer abc.

Field value#

silk
pub value: &'value [u8]

The whole field value.

Basic#

silk
Basic

Send HTTP Basic credentials. See silk.http.basic.

Field username#

silk
pub username: &'value [u8]

The username. It cannot have a colon or a control byte.

Field password#

silk
pub password: &'value [u8]

The password. It cannot have a control byte.

Implementation Copy for Authorization<'value>#

silk
impl<'value> Copy for Authorization<'value>

BasicSecurity#

silk
pub enum BasicSecurity

Whether Basic credentials can go over plain HTTP.

RequireTls#

silk
RequireTls

Basic credentials need HTTPS. Over plain HTTP, the request fails with InsecureBasic.

AllowInsecureBasic#

silk
AllowInsecureBasic

Basic credentials can go over plain HTTP, where anyone on the path can read them.

HeaderPolicy#

silk
pub struct HeaderPolicy<'value>

The fields that the request adds: Host, User-Agent, Accept, and Authorization. Start from HeaderPolicy.defaults. You cannot also give these fields in headers.

Gotchas#

By default, Basic credentials fail over plain HTTP with InsecureBasic. Set basicSecurity to AllowInsecureBasic only when you accept that the password is sent as plain text.

Field host#

silk
pub host: HeaderControl<'value>

The Host field. The default is the authority of the origin, such as example.com:8080.

Field userAgent#

silk
pub userAgent: HeaderControl<'value>

The User-Agent field. The default is silk-http/1.

Field accept#

silk
pub accept: HeaderControl<'value>

The Accept field. The default is */*.

Field authorization#

silk
pub authorization: Authorization<'value>

The Authorization field. The default is none.

Field basicSecurity#

silk
pub basicSecurity: BasicSecurity

Whether Basic credentials can go over plain HTTP. The default is RequireTls.

Field http10KeepAlive#

silk
pub http10KeepAlive: bool

When true, an HTTP/1.0 request sends Connection: keep-alive. HTTP/1.1 keeps the connection by default.

Implementation Copy for HeaderPolicy<'value>#

silk
impl<'value> Copy for HeaderPolicy<'value>

Implementation HeaderPolicy<'value>#

silk
impl<'value> HeaderPolicy<'value>

Associated function defaults#

silk
pub fn defaults() -> HeaderPolicy<'value>

Returns the default policy: default Host, User-Agent, and Accept, no Authorization, and RequireTls.

RequestError#

silk
pub union RequestError

The reason that a request was not prepared. Nothing was sent.

Value#

silk
Value

A method, target, or field is not valid.

Field error#

silk
pub error: ValueError

The error from silk.http.message.

Origin#

silk
Origin

The origin is not valid, or its authority does not fit.

Field error#

silk
pub error: OriginError

The error from silk.http.origin.

Basic#

silk
Basic

The Basic credentials are not valid.

Field error#

silk
pub error: BasicError

The error from silk.http.basic. It does not hold the credentials.

ConflictingHeader#

silk
ConflictingHeader

headers has a field that the policy or the body mode makes, such as Host or Content-Length.

ConflictingAuthority#

silk
ConflictingAuthority

The Host value in the policy names another origin.

MissingHost#

silk
MissingHost

The policy omits Host in an HTTP/1.1 request.

InvalidBodyMode#

silk
InvalidBodyMode

The body mode or require100 does not work with the version. For example, HTTP/1.0 cannot send chunks.

MultipleAuthorization#

silk
MultipleAuthorization

There is more than one source of credentials.

InsecureBasic#

silk
InsecureBasic

Basic credentials would go over plain HTTP. See BasicSecurity.

ProxyAuthorization#

silk
ProxyAuthorization

A direct request has a Proxy-Authorization field. Proxy credentials come only from a route.

CredentialLimit#

silk
CredentialLimit

The credentials are longer than maxCredentialBytes.

HeaderLimit#

silk
HeaderLimit

The request has more fields than the limits allow.

InvalidTarget#

silk
InvalidTarget

A direct request has an absolute target for another origin.

Implementation Debug for RequestError#

silk
impl Debug for RequestError

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

PreparedRequest#

silk
pub struct PreparedRequest

A checked request head, written as bytes and ready to send. It owns its memory.

When to use#

Make one with fromUri or prepare for a direct connection to an origin, or with prepareForward and prepareConnect for a route from silk.http.proxy. Send it with silk.http.exchange.

Implementation PreparedRequest#

silk
impl PreparedRequest

Method bytes#

silk
pub fn bytes<'a>(self: &'a Self) -> &'a [u8]

Returns the bytes of the head, ending with the empty line.

Method origin#

silk
pub fn origin(self: &Self) -> Origin

Returns the origin that the request is for.

Method physicalPeer#

silk
pub fn physicalPeer(self: &Self) -> Origin

Returns the server to connect to. It is the origin, or the proxy for a routed request.

Method version#

silk
pub fn version(self: &Self) -> Version

Returns the HTTP version.

Method bodyMode#

silk
pub fn bodyMode(self: &Self) -> BodyMode

Returns how the body is framed.

Method require100#

silk
pub fn require100(self: &Self) -> bool

Returns true if the head has Expect: 100-continue.

prepare#

silk
pub effect fn prepare<'value>(
  origin: &Origin,
  version: Version,
  method: Method<'value>,
  target: RequestTarget<'value>,
  headers: &Headers<'value>,
  policy: &HeaderPolicy<'value>,
  body: BodyMode,
  require100: bool,
  limits: Limits,
  maxHeadBytes: usize,
  maxCredentialBytes: usize,
) -> PreparedRequest ! RequestError | OutOfMemoryError ? &mut Allocator

Checks a request to origin and writes its head.

Details#

The HeaderPolicy adds Host, User-Agent, Accept, and Authorization. The BodyMode adds Content-Length or Transfer-Encoding. You cannot also give these fields, or Expect and Connection, in headers. The prepared request owns a copy of all its bytes. Nothing is sent and nothing is resolved here. Your fields in headers keep their order. When require100 is true, the head has Expect: 100-continue, and the client waits for the server before it sends the body. maxHeadBytes limits the size of the head, and maxCredentialBytes the size of the credentials.

prepareForward#

silk
pub effect fn prepareForward<'configuration, 'value>(
  route: &Route<'configuration>,
  uri: &Uri<'value>,
  version: Version,
  method: Method<'value>,
  headers: &Headers<'value>,
  policy: &HeaderPolicy<'value>,
  body: BodyMode,
  require100: bool,
  limits: Limits,
  maxHeadBytes: usize,
  maxCredentialBytes: usize,
) -> PreparedRequest ! ProxyError | RequestError | OutOfMemoryError ? &mut Allocator

Prepares a request for a Forward route, which sends it to a plain HTTP proxy.

Details#

The target is the full URI, without user information and fragment, and an empty path becomes /. The route gives the proxy and its Proxy-Authorization. Host is always sent, also for HTTP/1.0.

prepareConnect#

silk
pub effect fn prepareConnect<'configuration>(
  route: &Route<'configuration>,
  limits: Limits,
  maxHeadBytes: usize,
  maxCredentialBytes: usize,
) -> PreparedRequest ! ProxyError | RequestError | OutOfMemoryError ? &mut Allocator

Prepares the HTTP/1.1 CONNECT request for a Tunnel route.

The target and Host are host:port of the origin, always with the port. You cannot give fields, a body, or credentials. The route gives them.

fromUri#

silk
pub effect fn fromUri<'value>(
  uri: &Uri<'value>,
  version: Version,
  method: Method<'value>,
  headers: &Headers<'value>,
  policy: &HeaderPolicy<'value>,
  body: BodyMode,
  require100: bool,
  limits: Limits,
  maxHeadBytes: usize,
  maxCredentialBytes: usize,
) -> PreparedRequest ! RequestError | OutOfMemoryError ? &mut Allocator

Checks a direct request for uri and writes its head. The origin and the target come from uri. The fragment is not sent.

Details#

The HeaderPolicy adds Host, User-Agent, Accept, and Authorization, and the BodyMode adds Content-Length or Transfer-Encoding. Your other fields in headers keep their order. Nothing is sent and nothing is resolved here.

Gotchas#

By default, Basic credentials fail over plain HTTP with InsecureBasic.

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