Skip to content
heff

std.http.cmlserver

An HTTP/1.1 server of many connections at once, on cml’s threads: a listener’s connections each served by a thread of its own, by std.http.server’s serveConnection, the others served meanwhile. Run under cml’s threadsWithReactor, each thread waits on its socket apart, in its domain’s reactor; under scheduled, a thread that waits holds its domain until it is done. It is the C backend’s alone, as std.net is. Import it by a name, import std.http.cmlserver as CmlServer.

import cml as CML
import co
import std.domain as Domain
import std.http as HTTP
import std.http.cmlserver as CmlServer
import std.http.server as Server
import std.net as Net
import std.reactor as Reactor
import std.signal as Signal
import std.time as Time
import custodian as Custodian
import resource
hello : {e : HFunctor} -> Server.Request -> Co e HTTP.Response
hello r = pure (Server.plain 200 ("hello, " ^ r.target ^ "\n"))
-- | `hello` served on the listener, each connection a thread of its
-- | own, until one cannot be accepted, or until SIGTERM or Ctrl-C
served : {w : HFunctor}
-> {s : Type}
-> {{nt : Net.net <: w}}
-> {{clock : Time.time <: w}}
-> {{sg : Signal.signals <: w}}
-> With s Net.TcpListener
-> Co (CML.cml w <+> Custodian.custodian <+> w) Unit
served l =
CML.choose
[ CmlServer.serve Empty l hello
, bind (Signal.wait [Terminate, Interrupt]) (\s -> pure ())
]
-- | on port 8080, the threads run on the program's world itself, each
-- | waiting on the world through its domain's reactor
main : Co (Net.net <+> Time.time <+> Reactor.reactor <+> Domain.domain <+> Signal.signals) Unit
main = do
r <-
Net.withListener
{ host = "127.0.0.1", port = 8080 }
128
(\l ->
CML.scheduledWithReactor (served l))
pure ()

One thread accepts, in a loop: each connection accepted is given a thread of its own, which serves it and closes it as it ends, and the loop goes on to accept the next. A connection’s thread is of cml’s world, cml w <+> custodian <+> w, as every thread is, and so is serve, a thread’s or main’s own: its w is found from the world it is run in, a spawn’s or a choose’s. The handler is of that world too, with what it may throw, an err (Empty where it throws nothing): a throw no recover of the handler’s hears closes that connection at once, unanswered, what was thrown let go of — a handler that is to say why catches it itself — and the others are served on. A connection whose client sends nothing for idle milliseconds — 30 seconds unless another is given, by name, CmlServer.serve (idle := 5000) Empty l handle; 0 for no limit — is closed, its thread done; so is one that sends a request’s head a little at a time and has not sent it all within headerWithin milliseconds — 30 seconds unless another is given, by name — of the connection’s start, or of the response before it. Where a connection cannot be accepted (the program out of descriptors, EMFILE; the listener closed, EBADF) the server stops: serve comes back, the connections being served going on, the listener left as it is. serve comes back at a base event of cml’s, so it is stopped otherwise by a choose: beside std.signal’s wait, as above, at the signal — the listener closed as its with ends, and the connections being served dropped with the other threads as main ends.

serve

serve : {w : HFunctor}
-> {s : Type}
-> {idle : Int := 30000}
-> {headerWithin : Int := 30000}
-> (err : Type)
-> {{nt : Net.net <: w}}
-> {{clock : Time.time <: w}}
-> With s Net.TcpListener
-> (Server.Request
-> Co
(exception err <+> ((CML.cml w <+> Custodian.custodian) <+> w))
HTTP.Response)
-> Co ((CML.cml w <+> Custodian.custodian) <+> w) Unit

The listener’s connections served at once, each by a cml thread of its own (Server.serveConnection), spawned by one thread accepting them in a loop, and closed as it is served. Until one fails to be accepted: the listener left as it is, the connections being served going on. A connection whose client sends nothing for idle milliseconds is closed, its thread done; one whose request’s head is not all read within headerWithin milliseconds of its start, or of the response before it, too. The handler runs in its connection’s thread, in the scheduler’s world, and may throw an err (Empty for one that throws nothing): that connection is closed at once, unanswered, and what was thrown let go of; the others go on. It is of cml’s world, a thread’s, so its w is the one the thread runs in.