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 CMLimport coimport std.domain as Domainimport std.http as HTTPimport std.http.cmlserver as CmlServerimport std.http.server as Serverimport std.net as Netimport std.reactor as Reactorimport std.signal as Signalimport std.time as Timeimport custodian as Custodianimport resource
hello : {e : HFunctor} -> Server.Request -> Co e HTTP.Responsehello 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-Cserved : {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) Unitserved 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 reactormain : Co (Net.net <+> Time.time <+> Reactor.reactor <+> Domain.domain <+> Signal.signals) Unitmain = 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) UnitThe 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.