Mercurial Hosting > luan
view src/luan/modules/http/LuanHandler.java @ 1359:9721d4709bfb
minor
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 19 Apr 2019 00:58:18 -0600 |
parents | 1d31c1f3ea30 |
children | ee0f0e6c89a0 |
line wrap: on
line source
package luan.modules.http; import java.io.Closeable; import java.io.Writer; import java.io.PrintWriter; import java.io.IOException; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.net.BindException; import java.util.List; import java.util.ArrayList; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import luan.lib.logging.Logger; import luan.lib.webserver.Request; import luan.lib.webserver.Response; import luan.lib.webserver.Status; import luan.lib.webserver.Server; import luan.lib.webserver.Handler; import luan.lib.webserver.ResponseOutputStream; import luan.Luan; import luan.LuanTable; import luan.LuanFunction; import luan.LuanJavaFunction; import luan.LuanCloner; import luan.LuanException; import luan.modules.PackageLuan; import luan.modules.BasicLuan; import luan.modules.logging.LuanLogger; public final class LuanHandler implements Handler, Luan.OnClose { private final Luan luanInit; private final Logger logger; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final List<Reference<Closeable>> onClose = new ArrayList<Reference<Closeable>>(); private volatile Luan currentLuan; private volatile boolean isDisabled = false; private static final Method resetLuanMethod; private static final Method evalInRootMethod; private static final Method disableLuanMethod; static { try { resetLuanMethod = LuanHandler.class.getMethod( "reset_luan" ); evalInRootMethod = LuanHandler.class.getMethod( "eval_in_root", String.class ); disableLuanMethod = LuanHandler.class.getMethod( "disable_luan" ); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } } public LuanHandler(Luan luanInit) { this.luanInit = luanInit; this.logger = LuanLogger.getLogger(luanInit,LuanHandler.class.getName()); try { LuanTable Http = (LuanTable)luanInit.require("luan:http/Http.luan"); if( Http.get("reset_luan") == null ) Http.put( "reset_luan", new LuanJavaFunction(luanInit,resetLuanMethod,this) ); Http.put( "eval_in_root", new LuanJavaFunction(luanInit,evalInRootMethod,this) ); Http.put( "disable_luan", new LuanJavaFunction(luanInit,disableLuanMethod,this) ); } catch(LuanException e) { throw new RuntimeException(e); } currentLuan = newLuan(); } private Luan newLuan() { Luan luan; synchronized(luanInit) { LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE); luan = (Luan)cloner.clone(luanInit); } luan.onClose = this; try { PackageLuan.load(luan,"site:/init.luan"); } catch(LuanException e) { //e.printStackTrace(); String err = e.getLuanStackTraceString(); logger.error(err); } return luan; } /* public Luan getLuan() { return luan; } */ @Override public Response handle(Request request) { if( isDisabled ) return null; if( request.path.endsWith("/") ) return null; return handle(request,false); } Response handle(Request request,boolean notFound) { Thread thread = Thread.currentThread(); String oldName = thread.getName(); thread.setName(request.headers.get("host")+request.path); lock.readLock().lock(); try { return service(request,notFound); } catch(LuanException e) { String err = e.getLuanStackTraceString(); logger.error(err+"\n"+request.rawHead.trim()+"\n"); String msg = "Internel Server Error\n\n" + err; return Response.errorResponse( Status.INTERNAL_SERVER_ERROR, msg ); } finally { lock.readLock().unlock(); thread.setName(oldName); } } public void onClose(Closeable c) { synchronized(onClose) { onClose.add(new WeakReference<Closeable>(c)); } } public void close() { synchronized(onClose) { for( Reference<Closeable> ref : onClose ) { Closeable c = ref.get(); if( c != null ) { try { c.close(); } catch(IOException e) { logger.error(c.toString(),e); } } } onClose.clear(); } } public Object call_rpc(String fnName,Object... args) throws LuanException { lock.readLock().lock(); try { LuanFunction fn; synchronized(luanInit) { enableLoad("luan:Rpc.luan"); LuanTable rpc = (LuanTable)currentLuan.require("luan:Rpc.luan"); LuanTable fns = (LuanTable)rpc.get("functions"); fn = (LuanFunction)fns.get(fnName); if( fn == null ) throw new LuanException( "function not found: " + fnName ); LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL); fn = (LuanFunction)cloner.get(fn); } return fn.call(args); } finally { lock.readLock().unlock(); } } public void reset_luan() { lock.writeLock().lock(); try { close(); currentLuan = newLuan(); } finally { lock.writeLock().unlock(); } } public void disable_luan() { isDisabled = true; } public Object runLuan(String sourceText,String sourceName) throws LuanException { lock.readLock().lock(); try { Luan luan = currentLuan; synchronized(luanInit) { LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL); Luan luan2 = (Luan)cloner.clone(luan); LuanFunction fn = luan2.load(sourceText,sourceName); return fn.call(); } } finally { lock.readLock().unlock(); } } public void eval_in_root(String text) throws LuanException { Luan luan; synchronized(luanInit) { LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE); luan = (Luan)cloner.clone(currentLuan); } luan.onClose = this; luan.load(text,"<eval_in_root>",null).call(); currentLuan = luan; } public static void start(Server server) throws Exception { try { server.start(); } catch(BindException e) { throw new LuanException(e.toString()); } } // from HttpServicer private Response service(Request request,boolean notFound) throws LuanException { try { if( !notFound ) return serviceLuan(request); else return serviceNotFound(request); } catch(LuanException e) { return handleError(request,e); } } private Response handleError(Request request,LuanException e) throws LuanException { //e.printStackTrace(); Luan luan; synchronized(luanInit) { LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL); luan = (Luan)cloner.clone(currentLuan); } LuanTable module = (LuanTable)luan.require("luan:http/Http.luan"); return (Response)module.fn("handle_error").call( request, e.table(luan) ); } private Response serviceLuan(Request request) throws LuanException { String modName = "site:" + request.path +".luan"; LuanFunction fn; Luan luan; synchronized(luanInit) { enableLoad("luan:http/Http.luan",modName); currentLuan.require("luan:http/Http.luan"); Object mod = PackageLuan.load(currentLuan,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 = (Luan)cloner.clone(currentLuan); fn = (LuanFunction)cloner.get(mod); } LuanTable module = (LuanTable)luan.require("luan:http/Http.luan"); module.fn("new_request").call(request); module.fn("new_response").call(); fn.call(); return (Response)module.fn("finish").call(); } private Response serviceNotFound(Request request) throws LuanException { LuanFunction fn; Luan luan; synchronized(luanInit) { enableLoad("luan:http/Http.luan"); LuanTable module = (LuanTable)currentLuan.require("luan:http/Http.luan"); fn = module.fn("not_found_handler"); if( fn == null ) return null; LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL); luan = (Luan)cloner.clone(currentLuan); fn = (LuanFunction)cloner.get(fn); } LuanTable module = (LuanTable)luan.require("luan:http/Http.luan"); module.fn("new_request").call(request); module.fn("new_response").call(); Object obj = Luan.first(fn.call()); if( !(obj instanceof Boolean) ) throw new LuanException("not_found_handler must return boolean"); boolean handled = (Boolean)obj; return handled ? (Response)module.fn("finish").call() : null; } private void enableLoad(String... mods) throws LuanException { LuanTable loaded = PackageLuan.loaded(currentLuan); for( String mod : mods ) { if( loaded.rawGet(mod) == null ) { LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE); currentLuan = (Luan)cloner.clone(currentLuan); break; } } } }