Mercurial Hosting > luan
view src/luan/host/WebHandler.java @ 1767:9157e0d5936e
minor fix
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Tue, 06 Jun 2023 20:31:19 -0600 |
parents | 8df0b80e715e |
children |
line wrap: on
line source
package luan.host; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.util.Set; import java.util.Collections; import java.util.concurrent.ConcurrentHashMap; import goodjava.logging.Logger; import goodjava.logging.LoggerFactory; import goodjava.io.IoUtils; import goodjava.webserver.Handler; import goodjava.webserver.Request; import goodjava.webserver.Response; import goodjava.webserver.handlers.DomainHandler; import goodjava.webserver.handlers.IndexHandler; import goodjava.webserver.handlers.ListHandler; import goodjava.webserver.handlers.ContentTypeHandler; import goodjava.webserver.handlers.SafeHandler; import goodjava.webserver.handlers.LogHandler; import goodjava.webserver.handlers.FileHandler; import goodjava.webserver.handlers.DirHandler; import goodjava.webserver.handlers.HeadersHandler; import goodjava.webserver.handlers.BasicAuthHandler; import goodjava.webserver.handlers.RegexHandler; import luan.Luan; import luan.LuanException; import luan.LuanTable; import luan.LuanFunction; import luan.LuanJavaFunction; import luan.LuanClosure; import luan.LuanRuntimeException; import luan.modules.http.LuanHandler; import luan.modules.http.NotFound; import luan.modules.logging.LuanLogger; public class WebHandler implements Handler { private static final Logger logger = LoggerFactory.getLogger(WebHandler.class); private static final long days30 = 1000L*60*60*24*30; private static final class MyHandler implements Handler, Closeable { private final Handler handler; final LuanHandler luanHandler; MyHandler(Handler handler,LuanHandler luanHandler) { this.handler = handler; this.luanHandler = luanHandler; } @Override public Response handle(Request request) { return handler.handle(request); } @Override public void close() { dontGc.remove(this); luanHandler.close(); } protected void finalize() throws Throwable { // logger.info("gc "+luanHandler.domain); close(); } } private static final DomainHandler.Factory factory = new DomainHandler.Factory() { public Handler newHandler(String domain) { File dir = new File(sitesDir,domain); if( !dir.exists() ) return null; String dirStr = dir.toString(); String logDir = dirStr + "/site/private/local/logs/web"; try { IoUtils.mkdirs(new File(logDir)); } catch(IOException e) { throw new RuntimeException(e); } Luan luan = new Luan(); String password; LuanLogger.startThreadLogging(luan); try { LuanFunction fn = Luan.loadClasspath(luan,"luan/host/init.luan"); fn.call(luan,dirStr,domain); LuanTable Io = (LuanTable)luan.require("luan:Io.luan"); password = (String)Io.get(luan,"password"); if( password==null ) throw new NullPointerException(); } catch(LuanException e) { throw new LuanRuntimeException(e); } finally { LuanLogger.endThreadLogging(); } Fns fns = new Fns(); try { LuanTable Http = (LuanTable)luan.require("luan:http/Http.luan"); Http.put( luan, "dont_gc", new LuanJavaFunction(dontGcMethod,fns) ); } catch(LuanException e) { throw new RuntimeException(e); } security(luan,dirStr); LuanHandler luanHandler = new LuanHandler(luan,domain); FileHandler fileHandler = new FileHandler(dirStr+"/site/"); Handler handler = new ListHandler( luanHandler, fileHandler ); handler = new ContentTypeHandler(handler); handler = new IndexHandler(handler); DirHandler dirHandler = new DirHandler(fileHandler); Handler notFoundHander = new NotFound(luanHandler); notFoundHander = new ContentTypeHandler(notFoundHander); handler = new ListHandler( handler, dirHandler, notFoundHander ); Handler auth = new BasicAuthHandler(handler,"Private","admin",password); handler = new RegexHandler("^/private/",auth,handler); handler = new HeadersHandler(handler); handler = new SafeHandler(handler); handler = new LogHandler(handler,LogHandler.dirLogger(new File(logDir),days30)); MyHandler myHandler = new MyHandler(handler,luanHandler); fns.set(myHandler); return myHandler; } }; public static LuanTable config; private static final DomainHandler domainHandler = new DomainHandler(factory); private static String sitesDir = null; public WebHandler(String dir) { if( sitesDir != null ) throw new RuntimeException("already set"); if( !new File(dir).exists() ) throw new RuntimeException(); sitesDir = dir; LuanLogger.initThreadLogging(); } @Override public Response handle(Request request) { return domainHandler.handle(request); } public static Object callSite(String domain,String fnName,Object... args) throws LuanException { MyHandler handler = (MyHandler)domainHandler.getHandler(domain); return handler.luanHandler.call_rpc(fnName,args); } public static void removeHandler(String domain) { domainHandler.removeHandler(domain); } public static void moveTo(String domain,String toDomain,String password) throws LuanException, IOException { toDomain = toDomain.toLowerCase(); File fromDir = new File(sitesDir,domain); File toDir = new File(sitesDir,toDomain); synchronized(domain) { if( !fromDir.exists() ) throw new LuanException("domain not found"); if( toDir.exists() ) throw new LuanException("new_domain already exists"); callSite(domain,"close_lucene",password); removeHandler(domain); IoUtils.move(fromDir,toDir); } loadHandler(toDomain); } public static void loadHandler(String domain) throws LuanException { try { domainHandler.getHandler(domain); } catch(LuanRuntimeException e) { throw (LuanException)e.getCause(); } } private static final void security(Luan luan,String dir) { final String siteUri = "file:" + dir + "/site"; Luan.Security security = new Luan.Security() { public void check(Luan luan,LuanClosure closure,String op,Object... args) throws LuanException { if( op.equals("uri") ) { String name = (String)args[0]; if( name.startsWith("file:") ) { if( name.contains("..") ) throw new LuanException("Security violation - '"+name+"' contains '..'"); if( !(name.equals(siteUri) || name.startsWith(siteUri+"/")) ) 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"); } } else { String name = closure.sourceName; if( !( name.startsWith("luan:") || name.startsWith("classpath:") || name.matches("^file:[^/]+$") ) ) 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"); } } }; Luan.setSecurity(luan,security); } private static final Set<MyHandler> dontGc = Collections.newSetFromMap(new ConcurrentHashMap<MyHandler,Boolean>()); private static final Method dontGcMethod; static { try { dontGcMethod = WebHandler.Fns.class.getMethod( "dont_gc" ); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } } public static final class Fns { private Reference<MyHandler> ref = null; private boolean dont = false; private void set(MyHandler myHandler) { if( dont ) { dontGc.add(myHandler); } else { ref = new WeakReference<MyHandler>(myHandler); } } public void dont_gc() throws LuanException { logger.info("dont_gc"); if( ref == null ) { dont = true; } else { MyHandler mh = ref.get(); if( mh == null ) throw new LuanException("HTTP handler has been garbage collected"); dontGc.add(mh); } } } }