scansion/serve.lua

Sun, 30 Dec 2018 09:43:36 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Sun, 30 Dec 2018 09:43:36 +0000
changeset 164
14500a149b31
parent 162
f888f236321f
permissions
-rw-r--r--

client: Ignore timeout timer if we received a stanza

local verse = require "verse";

local server = require "net.server";
local http_server = require "net.http.server";
local http = require "net.http";
local json = require "util.json";
local time = require "socket".gettime;

function handle_request()
	return "Hello world";
end

local function set_cross_domain_headers(response, origin)
	local headers = response.headers;
	headers.access_control_allow_methods = "GET, POST, OPTIONS";
	headers.access_control_allow_headers = "Content-Type";
	headers.access_control_max_age = "7200";
	headers.access_control_allow_origin = origin;
	return response;
end

local function run(config, run_script)
	function handle_run_request(event)
		local request, response = event.request, event.response;

		local function log(type, data)
			local entry = { type = type, data = data, time = time() };
			local chunk = json.encode(entry) .. "\r\n";
			response.conn:write(("%x\r\n%s\r\n"):format(#chunk, chunk));
		end

		-- SECURITY NOTE: We MUST validate Origin before running the Scansion script,
		-- since we don't want arbitrary websites to have local RCEs (CORS does not
		-- protect us here, it at best keeps the script from seeing the response)
		if request.headers.origin ~= config.origin and config.origin ~= "*" then
			verse.log("warn", "Rejecting origin: %s", request.headers.origin);
			response.status_code = 403; -- spec suggested response when we don't like the origin
			response.headers.connection = "close";
			return "";
		end

		response.status_code = 201;
		response.headers.connection = "close";
		response.headers.transfer_encoding = "chunked";
		set_cross_domain_headers(response, config.origin); -- Let browser JS see the response
		response.conn:send(table.concat(http_server.prepare_header(response)));

		local ok, ret = pcall(run_script, "web", event.request.body, log);
	
		if not ok then
			local line = nil;
			if type(ret) == "string" then
				line = tonumber(ret:match("%(line (%d+)%)$"));
			end
			log("error", { message = ret, line = line });
		end

		response.conn:write("0\r\n\r\n");
		response:done();
	
		return true;
	end
	
	http_server.add_host("localhost");
	http_server.set_default_host("localhost");
	http_server.add_handler("GET localhost/*", handle_request);
	http_server.add_handler("POST localhost/run", handle_run_request);
	http_server.add_handler("http-error", function (e)
		verse.log("error", "HTTP error: %s", e.code);
	end);
	--http_server.add_handler("GET localhost/stream/*", handle_stream_request);
	local port = config.port or 8007;
	http_server.listen_on(port);
	verse.log("info", "Listening on port %d for origin %s", port, config.origin);
	repeat until not verse.loop();
end

return {
	run = run;
}

mercurial