changeset 1171:794ddcfbee20

remove http/impl
author Franklin Schmidt <fschmidt@gmail.com>
date Sun, 11 Feb 2018 02:41:23 -0700
parents 3a0f58d09ee7
children 1aa6dd74f3fc
files src/luan/modules/http/Http.luan src/luan/modules/http/HttpServicer.java src/luan/modules/http/LuanHandler.java src/luan/modules/http/NotFound.java src/luan/modules/http/Server.luan src/luan/modules/http/impl/Http.luan src/luan/modules/http/impl/HttpServicer.java src/luan/modules/http/impl/LuanHandler.java src/luan/modules/http/impl/NotFound.java src/luan/modules/http/impl/Server.luan
diffstat 10 files changed, 476 insertions(+), 478 deletions(-) [+]
line wrap: on
line diff
--- a/src/luan/modules/http/Http.luan	Sun Feb 11 02:34:35 2018 -0700
+++ b/src/luan/modules/http/Http.luan	Sun Feb 11 02:41:23 2018 -0700
@@ -1,1 +1,160 @@
-return require "luan:http/impl/Http.luan"
+java()
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local ipairs = Luan.ipairs or error()
+local pairs = Luan.pairs or error()
+local type = Luan.type or error()
+local Io = require "luan:Io.luan"
+local Html = require "luan:Html.luan"
+local url_encode = Html.url_encode or error()
+local Table = require "luan:Table.luan"
+local clear = Table.clear or error()
+local Package = require "luan:Package.luan"
+local String = require "luan:String.luan"
+local lower = String.lower or error()
+local matches = String.matches or error()
+local HttpServicer = require "java:luan.modules.http.HttpServicer"
+local IoLuan = require "java:luan.modules.IoLuan"
+local JavaLuan = require "java:luan.Luan"
+local Response = require "java:luan.webserver.Response"
+local ResponseOutputStream = require "java:luan.webserver.ResponseOutputStream"
+local Status = require "java:luan.webserver.Status"
+local OutputStreamWriter = require "java:java.io.OutputStreamWriter"
+local HashMap = require "java:java.util.HashMap"
+
+
+local Http = {}
+
+Http.version = "luan"
+
+function Http.new_request(java)
+	local this = {}
+	Http.request = this
+	if java == nil then
+		this.method = "GET"
+		this.headers = {}
+		this.parameters = {}
+		this.cookies = {}
+	else
+		this.java = java
+		this.method = java.method or error()
+		this.raw_path = java.rawPath or error()
+		this.path = java.path or error()
+		this.protocol = java.protocol or error()
+		this.headers = JavaLuan.toLuan(java.headers)
+		this.parameters = JavaLuan.toLuan(java.parameters)
+		this.cookies = JavaLuan.toLuan(java.cookies)
+	end
+	this.scheme = "http"
+
+	function this.full_path()  -- compatible with jetty
+		return this.raw_path or this.path
+	end
+
+	function this.url()
+		return this.scheme.."://"..this.headers["host"]..this.raw_path
+	end
+
+	return this
+end
+
+local STATUS = {
+	OK = 200
+	MOVED_PERMANENTLY = 301
+	FOUND = 302
+	-- add more as needed
+}
+Http.STATUS = STATUS
+
+function Http.new_response()
+	local this = {}
+	Http.response = this
+
+	function this.reset()
+		this.java = Response.new()
+		this.headers = {}
+		this.status = STATUS.OK
+		this.writer = nil
+	end
+
+	this.reset()
+
+	function this.send_redirect(location)
+		this.reset()
+		this.status = STATUS.FOUND
+		this.headers["location"] = location
+	end
+
+	function this.send_error(status,msg)
+		this.reset()
+		this.status = status
+		if msg ~= nil then
+			this.headers["content-type"] = "text/plain; charset=utf-8"
+			local writer = this.text_writer()
+			writer.write(msg)
+		end
+	end
+
+	function this.set_cookie(name,value,attributes)
+		attributes = attributes or {}
+		local attrMap = HashMap.new()
+		for attr_name, attr_value in pairs(attributes) do
+			type(attr_name)=="string" or "cookie attribute name must be string"
+			type(attr_value)=="string" or "cookie attribute value must be string"
+			attrMap.put(attr_name,attr_value)
+		end
+		this.java.setCookie(name,value,attrMap)
+	end
+
+	function this.set_persistent_cookie(name,value,attributes)
+		attributes = attributes or {}
+		attributes["Max-Age"] = "10000000"
+		this.set_cookie(name,value,attributes)
+	end
+
+	function this.remove_cookie(name,attributes)
+		attributes = attributes or {}
+		attributes["Max-Age"] = "0"
+		this.set_cookie(name,"delete",attributes)
+	end
+
+	function this.text_writer()
+		this.writer and "writer already set"
+		this.writer = ResponseOutputStream.new(this.java)
+		this.writer = OutputStreamWriter.new(this.writer)
+		return IoLuan.textWriter(this.writer)
+	end
+
+	function this.binary_writer()
+		this.writer and "writer already set"
+		this.writer = ResponseOutputStream.new(this.java)
+		return IoLuan.binaryWriter(this.writer)
+	end
+
+	return this
+end
+
+function Http.finish()  -- called only from java
+	local response = Http.response or error()
+	local java = response.java or error()
+	java.status = Status.getStatus(response.status)
+	for name, value in pairs(response.headers) do
+		type(name)=="string" or "header name must be string"
+		name = lower(name)
+		value = JavaLuan.toJava(value)
+		java.headers.put(name,value)
+	end
+	response.writer and response.writer.close()
+	return java
+end
+
+
+function Http.uncache_site()
+	for k in pairs(Table.copy(Package.loaded)) do
+		if matches(k,"^site:") then
+			Package.loaded[k] = nil
+		end
+	end
+end
+
+return Http
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/http/HttpServicer.java	Sun Feb 11 02:41:23 2018 -0700
@@ -0,0 +1,52 @@
+package luan.modules.http;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import luan.webserver.Request;
+import luan.webserver.Response;
+import luan.LuanState;
+import luan.LuanFunction;
+import luan.LuanException;
+import luan.LuanTable;
+import luan.LuanCloner;
+import luan.modules.PackageLuan;
+
+
+public final class HttpServicer {
+	private static final Logger logger = LoggerFactory.getLogger(HttpServicer.class);
+
+	public static Response service(LuanState luan,Request request,String modName)
+		throws LuanException
+	{
+		LuanFunction fn;
+		synchronized(luan) {
+			PackageLuan.enableLoad(luan,"luan:http/Http.luan",modName);
+			LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan");
+			Object mod = PackageLuan.load(luan,modName);
+			if( mod.equals(Boolean.FALSE) )
+				return null;
+			if( !(mod instanceof LuanFunction) )
+				throw new LuanException( "module '"+modName+"' must return a function" );
+			LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL);
+			luan = (LuanState)cloner.clone(luan);
+			fn = (LuanFunction)cloner.get(mod);
+		}
+
+		LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan");
+
+		// request
+		LuanFunction newRequestFn = (LuanFunction)module.rawGet("new_request");
+		newRequestFn.call( luan, new Object[]{request} );
+
+		// response
+		LuanFunction newResponseFn = (LuanFunction)module.rawGet("new_response");
+		newResponseFn.call(luan);
+
+		fn.call(luan);
+
+		LuanFunction finishFn = (LuanFunction)module.rawGet("finish");
+		Response response = (Response)finishFn.call(luan);
+		return response;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/http/LuanHandler.java	Sun Feb 11 02:41:23 2018 -0700
@@ -0,0 +1,167 @@
+package luan.modules.http;
+
+import java.io.Writer;
+import java.io.PrintWriter;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.BindException;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import luan.webserver.Request;
+import luan.webserver.Response;
+import luan.webserver.Server;
+import luan.webserver.Handler;
+import luan.webserver.Status;
+import luan.webserver.ResponseOutputStream;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanCloner;
+import luan.LuanException;
+import luan.modules.PackageLuan;
+
+
+public class LuanHandler implements Handler {
+	private final LuanState luanInit;
+	private final Logger logger;
+	private final ReadWriteLock lock = new ReentrantReadWriteLock();
+	private LuanState luan;
+
+	private static final Method resetLuanMethod;
+	static {
+		try {
+			resetLuanMethod = LuanHandler.class.getMethod("reset_luan");
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public LuanHandler(LuanState luan,String loggerRoot) {
+		this.luanInit = luan;
+		if( loggerRoot==null )
+			loggerRoot = "";
+		logger = LoggerFactory.getLogger(loggerRoot+LuanHandler.class.getName());
+		try {
+			LuanTable Http = (LuanTable)PackageLuan.require(luanInit,"luan:http/Http.luan");
+			Http.rawPut( "reset_luan", new LuanJavaFunction(resetLuanMethod,this) );
+		} catch(LuanException e) {
+			throw new RuntimeException(e);
+		}
+		setLuan();
+	}
+
+	@Override public Response handle(Request request) {
+		if( request.path.endsWith("/") )
+			return null;
+		String modName = "site:" + request.path +".luan";
+		return handle(request,modName);
+	}
+
+	Response handle(Request request,String modName) {
+		Thread thread = Thread.currentThread();
+		String oldName = thread.getName();
+		thread.setName(request.headers.get("host")+request.path);
+		lock.readLock().lock();
+		try {
+			Response response = HttpServicer.service(luan,request,modName);
+			return response;
+		} catch(LuanException e) {
+//e.printStackTrace();
+			String err = e.getLuanStackTraceString();
+			logger.error(err);
+			String msg = "Internel Server Error\n\n" + err;
+			return Response.errorResponse( Status.INTERNAL_SERVER_ERROR, msg );
+		} finally {
+			lock.readLock().unlock();
+			thread.setName(oldName);
+		}
+	}
+/*
+	@Override protected void doStart() throws Exception {
+//		Thread.dumpStack();
+//System.out.println("qqqqqqqqqqqqqqqqqqqq doStart "+this);
+		setLuan();
+		super.doStart();
+	}
+
+	@Override protected void doStop() throws Exception {
+		synchronized(luan) {
+			luan.close();
+		}
+//System.out.println("qqqqqqqqqqqqqqqqqqqq doStop "+this);
+		super.doStop();
+	}
+*/
+	public Object call_rpc(String fnName,Object... args) throws LuanException {
+		lock.readLock().lock();
+		try {
+			LuanFunction fn;
+			LuanState luan = this.luan;
+			synchronized(luan) {
+				PackageLuan.enableLoad(luan,"luan:Rpc.luan");
+				LuanTable rpc = (LuanTable)PackageLuan.require(luan,"luan:Rpc.luan");
+				LuanTable fns = (LuanTable)rpc.get(luan,"functions");
+				fn = (LuanFunction)fns.get(luan,fnName);
+				if( fn == null )
+					throw new LuanException( "function not found: " + fnName );
+				LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL);
+				luan = (LuanState)cloner.clone(luan);
+				fn = (LuanFunction)cloner.get(fn);
+			}
+			return fn.call(luan,args);
+		} finally {
+			lock.readLock().unlock();
+		}
+	}
+
+	public void reset_luan() {
+		new Thread() {
+			public void run() {
+				lock.writeLock().lock();
+				try {
+					synchronized(luan) {
+						luan.close();
+						setLuan();
+					}
+				} catch(IOException e) {
+					logger.error("reset_luan failed",e);
+				} finally {
+					lock.writeLock().unlock();
+				}
+			}
+		}.start();
+	}
+
+	private void setLuan() {
+		LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
+		luan = (LuanState)cloner.clone(luanInit);
+		try {
+			PackageLuan.load(luan,"site:/init.luan");
+		} catch(LuanException e) {
+			String err = e.getLuanStackTraceString();
+			logger.error(err);
+		}
+	}
+
+	public Object runLuan(String sourceText,String sourceName) throws LuanException {
+		LuanFunction fn = Luan.load(sourceText,sourceName);
+		synchronized(luan) {
+			LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL);
+			LuanState luan = (LuanState)cloner.clone(this.luan);
+			return fn.call(luan);
+		}
+	}
+
+	public static void start(Server server) throws Exception {
+		try {
+			server.start();
+		} catch(BindException e) {
+			throw new LuanException(e.toString());
+		}
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/http/NotFound.java	Sun Feb 11 02:41:23 2018 -0700
@@ -0,0 +1,19 @@
+package luan.modules.http;
+
+import luan.webserver.Request;
+import luan.webserver.Response;
+import luan.webserver.Handler;
+
+
+public class NotFound implements Handler {
+	private final LuanHandler luanHandler;
+
+	public NotFound(LuanHandler luanHandler) {
+		this.luanHandler = luanHandler;
+	}
+
+	@Override public Response handle(Request request) {
+		return luanHandler.handle(request,"site:/not_found.luan");
+	}
+
+}
--- a/src/luan/modules/http/Server.luan	Sun Feb 11 02:34:35 2018 -0700
+++ b/src/luan/modules/http/Server.luan	Sun Feb 11 02:41:23 2018 -0700
@@ -1,1 +1,78 @@
-return require "luan:http/impl/Server.luan"
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local String = require "luan:String.luan"
+local gsub = String.gsub or error()
+local match = String.match or error()
+local matches = String.matches or error()
+local Io = require "luan:Io.luan"
+local Package = require "luan:Package.luan"
+local Rpc = require "luan:Rpc.luan"
+local Thread = require "luan:Thread.luan"
+local Http = require "luan:http/Http.luan"
+require "luan:logging/init.luan"  -- initialize logging
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "http/Server"
+
+java()
+local JavaServer = require "java:luan.webserver.Server"
+local FileHandler = require "java:luan.webserver.handlers.FileHandler"
+local DirHandler = require "java:luan.webserver.handlers.DirHandler"
+local IndexHandler = require "java:luan.webserver.handlers.IndexHandler"
+local ContentTypeHandler = require "java:luan.webserver.handlers.ContentTypeHandler"
+local SafeHandler = require "java:luan.webserver.handlers.SafeHandler"
+local LogHandler = require "java:luan.webserver.handlers.LogHandler"
+local ListHandler = require "java:luan.webserver.handlers.ListHandler"
+local LuanHandler = require "java:luan.modules.http.LuanHandler"
+local NotFound = require "java:luan.modules.http.NotFound"
+
+
+local Server = {}
+
+Server.port = 8080
+
+function Server.init(dir)
+	local dir_uri, dir_path
+	if matches(dir,":") then
+		dir_uri = dir
+		dir_path = match(dir,"^file:(.*)$") or error "server dir must be scheme 'file:'"
+	else
+		dir_path = dir
+		dir_uri = "file:"..dir
+	end
+	dir_uri = gsub(dir_uri,"/$","")  -- remove trailing '/' if any
+	Http.dir = dir_uri
+	function Io.schemes.site(path)
+		return Io.uri( dir_uri..path )
+	end
+	local file_handler = FileHandler.new(dir_path)
+	local dir_handler = DirHandler.new(file_handler)
+	local luan_handler = LuanHandler.new()
+	local not_found_hander = NotFound.new(luan_handler)
+	local handler = ListHandler.new( luan_handler, file_handler )
+	handler = IndexHandler.new(handler)
+	handler = ListHandler.new( handler, dir_handler, not_found_hander )
+	handler = ContentTypeHandler.new(handler)
+	handler = SafeHandler.new(handler)
+	handler = LogHandler.new(handler)
+	Server.server = JavaServer.new(Server.port,handler)
+end
+
+function Server.start()
+	LuanHandler.start(Server.server)
+end
+
+function Server.start_rpc()
+	function Rpc.functions.call(domain,fn_name,...)
+		return Server.luan_handler.call_rpc(fn_name,...)
+	end
+
+	Thread.fork(Rpc.serve)
+end
+
+function Server.serve(dir)
+	Server.init(dir)
+	Server.start_rpc()
+	Server.start()
+end
+
+return Server
--- a/src/luan/modules/http/impl/Http.luan	Sun Feb 11 02:34:35 2018 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-java()
-local Luan = require "luan:Luan.luan"
-local error = Luan.error
-local ipairs = Luan.ipairs or error()
-local pairs = Luan.pairs or error()
-local type = Luan.type or error()
-local Io = require "luan:Io.luan"
-local Html = require "luan:Html.luan"
-local url_encode = Html.url_encode or error()
-local Table = require "luan:Table.luan"
-local clear = Table.clear or error()
-local Package = require "luan:Package.luan"
-local String = require "luan:String.luan"
-local lower = String.lower or error()
-local matches = String.matches or error()
-local HttpServicer = require "java:luan.modules.http.impl.HttpServicer"
-local IoLuan = require "java:luan.modules.IoLuan"
-local JavaLuan = require "java:luan.Luan"
-local Response = require "java:luan.webserver.Response"
-local ResponseOutputStream = require "java:luan.webserver.ResponseOutputStream"
-local Status = require "java:luan.webserver.Status"
-local OutputStreamWriter = require "java:java.io.OutputStreamWriter"
-local HashMap = require "java:java.util.HashMap"
-
-
-local Http = {}
-
-Http.version = "impl"
-
-function Http.new_request(java)
-	local this = {}
-	Http.request = this
-	if java == nil then
-		this.method = "GET"
-		this.headers = {}
-		this.parameters = {}
-		this.cookies = {}
-	else
-		this.java = java
-		this.method = java.method or error()
-		this.raw_path = java.rawPath or error()
-		this.path = java.path or error()
-		this.protocol = java.protocol or error()
-		this.headers = JavaLuan.toLuan(java.headers)
-		this.parameters = JavaLuan.toLuan(java.parameters)
-		this.cookies = JavaLuan.toLuan(java.cookies)
-	end
-	this.scheme = "http"
-
-	function this.full_path()  -- compatible with jetty
-		return this.raw_path or this.path
-	end
-
-	function this.url()
-		return this.scheme.."://"..this.headers["host"]..this.raw_path
-	end
-
-	return this
-end
-
-local STATUS = {
-	OK = 200
-	MOVED_PERMANENTLY = 301
-	FOUND = 302
-	-- add more as needed
-}
-Http.STATUS = STATUS
-
-function Http.new_response()
-	local this = {}
-	Http.response = this
-
-	function this.reset()
-		this.java = Response.new()
-		this.headers = {}
-		this.status = STATUS.OK
-		this.writer = nil
-	end
-
-	this.reset()
-
-	function this.send_redirect(location)
-		this.reset()
-		this.status = STATUS.FOUND
-		this.headers["location"] = location
-	end
-
-	function this.send_error(status,msg)
-		this.reset()
-		this.status = status
-		if msg ~= nil then
-			this.headers["content-type"] = "text/plain; charset=utf-8"
-			local writer = this.text_writer()
-			writer.write(msg)
-		end
-	end
-
-	function this.set_cookie(name,value,attributes)
-		attributes = attributes or {}
-		local attrMap = HashMap.new()
-		for attr_name, attr_value in pairs(attributes) do
-			type(attr_name)=="string" or "cookie attribute name must be string"
-			type(attr_value)=="string" or "cookie attribute value must be string"
-			attrMap.put(attr_name,attr_value)
-		end
-		this.java.setCookie(name,value,attrMap)
-	end
-
-	function this.set_persistent_cookie(name,value,attributes)
-		attributes = attributes or {}
-		attributes["Max-Age"] = "10000000"
-		this.set_cookie(name,value,attributes)
-	end
-
-	function this.remove_cookie(name,attributes)
-		attributes = attributes or {}
-		attributes["Max-Age"] = "0"
-		this.set_cookie(name,"delete",attributes)
-	end
-
-	function this.text_writer()
-		this.writer and "writer already set"
-		this.writer = ResponseOutputStream.new(this.java)
-		this.writer = OutputStreamWriter.new(this.writer)
-		return IoLuan.textWriter(this.writer)
-	end
-
-	function this.binary_writer()
-		this.writer and "writer already set"
-		this.writer = ResponseOutputStream.new(this.java)
-		return IoLuan.binaryWriter(this.writer)
-	end
-
-	return this
-end
-
-function Http.finish()  -- called only from java
-	local response = Http.response or error()
-	local java = response.java or error()
-	java.status = Status.getStatus(response.status)
-	for name, value in pairs(response.headers) do
-		type(name)=="string" or "header name must be string"
-		name = lower(name)
-		value = JavaLuan.toJava(value)
-		java.headers.put(name,value)
-	end
-	response.writer and response.writer.close()
-	return java
-end
-
-
-function Http.uncache_site()
-	for k in pairs(Table.copy(Package.loaded)) do
-		if matches(k,"^site:") then
-			Package.loaded[k] = nil
-		end
-	end
-end
-
-return Http
--- a/src/luan/modules/http/impl/HttpServicer.java	Sun Feb 11 02:34:35 2018 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-package luan.modules.http.impl;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import luan.webserver.Request;
-import luan.webserver.Response;
-import luan.LuanState;
-import luan.LuanFunction;
-import luan.LuanException;
-import luan.LuanTable;
-import luan.LuanCloner;
-import luan.modules.PackageLuan;
-
-
-public final class HttpServicer {
-	private static final Logger logger = LoggerFactory.getLogger(HttpServicer.class);
-
-	public static Response service(LuanState luan,Request request,String modName)
-		throws LuanException
-	{
-		LuanFunction fn;
-		synchronized(luan) {
-			PackageLuan.enableLoad(luan,"luan:http/Http.luan",modName);
-			LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan");
-			Object mod = PackageLuan.load(luan,modName);
-			if( mod.equals(Boolean.FALSE) )
-				return null;
-			if( !(mod instanceof LuanFunction) )
-				throw new LuanException( "module '"+modName+"' must return a function" );
-			LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL);
-			luan = (LuanState)cloner.clone(luan);
-			fn = (LuanFunction)cloner.get(mod);
-		}
-
-		LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan");
-
-		// request
-		LuanFunction newRequestFn = (LuanFunction)module.rawGet("new_request");
-		newRequestFn.call( luan, new Object[]{request} );
-
-		// response
-		LuanFunction newResponseFn = (LuanFunction)module.rawGet("new_response");
-		newResponseFn.call(luan);
-
-		fn.call(luan);
-
-		LuanFunction finishFn = (LuanFunction)module.rawGet("finish");
-		Response response = (Response)finishFn.call(luan);
-		return response;
-	}
-
-}
--- a/src/luan/modules/http/impl/LuanHandler.java	Sun Feb 11 02:34:35 2018 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,167 +0,0 @@
-package luan.modules.http.impl;
-
-import java.io.Writer;
-import java.io.PrintWriter;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.net.BindException;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import luan.webserver.Request;
-import luan.webserver.Response;
-import luan.webserver.Server;
-import luan.webserver.Handler;
-import luan.webserver.Status;
-import luan.webserver.ResponseOutputStream;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanCloner;
-import luan.LuanException;
-import luan.modules.PackageLuan;
-
-
-public class LuanHandler implements Handler {
-	private final LuanState luanInit;
-	private final Logger logger;
-	private final ReadWriteLock lock = new ReentrantReadWriteLock();
-	private LuanState luan;
-
-	private static final Method resetLuanMethod;
-	static {
-		try {
-			resetLuanMethod = LuanHandler.class.getMethod("reset_luan");
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public LuanHandler(LuanState luan,String loggerRoot) {
-		this.luanInit = luan;
-		if( loggerRoot==null )
-			loggerRoot = "";
-		logger = LoggerFactory.getLogger(loggerRoot+LuanHandler.class.getName());
-		try {
-			LuanTable Http = (LuanTable)PackageLuan.require(luanInit,"luan:http/Http.luan");
-			Http.rawPut( "reset_luan", new LuanJavaFunction(resetLuanMethod,this) );
-		} catch(LuanException e) {
-			throw new RuntimeException(e);
-		}
-		setLuan();
-	}
-
-	@Override public Response handle(Request request) {
-		if( request.path.endsWith("/") )
-			return null;
-		String modName = "site:" + request.path +".luan";
-		return handle(request,modName);
-	}
-
-	Response handle(Request request,String modName) {
-		Thread thread = Thread.currentThread();
-		String oldName = thread.getName();
-		thread.setName(request.headers.get("host")+request.path);
-		lock.readLock().lock();
-		try {
-			Response response = HttpServicer.service(luan,request,modName);
-			return response;
-		} catch(LuanException e) {
-//e.printStackTrace();
-			String err = e.getLuanStackTraceString();
-			logger.error(err);
-			String msg = "Internel Server Error\n\n" + err;
-			return Response.errorResponse( Status.INTERNAL_SERVER_ERROR, msg );
-		} finally {
-			lock.readLock().unlock();
-			thread.setName(oldName);
-		}
-	}
-/*
-	@Override protected void doStart() throws Exception {
-//		Thread.dumpStack();
-//System.out.println("qqqqqqqqqqqqqqqqqqqq doStart "+this);
-		setLuan();
-		super.doStart();
-	}
-
-	@Override protected void doStop() throws Exception {
-		synchronized(luan) {
-			luan.close();
-		}
-//System.out.println("qqqqqqqqqqqqqqqqqqqq doStop "+this);
-		super.doStop();
-	}
-*/
-	public Object call_rpc(String fnName,Object... args) throws LuanException {
-		lock.readLock().lock();
-		try {
-			LuanFunction fn;
-			LuanState luan = this.luan;
-			synchronized(luan) {
-				PackageLuan.enableLoad(luan,"luan:Rpc.luan");
-				LuanTable rpc = (LuanTable)PackageLuan.require(luan,"luan:Rpc.luan");
-				LuanTable fns = (LuanTable)rpc.get(luan,"functions");
-				fn = (LuanFunction)fns.get(luan,fnName);
-				if( fn == null )
-					throw new LuanException( "function not found: " + fnName );
-				LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL);
-				luan = (LuanState)cloner.clone(luan);
-				fn = (LuanFunction)cloner.get(fn);
-			}
-			return fn.call(luan,args);
-		} finally {
-			lock.readLock().unlock();
-		}
-	}
-
-	public void reset_luan() {
-		new Thread() {
-			public void run() {
-				lock.writeLock().lock();
-				try {
-					synchronized(luan) {
-						luan.close();
-						setLuan();
-					}
-				} catch(IOException e) {
-					logger.error("reset_luan failed",e);
-				} finally {
-					lock.writeLock().unlock();
-				}
-			}
-		}.start();
-	}
-
-	private void setLuan() {
-		LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
-		luan = (LuanState)cloner.clone(luanInit);
-		try {
-			PackageLuan.load(luan,"site:/init.luan");
-		} catch(LuanException e) {
-			String err = e.getLuanStackTraceString();
-			logger.error(err);
-		}
-	}
-
-	public Object runLuan(String sourceText,String sourceName) throws LuanException {
-		LuanFunction fn = Luan.load(sourceText,sourceName);
-		synchronized(luan) {
-			LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL);
-			LuanState luan = (LuanState)cloner.clone(this.luan);
-			return fn.call(luan);
-		}
-	}
-
-	public static void start(Server server) throws Exception {
-		try {
-			server.start();
-		} catch(BindException e) {
-			throw new LuanException(e.toString());
-		}
-	}
-
-}
--- a/src/luan/modules/http/impl/NotFound.java	Sun Feb 11 02:34:35 2018 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-package luan.modules.http.impl;
-
-import luan.webserver.Request;
-import luan.webserver.Response;
-import luan.webserver.Handler;
-
-
-public class NotFound implements Handler {
-	private final LuanHandler luanHandler;
-
-	public NotFound(LuanHandler luanHandler) {
-		this.luanHandler = luanHandler;
-	}
-
-	@Override public Response handle(Request request) {
-		return luanHandler.handle(request,"site:/not_found.luan");
-	}
-
-}
--- a/src/luan/modules/http/impl/Server.luan	Sun Feb 11 02:34:35 2018 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-local Luan = require "luan:Luan.luan"
-local error = Luan.error
-local String = require "luan:String.luan"
-local gsub = String.gsub or error()
-local match = String.match or error()
-local matches = String.matches or error()
-local Io = require "luan:Io.luan"
-local Package = require "luan:Package.luan"
-local Rpc = require "luan:Rpc.luan"
-local Thread = require "luan:Thread.luan"
-local Http = require "luan:http/Http.luan"
-require "luan:logging/init.luan"  -- initialize logging
-local Logging = require "luan:logging/Logging.luan"
-local logger = Logging.logger "http/Server"
-
-java()
-local JavaServer = require "java:luan.webserver.Server"
-local FileHandler = require "java:luan.webserver.handlers.FileHandler"
-local DirHandler = require "java:luan.webserver.handlers.DirHandler"
-local IndexHandler = require "java:luan.webserver.handlers.IndexHandler"
-local ContentTypeHandler = require "java:luan.webserver.handlers.ContentTypeHandler"
-local SafeHandler = require "java:luan.webserver.handlers.SafeHandler"
-local LogHandler = require "java:luan.webserver.handlers.LogHandler"
-local ListHandler = require "java:luan.webserver.handlers.ListHandler"
-local LuanHandler = require "java:luan.modules.http.impl.LuanHandler"
-local NotFound = require "java:luan.modules.http.impl.NotFound"
-
-
-local Server = {}
-
-Server.port = 8080
-
-function Server.init(dir)
-	local dir_uri, dir_path
-	if matches(dir,":") then
-		dir_uri = dir
-		dir_path = match(dir,"^file:(.*)$") or error "server dir must be scheme 'file:'"
-	else
-		dir_path = dir
-		dir_uri = "file:"..dir
-	end
-	dir_uri = gsub(dir_uri,"/$","")  -- remove trailing '/' if any
-	Http.dir = dir_uri
-	function Io.schemes.site(path)
-		return Io.uri( dir_uri..path )
-	end
-	local file_handler = FileHandler.new(dir_path)
-	local dir_handler = DirHandler.new(file_handler)
-	local luan_handler = LuanHandler.new()
-	local not_found_hander = NotFound.new(luan_handler)
-	local handler = ListHandler.new( luan_handler, file_handler )
-	handler = IndexHandler.new(handler)
-	handler = ListHandler.new( handler, dir_handler, not_found_hander )
-	handler = ContentTypeHandler.new(handler)
-	handler = SafeHandler.new(handler)
-	handler = LogHandler.new(handler)
-	Server.server = JavaServer.new(Server.port,handler)
-end
-
-function Server.start()
-	LuanHandler.start(Server.server)
-end
-
-function Server.start_rpc()
-	function Rpc.functions.call(domain,fn_name,...)
-		return Server.luan_handler.call_rpc(fn_name,...)
-	end
-
-	Thread.fork(Rpc.serve)
-end
-
-function Server.serve(dir)
-	Server.init(dir)
-	Server.start_rpc()
-	Server.start()
-end
-
-return Server