~/bend-docscommunity

src/server.bend fails

raw source on the hub · import emerging-ezhttp@0.8.0.0/src/server.bend as Server

ezhttp/src/server: listen, accept, parse one request, write one response. Uses Base TCP (no TLS in v0). One exchange per connection with Connection: close. Handler is a pure Request -> Reply function.

3 imports
import Base
import ./http.bend as Http
import ./cors.bend as Cors

Definitions

def server.max source · line 9 · raw

U32

default recv budget for one request message

def server.listen source · line 14 · raw

@host:String -> @port:U32 -> IO(Listener)

listen on a host address and port, e.g. "127.0.0.1" or "0.0.0.0" (RFC 9112 connection establishment stays at the socket)

def server.accept.of source · line 18 · raw

@lst:Listener -> @res:Result<&1, &1, Pair(U32, String), Socket> -> IO(Pair(Listener, Socket))

unpack accept: listener stays, socket from the Result

def server.accept.un source · line 26 · raw

@pair:Pair(Listener, Result<&1, &1, Pair(U32, String), Socket>) -> IO(Pair(Listener, Socket))

def server.accept source · line 31 · raw

@listener:Listener -> IO(Pair(Listener, Socket))

def server.read.why source · line 39 · raw

String

the short-body refusal, so a partial recv can continue

def server.read.short source · line 43 · raw

@why:String -> Bool

whether this refusal is a body that has not arrived yet

def server.read.need source · line 47 · raw

@req:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> Bool

a parsed request still wants more bytes

def server.read.fuel source · line 55 · raw

Nat

how many extra recvs to chase a Content-Length

def server.read.done source · line 59 · raw

@sock:Socket -> @got:String -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))

parse what has arrived

def server.read.next source · line 63 · raw

@sock:Socket -> @got:String -> @req:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> @rest:(@_:Socket -> @_:String -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))) -> @more:Bool -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))

stop on a finished message, or ask for another recv

def server.read.grew source · line 77 · raw

@sock:Socket -> @+got:String -> @rest:(@_:Socket -> @_:String -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))) -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))

append a chunk and decide whether it completes the request

def server.read.empty source · line 86 · raw

@sock:Socket -> @got:String -> @chunk:String -> @rest:(@_:Socket -> @_:String -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))) -> @closed:Bool -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))

peer closed, or the chunk extends the buffer

def server.read.use source · line 100 · raw

@sock:Socket -> @got:String -> @+chunk:String -> @rest:(@_:Socket -> @_:String -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))) -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))

one chunk of the request message

def server.read.pass source · line 109 · raw

@sock:Socket -> @got:String -> @rest:(@_:Socket -> @_:String -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))) -> @r:Result<&1, &1, Pair(U32, String), String> -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))

the bytes from one recv

def server.read.un source · line 119 · raw

@got:String -> @rest:(@_:Socket -> @_:String -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))) -> @pair:Pair(Socket, Result<&1, &1, Pair(U32, String), String>) -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))

unpack recv's socket and result

def server.read.go source · line 129 · raw

@fuel:Nat -> @sock:Socket -> @got:String -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))

recv until Content-Length is met, the peer closes, or fuel runs out. fuel is first so the recursive call is a decreasing self-call.

def server.read source · line 141 · raw

@sock:Socket -> IO(Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request))

recv then parse an HTTP request (RFC 9112 §3)

def server.write.res source · line 145 · raw

@sock:Socket -> @r:Result<&1, &1, Pair(U32, String), Unit> -> IO(Socket)

write a structured response and keep the socket

def server.write.un source · line 149 · raw

@pair:Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>) -> IO(Socket)

def server.write source · line 154 · raw

@sock:Socket -> @reply:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply -> IO(Socket)

Templates

template server.dispatch source · line 162 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @req:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply

turn a bad request into 400; otherwise call the handler

template server.reply.built source · line 171 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @method:String -> @req:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply

apply HEAD body stripping once the method token is known

template server.reply source · line 176 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @req:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply

HEAD responses are written with an empty body (RFC 9110 §9.3.2)

template server.cors_reply source · line 186 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @cfg:0xf1c957a470368870a6d1d62a8c0cbe32/src/cors.Cfg -> @+req:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply

CORS preflight or simple headers, after HEAD bodies are dropped

template server.exchange.go source · line 194 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @sock:Socket -> @req:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> IO(Unit)

after read: write the handler's reply and close

template server.exchange.un source · line 202 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @pair:Pair(Socket, 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request) -> IO(Unit)

template server.exchange source · line 210 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @sock:Socket -> IO(Unit)

one connection: read, handle, write, close

template server.loop.cont source · line 217 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @again:(@_:Listener -> IO(Unit)) -> @pair:Pair(Listener, Socket) -> IO(Unit)

continue after accept: exchange, then call again with the listener. again is a runtime closure so it can carry the fuel still left.

template server.loop.go source · line 227 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @fuel:Nat -> @listener:Listener -> IO(Unit)

fuel-bounded accept loop (self-recursive)

template server.loop source · line 241 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @listener:Listener -> @limit:U32 -> IO(Unit)

listen and serve up to limit connections (fuel from the limit)

template server.serve source · line 249 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @host:String -> @port:U32 -> @limit:U32 -> IO(Unit)

listen and serve up to limit connections

template server.once.cont source · line 259 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @pair:Pair(Listener, Socket) -> IO(Unit)

serve exactly one connection then stop

template server.once source · line 267 · raw

@-handle:(@_:0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Request -> 0xf1c957a470368870a6d1d62a8c0cbe32/src/http.Reply) -> @host:String -> @port:U32 -> IO(Unit)