Mercurial Hosting > luan
changeset 1136:d30d400fd43d
add http/jetty
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 29 Jan 2018 17:50:49 -0700 |
parents | 707a5d874f3e |
children | c123ee15f99b |
files | src/luan/host/WebHandler.java src/luan/modules/http/AuthenticationHandler.java src/luan/modules/http/Http.luan src/luan/modules/http/HttpServicer.java src/luan/modules/http/Implementation.luan src/luan/modules/http/LuanHandler.java src/luan/modules/http/NotFound.java src/luan/modules/http/Server.luan src/luan/modules/http/jetty/AuthenticationHandler.java src/luan/modules/http/jetty/HttpServicer.java src/luan/modules/http/jetty/LuanHandler.java src/luan/modules/http/jetty/NotFound.java src/luan/modules/http/jetty/Server.luan src/luan/modules/http/tools/Dump_mod.luan |
diffstat | 14 files changed, 702 insertions(+), 694 deletions(-) [+] |
line wrap: on
line diff
--- a/src/luan/host/WebHandler.java Sun Jan 28 21:36:58 2018 -0700 +++ b/src/luan/host/WebHandler.java Mon Jan 29 17:50:49 2018 -0700 @@ -29,9 +29,9 @@ import luan.modules.IoLuan; import luan.modules.JavaLuan; import luan.modules.PackageLuan; -import luan.modules.http.LuanHandler; -import luan.modules.http.AuthenticationHandler; -import luan.modules.http.NotFound; +import luan.modules.http.jetty.LuanHandler; +import luan.modules.http.jetty.AuthenticationHandler; +import luan.modules.http.jetty.NotFound; public class WebHandler extends AbstractHandler {
--- a/src/luan/modules/http/AuthenticationHandler.java Sun Jan 28 21:36:58 2018 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -package luan.modules.http; - -import java.io.IOException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.util.B64Code; - - -public class AuthenticationHandler extends AbstractHandler { - private final String path; - private String password = "password"; - - public AuthenticationHandler(String path) { - this.path = path; - } - - public void setPassword(String password) { - this.password = password; - } - - public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) - throws IOException - { - if( !target.startsWith(path) ) - return; - String pwd = getPassword(request); - if( password.equals(pwd) ) - return; - response.setHeader("WWW-Authenticate","Basic realm=\""+path+"\""); - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - baseRequest.setHandled(true); - } - - private static String getPassword(HttpServletRequest request) { - String auth = request.getHeader("Authorization"); - if( auth==null ) - return null; - String[] a = auth.split(" +"); - if( a.length != 2 ) - throw new RuntimeException("auth = "+auth); - if( !a[0].equals("Basic") ) - throw new RuntimeException("auth = "+auth); - auth = new String(B64Code.decode(a[1])); - a = auth.split(":"); - if( a.length != 2 ) - throw new RuntimeException("auth = "+auth); - return a[1]; - } -}
--- a/src/luan/modules/http/Http.luan Sun Jan 28 21:36:58 2018 -0700 +++ b/src/luan/modules/http/Http.luan Mon Jan 29 17:50:49 2018 -0700 @@ -12,7 +12,8 @@ local Package = require "luan:Package.luan" local String = require "luan:String.luan" local matches = String.matches or error() -local HttpServicer = require "java:luan.modules.http.HttpServicer" +local Implementation = require "luan:http/Implementation.luan" +local HttpServicer = require(Implementation.java.."HttpServicer") local IoLuan = require "java:luan.modules.IoLuan" local Http = {}
--- a/src/luan/modules/http/HttpServicer.java Sun Jan 28 21:36:58 2018 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,325 +0,0 @@ -package luan.modules.http; - -import java.io.InputStream; -import java.io.BufferedInputStream; -import java.io.PrintWriter; -import java.io.IOException; -import java.util.Map; -import java.util.Set; -import java.util.List; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.Enumeration; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import javax.servlet.ServletOutputStream; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.servlet.http.Part; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.eclipse.jetty.util.MultiPartInputStream; -import luan.Luan; -import luan.LuanState; -import luan.LuanFunction; -import luan.LuanException; -import luan.LuanTable; -//import luan.LuanPropertyMeta; -import luan.LuanCloner; -import luan.modules.PackageLuan; -import luan.modules.IoLuan; -import luan.modules.TableLuan; -import luan.modules.Utils; -import luan.modules.url.LuanUrl; - - -public final class HttpServicer { - private static final Logger logger = LoggerFactory.getLogger(HttpServicer.class); - - public static boolean service(LuanState luan,HttpServletRequest request,HttpServletResponse response,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"); - LuanTable per_session_pages = (LuanTable)module.rawGet("per_session_pages"); - Object mod = PackageLuan.load(luan,modName); - if( mod.equals(Boolean.FALSE) ) - return false; - if( !(mod instanceof LuanFunction) ) - throw new LuanException( "module '"+modName+"' must return a function" ); - if( Boolean.TRUE.equals(per_session_pages.rawGet(mod)) ) { - HttpSession session = request.getSession(); - LuanState sessionLuan = (LuanState)session.getAttribute("luan"); - if( sessionLuan!=null ) { - luan = sessionLuan; - } else { - LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE); - luan = (LuanState)cloner.clone(luan); - session.setAttribute("luan",luan); - } - fn = (LuanFunction)PackageLuan.require(luan,modName); - } else { - 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"); - LuanTable requestTbl = (LuanTable)newRequestFn.call(luan); - module.rawPut("request",requestTbl); - requestTbl.rawPut("java",request); - requestTbl.rawPut("method",request.getMethod()); - requestTbl.rawPut("path",request.getRequestURI()); - requestTbl.rawPut("protocol",request.getProtocol()); - requestTbl.rawPut("scheme",request.getScheme()); - requestTbl.rawPut("port",request.getServerPort()); - - LuanTable headersTbl = (LuanTable)requestTbl.rawGet("headers"); - for( Enumeration<String> enKeys = request.getHeaderNames(); enKeys.hasMoreElements(); ) { - String key = enKeys.nextElement(); - LuanTable values = new LuanTable(); - for( Enumeration<String> en = request.getHeaders(key); en.hasMoreElements(); ) { - values.rawPut(values.rawLength()+1,en.nextElement()); - } - key = toLuanHeaderName(key); - headersTbl.rawPut(key,values); - } - - LuanTable parametersTbl = (LuanTable)requestTbl.rawGet("parameters"); - String contentType = request.getContentType(); - if( contentType==null || !contentType.startsWith("multipart/form-data") ) { - for( Map.Entry<String,String[]> entry : request.getParameterMap().entrySet() ) { - parametersTbl.rawPut(entry.getKey(),new LuanTable(Arrays.asList(entry.getValue()))); - } - } else { // multipart - try { - InputStream in = new BufferedInputStream(request.getInputStream()); - final MultiPartInputStream mpis = new MultiPartInputStream(in,contentType,null,null); - mpis.setDeleteOnExit(true); - for( Part p : mpis.getParts() ) { - final MultiPartInputStream.MultiPart part = (MultiPartInputStream.MultiPart)p; - String name = part.getName(); -/* -System.out.println("name = "+name); -System.out.println("getContentType = "+part.getContentType()); -System.out.println("getHeaderNames = "+part.getHeaderNames()); -System.out.println("content-disposition = "+part.getHeader("content-disposition")); -System.out.println(); -*/ - Object value; - String filename = part.getContentDispositionFilename(); - if( filename == null ) { - value = new String(part.getBytes()); - } else { -/* - LuanTable partTbl = LuanPropertyMeta.INSTANCE.newTable(); - partTbl.rawPut("filename",filename); - partTbl.rawPut("content_type",part.getContentType()); - LuanPropertyMeta.INSTANCE.getters(partTbl).rawPut( "content", new LuanFunction() { - @Override public Object call(LuanState luan,Object[] args) throws LuanException { - try { - InputStream in = part.getInputStream(); - byte[] content = Utils.readAll(in); - in.close(); - return content; - } catch(IOException e) { - throw new RuntimeException(e); - } - } - } ); -*/ - LuanTable partTbl = new LuanTable(); - partTbl.rawPut("filename",filename); - partTbl.rawPut("content_type",part.getContentType()); - LuanTable mt = new LuanTable(); - partTbl.setMetatable(mt); - mt.rawPut( "__index", new LuanFunction() { - @Override public Object call(LuanState luan,Object[] args) throws LuanException { - Object key = args[1]; - if( "content".equals(key) ) { - try { - InputStream in = part.getInputStream(); - byte[] content = Utils.readAll(in); - in.close(); - return content; - } catch(IOException e) { - throw new RuntimeException(e); - } - } - return null; - } - } ); - value = partTbl; - } - LuanTable list = (LuanTable)parametersTbl.rawGet(name); - if( list == null ) { - list = new LuanTable(); - parametersTbl.rawPut(name,list); - } - list.rawPut(parametersTbl.rawLength()+1,value); - } - } catch(IOException e) { - throw new RuntimeException(e); - } catch(ServletException e) { - throw new RuntimeException(e); - } - } - - LuanTable cookieTbl = (LuanTable)requestTbl.rawGet("cookie"); - for( Cookie cookie : request.getCookies() ) { - cookieTbl.rawPut( cookie.getName(), unescape(cookie.getValue()) ); - } - - - // response - LuanTable responseTbl = new LuanTable(); - responseTbl.rawPut("java",response); - LuanFunction newResponseFn = (LuanFunction)module.rawGet("new_response"); - newResponseFn.call( luan, new Object[]{responseTbl} ); - module.rawPut("response",responseTbl); - - fn.call(luan); - handle_run_later(luan); - return true; - } - - public static void setResponse(LuanTable responseTbl,HttpServletResponse response) throws LuanException { - int status = Luan.asInteger(responseTbl.rawGet("status")); - response.setStatus(status); - LuanTable responseHeaders = (LuanTable)responseTbl.rawGet("headers"); - for( Map.Entry<Object,Object> entry : responseHeaders.rawIterable() ) { - String name = (String)entry.getKey(); - name = toHttpHeaderName(name); - LuanTable values = (LuanTable)entry.getValue(); - for( Object value : values.asList() ) { - if( value instanceof String ) { - response.setHeader(name,(String)value); - continue; - } - Integer i = Luan.asInteger(value); - if( i != null ) { - response.setIntHeader(name,i); - continue; - } - throw new IllegalArgumentException("value must be string or integer for headers table"); - } - } - } - - - - // static utils - - public static String toLuanHeaderName(String httpName) { - return httpName.toLowerCase().replace('-','_'); - } - - public static String toHttpHeaderName(String luanName) { -/* - StringBuilder buf = new StringBuilder(); - boolean capitalize = true; - char[] a = luanName.toCharArray(); - for( int i=0; i<a.length; i++ ) { - char c = a[i]; - if( c == '_' ) { - a[i] = '-'; - capitalize = true; - } else if( capitalize ) { - a[i] = Character.toUpperCase(c); - capitalize = false; - } - } - return String.valueOf(a); -*/ - return LuanUrl.toHttpHeaderName(luanName); - } - - private static String escape(String value) { - return value.replaceAll(";", "%3B"); - } - - private static String unescape(String value) { - return value.replaceAll("%3B", ";"); - } - - private static Cookie getCookie(HttpServletRequest request,String name) { - Cookie[] cookies = request.getCookies(); - if( cookies == null ) - return null; - for (Cookie cookie : cookies) { - if (cookie.getName().equals(name)) - return cookie; - } - return null; - } - - public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) { - Cookie cookie = getCookie(request,name); - if( cookie==null || !cookie.getValue().equals(value) ) { - cookie = new Cookie(name, escape(value)); - cookie.setPath("/"); - if (domain != null && domain.length() > 0) - cookie.setDomain(domain); - if( isPersistent ) - cookie.setMaxAge(10000000); - response.addCookie(cookie); - } - } - - public static void removeCookie(HttpServletRequest request, - HttpServletResponse response, - String name, - String domain - ) { - Cookie cookie = getCookie(request, name); - if(cookie != null) { - Cookie delCookie = new Cookie(name, "delete"); - delCookie.setPath("/"); - delCookie.setMaxAge(0); - if (domain != null && domain.length() > 0) - delCookie.setDomain(domain); - response.addCookie(delCookie); - } - } - - - - private static String RUN_LATER_KEY = "Http.run_later"; - private static final Executor exec = Executors.newSingleThreadExecutor(); - - public static void run_later(final LuanState luan,final LuanFunction fn,final Object... args) { - List list = (List)luan.registry().get(RUN_LATER_KEY); - if( list == null ) { - list = new ArrayList(); - luan.registry().put(RUN_LATER_KEY,list); - } - list.add( - new Runnable(){public void run() { - try { - fn.call(luan,args); - } catch(LuanException e) { - e.printStackTrace(); - } - }} - ); - } - - private static void handle_run_later(LuanState luan) { - List list = (List)luan.registry().get(RUN_LATER_KEY); - if( list==null ) - return; - for( Object obj : list ) { - exec.execute((Runnable)obj); - } - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/Implementation.luan Mon Jan 29 17:50:49 2018 -0700 @@ -0,0 +1,4 @@ +return { + luan = "luan:http/jetty/" + java = "java:luan.modules.http.jetty." +}
--- a/src/luan/modules/http/LuanHandler.java Sun Jan 28 21:36:58 2018 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,171 +0,0 @@ -package luan.modules.http; - -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 javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.AbstractHandler; -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 extends AbstractHandler { - private final LuanState luanInit; - private final Logger logger; - private String welcomeFile = "index.html"; - 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); - } - } - - @Override public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) - throws IOException - { - if( target.endsWith("/") ) - target += welcomeFile; - Thread thread = Thread.currentThread(); - String oldName = thread.getName(); - thread.setName(request.getHeader("host")+request.getRequestURI()); - lock.readLock().lock(); - try { - if( !HttpServicer.service(luan,request,response,"site:"+target+".luan") ) - return; - } catch(LuanException e) { -//e.printStackTrace(); - String err = e.getLuanStackTraceString(); - logger.error(err); - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,err); - } finally { - lock.readLock().unlock(); - thread.setName(oldName); - } - baseRequest.setHandled(true); - } - - public void setWelcomeFile(String welcomeFile) { - this.welcomeFile = welcomeFile; - } - - @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(); - } -/* - @Override public void destroy() { -System.out.println("qqqqqqqqqqqqqqqqqqqq destroy "+this); - super.destroy(); - } -*/ - - 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/NotFound.java Sun Jan 28 21:36:58 2018 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -package luan.modules.http; - -import java.io.IOException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; - - -public class NotFound extends AbstractHandler { - private final LuanHandler luanHandler; - - public NotFound(LuanHandler luanHandler) { - this.luanHandler = luanHandler; - } - - @Override public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) - throws IOException - { - luanHandler.handle("/not_found",baseRequest,request,response); - } - -}
--- a/src/luan/modules/http/Server.luan Sun Jan 28 21:36:58 2018 -0700 +++ b/src/luan/modules/http/Server.luan Mon Jan 29 17:50:49 2018 -0700 @@ -1,119 +1,2 @@ -local Luan = require "luan:Luan.luan" -local error = Luan.error -local String = require "luan:String.luan" -local gsub = String.gsub 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 TimeZone = require "java:java.util.TimeZone" -local JavaServer = require "java:org.eclipse.jetty.server.Server" -local NCSARequestLog = require "java:org.eclipse.jetty.server.NCSARequestLog" -local DefaultHandler = require "java:org.eclipse.jetty.server.handler.DefaultHandler" -local HandlerList = require "java:org.eclipse.jetty.server.handler.HandlerList" -local HandlerCollection = require "java:org.eclipse.jetty.server.handler.HandlerCollection" -local ResourceHandler = require "java:org.eclipse.jetty.server.handler.ResourceHandler" -local RequestLogHandler = require "java:org.eclipse.jetty.server.handler.RequestLogHandler" -local ContextHandler = require "java:org.eclipse.jetty.server.handler.ContextHandler" -local GzipHandler = require "java:org.eclipse.jetty.server.handler.GzipHandler" -local HandlerWrapper = require "java:org.eclipse.jetty.server.handler.HandlerWrapper" -local SessionHandler = require "java:org.eclipse.jetty.server.session.SessionHandler" -local AuthenticationHandler = require "java:luan.modules.http.AuthenticationHandler" -local LuanHandler = require "java:luan.modules.http.LuanHandler" -local NotFound = require "java:luan.modules.http.NotFound" - -local Server = {} - -Server.port = 8080 - -Server.welcome_file = "index.html" - - -Server.authentication_handler = AuthenticationHandler.new("/private/") - -Server.luan_handler = LuanHandler.new() - -Server.resource_handler = ResourceHandler.new() -Server.resource_handler.setDirectoriesListed(true) - -Server.handlers = HandlerList.new() -Server.handlers.setHandlers { Server.authentication_handler, Server.luan_handler, Server.resource_handler } - -function Server.add_folder(context,dir) - local rh = ResourceHandler.new() - rh.setResourceBase(dir) - rh.setDirectoriesListed(true) - local ch = ContextHandler.new(context) - ch.setHandler(rh) - Server.handlers.addHandler(ch) - return rh -end - -Server.handler_wrapper = HandlerWrapper.new() -Server.handler_wrapper.setHandler(Server.handlers) - -function Server.zip() - local h = GzipHandler.new() - h.setHandler(Server.handler_wrapper.getHandler()) - Server.handler_wrapper.setHandler(h) -end - -Server.log = NCSARequestLog.new() -Server.log.setExtended(false) -Server.log.setLogTimeZone(TimeZone.getDefault().getID()) -Server.log_handler = RequestLogHandler.new() -Server.log_handler.setRequestLog(Server.log) - -function Server.set_log_file(file_name) - Server.log.setFilename(file_name) -end - -local hc = HandlerCollection.new() -hc.setHandlers { SessionHandler.new(), Server.handler_wrapper, DefaultHandler.new(), Server.log_handler } - - -function Server.init(dir) - dir = gsub(dir,"/$","") -- remove trailing '/' if any - Http.dir = dir - function Io.schemes.site(path) - return Io.uri( dir..path ) - end - Server.authentication_handler.setPassword(Io.password) - local base = dir - if matches(base,"^classpath:") then - base = dir.."#"..Server.welcome_file.."#"..Server.welcome_file..".luan" - end - Server.resource_handler.setResourceBase(Io.uri(base).to_string()) - Server.resource_handler.setWelcomeFiles {Server.welcome_file} - Server.luan_handler.setWelcomeFile(Server.welcome_file) - Server.handlers.addHandler(NotFound.new(Server.luan_handler)) - Server.server = JavaServer.new(Server.port) - Server.server.setHandler(hc) -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 +local Implementation = require "luan:http/Implementation.luan" +return require(Implementation.luan.."Server.luan")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/jetty/AuthenticationHandler.java Mon Jan 29 17:50:49 2018 -0700 @@ -0,0 +1,51 @@ +package luan.modules.http.jetty; + +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.util.B64Code; + + +public class AuthenticationHandler extends AbstractHandler { + private final String path; + private String password = "password"; + + public AuthenticationHandler(String path) { + this.path = path; + } + + public void setPassword(String password) { + this.password = password; + } + + public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) + throws IOException + { + if( !target.startsWith(path) ) + return; + String pwd = getPassword(request); + if( password.equals(pwd) ) + return; + response.setHeader("WWW-Authenticate","Basic realm=\""+path+"\""); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + baseRequest.setHandled(true); + } + + private static String getPassword(HttpServletRequest request) { + String auth = request.getHeader("Authorization"); + if( auth==null ) + return null; + String[] a = auth.split(" +"); + if( a.length != 2 ) + throw new RuntimeException("auth = "+auth); + if( !a[0].equals("Basic") ) + throw new RuntimeException("auth = "+auth); + auth = new String(B64Code.decode(a[1])); + a = auth.split(":"); + if( a.length != 2 ) + throw new RuntimeException("auth = "+auth); + return a[1]; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/jetty/HttpServicer.java Mon Jan 29 17:50:49 2018 -0700 @@ -0,0 +1,325 @@ +package luan.modules.http.jetty; + +import java.io.InputStream; +import java.io.BufferedInputStream; +import java.io.PrintWriter; +import java.io.IOException; +import java.util.Map; +import java.util.Set; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Enumeration; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.Part; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.eclipse.jetty.util.MultiPartInputStream; +import luan.Luan; +import luan.LuanState; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanTable; +//import luan.LuanPropertyMeta; +import luan.LuanCloner; +import luan.modules.PackageLuan; +import luan.modules.IoLuan; +import luan.modules.TableLuan; +import luan.modules.Utils; +import luan.modules.url.LuanUrl; + + +public final class HttpServicer { + private static final Logger logger = LoggerFactory.getLogger(HttpServicer.class); + + public static boolean service(LuanState luan,HttpServletRequest request,HttpServletResponse response,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"); + LuanTable per_session_pages = (LuanTable)module.rawGet("per_session_pages"); + Object mod = PackageLuan.load(luan,modName); + if( mod.equals(Boolean.FALSE) ) + return false; + if( !(mod instanceof LuanFunction) ) + throw new LuanException( "module '"+modName+"' must return a function" ); + if( Boolean.TRUE.equals(per_session_pages.rawGet(mod)) ) { + HttpSession session = request.getSession(); + LuanState sessionLuan = (LuanState)session.getAttribute("luan"); + if( sessionLuan!=null ) { + luan = sessionLuan; + } else { + LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE); + luan = (LuanState)cloner.clone(luan); + session.setAttribute("luan",luan); + } + fn = (LuanFunction)PackageLuan.require(luan,modName); + } else { + 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"); + LuanTable requestTbl = (LuanTable)newRequestFn.call(luan); + module.rawPut("request",requestTbl); + requestTbl.rawPut("java",request); + requestTbl.rawPut("method",request.getMethod()); + requestTbl.rawPut("path",request.getRequestURI()); + requestTbl.rawPut("protocol",request.getProtocol()); + requestTbl.rawPut("scheme",request.getScheme()); + requestTbl.rawPut("port",request.getServerPort()); + + LuanTable headersTbl = (LuanTable)requestTbl.rawGet("headers"); + for( Enumeration<String> enKeys = request.getHeaderNames(); enKeys.hasMoreElements(); ) { + String key = enKeys.nextElement(); + LuanTable values = new LuanTable(); + for( Enumeration<String> en = request.getHeaders(key); en.hasMoreElements(); ) { + values.rawPut(values.rawLength()+1,en.nextElement()); + } + key = toLuanHeaderName(key); + headersTbl.rawPut(key,values); + } + + LuanTable parametersTbl = (LuanTable)requestTbl.rawGet("parameters"); + String contentType = request.getContentType(); + if( contentType==null || !contentType.startsWith("multipart/form-data") ) { + for( Map.Entry<String,String[]> entry : request.getParameterMap().entrySet() ) { + parametersTbl.rawPut(entry.getKey(),new LuanTable(Arrays.asList(entry.getValue()))); + } + } else { // multipart + try { + InputStream in = new BufferedInputStream(request.getInputStream()); + final MultiPartInputStream mpis = new MultiPartInputStream(in,contentType,null,null); + mpis.setDeleteOnExit(true); + for( Part p : mpis.getParts() ) { + final MultiPartInputStream.MultiPart part = (MultiPartInputStream.MultiPart)p; + String name = part.getName(); +/* +System.out.println("name = "+name); +System.out.println("getContentType = "+part.getContentType()); +System.out.println("getHeaderNames = "+part.getHeaderNames()); +System.out.println("content-disposition = "+part.getHeader("content-disposition")); +System.out.println(); +*/ + Object value; + String filename = part.getContentDispositionFilename(); + if( filename == null ) { + value = new String(part.getBytes()); + } else { +/* + LuanTable partTbl = LuanPropertyMeta.INSTANCE.newTable(); + partTbl.rawPut("filename",filename); + partTbl.rawPut("content_type",part.getContentType()); + LuanPropertyMeta.INSTANCE.getters(partTbl).rawPut( "content", new LuanFunction() { + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + InputStream in = part.getInputStream(); + byte[] content = Utils.readAll(in); + in.close(); + return content; + } catch(IOException e) { + throw new RuntimeException(e); + } + } + } ); +*/ + LuanTable partTbl = new LuanTable(); + partTbl.rawPut("filename",filename); + partTbl.rawPut("content_type",part.getContentType()); + LuanTable mt = new LuanTable(); + partTbl.setMetatable(mt); + mt.rawPut( "__index", new LuanFunction() { + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + Object key = args[1]; + if( "content".equals(key) ) { + try { + InputStream in = part.getInputStream(); + byte[] content = Utils.readAll(in); + in.close(); + return content; + } catch(IOException e) { + throw new RuntimeException(e); + } + } + return null; + } + } ); + value = partTbl; + } + LuanTable list = (LuanTable)parametersTbl.rawGet(name); + if( list == null ) { + list = new LuanTable(); + parametersTbl.rawPut(name,list); + } + list.rawPut(parametersTbl.rawLength()+1,value); + } + } catch(IOException e) { + throw new RuntimeException(e); + } catch(ServletException e) { + throw new RuntimeException(e); + } + } + + LuanTable cookieTbl = (LuanTable)requestTbl.rawGet("cookie"); + for( Cookie cookie : request.getCookies() ) { + cookieTbl.rawPut( cookie.getName(), unescape(cookie.getValue()) ); + } + + + // response + LuanTable responseTbl = new LuanTable(); + responseTbl.rawPut("java",response); + LuanFunction newResponseFn = (LuanFunction)module.rawGet("new_response"); + newResponseFn.call( luan, new Object[]{responseTbl} ); + module.rawPut("response",responseTbl); + + fn.call(luan); + handle_run_later(luan); + return true; + } + + public static void setResponse(LuanTable responseTbl,HttpServletResponse response) throws LuanException { + int status = Luan.asInteger(responseTbl.rawGet("status")); + response.setStatus(status); + LuanTable responseHeaders = (LuanTable)responseTbl.rawGet("headers"); + for( Map.Entry<Object,Object> entry : responseHeaders.rawIterable() ) { + String name = (String)entry.getKey(); + name = toHttpHeaderName(name); + LuanTable values = (LuanTable)entry.getValue(); + for( Object value : values.asList() ) { + if( value instanceof String ) { + response.setHeader(name,(String)value); + continue; + } + Integer i = Luan.asInteger(value); + if( i != null ) { + response.setIntHeader(name,i); + continue; + } + throw new IllegalArgumentException("value must be string or integer for headers table"); + } + } + } + + + + // static utils + + public static String toLuanHeaderName(String httpName) { + return httpName.toLowerCase().replace('-','_'); + } + + public static String toHttpHeaderName(String luanName) { +/* + StringBuilder buf = new StringBuilder(); + boolean capitalize = true; + char[] a = luanName.toCharArray(); + for( int i=0; i<a.length; i++ ) { + char c = a[i]; + if( c == '_' ) { + a[i] = '-'; + capitalize = true; + } else if( capitalize ) { + a[i] = Character.toUpperCase(c); + capitalize = false; + } + } + return String.valueOf(a); +*/ + return LuanUrl.toHttpHeaderName(luanName); + } + + private static String escape(String value) { + return value.replaceAll(";", "%3B"); + } + + private static String unescape(String value) { + return value.replaceAll("%3B", ";"); + } + + private static Cookie getCookie(HttpServletRequest request,String name) { + Cookie[] cookies = request.getCookies(); + if( cookies == null ) + return null; + for (Cookie cookie : cookies) { + if (cookie.getName().equals(name)) + return cookie; + } + return null; + } + + public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) { + Cookie cookie = getCookie(request,name); + if( cookie==null || !cookie.getValue().equals(value) ) { + cookie = new Cookie(name, escape(value)); + cookie.setPath("/"); + if (domain != null && domain.length() > 0) + cookie.setDomain(domain); + if( isPersistent ) + cookie.setMaxAge(10000000); + response.addCookie(cookie); + } + } + + public static void removeCookie(HttpServletRequest request, + HttpServletResponse response, + String name, + String domain + ) { + Cookie cookie = getCookie(request, name); + if(cookie != null) { + Cookie delCookie = new Cookie(name, "delete"); + delCookie.setPath("/"); + delCookie.setMaxAge(0); + if (domain != null && domain.length() > 0) + delCookie.setDomain(domain); + response.addCookie(delCookie); + } + } + + + + private static String RUN_LATER_KEY = "Http.run_later"; + private static final Executor exec = Executors.newSingleThreadExecutor(); + + public static void run_later(final LuanState luan,final LuanFunction fn,final Object... args) { + List list = (List)luan.registry().get(RUN_LATER_KEY); + if( list == null ) { + list = new ArrayList(); + luan.registry().put(RUN_LATER_KEY,list); + } + list.add( + new Runnable(){public void run() { + try { + fn.call(luan,args); + } catch(LuanException e) { + e.printStackTrace(); + } + }} + ); + } + + private static void handle_run_later(LuanState luan) { + List list = (List)luan.registry().get(RUN_LATER_KEY); + if( list==null ) + return; + for( Object obj : list ) { + exec.execute((Runnable)obj); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/jetty/LuanHandler.java Mon Jan 29 17:50:49 2018 -0700 @@ -0,0 +1,171 @@ +package luan.modules.http.jetty; + +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 javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +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 extends AbstractHandler { + private final LuanState luanInit; + private final Logger logger; + private String welcomeFile = "index.html"; + 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); + } + } + + @Override public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) + throws IOException + { + if( target.endsWith("/") ) + target += welcomeFile; + Thread thread = Thread.currentThread(); + String oldName = thread.getName(); + thread.setName(request.getHeader("host")+request.getRequestURI()); + lock.readLock().lock(); + try { + if( !HttpServicer.service(luan,request,response,"site:"+target+".luan") ) + return; + } catch(LuanException e) { +//e.printStackTrace(); + String err = e.getLuanStackTraceString(); + logger.error(err); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,err); + } finally { + lock.readLock().unlock(); + thread.setName(oldName); + } + baseRequest.setHandled(true); + } + + public void setWelcomeFile(String welcomeFile) { + this.welcomeFile = welcomeFile; + } + + @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(); + } +/* + @Override public void destroy() { +System.out.println("qqqqqqqqqqqqqqqqqqqq destroy "+this); + super.destroy(); + } +*/ + + 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/jetty/NotFound.java Mon Jan 29 17:50:49 2018 -0700 @@ -0,0 +1,23 @@ +package luan.modules.http.jetty; + +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; + + +public class NotFound extends AbstractHandler { + private final LuanHandler luanHandler; + + public NotFound(LuanHandler luanHandler) { + this.luanHandler = luanHandler; + } + + @Override public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) + throws IOException + { + luanHandler.handle("/not_found",baseRequest,request,response); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/jetty/Server.luan Mon Jan 29 17:50:49 2018 -0700 @@ -0,0 +1,119 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local String = require "luan:String.luan" +local gsub = String.gsub 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 TimeZone = require "java:java.util.TimeZone" +local JavaServer = require "java:org.eclipse.jetty.server.Server" +local NCSARequestLog = require "java:org.eclipse.jetty.server.NCSARequestLog" +local DefaultHandler = require "java:org.eclipse.jetty.server.handler.DefaultHandler" +local HandlerList = require "java:org.eclipse.jetty.server.handler.HandlerList" +local HandlerCollection = require "java:org.eclipse.jetty.server.handler.HandlerCollection" +local ResourceHandler = require "java:org.eclipse.jetty.server.handler.ResourceHandler" +local RequestLogHandler = require "java:org.eclipse.jetty.server.handler.RequestLogHandler" +local ContextHandler = require "java:org.eclipse.jetty.server.handler.ContextHandler" +local GzipHandler = require "java:org.eclipse.jetty.server.handler.GzipHandler" +local HandlerWrapper = require "java:org.eclipse.jetty.server.handler.HandlerWrapper" +local SessionHandler = require "java:org.eclipse.jetty.server.session.SessionHandler" +local AuthenticationHandler = require "java:luan.modules.http.jetty.AuthenticationHandler" +local LuanHandler = require "java:luan.modules.http.jetty.LuanHandler" +local NotFound = require "java:luan.modules.http.jetty.NotFound" + +local Server = {} + +Server.port = 8080 + +Server.welcome_file = "index.html" + + +Server.authentication_handler = AuthenticationHandler.new("/private/") + +Server.luan_handler = LuanHandler.new() + +Server.resource_handler = ResourceHandler.new() +Server.resource_handler.setDirectoriesListed(true) + +Server.handlers = HandlerList.new() +Server.handlers.setHandlers { Server.authentication_handler, Server.luan_handler, Server.resource_handler } + +function Server.add_folder(context,dir) + local rh = ResourceHandler.new() + rh.setResourceBase(dir) + rh.setDirectoriesListed(true) + local ch = ContextHandler.new(context) + ch.setHandler(rh) + Server.handlers.addHandler(ch) + return rh +end + +Server.handler_wrapper = HandlerWrapper.new() +Server.handler_wrapper.setHandler(Server.handlers) + +function Server.zip() + local h = GzipHandler.new() + h.setHandler(Server.handler_wrapper.getHandler()) + Server.handler_wrapper.setHandler(h) +end + +Server.log = NCSARequestLog.new() +Server.log.setExtended(false) +Server.log.setLogTimeZone(TimeZone.getDefault().getID()) +Server.log_handler = RequestLogHandler.new() +Server.log_handler.setRequestLog(Server.log) + +function Server.set_log_file(file_name) + Server.log.setFilename(file_name) +end + +local hc = HandlerCollection.new() +hc.setHandlers { SessionHandler.new(), Server.handler_wrapper, DefaultHandler.new(), Server.log_handler } + + +function Server.init(dir) + dir = gsub(dir,"/$","") -- remove trailing '/' if any + Http.dir = dir + function Io.schemes.site(path) + return Io.uri( dir..path ) + end + Server.authentication_handler.setPassword(Io.password) + local base = dir + if matches(base,"^classpath:") then + base = dir.."#"..Server.welcome_file.."#"..Server.welcome_file..".luan" + end + Server.resource_handler.setResourceBase(Io.uri(base).to_string()) + Server.resource_handler.setWelcomeFiles {Server.welcome_file} + Server.luan_handler.setWelcomeFile(Server.welcome_file) + Server.handlers.addHandler(NotFound.new(Server.luan_handler)) + Server.server = JavaServer.new(Server.port) + Server.server.setHandler(hc) +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/tools/Dump_mod.luan Sun Jan 28 21:36:58 2018 -0700 +++ b/src/luan/modules/http/tools/Dump_mod.luan Mon Jan 29 17:50:49 2018 -0700 @@ -4,7 +4,8 @@ local Io = require "luan:Io.luan" local Http = require "luan:http/Http.luan" java() -local HttpServicer = require "java:luan.modules.http.HttpServicer" +local Implementation = require "luan:http/Implementation.luan" +local HttpServicer = require(Implementation.java.."HttpServicer") local Dump_mod = {}