parsav  http.t at [c774e2c5a9]

File http.t artifact 5f40c98144 part of check-in c774e2c5a9


-- vim: ft=terra
local m = {}
local util = lib.util

m.method = lib.enum { 'get', 'post', 'post_file', 'head', 'options', 'put', 'delete' }
m.mime = lib.enum {
	'html'; -- default
	'json';
	'mkdown';
	'text';
	'ansi';
	'none';
}

m.findheader = terralib.externfunction('mg_http_get_header', {&lib.net.mg_http_message, rawstring} -> &lib.mem.ref(int8)) -- unfortunately necessary to access this function, as its return type conflicts with a function name

struct m.header {
	key: rawstring
	value: rawstring
}
struct m.page {
	respcode: uint16
	body: lib.mem.ptr(int8)
	headers: lib.mem.ptr(m.header)
}
struct m.upload {
	ctype: lib.str.t;
	filename: lib.str.t;
	field: lib.str.t;
	body: lib.str.t;
}

local resps = {
	[200] = 'OK';
	[201] = 'Created';
	[301] = 'Moved Permanently';
	[302] = 'Found';
	[303] = 'See Other';
	[307] = 'Temporary Redirect';
	[400] = 'Bad Request';
	[401] = 'Unauthorized';
	[402] = 'Payment Required';
	[403] = 'Forbidden';
	[404] = 'Not Found';
	[405] = 'Method Not Allowed';
	[406] = 'Not Acceptable';
	[418] = 'I\'m a teapot';
	[405] = 'Method Not Allowed';
	[500] = 'Internal Server Error';
}
local resptext = symbol(rawstring)
local resplen = symbol(intptr)
local respbranches = {}
for k,v in pairs(resps) do
	local txt = string.format('%u %s\r\n',k,v)
	respbranches[#respbranches + 1] = quote
		case [uint16](k) then resptext = [txt] resplen = [#txt] end
	end
end
m.codestr = terra(code: uint16)
	var [resptext] var [resplen]
	switch code do [respbranches] end
	return resptext, resplen
end

terra m.hier(pool: &lib.mem.pool, uri: lib.mem.ptr(int8)): lib.mem.ptr(lib.mem.ref(int8))
	if uri.ct == 0 then return [lib.mem.ptr(lib.mem.ref(int8))] { ptr = nil, ct = 0 } end
	var sz = 1
	var start = 0 if uri.ptr[0] == @'/' then start = 1 end
	for i = start, uri.ct do if uri.ptr[i] == @'/' then sz = sz + 1 end end
	var lst = pool:alloc([lib.mem.ref(int8)], sz)
	if sz == 0 then
		lst.ptr[0].ptr = uri.ptr
		lst.ptr[0].ct = uri.ct
		return lst
	end

	var idx: intptr = 0
	var len: intptr = 0
	lst.ptr[0].ptr = uri.ptr
	for i = 0, uri.ct do
		if uri.ptr[i] == @'/' then
			if len == 0 then
				lst.ptr[idx].ptr = lst.ptr[idx].ptr + 1
				goto skip
			end
			lst.ptr[idx].ct = len
			idx = idx + 1
			lst.ptr[idx].ptr = uri.ptr + i + 1
			len = 0
		else
			len = len + 1
		end
	::skip::end
	lst.ptr[idx].ct = len
	
	return lst
end

m.page.methods = {
	free = terra(self: &m.page)
		self.body:free()
		self.headers:free()
	end;
	send = terra(self: &m.page, con: &lib.net.mg_connection)
		var code: rawstring
		var [resptext] var [resplen]
		switch self.respcode do [respbranches] end
		lib.net.mg_send(con, "HTTP/1.1 ", 9)
		lib.net.mg_send(con, resptext, resplen)
		for i = 0, self.headers.ct do
			var h = self.headers.ptr[i]
			lib.net.mg_send(con, h.key, lib.str.sz(h.key))
			lib.net.mg_send(con, ": ", 2)
			lib.net.mg_send(con, h.value, lib.str.sz(h.value))
			lib.net.mg_send(con, "\r\n", 2)
		end
		lib.net.mg_printf(con, 'Content-Length: %llu\r\n\r\n', self.body.ct)
		lib.net.mg_send(con,self.body.ptr,self.body.ct)
	end;
}
return m