comparison src/luan/modules/http/jetty/HttpServicer.java @ 1136:d30d400fd43d

add http/jetty
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 29 Jan 2018 17:50:49 -0700
parents src/luan/modules/http/HttpServicer.java@0d884377e923
children 0842b9b570f8
comparison
equal deleted inserted replaced
1135:707a5d874f3e 1136:d30d400fd43d
1 package luan.modules.http.jetty;
2
3 import java.io.InputStream;
4 import java.io.BufferedInputStream;
5 import java.io.PrintWriter;
6 import java.io.IOException;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.List;
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.Iterator;
13 import java.util.Enumeration;
14 import java.util.concurrent.Executor;
15 import java.util.concurrent.Executors;
16 import javax.servlet.ServletOutputStream;
17 import javax.servlet.ServletException;
18 import javax.servlet.http.Cookie;
19 import javax.servlet.http.HttpServletRequest;
20 import javax.servlet.http.HttpServletResponse;
21 import javax.servlet.http.HttpSession;
22 import javax.servlet.http.Part;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25 import org.eclipse.jetty.util.MultiPartInputStream;
26 import luan.Luan;
27 import luan.LuanState;
28 import luan.LuanFunction;
29 import luan.LuanException;
30 import luan.LuanTable;
31 //import luan.LuanPropertyMeta;
32 import luan.LuanCloner;
33 import luan.modules.PackageLuan;
34 import luan.modules.IoLuan;
35 import luan.modules.TableLuan;
36 import luan.modules.Utils;
37 import luan.modules.url.LuanUrl;
38
39
40 public final class HttpServicer {
41 private static final Logger logger = LoggerFactory.getLogger(HttpServicer.class);
42
43 public static boolean service(LuanState luan,HttpServletRequest request,HttpServletResponse response,String modName)
44 throws LuanException
45 {
46 LuanFunction fn;
47 synchronized(luan) {
48 PackageLuan.enableLoad(luan,"luan:http/Http.luan",modName);
49 LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan");
50 LuanTable per_session_pages = (LuanTable)module.rawGet("per_session_pages");
51 Object mod = PackageLuan.load(luan,modName);
52 if( mod.equals(Boolean.FALSE) )
53 return false;
54 if( !(mod instanceof LuanFunction) )
55 throw new LuanException( "module '"+modName+"' must return a function" );
56 if( Boolean.TRUE.equals(per_session_pages.rawGet(mod)) ) {
57 HttpSession session = request.getSession();
58 LuanState sessionLuan = (LuanState)session.getAttribute("luan");
59 if( sessionLuan!=null ) {
60 luan = sessionLuan;
61 } else {
62 LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
63 luan = (LuanState)cloner.clone(luan);
64 session.setAttribute("luan",luan);
65 }
66 fn = (LuanFunction)PackageLuan.require(luan,modName);
67 } else {
68 LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL);
69 luan = (LuanState)cloner.clone(luan);
70 fn = (LuanFunction)cloner.get(mod);
71 }
72 }
73
74 LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan");
75
76 // request
77 LuanFunction newRequestFn = (LuanFunction)module.rawGet("new_request");
78 LuanTable requestTbl = (LuanTable)newRequestFn.call(luan);
79 module.rawPut("request",requestTbl);
80 requestTbl.rawPut("java",request);
81 requestTbl.rawPut("method",request.getMethod());
82 requestTbl.rawPut("path",request.getRequestURI());
83 requestTbl.rawPut("protocol",request.getProtocol());
84 requestTbl.rawPut("scheme",request.getScheme());
85 requestTbl.rawPut("port",request.getServerPort());
86
87 LuanTable headersTbl = (LuanTable)requestTbl.rawGet("headers");
88 for( Enumeration<String> enKeys = request.getHeaderNames(); enKeys.hasMoreElements(); ) {
89 String key = enKeys.nextElement();
90 LuanTable values = new LuanTable();
91 for( Enumeration<String> en = request.getHeaders(key); en.hasMoreElements(); ) {
92 values.rawPut(values.rawLength()+1,en.nextElement());
93 }
94 key = toLuanHeaderName(key);
95 headersTbl.rawPut(key,values);
96 }
97
98 LuanTable parametersTbl = (LuanTable)requestTbl.rawGet("parameters");
99 String contentType = request.getContentType();
100 if( contentType==null || !contentType.startsWith("multipart/form-data") ) {
101 for( Map.Entry<String,String[]> entry : request.getParameterMap().entrySet() ) {
102 parametersTbl.rawPut(entry.getKey(),new LuanTable(Arrays.asList(entry.getValue())));
103 }
104 } else { // multipart
105 try {
106 InputStream in = new BufferedInputStream(request.getInputStream());
107 final MultiPartInputStream mpis = new MultiPartInputStream(in,contentType,null,null);
108 mpis.setDeleteOnExit(true);
109 for( Part p : mpis.getParts() ) {
110 final MultiPartInputStream.MultiPart part = (MultiPartInputStream.MultiPart)p;
111 String name = part.getName();
112 /*
113 System.out.println("name = "+name);
114 System.out.println("getContentType = "+part.getContentType());
115 System.out.println("getHeaderNames = "+part.getHeaderNames());
116 System.out.println("content-disposition = "+part.getHeader("content-disposition"));
117 System.out.println();
118 */
119 Object value;
120 String filename = part.getContentDispositionFilename();
121 if( filename == null ) {
122 value = new String(part.getBytes());
123 } else {
124 /*
125 LuanTable partTbl = LuanPropertyMeta.INSTANCE.newTable();
126 partTbl.rawPut("filename",filename);
127 partTbl.rawPut("content_type",part.getContentType());
128 LuanPropertyMeta.INSTANCE.getters(partTbl).rawPut( "content", new LuanFunction() {
129 @Override public Object call(LuanState luan,Object[] args) throws LuanException {
130 try {
131 InputStream in = part.getInputStream();
132 byte[] content = Utils.readAll(in);
133 in.close();
134 return content;
135 } catch(IOException e) {
136 throw new RuntimeException(e);
137 }
138 }
139 } );
140 */
141 LuanTable partTbl = new LuanTable();
142 partTbl.rawPut("filename",filename);
143 partTbl.rawPut("content_type",part.getContentType());
144 LuanTable mt = new LuanTable();
145 partTbl.setMetatable(mt);
146 mt.rawPut( "__index", new LuanFunction() {
147 @Override public Object call(LuanState luan,Object[] args) throws LuanException {
148 Object key = args[1];
149 if( "content".equals(key) ) {
150 try {
151 InputStream in = part.getInputStream();
152 byte[] content = Utils.readAll(in);
153 in.close();
154 return content;
155 } catch(IOException e) {
156 throw new RuntimeException(e);
157 }
158 }
159 return null;
160 }
161 } );
162 value = partTbl;
163 }
164 LuanTable list = (LuanTable)parametersTbl.rawGet(name);
165 if( list == null ) {
166 list = new LuanTable();
167 parametersTbl.rawPut(name,list);
168 }
169 list.rawPut(parametersTbl.rawLength()+1,value);
170 }
171 } catch(IOException e) {
172 throw new RuntimeException(e);
173 } catch(ServletException e) {
174 throw new RuntimeException(e);
175 }
176 }
177
178 LuanTable cookieTbl = (LuanTable)requestTbl.rawGet("cookie");
179 for( Cookie cookie : request.getCookies() ) {
180 cookieTbl.rawPut( cookie.getName(), unescape(cookie.getValue()) );
181 }
182
183
184 // response
185 LuanTable responseTbl = new LuanTable();
186 responseTbl.rawPut("java",response);
187 LuanFunction newResponseFn = (LuanFunction)module.rawGet("new_response");
188 newResponseFn.call( luan, new Object[]{responseTbl} );
189 module.rawPut("response",responseTbl);
190
191 fn.call(luan);
192 handle_run_later(luan);
193 return true;
194 }
195
196 public static void setResponse(LuanTable responseTbl,HttpServletResponse response) throws LuanException {
197 int status = Luan.asInteger(responseTbl.rawGet("status"));
198 response.setStatus(status);
199 LuanTable responseHeaders = (LuanTable)responseTbl.rawGet("headers");
200 for( Map.Entry<Object,Object> entry : responseHeaders.rawIterable() ) {
201 String name = (String)entry.getKey();
202 name = toHttpHeaderName(name);
203 LuanTable values = (LuanTable)entry.getValue();
204 for( Object value : values.asList() ) {
205 if( value instanceof String ) {
206 response.setHeader(name,(String)value);
207 continue;
208 }
209 Integer i = Luan.asInteger(value);
210 if( i != null ) {
211 response.setIntHeader(name,i);
212 continue;
213 }
214 throw new IllegalArgumentException("value must be string or integer for headers table");
215 }
216 }
217 }
218
219
220
221 // static utils
222
223 public static String toLuanHeaderName(String httpName) {
224 return httpName.toLowerCase().replace('-','_');
225 }
226
227 public static String toHttpHeaderName(String luanName) {
228 /*
229 StringBuilder buf = new StringBuilder();
230 boolean capitalize = true;
231 char[] a = luanName.toCharArray();
232 for( int i=0; i<a.length; i++ ) {
233 char c = a[i];
234 if( c == '_' ) {
235 a[i] = '-';
236 capitalize = true;
237 } else if( capitalize ) {
238 a[i] = Character.toUpperCase(c);
239 capitalize = false;
240 }
241 }
242 return String.valueOf(a);
243 */
244 return LuanUrl.toHttpHeaderName(luanName);
245 }
246
247 private static String escape(String value) {
248 return value.replaceAll(";", "%3B");
249 }
250
251 private static String unescape(String value) {
252 return value.replaceAll("%3B", ";");
253 }
254
255 private static Cookie getCookie(HttpServletRequest request,String name) {
256 Cookie[] cookies = request.getCookies();
257 if( cookies == null )
258 return null;
259 for (Cookie cookie : cookies) {
260 if (cookie.getName().equals(name))
261 return cookie;
262 }
263 return null;
264 }
265
266 public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) {
267 Cookie cookie = getCookie(request,name);
268 if( cookie==null || !cookie.getValue().equals(value) ) {
269 cookie = new Cookie(name, escape(value));
270 cookie.setPath("/");
271 if (domain != null && domain.length() > 0)
272 cookie.setDomain(domain);
273 if( isPersistent )
274 cookie.setMaxAge(10000000);
275 response.addCookie(cookie);
276 }
277 }
278
279 public static void removeCookie(HttpServletRequest request,
280 HttpServletResponse response,
281 String name,
282 String domain
283 ) {
284 Cookie cookie = getCookie(request, name);
285 if(cookie != null) {
286 Cookie delCookie = new Cookie(name, "delete");
287 delCookie.setPath("/");
288 delCookie.setMaxAge(0);
289 if (domain != null && domain.length() > 0)
290 delCookie.setDomain(domain);
291 response.addCookie(delCookie);
292 }
293 }
294
295
296
297 private static String RUN_LATER_KEY = "Http.run_later";
298 private static final Executor exec = Executors.newSingleThreadExecutor();
299
300 public static void run_later(final LuanState luan,final LuanFunction fn,final Object... args) {
301 List list = (List)luan.registry().get(RUN_LATER_KEY);
302 if( list == null ) {
303 list = new ArrayList();
304 luan.registry().put(RUN_LATER_KEY,list);
305 }
306 list.add(
307 new Runnable(){public void run() {
308 try {
309 fn.call(luan,args);
310 } catch(LuanException e) {
311 e.printStackTrace();
312 }
313 }}
314 );
315 }
316
317 private static void handle_run_later(LuanState luan) {
318 List list = (List)luan.registry().get(RUN_LATER_KEY);
319 if( list==null )
320 return;
321 for( Object obj : list ) {
322 exec.execute((Runnable)obj);
323 }
324 }
325 }