gaato/mttp wires this engine to sockets, TLS and an event loop; most applications use that instead.
///|
test "send a request and read the response" {
let driver = @driver.Driver::new()
let conn = @sim.ScriptedConn::new([
Chunk(b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello"),
])
let client = @h1client.Client::new(
dialer=@sim.ScriptedDialer::new([conn]),
clock=@sim.SimClock::new(driver),
timer=@sim.SimTimer::new(driver),
coordination=@sim.SimCoordination::new(driver),
config={
..@h1client.ClientConfig::default(),
connect_timeout_ms: Some(10000),
},
)
driver.run(() => {
let response = client.send(
@mttp_core.Request::get("http://example.test/"),
{ timeout_ms: Some(30000), body: Exchange(max_bytes=1 << 20), },
)
assert_eq(response.status, 200)
guard response.body is Stream(body) else { fail("expected a body") }
assert_eq(@mttp_core.read_bounded(body.take(), 1 << 20), b"hello")
// Read to its end, the connection is parked for the next request.
assert_eq(client.idle_connections(), 1)
client.close()
})
assert_eq(conn.closes(), 1)
}pub struct Client {
// private fields
}async fn send(self : Client, request : Request, options : SendOptions) -> Response raise TransportErrorfn Client::new(dialer~ : &Dialer, clock~ : &Clock, timer~ : &Timer, coordination~ : &Coordination, config~ : ClientConfig) -> Clienttest {
let driver = @driver.Driver::new()
let conn = @sim.ScriptedConn::new([
Chunk(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"),
])
let client = @h1client.Client::new(
dialer=@sim.ScriptedDialer::new([conn]),
clock=@sim.SimClock::new(driver),
timer=@sim.SimTimer::new(driver),
coordination=@sim.SimCoordination::new(driver),
config=@h1client.ClientConfig::default(),
)
driver.run(() => {
let response = client.send(
@mttp_core.Request::get("http://example.test/"),
@mttp_core.SendOptions::default(),
)
guard response.body is Stream(body) else { fail("expected a body") }
assert_eq(@mttp_core.read_bounded(body.take(), 1024), b"hi")
assert_eq(client.idle_connections(), 1)
client.close()
})
assert_eq(conn.closes(), 1)
}async fn Client::send(self : Client, request : Request, options : SendOptions) -> Response raise TransportErrorpub(all) struct ClientConfig {
pool_timeout_ms : Int?
connect_timeout_ms : Int?
write_timeout_ms : Int?
read_timeout_ms : Int?
max_connections : Int?
max_connections_per_origin : Int?
max_idle_connections : Int
max_idle_per_origin : Int
idle_max_ms : Int
max_body_bytes : Int?
head_limits : Limits
proxy : ProxyConfig
} derive(Eq, Debug)test {
let config = {
..@h1client.ClientConfig::default(),
connect_timeout_ms: Some(5000),
max_connections_per_origin: Some(8),
}
assert_eq(config.max_idle_per_origin, 4)
assert_eq(config.max_body_bytes, None)
}test {
let env = {
"https_proxy": "http://proxy.test:3128",
"NO_PROXY": "internal.test",
}
let proxy = @h1client.ProxyConfig::from_env(name => env.get(name))
assert_eq(proxy.http, None)
assert_eq(proxy.https, Some("http://proxy.test:3128"))
assert_eq(proxy.no_proxy, "internal.test")
}Install
Download zipHTTP for MoonBit without I/O, for libraries such as SDKs: the types, an HTTP/1.1 codec and engines, a client with retries under a budget, deadlines, rate limits, redirects and cookies, SSE, multipart, and typed endpoints with a router and OpenAPI. Apps add gaato/mttp.