ewe
Types
The body of a HTTP response to be sent to the client.
pub type Body {
Bytes(bytes_tree.BytesTree)
Text(String)
Empty
File(@internal File)
Streaming(@internal Streaming)
Sse(@internal Sse)
Websocket(@internal Websocket)
}
Constructors
-
Bytes(bytes_tree.BytesTree)A body of binary data stored as a
BytesTree.If you have a
BitArrayyou can use thebytes_tree.from_bit_arrayfunction to convert it. -
Text(String)A body of unicode text sent as UTF-8.
-
EmptyNo body.
-
File(@internal File)A body of the contents of a file created with the
filefunction. -
Streaming(@internal Streaming)A body written a chunk at a time created with the
stream_responsefunction. -
Sse(@internal Sse)A Server-Sent Events stream created with the
ssefunction. -
Websocket(@internal Websocket)A WebSocket created with the
websocketfunction.
The reason a request body could not be read.
pub type BodyError {
BodyTooLarge
InvalidBody
}
Constructors
-
BodyTooLargeThe body is larger than the limit that was given.
-
InvalidBodyThe body could not be read to the end. The connection dropped, the read timed out or the chunked framing was malformed.
The configuration of a server.
Create one with new, adjust it with the builder functions, then give it to
start or supervised.
pub opaque type Builder
The certificate authority a client’s certificate has to be signed by.
pub type ClientVerification {
CaCertFile(path: String)
CaCertData(certs: List(BitArray))
}
Constructors
-
CaCertFile(path: String)Path to a PEM file holding the CA certificate.
-
CaCertData(certs: List(BitArray))In-memory DER-encoded CA certificates.
The status code a close frame carries.
pub type CloseCode {
NormalClosure
GoingAway
ProtocolError
UnsupportedData
InvalidPayloadData
PolicyViolation
MessageTooBig
MandatoryExtension
InternalError
ServiceRestart
TryAgainLater
BadGateway
ApplicationCode(code: Int)
}
Constructors
-
NormalClosureThe connection is closing normally (1000).
-
GoingAwayThe endpoint is going away, can be a server shutdown or a client going away (1001).
-
ProtocolErrorThe other end broke the protocol (1002).
-
UnsupportedDataData arrived that this endpoint cannot accept (1003).
-
InvalidPayloadDataA message’s data did not fit its type (1007).
-
PolicyViolationThe other end broke your rules when no more specific code applies (1008).
-
MessageTooBigA message was larger than this endpoint will handle (1009).
-
MandatoryExtensionAn extension the client required was not negotiated (1010).
-
InternalErrorSomething went wrong (1011).
-
ServiceRestartThe server is restarting and clients may reconnect shortly (1012).
-
TryAgainLaterThe server is overloaded and the client should retry later (1013).
-
BadGatewayAn upstream server answered badly (1014).
-
ApplicationCode(code: Int)An application specific code which must be between 3000 and 4999.
The reason a WebSocket is being closed, sent to the client in the close frame.
pub type CloseReason {
NoCloseReason
CloseReason(code: CloseCode, reason: String)
}
Constructors
-
NoCloseReasonClose without saying why.
-
CloseReason(code: CloseCode, reason: String)Close with a status code and a description.
The connection a request arrived on.
This is the body of the request given to your handler. Pass it to read_body
or read_body_chunk to read the request body, or to file to send a file.
pub type Connection =
@internal Connection
The reason a file could not be prepared by the file function.
pub type FileError {
NotFound
IsDirectory
AccessDenied
UnknownError
InvalidOffset
InvalidLimit
}
Constructors
-
NotFoundThere is nothing at the given path.
-
IsDirectoryThe path is a directory.
-
AccessDeniedThe server is not permitted to read the file.
-
UnknownErrorThe file could not be opened or measured for a reason ewe does not name.
-
InvalidOffsetThe offset is negative or past the end of the file.
-
InvalidLimitThe limit is negative.
The limits and timeouts applied to every HTTP/1 connection.
Sizes are in bytes and timeouts in milliseconds. Build the options by
updating default_http1_options.
A value outside the range a field accepts is replaced with the default and logged as a warning when the server starts.
Examples
Http1Options(..ewe.default_http1_options(), max_headers: 50)
pub type Http1Options {
Http1Options(
max_request_line: Int,
max_header_line: Int,
max_headers: Int,
max_chunk_size_line: Int,
idle_timeout: Int,
body_read_timeout: Int,
auto_drain_limit: Int,
auto_drain_chunk_bytes: Int,
)
}
Constructors
-
Http1Options( max_request_line: Int, max_header_line: Int, max_headers: Int, max_chunk_size_line: Int, idle_timeout: Int, body_read_timeout: Int, auto_drain_limit: Int, auto_drain_chunk_bytes: Int, )Arguments
- max_request_line
-
The longest request line accepted. A longer request line is refused with status code 414: URI Too Long.
- max_header_line
-
The longest single header line accepted. A longer header line is refused with status code 431: Request Header Fields Too Large.
- max_headers
-
The maximum amount of header fields a request may carry. More than this limit is refused with status code 431: Request Header Fields Too Large.
- max_chunk_size_line
-
The longest chunk size line accepted in a chunked body. A longer chunk line is refused with status code 400: Bad Request.
- idle_timeout
-
How long a connection may wait for the next request before it is closed. The whole request line and headers must arrive within this time, so a client cannot hold the connection open by sending them a byte at a time.
- body_read_timeout
-
How long
read_bodyandread_body_chunkwait for the client to send more of the body before they fail. - auto_drain_limit
-
The largest request body the server reads and discards when the handler left it unread, so that the connection can be reused. A larger unread body closes the connection instead.
- auto_drain_chunk_bytes
-
How many bytes are read at a time while an unread body is discarded.
The limits and timeouts applied to every HTTP/2 connection.
Sizes are in bytes and timeouts in milliseconds. Build the options by
updating default_http2_options.
A value outside the range a field accepts is replaced with the default and logged as a warning when the server starts.
Examples
Http2Options(..ewe.default_http2_options(), max_concurrent_streams: Some(100))
pub type Http2Options {
Http2Options(
max_concurrent_streams: option.Option(Int),
initial_window_size: Int,
max_frame_size: Int,
max_header_list_size: option.Option(Int),
header_table_size: Int,
max_continuation_frames: Int,
max_header_block_bytes: Int,
rapid_reset_window: Int,
rapid_reset_threshold: Int,
handshake_timeout: Int,
idle_timeout: Int,
recv_window_low_water_mark: Int,
recv_window_high_water_mark: Int,
file_read_threshold: Int,
body_read_timeout: Int,
websocket: Bool,
send_buffer_limit: Int,
)
}
Constructors
-
Http2Options( max_concurrent_streams: option.Option(Int), initial_window_size: Int, max_frame_size: Int, max_header_list_size: option.Option(Int), header_table_size: Int, max_continuation_frames: Int, max_header_block_bytes: Int, rapid_reset_window: Int, rapid_reset_threshold: Int, handshake_timeout: Int, idle_timeout: Int, recv_window_low_water_mark: Int, recv_window_high_water_mark: Int, file_read_threshold: Int, body_read_timeout: Int, websocket: Bool, send_buffer_limit: Int, )Arguments
- max_concurrent_streams
-
The most streams a client may have open at once. The default is 100.
Nonemeans no limit. - initial_window_size
-
How much request body a client may send on a new stream before the server allows more. Must be within 0 and 2147483647.
- max_frame_size
-
The largest frame the server accepts. Must be within 16384 and 16777215.
- max_header_list_size
-
The largest header list the server accepts.
Nonemeans no limit. - header_table_size
-
How much HPACK dynamic table the server keeps for decoding.
- max_continuation_frames
-
The most CONTINUATION frames one header block may use.
- max_header_block_bytes
-
The most bytes one header block may have in HEADERS and CONTINUATION frames together.
- rapid_reset_window
-
The window over which stream resets are counted.
- rapid_reset_threshold
-
The most streams that may be reset while their handler is still running, within
rapid_reset_window. More resets lead to GOAWAY. Guards against Rapid Reset (CVE-2023-44487) and MadeYouReset (CVE-2025-8671). - handshake_timeout
-
How long the client has to send its SETTINGS and acknowledge ours before the connection is closed.
- idle_timeout
-
How long a connection can stay idle before it is sent GOAWAY and closed.
- recv_window_low_water_mark
-
When a client can send only this much more on a stream, the server lets it send more.
- recv_window_high_water_mark
-
How much request body a stream holds before the handler reads it.
- file_read_threshold
-
Files up to this size are allowed to be read fully into memory.
- body_read_timeout
-
How long
read_bodyandread_body_chunkwait for the client to send more of the body before they fail. - websocket
-
Whether a client may open a WebSocket over HTTP/2 with the extended
CONNECTof RFC 8441. By default the server advertisesSETTINGS_ENABLE_CONNECT_PROTOCOL. - send_buffer_limit
-
How many bytes a streamed body, SSE or WebSocket may have queued. Past this limit, each write waits until the queue drains.
An IP address.
pub type IpAddress {
IpV4(Int, Int, Int, Int)
IpV6(Int, Int, Int, Int, Int, Int, Int, Int)
}
Constructors
-
IpV4(Int, Int, Int, Int)An IPv4 address, represented as four bytes.
-
IpV6(Int, Int, Int, Int, Int, Int, Int, Int)An IPv6 address, represented as eight groups.
What a WebSocket or a Server-Sent Events stream does once the handler has dealt with a message.
Create one with continue, continue_with_selector, stop or
stop_abnormal.
pub opaque type Next(user_state, user_message)
The result of a single call to read_body_chunk.
pub type ReadEvent {
Chunk(data: BitArray, request: request.Request(Connection))
Done(request: request.Request(Nil))
}
Constructors
-
Chunk(data: BitArray, request: request.Request(Connection))A piece of the body along with the request to pass to the next call.
-
Done(request: request.Request(Nil))The body has been read to the end.
Any trailer fields are appended to the request’s headers and the request no longer carries a connection.
A handle for writing the body of a streamed response.
pub type ResponseWriter =
@internal ResponseWriter
The reason a write to the client is not successful.
pub type SendError {
ConnectionClosed
StreamReset
SendTimedOut
SocketError(reason: SocketReason)
}
Constructors
-
ConnectionClosedThe client is gone or the response has already ended.
-
StreamResetThe client cancelled this HTTP/2 stream while the rest of the connection carries on.
-
SendTimedOutOver HTTP/1, the client stopped reading for so long that the write timed out. The connection is closed. Over HTTP/2 this is
ConnectionClosed. -
SocketError(reason: SocketReason)The socket refused the write for a reason of its own.
The address a socket is bound to or the address of a connected peer.
pub type SocketAddress {
TcpSocketAddress(ip_address: IpAddress, port: Int)
UnixSocketAddress(path: String)
}
Constructors
-
TcpSocketAddress(ip_address: IpAddress, port: Int)An address and port on a TCP socket.
-
UnixSocketAddress(path: String)The path of a Unix domain socket.
What the socket said when it refused a write, carried by the SocketError
variant of SendError.
pub type SocketReason {
OutOfBuffers
TooManyOpenFiles
NetworkDown
NetworkUnreachable
HostUnreachable
MessageTooLarge
PermissionDenied
WouldBlock
Interrupted
NotSupported
IoError
UnknownReason
}
Constructors
-
OutOfBuffersThe kernel has no socket buffer space or memory left to take the write.
-
TooManyOpenFilesThe node is at its file descriptor limit or the whole host is.
-
NetworkDownThe interface the connection runs over is down.
-
NetworkUnreachableThere is no route to the client’s network.
-
HostUnreachableThe client’s network is reachable but the client’s host is not.
-
MessageTooLargeThe write is larger than the socket will send in one piece.
-
PermissionDeniedThe socket refused the write on permission grounds.
-
WouldBlockThe write would have blocked and the socket is not willing to.
-
InterruptedA signal arrived mid write. Nothing was sent.
-
NotSupportedThe socket does not support the write as it was made.
-
IoErrorThe write failed below the socket in the network stack or the device.
-
UnknownReasonThe socket reported something ewe does not classify.
A handle for sending on an open Server-Sent Events stream.
pub type SseConnection =
@internal SseConnection
A message on a Server-Sent Events stream.
Create one with event or comment, then set the rest of its fields with
event_name, event_id and event_retry.
pub type SseEvent =
@internal Event
The source of the TLS certificate and key given to with_tls.
pub type Tls {
Disk(cert: String, key: String)
Pem(cert: BitArray, key: BitArray)
Der(cert: BitArray, key: BitArray, key_type: TlsKeyType)
}
Constructors
-
Disk(cert: String, key: String)Paths to PEM-encoded certificate and key files on disk.
-
Pem(cert: BitArray, key: BitArray)In-memory PEM-encoded certificate and key.
-
Der(cert: BitArray, key: BitArray, key_type: TlsKeyType)In-memory DER-encoded certificate and key.
The type of a DER-encoded private key needed by the Der variant of Tls.
pub type TlsKeyType {
RsaPrivateKey
EcPrivateKey
DsaPrivateKey
PrivateKeyInfo
}
Constructors
-
RsaPrivateKeyTraditional RSA key.
-
EcPrivateKeyElliptic curve key.
-
DsaPrivateKeyDSA key.
-
PrivateKeyInfoPKCS#8 key.
A handle for sending frames on an open WebSocket.
pub type WebsocketConnection =
@internal WebsocketConnection
A message reaching a WebSocket handler.
Ping and pong frames are answered by the server.
pub type WebsocketMessage(user_message) {
TextFrame(text: String)
BinaryFrame(data: BitArray)
UserMessage(message: user_message)
}
Constructors
-
TextFrame(text: String)A text frame from the client with the valid UTF-8 payload.
-
BinaryFrame(data: BitArray)A binary frame from the client.
-
UserMessage(message: user_message)A message picked up by the selector.
Values
pub fn bind(builder: Builder, to interface: String) -> Builder
Set the network interface the server listens on. "127.0.0.1" and
"localhost" are the loopback, "0.0.0.0" is every IPv4 interface, "::1"
is the IPv6 loopback and "::" is every IPv6 interface.
A server listens on either a network interface or a Unix socket so this
undoes a previous call to unix.
start returns an error if the interface is not "localhost" or a valid
IPv4 or IPv6 address.
pub fn buffer_size(builder: Builder, bytes: Int) -> Builder
Set the most bytes a single socket read buffers. 64 KiB by default.
A larger buffer lets one read take in more at once which pays off when clients send large bodies.
pub fn comment(text: String) -> SseEvent
Create a comment.
Sending one every so often is the usual way to keep an idle stream from being closed by a proxy in between.
pub fn continue(
user_state: user_state,
) -> Next(user_state, user_message)
Carry on handling further messages with the given state and the selector the connection already uses.
pub fn continue_with_selector(
user_state: user_state,
selector: process.Selector(user_message),
) -> Next(user_state, user_message)
Carry on using the given selector from here on.
pub fn default_http1_options() -> Http1Options
Get the default HTTP/1 limits and timeouts.
pub fn default_http2_options() -> Http2Options
Get the default HTTP/2 limits and timeouts.
pub fn event(data: String) -> SseEvent
Create an event carrying the given data.
Data spanning several lines is sent as the repeated data: fields that the
client joins back together.
Examples
event("Hello, Joe!")
|> event_name("greeting")
|> event_id("1")
pub fn event_id(event: SseEvent, id: String) -> SseEvent
Set the ID of an event. A reconnecting client sends the last ID it saw back
in the last-event-id header.
pub fn event_name(event: SseEvent, name: String) -> SseEvent
Set the name of an event which clients use to route it to a listener.
pub fn event_retry(event: SseEvent, retry: Int) -> SseEvent
Set how long, in milliseconds, the client waits before reconnecting.
pub fn file(
connection: Connection,
path: String,
offset offset: option.Option(Int),
limit limit: option.Option(Int),
) -> Result(Body, FileError)
Create a response body from a file on the disc.
The offset and limit are in bytes and serve a range of the file. Leave
either as None to start at the beginning or to run to the end.
How the file reaches the client depends on the protocol. HTTP/1 lets the
kernel copy it straight to the socket and falls back to reading it in 64kb
pieces when TLS is enabled. HTTP/2 reads a file at or below the
file_read_threshold of Http2Options into memory and frames it and
streams anything larger from the disc.
Examples
let assert Ok(body) =
ewe.file(request.body, "/tmp/report.pdf", offset: None, limit: None)
response.new(200)
|> response.set_header("content-type", "application/pdf")
|> response.set_body(body)
pub fn finish_chunk(
writer: ResponseWriter,
chunk: BitArray,
) -> Result(Nil, SendError)
Send the last chunk of a streamed response body and close the body.
pub fn finish_response(
writer: ResponseWriter,
) -> Result(Nil, SendError)
Close a streamed response body without sending any more data. Use
finish_chunk instead if there is one last chunk to send.
pub fn force_ipv6(builder: Builder) -> Builder
Serve over IPv6 only. IPv4 clients are refused.
"localhost" and "127.0.0.1" will mean the IPv6 loopback ::1 and
"0.0.0.0" mean every IPv6 interface. start returns an error for binded
IPv4 address or when the system has no IPv6 support.
pub fn get_client_info(connection: Connection) -> SocketAddress
Get the address of the client at the other end of the connection.
The address is read once when the client connects.
pub fn get_server_info(
name: process.Name(tup.Server),
) -> Result(SocketAddress, Nil)
Get the address the server is listening on. This is how you find the port
picked by listening_random.
Returns an error when no server runs under the name or when it does not
answer within a second. Pass the name given to named.
Examples
ewe.get_server_info(name)
// -> Ok(TcpSocketAddress(IpV4(127, 0, 0, 1), 3000))
pub fn ip_address_to_string(address: IpAddress) -> String
Convert an IP address to the string form.
Examples
ip_address_to_string(IpV4(127, 0, 0, 1))
// -> "127.0.0.1"
ip_address_to_string(IpV6(0, 0, 0, 0, 0, 0, 0, 1))
// -> "::1"
pub fn listening(builder: Builder, on port: Int) -> Builder
Set the port the server listens on.
A server listens on either a network interface or a Unix socket so this
undoes a previous call to unix.
pub fn listening_random(builder: Builder) -> Builder
Listen on port 0 which asks the operating system for any free port.
pub fn named(
builder: Builder,
name: process.Name(tup.Server),
) -> Builder
Register the running server under a name. Create the name once where your program starts!
Examples
let name = process.new_name("ewe")
let assert Ok(_) =
ewe.new(handler: handle_request)
|> ewe.named(name)
|> ewe.listening_random
|> ewe.start
ewe.get_server_info(name)
// -> Ok(TcpSocketAddress(IpV4(127, 0, 0, 1), 54321))
pub fn new(
handler handler: fn(request.Request(Connection)) -> response.Response(
Body,
),
) -> Builder
Create a new server configuration. The handler is called for every request and the response it returns is sent to the client.
By default, the server listens on 127.0.0.1:3000 and prints its address once started.
start returns the address the server listens on. You can give the server a
name with named to look the address with get_server_info.
Examples
pub fn main() {
let assert Ok(_) =
ewe.new(handler: handle_request)
|> ewe.bind(to: "0.0.0.0")
|> ewe.listening(on: 8080)
|> ewe.start
process.sleep_forever()
}
pub fn on_crash(
builder: Builder,
on_crash: response.Response(Body),
) -> Builder
Set the response sent when the handler crashes. By default that is an empty 500.
The body must be Bytes, Text or Empty. Any other body is replaced with
Empty and logged as a warning when the server starts.
Examples
response.new(500)
|> response.set_header("content-type", "text/html")
|> response.set_body(ewe.Text("<h1>Something went wrong</h1>"))
|> ewe.on_crash(builder, _)
pub fn on_start(
builder: Builder,
on_start: fn(http.Scheme, SocketAddress) -> Nil,
) -> Builder
Set the function to run once the server is listening. It is given the scheme and the address the server listens on.
By default the function prints the address. Use quiet for empty silet
start.
pub fn read_body(
req: request.Request(Connection),
limit limit: Int,
) -> Result(request.Request(BitArray), BodyError)
Read the entire request body into memory up to the given limit in bytes.
Any trailer fields a chunked request ends with are appended to the returned request’s headers.
Use read_body_chunk instead if the body may be too large to hold in memory.
Examples
case ewe.read_body(request, limit: 1_048_576) {
Ok(request) -> handle(request.body)
Error(_body_error) -> response.new(400) |> response.set_body(ewe.Empty)
}
pub fn read_body_chunk(
req: request.Request(Connection),
max_chunk_bytes max_chunk_bytes: Int,
limit limit: Int,
) -> Result(ReadEvent, BodyError)
Read the request body a chunk at a time, taking up to max_chunk_bytes per
call and refusing a body larger than limit bytes in total.
Each Chunk carries the request to use for the next call. Keep going until
you receive Done.
Examples
fn count(request: request.Request(ewe.Connection), total: Int) -> Int {
case ewe.read_body_chunk(request, max_chunk_bytes: 4096, limit: 10_000_000) {
Ok(ewe.Chunk(data:, request:)) ->
count(request, total + bit_array.byte_size(data))
Ok(ewe.Done(_request)) -> total
Error(_body_error) -> total
}
}
pub fn send_binary_frame(
conn: WebsocketConnection,
data: BitArray,
) -> Result(Nil, SendError)
Send a binary frame to the client.
pub fn send_chunk(
writer: ResponseWriter,
chunk: BitArray,
) -> Result(ResponseWriter, SendError)
Send one chunk of a streamed response body. The writer is handed back so that it can be threaded into the next call.
For the last chunk use finish_chunk instead which closes the body in the
same write.
pub fn send_close_frame(
conn: WebsocketConnection,
reason: CloseReason,
) -> Next(user_state, user_message)
Start the closing handshake and end the WebSocket.
Return the value this gives back from your handler. No frame can be sent after it.
Examples
ewe.send_close_frame(conn, ewe.CloseReason(ewe.GoingAway, "shutting down"))
pub fn send_error_to_string(error: SendError) -> String
Describe a SendError in a form that reads inside a log line.
Examples
send_error_to_string(ConnectionClosed)
// -> "the client is gone"
pub fn send_event(
conn: SseConnection,
event: SseEvent,
) -> Result(Nil, SendError)
Send an event to the client of a Server-Sent Events stream.
pub fn send_text_frame(
conn: WebsocketConnection,
text: String,
) -> Result(Nil, SendError)
Send a text frame to the client.
pub fn shutdown_timeout(
builder: Builder,
milliseconds: Int,
) -> Builder
Set how long each connection gets to finish when the server shuts down in milliseconds. 15 seconds by default.
HTTP/1 connections finish the request they are serving, WebSockets are sent a going-away close frame and HTTP/2 connections send GOAWAY and wait for their open streams. A connection still open once the time is up is closed.
This only happens when the server runs in the supervision tree of an OTP
application. One started from main, even under a supervisor, is killed
with the VM instead.
pub fn socket_reason_to_string(reason: SocketReason) -> String
Describe a SocketReason in a form that reads inside a log line.
Examples
socket_reason_to_string(NetworkDown)
// -> "the network is down"
pub fn sse(
response: response.Response(a),
on_init on_init: fn(
SseConnection,
process.Selector(user_message),
) -> #(user_state, process.Selector(user_message)),
handler handler: fn(SseConnection, user_state, user_message) -> Next(
user_state,
user_message,
),
on_close on_close: fn(user_state) -> Nil,
) -> response.Response(Body)
Set the body of a response to a Server-Sent Events stream which runs until the handler stops it or the client goes away.
on_initis called once with a selector to add whatever the rest of your program sends this stream to and returns the starting state along with that selector.handleris called for each message the selector selects.on_closeis called once with the last state after the stream has ended.
The content-type and cache-control headers the stream needs are set by
ewe.
On HTTP/1.1 the connection can carry another request afterwards as long as the handler ended the stream itself and the client sent nothing during the connection.
Examples
response.new(200)
|> ewe.sse(
on_init: fn(_conn, selector) {
let client = process.new_subject()
pubsub.subscribe(pubsub, client)
#(0, process.select(selector, client))
},
handler: fn(conn, sent, message) {
case ewe.send_event(conn, ewe.event(message)) {
Ok(Nil) -> ewe.continue(sent + 1)
Error(_send_error) -> ewe.stop()
}
},
on_close: fn(_sent) { Nil },
)
pub fn start(
builder: Builder,
) -> Result(actor.Started(SocketAddress), actor.StartError)
Start the server, running the on_start function once it is listening.
The started data is the address the server listens on. To put the server
under a supervision tree use supervised instead.
Examples
let assert Ok(actor.Started(data: address, ..)) =
ewe.new(handler: handle_request)
|> ewe.listening_random
|> ewe.quiet
|> ewe.start
address
// -> TcpSocketAddress(IpV4(127, 0, 0, 1), 54321)
pub fn stop() -> Next(user_state, user_message)
End the connection. To tell a WebSocket client the reason use
send_close_frame instead.
pub fn stop_abnormal(
reason: String,
) -> Next(user_state, user_message)
End the connection and exit its process abnormally with the given reason.
pub fn stream_response(
response: response.Response(a),
handler: fn(ResponseWriter) -> Result(Nil, SendError),
) -> response.Response(Body)
Set the body of a response to be a streamed body.
The handler is given a writer and must finish the body with finish_chunk
or finish_response. A handler that returns without calling either leaves
the body cut short: HTTP/1.1 closes the connection without the final chunk
and HTTP/2 resets the stream, so the client does not mistake the partial
body for a complete one.
Examples
response.new(200)
|> response.set_header("content-type", "text/plain")
|> ewe.stream_response(fn(writer) {
use writer <- result.try(ewe.send_chunk(writer, <<"Hello, ":utf8>>))
ewe.finish_chunk(writer, <<"Joe!":utf8>>)
})
pub fn supervised(
builder: Builder,
) -> supervision.ChildSpecification(SocketAddress)
Create a child specification for the server so that it can be added to a supervision tree.
pub fn unix(builder: Builder, path: String) -> Builder
Listen on a Unix domain socket at the given path instead of on TCP.
A server listens on either a network interface or a Unix socket so this discards any interface, port and IPv6 setting made before it.
pub fn websocket(
request request: request.Request(Connection),
on_init on_init: fn(
WebsocketConnection,
process.Selector(user_message),
) -> #(user_state, process.Selector(user_message)),
handler handler: fn(
WebsocketConnection,
user_state,
WebsocketMessage(user_message),
) -> Next(user_state, user_message),
on_close on_close: fn(user_state) -> Nil,
) -> response.Response(Body)
Upgrade the request to a WebSocket which runs until the handler stops it or the client goes away.
on_initis called once with a selector to add whatever the rest of your program sends this connection to and returns the starting state along with that selector.handleris called for each frame from the client and each message the selector picks up.on_closeis called once with the last state after the WebSocket has ended.
A request that is not a valid handshake is answered with status code 400: Bad Request.
Examples
ewe.websocket(
request:,
on_init: fn(_conn, selector) { #(0, selector) },
handler: fn(conn, count, message) {
case message {
ewe.TextFrame(text) -> {
let assert Ok(Nil) = ewe.send_text_frame(conn, text)
ewe.continue(count + 1)
}
ewe.BinaryFrame(_data) | ewe.UserMessage(_message) ->
ewe.continue(count)
}
},
on_close: fn(_count) { Nil },
)
pub fn with_client_verification(
builder: Builder,
ca_cert: ClientVerification,
) -> Builder
Require clients to present a certificate signed by the given authority. Clients that do not provide the certificates are refused.
This requires the TLS enabled.
pub fn with_http1(
builder: Builder,
options: Http1Options,
) -> Builder
Set the limits and timeouts applied to every HTTP/1 connection.
pub fn with_http2(
builder: Builder,
options: Http2Options,
) -> Builder
Set the limits and timeouts applied to every HTTP/2 connection.
pub fn with_tls(builder: Builder, tls: Tls) -> Builder
Serve over TLS with the given certificate and key.
This is also what offers HTTP/2 to clients through ALPN. Without TLS a client only gets HTTP/2 by opening the connection with the h2c preface.
Examples
ewe.with_tls(builder, ewe.Disk("cert.pem", "key.pem"))
ewe.with_tls(builder, ewe.Pem(cert, key))
ewe.with_tls(builder, ewe.Der(cert, key, ewe.RsaPrivateKey))