shithub: mc

ref: 95a93936f420dd480454eb97c25b02874da7a966
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)
		| `std.Err e:	break
		;;
	;;
	std.close(fd)
	unref(srv)
}

const respond = {srv, s, resp
	var sb

	sb = std.mksb()
	bio.put(s.f, "HTTP/1.1 {} {}\r\n", resp.status, statusstr(resp.status))
	bio.put(s.f, "Content-Length: {}\r\n", resp.body.len)
	bio.put(s.f, "Encoding: {}\r\n", resp.enc)
	for (k, v) : resp.hdrs
		bio.put(s.f, "{}: {}\r\n", k, v)
	;;
	bio.put(s.f, "\r\n")
	bio.write(s.f, resp.body)
	bio.flush(s.f)
}

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)
	;;
}