Skip to content

Instantly share code, notes, and snippets.

@eatonphil
Last active April 20, 2024 10:42
Show Gist options
  • Save eatonphil/ddc185ced78d79f87fcd to your computer and use it in GitHub Desktop.
Save eatonphil/ddc185ced78d79f87fcd to your computer and use it in GitHub Desktop.
A simple threaded web server in SML
(*
* Tested on PolyML. `polyc server.ml && ./a.out`
*)
fun println s =
(print (s ^ "\n"); TextIO.flushOut TextIO.stdOut)
fun readAll conn req =
let val ntoread = Socket.Ctl.getNREAD conn in
if ntoread > 0
then
let
val ntoreadMax1024x80 = if ntoread > 1024 * 80 then 1024 * 80 else ntoread;
val vec = Socket.recvVec (conn, ntoreadMax1024x80);
val vecLength = Word8Vector.length vec;
val reqSoFar = req ^ (String.substring (Byte.bytesToString vec, 0, vecLength))
in
if vecLength < ntoreadMax1024x80
then reqSoFar
else readAll conn reqSoFar
end
else req
end
fun handleConn conn handler =
let
val req = readAll conn "";
val resp = handler req;
val vec = Byte.stringToBytes resp;
val len = Word8Vector.length vec;
val slice = Word8VectorSlice.slice (vec, 0, SOME len)
in
println req;
Socket.sendVec (conn, slice);
Socket.close conn
end
fun serveForever sock handler =
let
val acceptOption = Socket.acceptNB sock;
fun fork conn =
Thread.Thread.fork (fn () => handleConn conn handler, [])
in
case acceptOption of
SOME (conn, _) => (fork conn; ())
| NONE => ();
serveForever sock handler;
()
end
fun listenAndServe port handler =
let
val sock = INetSock.TCP.socket();
val sleep = OS.Process.sleep;
fun bind () =
let
fun doBind () = Socket.bind (sock, INetSock.any port)
in
doBind() handle SysError => (sleep (Time.fromSeconds 1); bind());
()
end
in
println "Binding server...";
bind();
println ("Server bound... Listening on port " ^ (Int.toString port));
Socket.listen (sock, 1024);
Socket.Ctl.setREUSEADDR(sock, true);
serveForever sock handler;
()
end
fun main () =
listenAndServe 8080 (fn req => let in
print req;
"HTTP/1.1 200 OK\r\n\r\nHello World!\r\n\r\n" end);
()
@pscollins
Copy link

Hey, I just saw this from your reddit post --- this is a cool idea! I'd love to contribute if you want to spin this out into a full repo.

FWIW also I think you should consider looking into one of MLton or SML/NJ --- both have mature build systems that would be extremely useful for a project of this scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment