shithub: mc

ref: 74d91a0021de012908cfdb35fb61a1473a376130
dir: /lib/http/server.myr/

View raw version
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)
	;;
}