HTTP client Reference
API reference
Every public type of ioxide.httpclient, in namespace ioxide.httpclient. All of them belong to one reactor and are used from its thread.
HttpClientPool#
Keep-alive HTTP/1.1 connections to one origin, on one reactor's ring. sealed class HttpClientPool : IDisposable
| Member | What it does |
|---|---|
static HttpClientPool Start(Reactor reactor, HttpClientOptions options) | Opens PoolSize connections, registers the pool as a reactor service - reactor.GetService<HttpClientPool>() - and its sweep as a reactor ticker. Call it from Reactor.OnStart. Services are keyed by type, so a second pool on the same reactor replaces the first there: with several backends, keep the pools it returns (several backends). |
ValueTask<HttpClientResponse> GetAsync(ReadOnlyMemory<byte> path) | A GET for path, which starts with /. The byte form costs no encoding: build the path once. |
ValueTask<HttpClientResponse> GetAsync(string path) | The same, encoding the path on each call. |
ValueTask<HttpClientResponse> PostAsync(ReadOnlyMemory<byte> path, ReadOnlyMemory<byte> body) | A POST with body; content-length is written for you. |
ValueTask<HttpClientResponse> SendAsync(HttpClientRequest request) | Any request. Nothing of request is kept once it returns, so one instance can be sent again and again. |
int ConnectionCount | Connections open or opening. |
int IdleCount | Connections idle in the pool right now. |
void Dispose() | Closes the idle connections and stops opening new ones. A request in flight finishes, and its connection is closed when it does; requests waiting for a connection fail. |
RingHttpClient#
HTTP/1.1 to any origin by URL, with a pool per origin. sealed class RingHttpClient : IDisposable
| Member | What it does |
|---|---|
static RingHttpClient Start(Reactor reactor, RingHttpClientOptions? options = null) | Creates the client and registers it as a reactor service - reactor.GetService<RingHttpClient>() - and its sweep as a ticker. Opens nothing until a request asks. |
ValueTask<HttpClientResponse> GetAsync(string url) | A GET for an absolute http or https URL, following redirects. |
ValueTask<HttpClientResponse> PostAsync(string url, ReadOnlyMemory<byte> body) | A POST with body, following redirects by .NET's rules. |
ValueTask<HttpClientResponse> SendAsync(string url, HttpClientRequest request) | Any request: its method, headers and body go to the URL, whose path and query replace the request's own path. |
int OriginCount | Origins with a pool right now. |
void Dispose() | Disposes every origin's pool and the TLS context it made. |
HttpClientOptions#
One origin, and how to keep connections to it. sealed record HttpClientOptions, set with init.
| Property | Default | Meaning |
|---|---|---|
string Host | "127.0.0.1" | A name, an IPv4 address or an IPv6 address. Sent as the request's host; over https, the name the certificate is checked against unless TlsClientOptions.ServerName is set. |
ushort Port | 80 | The origin's port. |
int PoolSize | 4 | Connections to keep, opened and opening together - the most requests in flight to the origin from one reactor. |
TlsClientContext? Tls | null | The TLS context for an https origin; null is cleartext. Shared by the pool's connections; the caller disposes it. |
int MaxResponseBytes | 8 MiB | The ceiling for one response's head and body. A larger one fails its request. |
int SendBufferSize | 16 KiB | Each connection's send buffer. A body larger than it is sent from your memory directly. |
int ReceiveBufferSize | 16 KiB | Each connection's receive buffer, grown up to MaxResponseBytes when a response needs it. |
int AcquireTimeoutMs | 10 000 | How long a request may wait for a connection. |
int ConnectTimeoutMs | 10 000 | How long one address may take to accept a connection before the next is tried. Enforced by the kernel; about a second at least. |
int IdleTimeoutMs | 135 000 | An idle connection unused this long is closed, and in a warm pool replaced. 0: as long as the origin keeps it. |
int MaxLifetimeMs | 0 | A connection older than this is closed after its request, or from the sweep while idle. 0: no limit. |
int SweepIntervalMs | 250 | How often the sweep looks at idle connections and opens replacements, in whole reactor ticks of 250 ms. |
RingHttpClientOptions#
sealed record RingHttpClientOptions
| Property | Default | Meaning |
|---|---|---|
HttpClientOptions Pool | new() { PoolSize = 64 } | What every origin's pool is made from. Host, Port and Tls come from the URL; the rest applies as given. |
TlsClientOptions? Tls | null | TLS for every https origin, each connection checked against its URL's host; ServerName is ignored. Null: the system trust store, offering http/1.1. |
int MaxRedirects | 50 | Redirects followed before the last is returned as the response; 0 follows none. |
TlsClientOptions and TlsClientContext#
sealed record TlsClientOptions - see TLS for what each does.
| Property | Default |
|---|---|
string? ServerName | null: the name the connection is opened to |
string[] AlpnProtocols | none offered |
bool VerifyCertificate | true |
string? CaFile | null: the system trust store |
string? CertificateFile, string? PrivateKeyFile | null: no client certificate |
int MinimumVersion | OpenSslVersions.Tls12 |
int HandshakeTimeoutMs | 10 000 |
sealed class TlsClientContext : IDisposable - the configured BoringSSL client context.
| Member | What it does |
|---|---|
static TlsClientContext Create(TlsClientOptions options) | Builds the context: trust anchors, client certificate, protocol floor, ALPN. Throws IOException if BoringSSL refuses any of it, ArgumentException for a blank ServerName, a lone certificate or key, or an ALPN name that is not 1 to 255 ASCII bytes. |
ValueTask<TlsClientStream> ConnectAsync(RingSocket socket) | A TLS session over a connected ring socket, verified as ServerName, which must then be set. The stream owns the socket. |
ValueTask<TlsClientStream> ConnectAsync(RingSocket socket, string serverName) | The same, verified as serverName. |
void Dispose() | Frees the context. Connections opened from it keep working. |
HttpClientRequest and HttpMethods#
sealed class HttpClientRequest - one request, as bytes. Reusable: nothing of it is kept once a send returns.
| Member | Meaning |
|---|---|
ReadOnlyMemory<byte> Method | The method token; HttpMethods.Get by default. |
ReadOnlyMemory<byte> Path | The request target, starting with /, e.g. "/api/things?id=7"u8. Replaced by the URL's with RingHttpClient. |
KeyValueList Headers | Extra header lines: lowercase names, raw values. Not host, content-length or connection, which the client writes. |
ReadOnlyMemory<byte> Body | The body; content-length is written whenever it is not empty. |
HttpClientRequest(), HttpClientRequest(ReadOnlyMemory<byte> method, ReadOnlyMemory<byte> path), HttpClientRequest(ReadOnlyMemory<byte> method, string path) | Constructors; the string form encodes the path once. |
static class HttpMethods - the method tokens as bytes: Get, Head, Post, Put, Patch, Delete.
HttpClientResponse#
sealed class HttpClientResponse : IDisposable - one response, as raw bytes in one pooled buffer it owns.
| Member | Meaning |
|---|---|
int Status | The status code, e.g. 200. |
KeyValueList Headers | The header lines; names lowercased, values as sent. |
bool TryGetHeader(ReadOnlySpan<byte> name, out ReadOnlyMemory<byte> value) | The first value for a lowercase name: response.TryGetHeader("content-type"u8, out var type). |
ReadOnlyMemory<byte> Body | The body, de-chunked. |
bool ConnectionClose | Whether this response ended its connection: the origin asked to close, or the body ran until the close. |
void Dispose() | Returns the buffer. Every memory above points at recycled storage afterwards. |
KeyValueList#
sealed class KeyValueList - header lines as byte pairs, without a dictionary.
| Member | Meaning |
|---|---|
void Add(ReadOnlyMemory<byte> name, ReadOnlyMemory<byte> value) | Appends a line. |
int Count, this[int index] | The lines, as KeyValuePair<ReadOnlyMemory<byte>, ReadOnlyMemory<byte>>. |
ReadOnlySpan<KeyValuePair<ReadOnlyMemory<byte>, ReadOnlyMemory<byte>>> AsSpan() | All lines, to iterate without copying. |
void Clear() | Removes every line. |
HttpClientException#
sealed class HttpClientException : Exception - any failure to connect, send, receive or parse. The message says which, and for a response the parser refused, the inner exception names the rule broken. The messages are listed under Errors. A URL RingHttpClient cannot use throws ArgumentException instead, and so does a request whose method, target or headers hold a CR, LF or NUL.
Lower down: RingSocket#
The client's connections are core's RingSocket: a TCP socket whose connect, sends and receives run on a reactor's ring, which you can use directly - with TlsClientContext.ConnectAsync for TLS - to build a client of your own.
| Member | What it does |
|---|---|
static RingSocket CreateTcp(IRingHost host, AddressFamily family = AddressFamily.InterNetwork) | A TCP socket with TCP_NODELAY, IPv4 or IPv6. A Reactor is an IRingHost. |
ValueTask<int> ConnectAsync(IPAddress address, ushort port) | Connects on the ring to an address of the socket's family; 0 or a negative errno. |
ValueTask<int> ConnectAsync(string ipv4, ushort port) | The same from an IPv4 literal. |
ValueTask<int> SendAsync(nint buffer, int length), RecvAsync(nint buffer, int length) | One send and one receive may be in flight at once; bytes, 0 at end of stream, or a negative errno. |
int Fd, void Dispose() | The descriptor; closing it, with nothing in flight. |