291 lines · 4.8 KB · Text
RawDownload
use stduse bio use "types" pkg http =	pkglocal const parsereq		: (s : session# -> std.result(req#, err))	pkglocal const parseresp	: (s : session#, r : resp# -> bool)	pkglocal const parsechunksz	: (s : session# -> std.result(std.size, err))	pkglocal const parsenumber	: (str : byte[:]#, base : int -> std.option(int));; const strcaseeq = {a : byte[:], b : byte[:] -> bool	var ca, cb 	while a.len == 0 || b.len == 0		(ca, a) = std.charstep(a)		(cb, b) = std.charstep(b)		if std.tolower(ca) != std.tolower(cb)			-> false		;;	;;	-> a.len == b.len} const parsereq = {s	var r, err 	r.hdrs = [][:]	r.url = std.mk([		.schema=`Http,		.host=std.sldup(s.host),		.port=s.port,		.path="",		.params=[][:],	]) 	match bio.readln(s.f)	| `std.Err e:		err = `Econn		goto error	| `std.Ok ln:		match parsereqstatus(s, r, ln)		| `std.Ok void:		| `std.Err e:			std.slfree(ln)			err = e			-> `std.Err e		;;		std.slfree(ln)	;; 	while true		match bio.readln(s.f)		| `std.Err e:				err = `Econn			goto error		| `std.Ok ln:			if std.strstrip(ln).len == 0				std.slfree(ln)				break			;;			match parsehdr(s, ln)			| `std.Ok kvp:				std.slpush(&r.hdrs, kvp)			| `std.Err e:				std.slfree(ln)				-> `std.Err e			;;			std.slfree(ln)		;;	;;	-> `std.Ok std.mk(r):error	-> `std.Err err} const parseresp = {s, r : resp#	match bio.readln(s.f)	| `std.Err _:	r.err = `std.Some `Econn	| `std.Ok ln:		if !parserespstatus(s, r, ln)			std.slfree(ln)			-> false		;;		std.slfree(ln)	;; 	while true		match bio.readln(s.f)		| `std.Err e:	r.err = `std.Some `Econn		| `std.Ok ln:			if std.strstrip(ln).len == 0				std.slfree(ln)				break			;;			match parsehdr(s, ln)			| `std.Ok kvp:				std.slpush(&r.hdrs, kvp)			| `std.Err e:				r.err = `std.Some e				std.slfree(ln)				-> false			;;			std.slfree(ln)		;;	;; 	match getenc(r)	| `std.Ok `Length:		r.enc = `Length		match getlen(r)		| `std.Some n:			r.len = n		| `std.None:			r.err = `std.Some `Eproto			-> false		;;	| `std.Ok enc:		r.enc = enc	| `std.Err e:		r.err = `std.Some e		-> false	;;	-> true } const parsereqstatus = {s, r, ln	match parseword(&ln)	| `std.Some "GET":	r.method = `Get	| `std.Some "HEAD":	r.method = `Head	| `std.Some "POST":	r.method = `Post	| `std.Some "DELETE":	r.method = `Delete	| `std.Some "TRACE":	r.method = `Trace	| `std.Some "OPTIONS":	r.method = `Options	| `std.Some _:	-> `std.Err `Eproto	| `std.None:	-> `std.Err `Eproto	;; 	match parseword(&ln)	| `std.Some w:	r.url.path = std.sldup(w)	| `std.None:	-> `std.Err `Eproto	;; 	match parseword(&ln)	| `std.Some "HTTP/1.1":	/* ok */	| `std.Some w:	std.put("warn: http version '{}'\n", w)	| `std.None:	-> `std.Err `Eproto	;; 	ln = std.strfstrip(ln)	-> `std.Ok void} const parserespstatus = {s, r, ln	/* HTTP/1.1 */	ln = std.strfstrip(ln)	if !std.chomp(&ln, "HTTP")		r.err = `std.Some `Eproto		-> false	;;	if !std.chomp(&ln, "/1.1")		r.err = `std.Some `Eproto		-> false	;; 	ln = std.strfstrip(ln)	match parsenumber(&ln, 10)	| `std.Some n:		r.status = n	| `std.None:		r.err = `std.Some `Eproto		-> false	;; 	ln = std.strfstrip(ln)	r.reason = std.sldup(ln)	-> true} const parsehdr = {s, ln	var key, val 	match std.strfind(ln, ":")	| `std.Some idx:		key = std.sldup(std.strstrip(ln[:idx]))		val = std.sldup(std.strstrip(ln[idx+1:]))		-> `std.Ok (key, val)	| `std.None:		-> `std.Err `Ehdr	;;} const getlen = {r	match findhdr(r, "Content-Length")	| `std.Some v:		match std.intparsebase(v, 10)		| `std.Some n:	-> `std.Some n		| `std.None:	-> `std.None		;;	| `std.None:		-> `std.None	;;} const parsechunksz = {s	var ret, str 	match bio.readln(s.f)	| `std.Err e:	ret = `std.Err `Econn	| `std.Ok ln:		str = ln		match parsenumber(&str, 16)		| `std.Some n:	ret = `std.Ok (n : std.size)		| `std.None:			ret = `std.Err `Eproto		;;		std.slfree(ln)	;;	-> ret} const getenc = {r	match findhdr(r, "Transfer-Encoding")	| `std.None:	-> `std.Ok `Length	| `std.Some "chunked":	-> `std.Ok `Chunked	| `std.Some "compress":	-> `std.Ok `Compress	| `std.Some "deflate":	-> `std.Ok `Deflate	| `std.Some "gzip":	-> `std.Ok `Gzip	| `std.Some unknown:	-> `std.Err `Eenc	;;} const findhdr = {r, name	for (k, v) : r.hdrs		if strcaseeq(k, name)			-> `std.Some v		;;	;;	-> `std.None} const parseword = {ln	var w, end 	ln# = std.strfstrip(ln#)	end = 0	for var i = 0; i < ln#.len; i++		if i == ln#.len - 1			end = i + 1		elif std.isspace(std.decode(ln#[i:]))			end = i			break		;;	;;	if end == 0		-> `std.None	else		w = ln#[:end]		ln# = ln#[end:]		-> `std.Some w	;;} const parsenumber = {ln, base	var n, ok	var c, s, dig 	n = 0	s = ln#	ok = false	while true		(c, s) = std.charstep(s)		dig = std.charval(c, base)		if dig >= 0 && dig < base			ok = true			n *= base			n += dig		else			break		;;	;;	ln# = s	if ok		-> `std.Some n	else		-> `std.None	;;}