Mercurial Hosting > luan
diff src/luan/host/WebHandler.java @ 1135:707a5d874f3e
add luan.host
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Sun, 28 Jan 2018 21:36:58 -0700 |
parents | |
children | d30d400fd43d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/host/WebHandler.java Sun Jan 28 21:36:58 2018 -0700 @@ -0,0 +1,229 @@ +package luan.host; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.HashMap; +import java.util.TimeZone; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +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.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.NCSARequestLog; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.server.handler.RequestLogHandler; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerCollection; +import luan.Luan; +import luan.LuanState; +import luan.LuanException; +import luan.LuanTable; +import luan.LuanFunction; +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; + + +public class WebHandler extends AbstractHandler { + private static final Logger logger = LoggerFactory.getLogger(WebHandler.class); + + private static class Site { + final Handler handler; + final LuanHandler luanHandler; + + Site(Handler handler,LuanHandler luanHandler) { + this.handler = handler; + this.luanHandler = luanHandler; + } + } + + public static String allowJavaFileName = "allow_java"; // change for security + private static final String tz = TimeZone.getDefault().getID(); + private static final Map<String,Site> siteMap = new HashMap<String,Site>(); + private static String sitesDir = null; + private static Server server = null; + + public static boolean isServing() { + return sitesDir != null; + } + + public WebHandler(String dir,Server server) { + if( sitesDir != null ) + throw new RuntimeException("already set"); + if( !new File(dir).exists() ) + throw new RuntimeException(); + this.sitesDir = dir; + this.server = server; + } + + public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) + throws IOException, ServletException + { + String domain = baseRequest.getServerName(); +// System.out.println("handle "+domain); + Site site = getSite(domain); + if( site != null ) { + site.handler.handle(target,baseRequest,request,response); + } + } + + public static Object runLuan(String domain,String sourceText,String sourceName) throws LuanException { + return getSite(domain).luanHandler.runLuan(sourceText,sourceName); + } + + public static Object callSite(String domain,String fnName,Object... args) throws LuanException { + return getSite(domain).luanHandler.call_rpc(fnName,args); + } + + private static Site getSite(String domain) { + synchronized(siteMap) { + Site site = siteMap.get(domain); + if( site == null ) { + if( sitesDir==null ) + throw new NullPointerException("sitesDir"); + File dir = new File(sitesDir,domain); + if( !dir.exists() /* && !recover(dir) */ ) + return null; + site = newSite(dir.toString(),domain); + siteMap.put(domain,site); + } + return site; + } + } +/* + private static boolean recover(File dir) { + File backups = new File(dir.getParentFile().getParentFile(),"backups"); + if( !backups.exists() ) + return false; + String name = dir.getName(); + File from = null; + for( File backup : backups.listFiles() ) { + File d = new File(backup,"current/"+name); + if( d.exists() && (from==null || from.lastModified() < d.lastModified()) ) + from = d; + } + if( from == null ) + return false; + if( !from.renameTo(dir) ) + throw new RuntimeException("couldn't rename "+from+" to "+dir); + logger.info("recovered "+name+" from "+from); + return true; + } +*/ + static LuanTable initLuan(LuanState luan,String dir,String domain) { + LuanTable init; + try { + init = (LuanTable)luan.eval( + "local Luan = require 'luan:Luan.luan'\n" + +"local f = Luan.load_file 'classpath:luan/host/Init.luan'\n" + +"return f('"+dir+"','"+domain+"')\n" + ); + } catch(LuanException e) { + throw new RuntimeException(e); + } + File allowJavaFile = new File(dir,"site/private/"+allowJavaFileName); + if( !allowJavaFile.exists() ) { + JavaLuan.setSecurity( luan, javaSecurity ); + IoLuan.setSecurity( luan, ioSecurity(dir) ); + } + return init; + } + + private static Site newSite(String dir,String domain) { + LuanState luan = new LuanState(); + LuanTable init = initLuan(luan,dir,domain); + String password = (String)init.rawGet("password"); + + AuthenticationHandler authenticationHandler = new AuthenticationHandler("/private/"); + authenticationHandler.setPassword(password); + String loggerRoot = (String)init.rawGet("logger_root"); + LuanHandler luanHandler = new LuanHandler(luan,loggerRoot); + + ResourceHandler resourceHandler = new ResourceHandler(); + resourceHandler.setResourceBase(dir+"/site"); + resourceHandler.setDirectoriesListed(true); + resourceHandler.setAliases(true); + + NotFound notFoundHandler = new NotFound(luanHandler); + DefaultHandler defaultHandler = new DefaultHandler(); + + HandlerList handlers = new HandlerList(); + handlers.setHandlers(new Handler[]{authenticationHandler,luanHandler,resourceHandler,notFoundHandler,defaultHandler}); + + String logDir = dir+"/site/private/local/logs/web"; + new File(logDir).mkdirs(); + NCSARequestLog log = new NCSARequestLog(logDir+"/yyyy_mm_dd.log"); + log.setExtended(false); + log.setLogTimeZone(tz); + RequestLogHandler logHandler = new RequestLogHandler(); + logHandler.setRequestLog(log); + + HandlerCollection hc = new HandlerCollection(); + hc.setHandlers(new Handler[]{handlers,logHandler}); +// hc.setServer(getServer()); + + try { + hc.start(); + } catch(Exception e) { + throw new RuntimeException(e); + } + return new Site(hc,luanHandler); + } + + public static void removeHandler(String domain) throws Exception { + synchronized(siteMap) { + Site site = siteMap.remove(domain); + if( site != null ) { + site.handler.stop(); + site.handler.destroy(); + } + } + } + + public static void loadHandler(String domain) { + getSite(domain); + } + + public static Server server() { + return server; + } + + private static final IoLuan.Security ioSecurity(String dir) { + final String siteDir = dir + "/site/"; + return new IoLuan.Security() { + public void check(LuanState luan,String name) throws LuanException { + if( name.startsWith("file:") ) { + if( name.contains("..") ) + throw new LuanException("Security violation - '"+name+"' contains '..'"); + if( !name.startsWith("file:"+siteDir) ) + throw new LuanException("Security violation - '"+name+"' outside of site dir"); + } + else if( name.startsWith("classpath:luan/host/") ) { + throw new LuanException("Security violation"); + } + else if( name.startsWith("os:") || name.startsWith("bash:") ) { + throw new LuanException("Security violation"); + } + } + }; + } + + private static final JavaLuan.Security javaSecurity = new JavaLuan.Security() { + public void check(LuanState luan,String name) throws LuanException { + if( !name.startsWith("luan:") ) + throw new LuanException("Security violation - only luan:* modules can load Java"); + if( name.equals("luan:logging/Logging") ) + throw new LuanException("Security violation - cannot reload Logging"); + } + }; +}