Experimental. The engine's settings and its reporting may still change; an importer lists it in options("allow-experimental-packages": ["gaato/http/h1server"]). gaato/http-async wires it to sockets, TLS and an event loop; most applications use that instead.
///|
test "serve a request" {
let driver = @driver.Driver::new()
let coordination = @sim.SimCoordination::new(driver)
let (client, server) = @sim.pair(coordination)
let errors = []
driver.run(() => {
let task = driver.spawn(() => {
@h1server.serve_conn(
server,
request => {
let greeting = "hello from " + request.url
@http.Response::new(200, body=Bytes(@http.encode_utf8(greeting)))
},
clock=@sim.SimClock::new(driver),
timer=@sim.SimTimer::new(driver),
coordination~,
scheme="https",
on_error=error => errors.push(error),
)
})
client.write(b"GET /docs HTTP/1.1\r\nHost: example.test\r\n\r\n")
// A head the codec refuses is answered and ends the connection.
client.write(b"GET / HTTP/1.1\r\nHost: a\r\nHost: b\r\n\r\n")
let out = Buffer()
while client.read(max_len=4096) is Some(bytes) {
out.write_bytes(bytes)
}
let text = @utf8.decode(out.to_bytes())
assert_true(text.has_prefix("HTTP/1.1 200 OK\r\n"))
assert_true(text.contains("hello from https://example.test/docs"))
assert_true(text.contains("HTTP/1.1 400 Bad Request\r\n"))
ignore(task.wait())
})
assert_eq(server.closes(), 1)
assert_eq(errors.length(), 1)
}pub(all) suberror ServerConfigError {
InvalidMaxBodyBytes(value~ : Int)
InvalidBodyTimeoutMs(value~ : Int)
InvalidHeaderTimeoutMs(value~ : Int)
InvalidMaxConnections(value~ : Int)
InvalidMaxHeaderBytes(value~ : Int)
InvalidDrainMaxBytes(value~ : Int)
InvalidHandlerTimeoutMs(value~ : Int)
InvalidDrainMs(value~ : Int)
} derive(Debug)test {
let limits = { ..@h1server.ServerLimits::default(), max_body_bytes: 4096, }
limits.validate()
assert_eq(limits.max_header_bytes, 16384)
}async fn serve_conn(conn : &Conn, handler : async (Request) -> Response noraise, clock~ : &Clock, timer~ : &Timer, coordination~ : &Coordination, scheme~ : String, limits? : ServerLimits, peer? : String, authority? : String, on_error? : (TransportError) -> Unit, stopping? : () -> Bool, background? : (async () -> Unit) -> Unit) -> Unit raise ServerConfigErrortest {
let driver = @driver.Driver::new()
let coordination = @sim.SimCoordination::new(driver)
let (client, server) = @sim.pair(coordination)
driver.run(() => {
let task = driver.spawn(() => {
@h1server.serve_conn(
server,
request => {
@http.Response::new(200, body=Bytes(@http.encode_utf8(request.url)))
},
clock=@sim.SimClock::new(driver),
timer=@sim.SimTimer::new(driver),
coordination~,
scheme="http",
)
})
client.write(
b"GET /hello HTTP/1.1\r\nHost: example.test\r\nConnection: close\r\n\r\n",
)
let out = Buffer()
while client.read(max_len=4096) is Some(bytes) {
out.write_bytes(bytes)
}
assert_true(out.to_bytes().has_suffix(b"http://example.test/hello"))
ignore(task.wait())
})
assert_eq(server.closes(), 1)
}Install
Download zipThe HTTP vocabulary, a sans-IO HTTP/1.1 codec and runtime-independent client and server connection engines for MoonBit. Depends on moonbitlang/core only; runs on every backend.