1135
|
1 package luan.host;
|
|
2
|
|
3 import java.io.File;
|
|
4 import java.io.IOException;
|
|
5 import java.util.Map;
|
|
6 import java.util.HashMap;
|
|
7 import java.util.TimeZone;
|
|
8 import javax.servlet.ServletException;
|
|
9 import javax.servlet.http.HttpServlet;
|
|
10 import javax.servlet.http.HttpServletRequest;
|
|
11 import javax.servlet.http.HttpServletResponse;
|
|
12 import org.slf4j.Logger;
|
|
13 import org.slf4j.LoggerFactory;
|
|
14 import org.eclipse.jetty.server.Request;
|
|
15 import org.eclipse.jetty.server.Handler;
|
|
16 import org.eclipse.jetty.server.Server;
|
|
17 import org.eclipse.jetty.server.NCSARequestLog;
|
|
18 import org.eclipse.jetty.server.handler.AbstractHandler;
|
|
19 import org.eclipse.jetty.server.handler.ResourceHandler;
|
|
20 import org.eclipse.jetty.server.handler.HandlerList;
|
|
21 import org.eclipse.jetty.server.handler.RequestLogHandler;
|
|
22 import org.eclipse.jetty.server.handler.DefaultHandler;
|
|
23 import org.eclipse.jetty.server.handler.HandlerCollection;
|
|
24 import luan.Luan;
|
|
25 import luan.LuanState;
|
|
26 import luan.LuanException;
|
|
27 import luan.LuanTable;
|
|
28 import luan.LuanFunction;
|
|
29 import luan.modules.IoLuan;
|
|
30 import luan.modules.JavaLuan;
|
|
31 import luan.modules.PackageLuan;
|
1136
|
32 import luan.modules.http.jetty.LuanHandler;
|
|
33 import luan.modules.http.jetty.AuthenticationHandler;
|
|
34 import luan.modules.http.jetty.NotFound;
|
1135
|
35
|
|
36
|
|
37 public class WebHandler extends AbstractHandler {
|
|
38 private static final Logger logger = LoggerFactory.getLogger(WebHandler.class);
|
|
39
|
|
40 private static class Site {
|
|
41 final Handler handler;
|
|
42 final LuanHandler luanHandler;
|
|
43
|
|
44 Site(Handler handler,LuanHandler luanHandler) {
|
|
45 this.handler = handler;
|
|
46 this.luanHandler = luanHandler;
|
|
47 }
|
|
48 }
|
|
49
|
|
50 public static String allowJavaFileName = "allow_java"; // change for security
|
|
51 private static final String tz = TimeZone.getDefault().getID();
|
|
52 private static final Map<String,Site> siteMap = new HashMap<String,Site>();
|
|
53 private static String sitesDir = null;
|
|
54 private static Server server = null;
|
|
55
|
|
56 public static boolean isServing() {
|
|
57 return sitesDir != null;
|
|
58 }
|
|
59
|
|
60 public WebHandler(String dir,Server server) {
|
|
61 if( sitesDir != null )
|
|
62 throw new RuntimeException("already set");
|
|
63 if( !new File(dir).exists() )
|
|
64 throw new RuntimeException();
|
|
65 this.sitesDir = dir;
|
|
66 this.server = server;
|
|
67 }
|
|
68
|
|
69 public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response)
|
|
70 throws IOException, ServletException
|
|
71 {
|
|
72 String domain = baseRequest.getServerName();
|
|
73 // System.out.println("handle "+domain);
|
|
74 Site site = getSite(domain);
|
|
75 if( site != null ) {
|
|
76 site.handler.handle(target,baseRequest,request,response);
|
|
77 }
|
|
78 }
|
|
79
|
|
80 public static Object runLuan(String domain,String sourceText,String sourceName) throws LuanException {
|
|
81 return getSite(domain).luanHandler.runLuan(sourceText,sourceName);
|
|
82 }
|
|
83
|
|
84 public static Object callSite(String domain,String fnName,Object... args) throws LuanException {
|
|
85 return getSite(domain).luanHandler.call_rpc(fnName,args);
|
|
86 }
|
|
87
|
|
88 private static Site getSite(String domain) {
|
|
89 synchronized(siteMap) {
|
|
90 Site site = siteMap.get(domain);
|
|
91 if( site == null ) {
|
|
92 if( sitesDir==null )
|
|
93 throw new NullPointerException("sitesDir");
|
|
94 File dir = new File(sitesDir,domain);
|
|
95 if( !dir.exists() /* && !recover(dir) */ )
|
|
96 return null;
|
|
97 site = newSite(dir.toString(),domain);
|
|
98 siteMap.put(domain,site);
|
|
99 }
|
|
100 return site;
|
|
101 }
|
|
102 }
|
|
103 /*
|
|
104 private static boolean recover(File dir) {
|
|
105 File backups = new File(dir.getParentFile().getParentFile(),"backups");
|
|
106 if( !backups.exists() )
|
|
107 return false;
|
|
108 String name = dir.getName();
|
|
109 File from = null;
|
|
110 for( File backup : backups.listFiles() ) {
|
|
111 File d = new File(backup,"current/"+name);
|
|
112 if( d.exists() && (from==null || from.lastModified() < d.lastModified()) )
|
|
113 from = d;
|
|
114 }
|
|
115 if( from == null )
|
|
116 return false;
|
|
117 if( !from.renameTo(dir) )
|
|
118 throw new RuntimeException("couldn't rename "+from+" to "+dir);
|
|
119 logger.info("recovered "+name+" from "+from);
|
|
120 return true;
|
|
121 }
|
|
122 */
|
|
123 static LuanTable initLuan(LuanState luan,String dir,String domain) {
|
|
124 LuanTable init;
|
|
125 try {
|
|
126 init = (LuanTable)luan.eval(
|
|
127 "local Luan = require 'luan:Luan.luan'\n"
|
|
128 +"local f = Luan.load_file 'classpath:luan/host/Init.luan'\n"
|
|
129 +"return f('"+dir+"','"+domain+"')\n"
|
|
130 );
|
|
131 } catch(LuanException e) {
|
|
132 throw new RuntimeException(e);
|
|
133 }
|
|
134 File allowJavaFile = new File(dir,"site/private/"+allowJavaFileName);
|
|
135 if( !allowJavaFile.exists() ) {
|
|
136 JavaLuan.setSecurity( luan, javaSecurity );
|
|
137 IoLuan.setSecurity( luan, ioSecurity(dir) );
|
|
138 }
|
|
139 return init;
|
|
140 }
|
|
141
|
|
142 private static Site newSite(String dir,String domain) {
|
|
143 LuanState luan = new LuanState();
|
|
144 LuanTable init = initLuan(luan,dir,domain);
|
|
145 String password = (String)init.rawGet("password");
|
|
146
|
|
147 AuthenticationHandler authenticationHandler = new AuthenticationHandler("/private/");
|
|
148 authenticationHandler.setPassword(password);
|
|
149 String loggerRoot = (String)init.rawGet("logger_root");
|
|
150 LuanHandler luanHandler = new LuanHandler(luan,loggerRoot);
|
|
151
|
|
152 ResourceHandler resourceHandler = new ResourceHandler();
|
|
153 resourceHandler.setResourceBase(dir+"/site");
|
|
154 resourceHandler.setDirectoriesListed(true);
|
|
155 resourceHandler.setAliases(true);
|
|
156
|
|
157 NotFound notFoundHandler = new NotFound(luanHandler);
|
|
158 DefaultHandler defaultHandler = new DefaultHandler();
|
|
159
|
|
160 HandlerList handlers = new HandlerList();
|
|
161 handlers.setHandlers(new Handler[]{authenticationHandler,luanHandler,resourceHandler,notFoundHandler,defaultHandler});
|
|
162
|
|
163 String logDir = dir+"/site/private/local/logs/web";
|
|
164 new File(logDir).mkdirs();
|
|
165 NCSARequestLog log = new NCSARequestLog(logDir+"/yyyy_mm_dd.log");
|
|
166 log.setExtended(false);
|
|
167 log.setLogTimeZone(tz);
|
|
168 RequestLogHandler logHandler = new RequestLogHandler();
|
|
169 logHandler.setRequestLog(log);
|
|
170
|
|
171 HandlerCollection hc = new HandlerCollection();
|
|
172 hc.setHandlers(new Handler[]{handlers,logHandler});
|
|
173 // hc.setServer(getServer());
|
|
174
|
|
175 try {
|
|
176 hc.start();
|
|
177 } catch(Exception e) {
|
|
178 throw new RuntimeException(e);
|
|
179 }
|
|
180 return new Site(hc,luanHandler);
|
|
181 }
|
|
182
|
|
183 public static void removeHandler(String domain) throws Exception {
|
|
184 synchronized(siteMap) {
|
|
185 Site site = siteMap.remove(domain);
|
|
186 if( site != null ) {
|
|
187 site.handler.stop();
|
|
188 site.handler.destroy();
|
|
189 }
|
|
190 }
|
|
191 }
|
|
192
|
|
193 public static void loadHandler(String domain) {
|
|
194 getSite(domain);
|
|
195 }
|
|
196
|
|
197 public static Server server() {
|
|
198 return server;
|
|
199 }
|
|
200
|
|
201 private static final IoLuan.Security ioSecurity(String dir) {
|
|
202 final String siteDir = dir + "/site/";
|
|
203 return new IoLuan.Security() {
|
|
204 public void check(LuanState luan,String name) throws LuanException {
|
|
205 if( name.startsWith("file:") ) {
|
|
206 if( name.contains("..") )
|
|
207 throw new LuanException("Security violation - '"+name+"' contains '..'");
|
|
208 if( !name.startsWith("file:"+siteDir) )
|
|
209 throw new LuanException("Security violation - '"+name+"' outside of site dir");
|
|
210 }
|
|
211 else if( name.startsWith("classpath:luan/host/") ) {
|
|
212 throw new LuanException("Security violation");
|
|
213 }
|
|
214 else if( name.startsWith("os:") || name.startsWith("bash:") ) {
|
|
215 throw new LuanException("Security violation");
|
|
216 }
|
|
217 }
|
|
218 };
|
|
219 }
|
|
220
|
|
221 private static final JavaLuan.Security javaSecurity = new JavaLuan.Security() {
|
|
222 public void check(LuanState luan,String name) throws LuanException {
|
|
223 if( !name.startsWith("luan:") )
|
|
224 throw new LuanException("Security violation - only luan:* modules can load Java");
|
|
225 if( name.equals("luan:logging/Logging") )
|
|
226 throw new LuanException("Security violation - cannot reload Logging");
|
|
227 }
|
|
228 };
|
|
229 }
|