changeset 1315:5763597ca5c0

add DomainHandler
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 30 Jan 2019 01:21:49 -0700
parents 51a1987b55a3
children 11d3640e739d
files conv.txt src/luan/host/Init.luan src/luan/host/WebHandler.java src/luan/host/run.luan src/luan/modules/http/Http.luan src/luan/modules/http/LuanDomainHandler.java src/luan/modules/http/Server.luan src/luan/modules/url/LuanUrl.java src/luan/webserver/handlers/DomainHandler.java
diffstat 9 files changed, 194 insertions(+), 103 deletions(-) [+]
line wrap: on
line diff
--- a/conv.txt	Tue Jan 29 19:10:39 2019 -0700
+++ b/conv.txt	Wed Jan 30 01:21:49 2019 -0700
@@ -1,3 +1,5 @@
+Hosting.domain
+
 Thread.fork
 Io.output_to
 Io.output_of
--- a/src/luan/host/Init.luan	Tue Jan 29 19:10:39 2019 -0700
+++ b/src/luan/host/Init.luan	Wed Jan 30 01:21:49 2019 -0700
@@ -72,7 +72,7 @@
 	return Io.uri( Http.dir..path, loading )
 end
 
-Hosting.domain = domain
+Http.domain = domain
 Io.password = Init.password
 
 
--- a/src/luan/host/WebHandler.java	Tue Jan 29 19:10:39 2019 -0700
+++ b/src/luan/host/WebHandler.java	Wed Jan 30 01:21:49 2019 -0700
@@ -1,70 +1,42 @@
 package luan.host;
 
 import java.io.File;
-import java.io.IOException;
-import java.lang.ref.Reference;
-import java.lang.ref.SoftReference;
-//import java.lang.ref.WeakReference;
-import java.lang.ref.ReferenceQueue;
-import java.util.Map;
-import java.util.HashMap;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import luan.webserver.Handler;
-import luan.webserver.Server;
 import luan.webserver.Request;
 import luan.webserver.Response;
-import luan.webserver.handlers.IndexHandler;
-import luan.webserver.handlers.ListHandler;
-import luan.Luan;
+import luan.webserver.handlers.DomainHandler;
 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;
 
 
 public class WebHandler implements Handler {
 	private static final Logger logger = LoggerFactory.getLogger(WebHandler.class);
 
-	private static class LuanRef {
-		private final Handler handler;
-		private final LuanHandler luanHandler;
-
-		private LuanRef(Handler handler,LuanHandler luanHandler) {
-			this.handler = handler;
-			this.luanHandler = luanHandler;
-		}
-	}
-
-	private static final ReferenceQueue<LuanRef> queue = new ReferenceQueue<LuanRef>();
-
-	private static class MyReference extends SoftReference<LuanRef> {
-		private LuanHandler luanHandler;
+	private static final DomainHandler.Factory factory = new DomainHandler.Factory() {
+		public Handler newHandler(String domain) {
+			File dir = new File(sitesDir,domain);
+			if( !dir.exists() /* && !recover(dir) */ )
+				return null;
+			String dirStr = dir.toString();
 
-		private MyReference(LuanRef lr) {
-			super(lr,queue);
-			this.luanHandler = lr.luanHandler;
-		}
-	}
+			String logDir = dirStr + "/site/private/local/logs/web";
+			new File(logDir).mkdirs();
 
-	private static void sweep() {
-		while(true) {
-			MyReference ref = (MyReference)queue.poll();
-			if( ref == null )
-				return;
-			//logger.info("sweep");
-			ref.luanHandler.close();
-			ref.luanHandler = null;
+			LuanState luan = new LuanState();
+			LuanTable init = initLuan(luan,dirStr,domain);
+			String loggerRoot = (String)init.rawGet("logger_root");
+			return new LuanHandler(luan,loggerRoot);
 		}
-	}
-
+	};
 
 	public static String allowJavaFileName = "allow_java";  // change for security
-	private static final Map<String,MyReference> siteMap = new HashMap<String,MyReference>();
+	private static final DomainHandler domainHandler = new DomainHandler(factory);
 	private static String sitesDir = null;
 
 	public static boolean isServing() {
@@ -79,45 +51,20 @@
 		sitesDir = dir;
 	}
 
-	public Response handle(Request request) {
-		String host = (String)request.headers.get("host");
-		int i = host.indexOf(':');
-		String domain = i == -1 ? host : host.substring(0,i);
-//		System.out.println("handle "+domain);
-		LuanRef lr = getSite(domain);
-		if( lr == null )
-			return null;
-		return lr.handler.handle(request);
+	@Override public Response handle(Request request) {
+		return domainHandler.handle(request);
 	}
 
 	public static Object runLuan(String domain,String sourceText,String sourceName) throws LuanException {
-		LuanRef lr = getSite(domain);
-		return lr.luanHandler.runLuan(sourceText,sourceName);
+		LuanHandler luanHandler = (LuanHandler)domainHandler.getHandler(domain);
+		return luanHandler.runLuan(sourceText,sourceName);
 	}
 
 	public static Object callSite(String domain,String fnName,Object... args) throws LuanException {
-		LuanRef lr = getSite(domain);
-		return lr.luanHandler.call_rpc(fnName,args);
+		LuanHandler luanHandler = (LuanHandler)domainHandler.getHandler(domain);
+		return luanHandler.call_rpc(fnName,args);
 	}
 
-	private static LuanRef getSite(String domain) {
-		synchronized(siteMap) {
-			Reference<LuanRef> ref = siteMap.get(domain);
-			LuanRef lr = ref==null ? null : ref.get();
-			if( lr == null ) {
-				//if(ref!=null) logger.info("gc "+domain);
-				if( sitesDir==null )
-					throw new NullPointerException("sitesDir");
-				File dir = new File(sitesDir,domain);
-				if( !dir.exists() /* && !recover(dir) */ )
-					return null;
-				sweep();
-				lr = newSite(dir.toString(),domain);
-				siteMap.put(domain,new MyReference(lr));
-			}
-			return lr;
-		}
-	}
 /*
 	private static boolean recover(File dir) {
 		File backups = new File(dir.getParentFile().getParentFile(),"backups");
@@ -157,34 +104,12 @@
 		return init;
 	}
 
-	private static LuanRef newSite(String dir,String domain) {
-		LuanState luan = new LuanState();
-		LuanTable init = initLuan(luan,dir,domain);
-
-		String loggerRoot = (String)init.rawGet("logger_root");
-		LuanHandler luanHandler = new LuanHandler(luan,loggerRoot);
-
-		Handler handler = luanHandler;
-		handler = new IndexHandler(handler);
-
-		String logDir = dir + "/site/private/local/logs/web";
-		new File(logDir).mkdirs();
-
-		return new LuanRef(handler,luanHandler);
-	}
-
-	public static void removeHandler(String domain) throws Exception {
-		synchronized(siteMap) {
-			Reference<LuanRef> ref = siteMap.remove(domain);
-			LuanRef lr = ref==null ? null : ref.get();
-			if( lr != null ) {
-				lr.luanHandler.close();
-			}
-		}
+	public static void removeHandler(String domain) {
+		domainHandler.removeHandler(domain);
 	}
 
 	public static void loadHandler(String domain) {
-		getSite(domain);
+		domainHandler.getHandler(domain);
 	}
 
 	private static final IoLuan.Security ioSecurity(String dir) {
--- a/src/luan/host/run.luan	Tue Jan 29 19:10:39 2019 -0700
+++ b/src/luan/host/run.luan	Wed Jan 30 01:21:49 2019 -0700
@@ -20,11 +20,13 @@
 -- web server
 
 local Server = require "java:luan.webserver.Server"
+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 webHandler = WebHandler.new(Hosting.sites_dir)
 local handler = webHandler
+handler = IndexHandler.new(handler)
 handler = ContentTypeHandler.new(handler)
 handler = SafeHandler.new(handler)
 local server = Server.ForAddress.new("127.0.0.1",8080,handler)
--- a/src/luan/modules/http/Http.luan	Tue Jan 29 19:10:39 2019 -0700
+++ b/src/luan/modules/http/Http.luan	Wed Jan 30 01:21:49 2019 -0700
@@ -172,4 +172,6 @@
 	return Response.errorResponse( Status.INTERNAL_SERVER_ERROR, msg )
 end
 
+Http.domain = nil  -- set in domain specific cases
+
 return Http
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/http/LuanDomainHandler.java	Wed Jan 30 01:21:49 2019 -0700
@@ -0,0 +1,41 @@
+package luan.modules.http;
+
+import luan.webserver.Request;
+import luan.webserver.Response;
+import luan.webserver.Handler;
+import luan.webserver.handlers.DomainHandler;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanCloner;
+import luan.LuanException;
+
+
+public final class LuanDomainHandler implements Handler {
+
+	private final LuanState luanInit;
+
+	private final DomainHandler.Factory factory = new DomainHandler.Factory() {
+		public Handler newHandler(String domain) {
+			LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
+			LuanState luan = (LuanState)cloner.clone(luanInit);
+			try {
+				LuanTable Http = (LuanTable)luan.require("luan:http/Http.luan");
+				Http.put( "domain", domain );
+			} catch(LuanException e) {
+				throw new RuntimeException(e);
+			}
+			return new LuanHandler(luan,null);
+		}
+	};
+
+	private final DomainHandler domainHandler = new DomainHandler(factory);
+
+	public LuanDomainHandler(LuanState luanInit) {
+		LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
+		this.luanInit = (LuanState)cloner.clone(luanInit);
+	}
+
+	@Override public Response handle(Request request) {
+		return domainHandler.handle(request);
+	}
+}
--- a/src/luan/modules/http/Server.luan	Tue Jan 29 19:10:39 2019 -0700
+++ b/src/luan/modules/http/Server.luan	Wed Jan 30 01:21:49 2019 -0700
@@ -6,6 +6,7 @@
 local matches = String.matches or error()
 local try = Luan.try or error()
 local Io = require "luan:Io.luan"
+local uri = Io.uri or error()
 local Package = require "luan:Package.luan"
 local Rpc = require "luan:Rpc.luan"
 local Thread = require "luan:Thread.luan"
@@ -35,7 +36,7 @@
 	dir = gsub(dir,"/$","")  -- remove trailing '/' if any
 	Http.dir = dir
 	function Io.schemes.site(path)
-		return Io.uri( dir..path )
+		return uri( dir..path )
 	end
 end
 
@@ -77,10 +78,11 @@
 	return server, luan_handler
 end
 
-function Server.serve_for_proxy(dir,port)
+function Server.serve_for_proxy(dir,port,LuanHandlerClass)
 	port = port or 8080
+	LuanHandlerClass = LuanHandlerClass or LuanHandler
 	Server.init_dir(dir)
-	local luan_handler = LuanHandler.new()
+	local luan_handler = LuanHandlerClass.new()
 	local handler = IndexHandler.new(luan_handler)
 	handler = ContentTypeHandler.new(handler)
 	handler = SafeHandler.new(handler)
--- a/src/luan/modules/url/LuanUrl.java	Tue Jan 29 19:10:39 2019 -0700
+++ b/src/luan/modules/url/LuanUrl.java	Wed Jan 30 01:21:49 2019 -0700
@@ -5,6 +5,7 @@
 import java.io.OutputStream;
 import java.io.Reader;
 import java.io.IOException;
+import java.io.FileNotFoundException;
 import java.io.UnsupportedEncodingException;
 import java.net.URL;
 import java.net.URLConnection;
@@ -253,6 +254,8 @@
 	private static InputStream getInputStream(LuanState luan,HttpURLConnection httpCon) throws IOException, LuanException {
 		try {
 			return httpCon.getInputStream();
+		} catch(FileNotFoundException e) {
+			throw e;
 		} catch(IOException e) {
 			int responseCode = httpCon.getResponseCode();
 			String responseMessage = httpCon.getResponseMessage();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/webserver/handlers/DomainHandler.java	Wed Jan 30 01:21:49 2019 -0700
@@ -0,0 +1,114 @@
+package luan.webserver.handlers;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.Map;
+import java.util.HashMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import luan.webserver.Handler;
+import luan.webserver.Request;
+import luan.webserver.Response;
+
+
+public final class DomainHandler implements Handler {
+	private static final Logger logger = LoggerFactory.getLogger(DomainHandler.class);
+
+	public interface Factory {
+		public Handler newHandler(String domain);
+	}
+
+	private static class Ref {
+		private final Handler handler;
+
+		private Ref(Handler handler) {
+			this.handler = handler;
+		}
+	}
+
+	private final ReferenceQueue<Ref> queue = new ReferenceQueue<Ref>();
+
+	private class MyReference extends SoftReference<Ref> {
+		private Handler handler;
+
+		private MyReference(Ref r) {
+			super(r,queue);
+			this.handler = r.handler;
+		}
+	}
+
+	private static void close(Handler handler) {
+		if( handler instanceof Closeable ) {
+			try {
+				((Closeable)handler).close();
+			} catch(IOException e) {
+				logger.error(handler.toString(),e);
+			}
+		}
+	}
+
+	private void sweep() {
+		while(true) {
+			MyReference ref = (MyReference)queue.poll();
+			if( ref == null )
+				return;
+			//logger.info("sweep");
+			close(ref.handler);
+			ref.handler = null;
+		}
+	}
+
+	private final Map<String,MyReference> map = new HashMap<String,MyReference>();
+
+	private final Factory factory;
+
+	public DomainHandler(Factory factory) {
+		this.factory = factory;
+	}
+
+	public Response handle(Request request) {
+		String host = (String)request.headers.get("host");
+		int i = host.indexOf(':');
+		String domain = i == -1 ? host : host.substring(0,i);
+		Handler handler = getHandler(domain);
+		return handler==null ? null : handler.handle(request);
+	}
+
+	public Handler getHandler(String domain) {
+		Ref r = getRef(domain);
+		return r==null ? null : r.handler;
+	}
+
+	public void removeHandler(String domain) {
+		domain = domain.toLowerCase();
+		synchronized(map) {
+			Reference<Ref> ref = map.remove(domain);
+			Ref r = ref==null ? null : ref.get();
+			if( r != null ) {
+				close(r.handler);
+			}
+		}
+	}
+
+	private Ref getRef(String domain) {
+		domain = domain.toLowerCase();
+		synchronized(map) {
+			Reference<Ref> ref = map.get(domain);
+			Ref r = ref==null ? null : ref.get();
+			if( r == null ) {
+				//if(ref!=null) logger.info("gc "+domain);
+				sweep();
+				Handler handler = factory.newHandler(domain);
+				if( handler == null )
+					return null;
+				r = new Ref(handler);
+				map.put(domain,new MyReference(r));
+			}
+			return r;
+		}
+	}
+
+}