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#
RequestErrortells which check failed. Nothing was sent.OutOfMemoryErrorif theAllocatorfails.
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#
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#
pub union BodyModeHow the request body is framed. It adds Content-Length or Transfer-Encoding, so you cannot
also give these fields in headers.
Empty#
EmptyThere is no body. No framing field is sent.
KnownLength#
KnownLengthThe body has a known length. Content-Length is sent.
Field length#
pub length: u64The length of the body, in bytes.
Chunked#
ChunkedThe body is sent in chunks, with trailers. Transfer-Encoding: chunked is sent. Only for
HTTP/1.1.
Implementation Copy for BodyMode#
impl Copy for BodyModeHeaderControl#
pub union HeaderControl<'value>What to do with a field that the policy makes.
Default#
DefaultSend the default value: for Host, the authority of the origin; for User-Agent,
silk-http/1; for Accept, */*; for Authorization, no field.
Omit#
OmitDo not send the field. HTTP/1.1 cannot omit Host.
Value#
ValueSend this value.
Field value#
pub value: &'value [u8]The field value, without the spaces around it.
Implementation Copy for HeaderControl<'value>#
impl<'value> Copy for HeaderControl<'value>Authorization#
pub union Authorization<'value>The Authorization field to send.
Absent#
AbsentSend no Authorization field.
Value#
ValueSend this value, such as Bearer abc.
Field value#
pub value: &'value [u8]The whole field value.
Basic#
BasicSend HTTP Basic credentials. See silk.http.basic.
Field username#
pub username: &'value [u8]The username. It cannot have a colon or a control byte.
Field password#
pub password: &'value [u8]The password. It cannot have a control byte.
Implementation Copy for Authorization<'value>#
impl<'value> Copy for Authorization<'value>BasicSecurity#
pub enum BasicSecurityWhether Basic credentials can go over plain HTTP.
RequireTls#
RequireTlsBasic credentials need HTTPS. Over plain HTTP, the request fails with InsecureBasic.
AllowInsecureBasic#
AllowInsecureBasicBasic credentials can go over plain HTTP, where anyone on the path can read them.
HeaderPolicy#
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#
pub host: HeaderControl<'value>The Host field. The default is the authority of the origin, such as example.com:8080.
Field userAgent#
pub userAgent: HeaderControl<'value>The User-Agent field. The default is silk-http/1.
Field accept#
pub accept: HeaderControl<'value>The Accept field. The default is */*.
Field authorization#
pub authorization: Authorization<'value>The Authorization field. The default is none.
Field basicSecurity#
pub basicSecurity: BasicSecurityWhether Basic credentials can go over plain HTTP. The default is RequireTls.
Field http10KeepAlive#
pub http10KeepAlive: boolWhen true, an HTTP/1.0 request sends Connection: keep-alive. HTTP/1.1 keeps the connection
by default.
Implementation Copy for HeaderPolicy<'value>#
impl<'value> Copy for HeaderPolicy<'value>Implementation HeaderPolicy<'value>#
impl<'value> HeaderPolicy<'value>Associated function defaults#
pub fn defaults() -> HeaderPolicy<'value>Returns the default policy: default Host, User-Agent, and Accept, no Authorization,
and RequireTls.
RequestError#
pub union RequestErrorThe reason that a request was not prepared. Nothing was sent.
Value#
ValueA method, target, or field is not valid.
Field error#
pub error: ValueErrorThe error from silk.http.message.
Origin#
OriginThe origin is not valid, or its authority does not fit.
Field error#
pub error: OriginErrorThe error from silk.http.origin.
Basic#
BasicThe Basic credentials are not valid.
Field error#
pub error: BasicErrorThe error from silk.http.basic. It does not hold the credentials.
ConflictingHeader#
ConflictingHeaderheaders has a field that the policy or the body mode makes, such as Host or
Content-Length.
ConflictingAuthority#
ConflictingAuthorityThe Host value in the policy names another origin.
MissingHost#
MissingHostThe policy omits Host in an HTTP/1.1 request.
InvalidBodyMode#
InvalidBodyModeThe body mode or require100 does not work with the version. For example, HTTP/1.0 cannot
send chunks.
MultipleAuthorization#
MultipleAuthorizationThere is more than one source of credentials.
InsecureBasic#
InsecureBasicBasic credentials would go over plain HTTP. See BasicSecurity.
ProxyAuthorization#
ProxyAuthorizationA direct request has a Proxy-Authorization field. Proxy credentials come only from a route.
CredentialLimit#
CredentialLimitThe credentials are longer than maxCredentialBytes.
HeaderLimit#
HeaderLimitThe request has more fields than the limits allow.
InvalidTarget#
InvalidTargetA direct request has an absolute target for another origin.
Implementation Debug for RequestError#
impl Debug for RequestErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant and its fields, such as Value { ... }.
PreparedRequest#
pub struct PreparedRequestA 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#
impl PreparedRequestMethod bytes#
pub fn bytes<'a>(self: &'a Self) -> &'a [u8]Returns the bytes of the head, ending with the empty line.
Method origin#
pub fn origin(self: &Self) -> OriginReturns the origin that the request is for.
Method physicalPeer#
pub fn physicalPeer(self: &Self) -> OriginReturns the server to connect to. It is the origin, or the proxy for a routed request.
Method version#
pub fn version(self: &Self) -> VersionReturns the HTTP version.
Method bodyMode#
pub fn bodyMode(self: &Self) -> BodyModeReturns how the body is framed.
Method require100#
pub fn require100(self: &Self) -> boolReturns true if the head has Expect: 100-continue.
prepare#
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 AllocatorChecks 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#
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 AllocatorPrepares 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#
pub effect fn prepareConnect<'configuration>(
route: &Route<'configuration>,
limits: Limits,
maxHeadBytes: usize,
maxCredentialBytes: usize,
) -> PreparedRequest ! ProxyError | RequestError | OutOfMemoryError ? &mut AllocatorPrepares 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#
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 AllocatorChecks 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.