ref: 74d91a0021de012908cfdb35fb61a1473a376130
dir: /lib/http/server.myr/
use bio use std use thread use "types" use "session" use "parse" pkg http = const announce : (ds : byte[:] -> std.result(server#, err)) const shutdown : (srv : server# -> void) const serve : (srv : server#, fn : (srv : server#, s : session#, req : req# -> void) -> void) const respond : (srv : server#, sess : session#, resp : resp# -> void) ;; const announce = {ds match std.announce(ds) | `std.Err e: -> `std.Err `Econn | `std.Ok a: -> `std.Ok std.mk([ .refs=1, .ann=a, .quit=false ]) ;; } const serve = {srv, fn while !srv.quit match waitconn(srv) | `std.Ok fd: ref(srv) thread.spawn({;communicate(srv, fd, fn)}) | `std.Err e: /* eh? */ ;; ;; unref(srv) } const communicate = {srv, fd, fn var s s = mksrvsession(fd) while !srv.quit match parsereq(s) | `std.Ok req: fn(srv, s, req) freereq(req) | `std.Err e: break ;; ;; freesession(s) std.close(fd) unref(srv) } const respond = {srv, s, resp ioput(s, "HTTP/1.1 {} {}\r\n", resp.status, statusstr(resp.status)) if resp.enc != `Chunked ioput(s, "Content-Length: {}\r\n", resp.body.len) ;; match resp.enc | `Length: /* noop */ | `Chunked: ioput(s, "Transfer-Encoding: chunked\r\n") | `Compress: ioput(s, "Transfer-Encoding: compress\r\n") | `Deflate: ioput(s, "Transfer-Encoding: deflate\r\n") | `Gzip: ioput(s, "Transfer-Encoding: gzip\r\n") ;; for (k, v) : resp.hdrs ioput(s, "{}: {}\r\n", k, v) ;; ioput(s, "\r\n") iowrite(s, resp.body) ioflush(s) } const statusstr = {st match st | 200: -> "OK" | 404: -> "Not Found" | 503: -> "Internal Error" | _: -> "Bad State" ;; } const shutdown = {srv std.aclose(srv.ann) srv.quit = true } const waitconn = {srv match std.accept(srv.ann) | `std.Ok fd: -> `std.Ok fd | `std.Err e: -> `std.Err `Econn ;; } const ref = {srv thread.xadd(&srv.refs, 1) } const unref = {srv thread.xadd(&srv.refs, -1) if thread.xget(&srv.refs) == 0 std.free(srv) ;; }