Mercurial Hosting > luan
comparison web/src/luan/modules/web/HttpServicer.java @ 251:705d14f4d8ee
start web testing
git-svn-id: https://luan-java.googlecode.com/svn/trunk@252 21e917c8-12df-6dd8-5cb6-c86387c605b9
author | fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9> |
---|---|
date | Sun, 19 Oct 2014 03:38:47 +0000 |
parents | web/src/luan/modules/web/HttpLuan.java@9737ebb9aaa0 |
children | 9e0d4452e649 |
comparison
equal
deleted
inserted
replaced
250:2b6f51d7af40 | 251:705d14f4d8ee |
---|---|
1 package luan.modules.web; | |
2 | |
3 import java.io.PrintWriter; | |
4 import java.io.IOException; | |
5 import java.util.Map; | |
6 import java.util.AbstractMap; | |
7 import java.util.Set; | |
8 import java.util.Arrays; | |
9 import java.util.Iterator; | |
10 import java.util.Enumeration; | |
11 import javax.servlet.ServletOutputStream; | |
12 import javax.servlet.http.Cookie; | |
13 import javax.servlet.http.HttpServletRequest; | |
14 import javax.servlet.http.HttpServletResponse; | |
15 import javax.servlet.http.HttpSession; | |
16 import luan.Luan; | |
17 import luan.LuanState; | |
18 import luan.LuanFunction; | |
19 import luan.LuanElement; | |
20 import luan.LuanException; | |
21 import luan.LuanTable; | |
22 import luan.AbstractLuanTable; | |
23 import luan.LuanJavaFunction; | |
24 import luan.LuanExitException; | |
25 import luan.LuanProperty; | |
26 import luan.DeepCloner; | |
27 import luan.modules.PackageLuan; | |
28 import luan.modules.IoLuan; | |
29 import luan.modules.TableLuan; | |
30 | |
31 | |
32 public final class HttpServicer { | |
33 | |
34 public static boolean service(LuanState luan,HttpServletRequest request,HttpServletResponse response,String modName) | |
35 throws LuanException | |
36 { | |
37 LuanFunction fn; | |
38 synchronized(luan) { | |
39 Object mod = PackageLuan.load(luan,modName); | |
40 if( mod==null ) | |
41 return false; | |
42 if( !(mod instanceof LuanTable) ) | |
43 throw luan.exception( "module '"+modName+"' must return a table" ); | |
44 LuanTable tbl = (LuanTable)mod; | |
45 if( Luan.toBoolean( tbl.get("per_session") ) ) { | |
46 HttpSession session = request.getSession(); | |
47 LuanState sessionLuan = (LuanState)session.getValue("luan"); | |
48 if( sessionLuan!=null ) { | |
49 luan = sessionLuan; | |
50 } else { | |
51 DeepCloner cloner = new DeepCloner(); | |
52 luan = cloner.deepClone(luan); | |
53 session.putValue("luan",luan); | |
54 } | |
55 tbl = (LuanTable)PackageLuan.require(luan,modName); | |
56 fn = (LuanFunction)tbl.get("service"); | |
57 } else { | |
58 fn = (LuanFunction)tbl.get("service"); | |
59 if( fn == null ) | |
60 throw luan.exception( "function 'service' is not defined" ); | |
61 DeepCloner cloner = new DeepCloner(); | |
62 luan = cloner.deepClone(luan); | |
63 fn = cloner.get(fn); | |
64 } | |
65 } | |
66 | |
67 LuanTable module = (LuanTable)PackageLuan.loaded(luan).get("web/Http"); | |
68 if( module == null ) | |
69 throw luan.exception( "module 'web/Http' not defined" ); | |
70 HttpServicer lib = new HttpServicer(request,response); | |
71 try { | |
72 module.put( "request", lib.requestTable() ); | |
73 module.put( "response", lib.responseTable() ); | |
74 module.put( "session", lib.sessionTable() ); | |
75 /* | |
76 module.put( "write", new LuanJavaFunction( | |
77 HttpServicer.class.getMethod( "text_write", LuanState.class, new Object[0].getClass() ), lib | |
78 ) ); | |
79 */ | |
80 } catch(NoSuchMethodException e) { | |
81 throw new RuntimeException(e); | |
82 } | |
83 | |
84 try { | |
85 luan.call(fn,"<http>"); | |
86 } catch(LuanExitException e) { | |
87 // System.out.println("caught LuanExitException"); | |
88 } | |
89 return true; | |
90 } | |
91 | |
92 | |
93 | |
94 private final HttpServletRequest request; | |
95 private final HttpServletResponse response; | |
96 // private PrintWriter writer = null; | |
97 // private ServletOutputStream sos = null; | |
98 | |
99 private HttpServicer(HttpServletRequest request,HttpServletResponse response) { | |
100 this.request = request; | |
101 this.response = response; | |
102 } | |
103 | |
104 private LuanTable requestTable() throws NoSuchMethodException { | |
105 LuanTable tbl = Luan.newPropertyTable(); | |
106 tbl.put("java",request); | |
107 LuanTable parameters = new NameTable() { | |
108 | |
109 @Override Object get(String name) { | |
110 return request.getParameter(name); | |
111 } | |
112 | |
113 @Override Iterator<String> names() { | |
114 return new EnumerationIterator(request.getParameterNames()); | |
115 } | |
116 | |
117 @Override protected String type() { | |
118 return "request.parameters-table"; | |
119 } | |
120 }; | |
121 tbl.put( "parameters", parameters ); | |
122 add( tbl, "get_parameter_values", String.class ); | |
123 LuanTable headers = new NameTable() { | |
124 | |
125 @Override Object get(String name) { | |
126 return request.getHeader(name); | |
127 } | |
128 | |
129 @Override Iterator<String> names() { | |
130 return new EnumerationIterator(request.getHeaderNames()); | |
131 } | |
132 | |
133 @Override protected String type() { | |
134 return "request.headers-table"; | |
135 } | |
136 }; | |
137 tbl.put( "headers", headers ); | |
138 tbl.put( "method", new LuanProperty() { public Object get() { | |
139 return request.getMethod(); | |
140 } } ); | |
141 tbl.put( "servlet_path", new LuanProperty() { public Object get() { | |
142 return request.getServletPath(); | |
143 } } ); | |
144 tbl.put( "server_name", new LuanProperty() { public Object get() { | |
145 return request.getServerName(); | |
146 } } ); | |
147 tbl.put( "current_url", new LuanProperty() { public Object get() { | |
148 return getCurrentURL(request); | |
149 } } ); | |
150 tbl.put( "remote_address", new LuanProperty() { public Object get() { | |
151 return request.getRemoteAddr(); | |
152 } } ); | |
153 LuanTable cookies = new AbstractLuanTable() { | |
154 | |
155 @Override public final Object get(Object key) { | |
156 if( !(key instanceof String) ) | |
157 return null; | |
158 String name = (String)key; | |
159 return getCookieValue(request,name); | |
160 } | |
161 | |
162 @Override public final Iterator<Map.Entry<Object,Object>> iterator() { | |
163 return new Iterator<Map.Entry<Object,Object>>() { | |
164 final Cookie[] cookies = request.getCookies(); | |
165 int i = 0; | |
166 | |
167 @Override public boolean hasNext() { | |
168 return i < cookies.length; | |
169 } | |
170 @Override public Map.Entry<Object,Object> next() { | |
171 Cookie cookie = cookies[i++]; | |
172 String name = cookie.getName(); | |
173 Object val = unescape(cookie.getValue()); | |
174 return new AbstractMap.SimpleEntry<Object,Object>(name,val); | |
175 } | |
176 @Override public void remove() { | |
177 throw new UnsupportedOperationException(); | |
178 } | |
179 }; | |
180 } | |
181 | |
182 @Override protected String type() { | |
183 return "request.cookies-table"; | |
184 } | |
185 }; | |
186 tbl.put( "cookies", cookies ); | |
187 return tbl; | |
188 } | |
189 | |
190 private LuanTable responseTable() throws NoSuchMethodException { | |
191 LuanTable tbl = Luan.newPropertyTable(); | |
192 tbl.put("java",response); | |
193 add( tbl, "send_redirect", String.class ); | |
194 add( tbl, "send_error", Integer.TYPE, String.class ); | |
195 LuanTable headers = new NameTable() { | |
196 | |
197 @Override Object get(String name) { | |
198 return response.getHeader(name); | |
199 } | |
200 | |
201 @Override Iterator<String> names() { | |
202 return response.getHeaderNames().iterator(); | |
203 } | |
204 | |
205 @Override public void put(Object key,Object val) { | |
206 if( !(key instanceof String) ) | |
207 throw new IllegalArgumentException("key must be string for headers table"); | |
208 String name = (String)key; | |
209 if( val instanceof String ) { | |
210 response.setHeader(name,(String)val); | |
211 return; | |
212 } | |
213 Integer i = Luan.asInteger(val); | |
214 if( i != null ) { | |
215 response.setIntHeader(name,i); | |
216 return; | |
217 } | |
218 throw new IllegalArgumentException("value must be string or integer for headers table"); | |
219 } | |
220 | |
221 @Override protected String type() { | |
222 return "response.headers-table"; | |
223 } | |
224 }; | |
225 tbl.put( "headers", headers ); | |
226 tbl.put( "content_type", new LuanProperty() { | |
227 @Override public Object get() { | |
228 return response.getContentType(); | |
229 } | |
230 @Override public boolean set(Object value) { | |
231 response.setContentType(string(value)); return true; | |
232 } | |
233 } ); | |
234 tbl.put( "character_encoding", new LuanProperty() { | |
235 @Override public Object get() { | |
236 return response.getCharacterEncoding(); | |
237 } | |
238 @Override public boolean set(Object value) { | |
239 response.setCharacterEncoding(string(value)); return true; | |
240 } | |
241 } ); | |
242 add( tbl, "text_writer" ); | |
243 add( tbl, "set_cookie", String.class, String.class, Boolean.TYPE, String.class ); | |
244 add( tbl, "remove_cookie", String.class, String.class ); | |
245 return tbl; | |
246 } | |
247 | |
248 private LuanTable sessionTable() throws NoSuchMethodException { | |
249 LuanTable tbl = Luan.newTable(); | |
250 LuanTable attributes = new NameTable() { | |
251 | |
252 @Override Object get(String name) { | |
253 return request.getSession().getAttribute(name); | |
254 } | |
255 | |
256 @Override Iterator<String> names() { | |
257 return new EnumerationIterator(request.getSession().getAttributeNames()); | |
258 } | |
259 | |
260 @Override public void put(Object key,Object val) { | |
261 if( !(key instanceof String) ) | |
262 throw new IllegalArgumentException("key must be string for session attributes table"); | |
263 String name = (String)key; | |
264 request.getSession().setAttribute(name,val); | |
265 } | |
266 | |
267 @Override protected String type() { | |
268 return "session.attributes-table"; | |
269 } | |
270 }; | |
271 tbl.put( "attributes", attributes ); | |
272 return tbl; | |
273 } | |
274 | |
275 private void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException { | |
276 t.put( method, new LuanJavaFunction(HttpServicer.class.getMethod(method,parameterTypes),this) ); | |
277 } | |
278 /* | |
279 public void text_write(LuanState luan,Object... args) throws LuanException, IOException { | |
280 if( writer == null ) | |
281 writer = response.getWriter(); | |
282 for( Object obj : args ) { | |
283 writer.print( luan.toString(obj) ); | |
284 } | |
285 } | |
286 */ | |
287 public LuanTable text_writer() throws IOException { | |
288 return IoLuan.textWriter(response.getWriter()); | |
289 } | |
290 | |
291 public LuanTable get_parameter_values(String name) { | |
292 Object[] a = request.getParameterValues(name); | |
293 return a==null ? null : TableLuan.pack(a); | |
294 } | |
295 | |
296 public void send_redirect(String redirectUrl) | |
297 throws IOException | |
298 { | |
299 response.sendRedirect(redirectUrl); | |
300 throw new LuanExitException(); | |
301 } | |
302 | |
303 public void send_error(int code,String text) | |
304 throws IOException | |
305 { | |
306 response.sendError(code, text); | |
307 throw new LuanExitException(); | |
308 } | |
309 | |
310 public void set_cookie(String name,String value,boolean isPersistent, String domain) { | |
311 setCookie(request,response,name,value,isPersistent,domain); | |
312 } | |
313 | |
314 public void remove_cookie(String name, String domain) { | |
315 removeCookie(request,response,name,domain); | |
316 } | |
317 | |
318 | |
319 // static utils | |
320 | |
321 public static String getQueryString(HttpServletRequest request) { | |
322 return getQueryString(request,0); | |
323 } | |
324 | |
325 public static String getQueryString(HttpServletRequest request,int maxValueLen) { | |
326 String method = request.getMethod(); | |
327 if( method.equals("GET") ) | |
328 return request.getQueryString(); | |
329 if( !method.equals("POST") && !method.equals("HEAD") ) | |
330 throw new RuntimeException(method); | |
331 Enumeration en = request.getParameterNames(); | |
332 StringBuilder queryBuf = new StringBuilder(); | |
333 if( !en.hasMoreElements() ) | |
334 return null; | |
335 do { | |
336 String param = (String)en.nextElement(); | |
337 String value = request.getParameter(param); | |
338 if( maxValueLen > 0 ) { | |
339 int len = value.length(); | |
340 if( len > maxValueLen ) | |
341 value = value.substring(0,maxValueLen) + "..." + (len-maxValueLen); | |
342 } | |
343 queryBuf.append(param); | |
344 queryBuf.append('='); | |
345 queryBuf.append(value); | |
346 queryBuf.append('&'); | |
347 } while( en.hasMoreElements() ); | |
348 queryBuf.deleteCharAt(queryBuf.length() - 1); | |
349 return queryBuf.toString(); | |
350 } | |
351 | |
352 public static String getCurrentURL(HttpServletRequest request) { | |
353 return getCurrentURL(request,0); | |
354 } | |
355 | |
356 public static String getCurrentURL(HttpServletRequest request,int maxValueLen) { | |
357 // StringBuffer buf = HttpUtils.getRequestURL(request); | |
358 StringBuffer buf = request.getRequestURL(); | |
359 String qStr = getQueryString(request,maxValueLen); | |
360 if(qStr != null && qStr.length() > 0) { | |
361 buf.append('?'); | |
362 buf.append(qStr); | |
363 } | |
364 return buf.toString(); | |
365 } | |
366 | |
367 private static String escape(String value) { | |
368 return value.replaceAll(";", "%3B"); | |
369 } | |
370 | |
371 private static String unescape(String value) { | |
372 return value.replaceAll("%3B", ";"); | |
373 } | |
374 | |
375 private static Cookie getCookie(HttpServletRequest request,String name) { | |
376 Cookie[] cookies = request.getCookies(); | |
377 if( cookies == null ) | |
378 return null; | |
379 for (Cookie cookie : cookies) { | |
380 if (cookie.getName().equals(name)) | |
381 return cookie; | |
382 } | |
383 return null; | |
384 } | |
385 | |
386 public static String getCookieValue(HttpServletRequest request,String name) { | |
387 Cookie cookie = getCookie(request,name); | |
388 return cookie==null ? null : unescape(cookie.getValue()); | |
389 } | |
390 | |
391 public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) { | |
392 Cookie cookie = getCookie(request,name); | |
393 if( cookie==null || !cookie.getValue().equals(value) ) { | |
394 cookie = new Cookie(name, escape(value)); | |
395 cookie.setPath("/"); | |
396 if (domain != null && domain.length() > 0) | |
397 cookie.setDomain(domain); | |
398 if( isPersistent ) | |
399 cookie.setMaxAge(10000000); | |
400 response.addCookie(cookie); | |
401 } | |
402 } | |
403 | |
404 public static void removeCookie(HttpServletRequest request, | |
405 HttpServletResponse response, | |
406 String name, | |
407 String domain | |
408 ) { | |
409 Cookie cookie = getCookie(request, name); | |
410 if(cookie != null) { | |
411 Cookie delCookie = new Cookie(name, "delete"); | |
412 delCookie.setPath("/"); | |
413 delCookie.setMaxAge(0); | |
414 if (domain != null && domain.length() > 0) | |
415 delCookie.setDomain(domain); | |
416 response.addCookie(delCookie); | |
417 } | |
418 } | |
419 | |
420 | |
421 | |
422 // util classes | |
423 | |
424 static final class EnumerationIterator<E> implements Iterator<E> { | |
425 private final Enumeration<E> en; | |
426 | |
427 EnumerationIterator(Enumeration<E> en) { | |
428 this.en = en; | |
429 } | |
430 | |
431 @Override public boolean hasNext() { | |
432 return en.hasMoreElements(); | |
433 } | |
434 | |
435 @Override public E next() { | |
436 return en.nextElement(); | |
437 } | |
438 | |
439 @Override public void remove() { | |
440 throw new UnsupportedOperationException(); | |
441 } | |
442 } | |
443 | |
444 private static abstract class NameTable extends AbstractLuanTable { | |
445 abstract Object get(String name); | |
446 abstract Iterator<String> names(); | |
447 | |
448 @Override public final Object get(Object key) { | |
449 if( !(key instanceof String) ) | |
450 return null; | |
451 String name = (String)key; | |
452 return get(name); | |
453 } | |
454 | |
455 @Override public final Iterator<Map.Entry<Object,Object>> iterator() { | |
456 return new Iterator<Map.Entry<Object,Object>>() { | |
457 Iterator<String> names = names(); | |
458 | |
459 @Override public boolean hasNext() { | |
460 return names.hasNext(); | |
461 } | |
462 @Override public Map.Entry<Object,Object> next() { | |
463 String name = names.next(); | |
464 Object val = get(name); | |
465 return new AbstractMap.SimpleEntry<Object,Object>(name,val); | |
466 } | |
467 @Override public void remove() { | |
468 throw new UnsupportedOperationException(); | |
469 } | |
470 }; | |
471 } | |
472 }; | |
473 | |
474 private static String string(Object value) { | |
475 if( !(value instanceof String) ) | |
476 throw new IllegalArgumentException("value must be string"); | |
477 return (String)value; | |
478 } | |
479 } |