276 lines · 5.3 KB · Text
RawDownload
use stduse bio use "types"use "session"use "parse" pkg http =	/* simple versions */	const get	: (s : session#, r : url# -> std.result(resp#, err))	const head	: (s : session#, r : url# -> std.result(resp#, err))	const put	: (s : session#, r : url#, data : byte[:] -> std.result(resp#, err))	const post	: (s : session#, r : url#, data : byte[:] -> std.result(resp#, err))	const delete	: (s : session#, r : url# -> std.result(resp#, err))	const options	: (s : session#, r : url# -> std.result(resp#, err))	const trace	: (s : session#, r : url# -> std.result(resp#, err)) 	/* request based versions */	const getreq	: (s : session#, r : req# -> std.result(resp#, err))	const headreq	: (s : session#, r : req# -> std.result(resp#, err))	const putreq	: (s : session#, r : req#, data : byte[:] -> std.result(resp#, err))	const postreq	: (s : session#, r : req#, data : byte[:] -> std.result(resp#, err))	const deletereq	: (s : session#, r : req# -> std.result(resp#, err))	const optionsreq	: (s : session#, r : req# -> std.result(resp#, err))	const tracereq	: (s : session#, r : req# -> std.result(resp#, err)) 	const freeresp	: (r : resp# -> void);; const get = {s, path; -> getreq(s, &[.url=path])}const head = {s, path; -> headreq(s, &[.url=path])}const put = {s, path, data; -> putreq(s, &[.url=path], data)}const post = {s, path, data; -> postreq(s, &[.url=path], data)}const delete = {s, path; -> deletereq(s, &[.url=path])}const options = {s, path; -> optionsreq(s, &[.url=path])}const trace = {s, path; -> tracereq(s, &[.url=path])}  const getreq = {s, r	match request(s, `Get, r, `std.None)	| `std.Ok _:	/* nothing */	| `std.Err e:	-> `std.Err e	;; 	-> response(s, true)} const headreq = {s, r	match request(s, `Head, r, `std.None)	| `std.Ok _:	/* nothing */	| `std.Err e:	-> `std.Err e	;; 	-> response(s, false)} const putreq = {s, r, data	match request(s, `Put, r, `std.Some data)	| `std.Ok _:	/* nothing */	| `std.Err e:	-> `std.Err e	;; 	-> response(s, true)} const postreq = {s, r, data	match request(s, `Post, r, `std.Some data)	| `std.Ok _:	/* nothing */	| `std.Err e:	-> `std.Err e	;; 	-> response(s, true)} const deletereq = {s, r	match request(s, `Delete, r, `std.None)	| `std.Ok _:	/* nothing */	| `std.Err e:	-> `std.Err e	;; 	-> response(s, true)} const optionsreq = {s, r	match request(s, `Options, r, `std.None)	| `std.Ok _:	/* nothing */	| `std.Err e:	-> `std.Err e	;; 	-> response(s, true)} const tracereq = {s, r	match request(s, `Trace, r, `std.None)	| `std.Ok _:	/* nothing */	| `std.Err e:	-> `std.Err e	;; 	-> response(s, true)} const response = {s, body	var resp 	resp = std.mk([		.hdrs = [][:],		.len = 0,		.err = `std.None,		.reason = "",		.status = 0,		.enc = `Length,	]) 	if parseresp(s, resp)		if !body			-> `std.Ok resp		else			match readbody(s, resp)			| `std.Ok buf:	resp.body = buf			| `std.Err e:	-> `std.Err e			;;		;;	else		match resp.err		| `std.Some e:	-> `std.Err e		| `std.None:	-> `std.Err `Ewat		;;	;;	-> `std.Ok resp} const request = {s, method, r, data	/* status */	ioput(s, "{} {p} HTTP/1.1\r\n", method, r.url) 	/* headers */	ioput(s, "Host: {}\r\n", s.host)	ioput(s, "User-Agent: {}\r\n", s.ua)	match data	| `std.Some d:	ioput(s, "Content-Length: {}\r\n", d.len)	| `std.None:	/* nothing to do */	;;	for (k, v) : r.hdrs		ioput(s, "{}: {}\r\n", k, v)	;;	ioput(s, "\r\n") 	/* body */	match data	| `std.None:	/* nothing to do */	| `std.Some d:		iowrite(s, d)		ioput(s, "\r\n")	;;	ioflush(s) 	if s.err		-> `std.Err `Econn	else		-> `std.Ok void	;; } const readbody = {s, r -> std.result(byte[:], err)	match r.enc	| `Length:	-> readlenbody(s, r)	| `Chunked:	-> readchunkedbody(s, r)	| badenc:	std.fatal("unsupported encoding {}\n", badenc)	;;} const readlenbody = {s, r	var buf 	buf = ""	if r.len == 0		-> `std.Ok buf	;; 	buf = std.slalloc(r.len)	match bio.read(s.f, buf)	| `std.Err e:	goto shortread	| `std.Ok rd:		if rd.len != r.len			goto shortread		;;	;;	-> `std.Ok buf:shortread	std.slfree(buf)	-> `std.Err `Eshort} const __init__ = {	var m 	m = `Get	std.fmtinstall(std.typeof(m), fmtmethod)} const readchunkedbody = {s, r	var buf, len 	buf = ""	len = 0	while true		match parsechunksz(s)		| `std.Err e:			std.slfree(buf)			-> `std.Err e		| `std.Ok 0:			break		| `std.Ok sz:			std.slgrow(&buf, buf.len + sz)			match bio.read(s.f, buf[len:len + sz])			| `std.Err e:				std.slfree(buf)				-> `std.Err `Econn			| `std.Ok str:				if str.len != sz					std.slfree(buf)					-> `std.Err `Eshort				;;				len += sz				match checkendln(s)				| `std.Ok _:	/* nothing */				| `std.Err e:						std.slfree(buf)					-> `std.Err e				;;			;;		;;	;;	-> `std.Ok buf} const checkendln = {s	var r 	match bio.readln(s.f)	| `std.Err e:	r = `std.Err `Econn	| `std.Ok crlf:		if std.strstrip(crlf).len == 0			r = `std.Ok void		else			r = `std.Err `Eproto		;;		std.slfree(crlf)	;;	-> r} const fmtmethod = {sb, ap, opt	var r 	r = std.vanext(ap)	match r	| `Get: std.sbputs(sb, "GET")	| `Head: std.sbputs(sb, "HEAD")	| `Put: std.sbputs(sb, "PUT")	| `Post: std.sbputs(sb, "POST")	| `Delete: std.sbputs(sb, "DELETE")	| `Trace: std.sbputs(sb, "TRACE")	| `Options: std.sbputs(sb, "OPTIONS")	;;} const freeresp = {r	for (k, v) : r.hdrs		std.slfree(k)		std.slfree(v)	;;	std.slfree(r.reason)	std.free(r)}