Mercurial Hosting > luan
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/jetty/HttpServicer.java Mon Jan 29 17:50:49 2018 -0700 @@ -0,0 +1,325 @@ +package luan.modules.http.jetty; + +import java.io.InputStream; +import java.io.BufferedInputStream; +import java.io.PrintWriter; +import java.io.IOException; +import java.util.Map; +import java.util.Set; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Enumeration; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.Part; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.eclipse.jetty.util.MultiPartInputStream; +import luan.Luan; +import luan.LuanState; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanTable; +//import luan.LuanPropertyMeta; +import luan.LuanCloner; +import luan.modules.PackageLuan; +import luan.modules.IoLuan; +import luan.modules.TableLuan; +import luan.modules.Utils; +import luan.modules.url.LuanUrl; + + +public final class HttpServicer { + private static final Logger logger = LoggerFactory.getLogger(HttpServicer.class); + + public static boolean service(LuanState luan,HttpServletRequest request,HttpServletResponse response,String modName) + throws LuanException + { + LuanFunction fn; + synchronized(luan) { + PackageLuan.enableLoad(luan,"luan:http/Http.luan",modName); + LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan"); + LuanTable per_session_pages = (LuanTable)module.rawGet("per_session_pages"); + Object mod = PackageLuan.load(luan,modName); + if( mod.equals(Boolean.FALSE) ) + return false; + if( !(mod instanceof LuanFunction) ) + throw new LuanException( "module '"+modName+"' must return a function" ); + if( Boolean.TRUE.equals(per_session_pages.rawGet(mod)) ) { + HttpSession session = request.getSession(); + LuanState sessionLuan = (LuanState)session.getAttribute("luan"); + if( sessionLuan!=null ) { + luan = sessionLuan; + } else { + LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE); + luan = (LuanState)cloner.clone(luan); + session.setAttribute("luan",luan); + } + fn = (LuanFunction)PackageLuan.require(luan,modName); + } else { + LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL); + luan = (LuanState)cloner.clone(luan); + fn = (LuanFunction)cloner.get(mod); + } + } + + LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan"); + + // request + LuanFunction newRequestFn = (LuanFunction)module.rawGet("new_request"); + LuanTable requestTbl = (LuanTable)newRequestFn.call(luan); + module.rawPut("request",requestTbl); + requestTbl.rawPut("java",request); + requestTbl.rawPut("method",request.getMethod()); + requestTbl.rawPut("path",request.getRequestURI()); + requestTbl.rawPut("protocol",request.getProtocol()); + requestTbl.rawPut("scheme",request.getScheme()); + requestTbl.rawPut("port",request.getServerPort()); + + LuanTable headersTbl = (LuanTable)requestTbl.rawGet("headers"); + for( Enumeration<String> enKeys = request.getHeaderNames(); enKeys.hasMoreElements(); ) { + String key = enKeys.nextElement(); + LuanTable values = new LuanTable(); + for( Enumeration<String> en = request.getHeaders(key); en.hasMoreElements(); ) { + values.rawPut(values.rawLength()+1,en.nextElement()); + } + key = toLuanHeaderName(key); + headersTbl.rawPut(key,values); + } + + LuanTable parametersTbl = (LuanTable)requestTbl.rawGet("parameters"); + String contentType = request.getContentType(); + if( contentType==null || !contentType.startsWith("multipart/form-data") ) { + for( Map.Entry<String,String[]> entry : request.getParameterMap().entrySet() ) { + parametersTbl.rawPut(entry.getKey(),new LuanTable(Arrays.asList(entry.getValue()))); + } + } else { // multipart + try { + InputStream in = new BufferedInputStream(request.getInputStream()); + final MultiPartInputStream mpis = new MultiPartInputStream(in,contentType,null,null); + mpis.setDeleteOnExit(true); + for( Part p : mpis.getParts() ) { + final MultiPartInputStream.MultiPart part = (MultiPartInputStream.MultiPart)p; + String name = part.getName(); +/* +System.out.println("name = "+name); +System.out.println("getContentType = "+part.getContentType()); +System.out.println("getHeaderNames = "+part.getHeaderNames()); +System.out.println("content-disposition = "+part.getHeader("content-disposition")); +System.out.println(); +*/ + Object value; + String filename = part.getContentDispositionFilename(); + if( filename == null ) { + value = new String(part.getBytes()); + } else { +/* + LuanTable partTbl = LuanPropertyMeta.INSTANCE.newTable(); + partTbl.rawPut("filename",filename); + partTbl.rawPut("content_type",part.getContentType()); + LuanPropertyMeta.INSTANCE.getters(partTbl).rawPut( "content", new LuanFunction() { + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + InputStream in = part.getInputStream(); + byte[] content = Utils.readAll(in); + in.close(); + return content; + } catch(IOException e) { + throw new RuntimeException(e); + } + } + } ); +*/ + LuanTable partTbl = new LuanTable(); + partTbl.rawPut("filename",filename); + partTbl.rawPut("content_type",part.getContentType()); + LuanTable mt = new LuanTable(); + partTbl.setMetatable(mt); + mt.rawPut( "__index", new LuanFunction() { + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + Object key = args[1]; + if( "content".equals(key) ) { + try { + InputStream in = part.getInputStream(); + byte[] content = Utils.readAll(in); + in.close(); + return content; + } catch(IOException e) { + throw new RuntimeException(e); + } + } + return null; + } + } ); + value = partTbl; + } + LuanTable list = (LuanTable)parametersTbl.rawGet(name); + if( list == null ) { + list = new LuanTable(); + parametersTbl.rawPut(name,list); + } + list.rawPut(parametersTbl.rawLength()+1,value); + } + } catch(IOException e) { + throw new RuntimeException(e); + } catch(ServletException e) { + throw new RuntimeException(e); + } + } + + LuanTable cookieTbl = (LuanTable)requestTbl.rawGet("cookie"); + for( Cookie cookie : request.getCookies() ) { + cookieTbl.rawPut( cookie.getName(), unescape(cookie.getValue()) ); + } + + + // response + LuanTable responseTbl = new LuanTable(); + responseTbl.rawPut("java",response); + LuanFunction newResponseFn = (LuanFunction)module.rawGet("new_response"); + newResponseFn.call( luan, new Object[]{responseTbl} ); + module.rawPut("response",responseTbl); + + fn.call(luan); + handle_run_later(luan); + return true; + } + + public static void setResponse(LuanTable responseTbl,HttpServletResponse response) throws LuanException { + int status = Luan.asInteger(responseTbl.rawGet("status")); + response.setStatus(status); + LuanTable responseHeaders = (LuanTable)responseTbl.rawGet("headers"); + for( Map.Entry<Object,Object> entry : responseHeaders.rawIterable() ) { + String name = (String)entry.getKey(); + name = toHttpHeaderName(name); + LuanTable values = (LuanTable)entry.getValue(); + for( Object value : values.asList() ) { + if( value instanceof String ) { + response.setHeader(name,(String)value); + continue; + } + Integer i = Luan.asInteger(value); + if( i != null ) { + response.setIntHeader(name,i); + continue; + } + throw new IllegalArgumentException("value must be string or integer for headers table"); + } + } + } + + + + // static utils + + public static String toLuanHeaderName(String httpName) { + return httpName.toLowerCase().replace('-','_'); + } + + public static String toHttpHeaderName(String luanName) { +/* + StringBuilder buf = new StringBuilder(); + boolean capitalize = true; + char[] a = luanName.toCharArray(); + for( int i=0; i<a.length; i++ ) { + char c = a[i]; + if( c == '_' ) { + a[i] = '-'; + capitalize = true; + } else if( capitalize ) { + a[i] = Character.toUpperCase(c); + capitalize = false; + } + } + return String.valueOf(a); +*/ + return LuanUrl.toHttpHeaderName(luanName); + } + + private static String escape(String value) { + return value.replaceAll(";", "%3B"); + } + + private static String unescape(String value) { + return value.replaceAll("%3B", ";"); + } + + private static Cookie getCookie(HttpServletRequest request,String name) { + Cookie[] cookies = request.getCookies(); + if( cookies == null ) + return null; + for (Cookie cookie : cookies) { + if (cookie.getName().equals(name)) + return cookie; + } + return null; + } + + public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) { + Cookie cookie = getCookie(request,name); + if( cookie==null || !cookie.getValue().equals(value) ) { + cookie = new Cookie(name, escape(value)); + cookie.setPath("/"); + if (domain != null && domain.length() > 0) + cookie.setDomain(domain); + if( isPersistent ) + cookie.setMaxAge(10000000); + response.addCookie(cookie); + } + } + + public static void removeCookie(HttpServletRequest request, + HttpServletResponse response, + String name, + String domain + ) { + Cookie cookie = getCookie(request, name); + if(cookie != null) { + Cookie delCookie = new Cookie(name, "delete"); + delCookie.setPath("/"); + delCookie.setMaxAge(0); + if (domain != null && domain.length() > 0) + delCookie.setDomain(domain); + response.addCookie(delCookie); + } + } + + + + private static String RUN_LATER_KEY = "Http.run_later"; + private static final Executor exec = Executors.newSingleThreadExecutor(); + + public static void run_later(final LuanState luan,final LuanFunction fn,final Object... args) { + List list = (List)luan.registry().get(RUN_LATER_KEY); + if( list == null ) { + list = new ArrayList(); + luan.registry().put(RUN_LATER_KEY,list); + } + list.add( + new Runnable(){public void run() { + try { + fn.call(luan,args); + } catch(LuanException e) { + e.printStackTrace(); + } + }} + ); + } + + private static void handle_run_later(LuanState luan) { + List list = (List)luan.registry().get(RUN_LATER_KEY); + if( list==null ) + return; + for( Object obj : list ) { + exec.execute((Runnable)obj); + } + } +}