Mercurial Hosting > luan
view src/luan/modules/url/LuanUrl.java @ 1288:8432d20a2729
minor
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 31 Dec 2018 18:39:04 -0700 |
parents | 781ec0a92bb5 |
children | 5763597ca5c0 |
line wrap: on
line source
package luan.modules.url; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLConnection; import java.net.HttpURLConnection; import java.net.URLEncoder; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.Base64; import luan.Luan; import luan.LuanState; import luan.LuanTable; import luan.LuanJavaFunction; import luan.LuanException; import luan.modules.IoLuan; import luan.modules.Utils; public final class LuanUrl extends IoLuan.LuanIn { private static enum Method { GET, POST, DELETE } private URL url; private Method method = Method.GET; private Map headers; private String content = ""; private MultipartClient multipart = null; private int timeout = 0; public LuanUrl(URL url,LuanTable options) throws LuanException { this.url = url; if( options != null ) { Map map = options.asMap(); String methodStr = getString(map,"method"); if( methodStr != null ) { methodStr = methodStr.toUpperCase(); try { this.method = Method.valueOf(methodStr); } catch(IllegalArgumentException e) { throw new LuanException( "invalid method: "+methodStr ); } } Map headerMap = getMap(map,"headers"); if( headerMap != null ) { headers = new HashMap(); for( Object hack : headerMap.entrySet() ) { Map.Entry entry = (Map.Entry)hack; String name = (String)entry.getKey(); Object val = entry.getValue(); if( val instanceof String ) { headers.put(name,val); } else { if( !(val instanceof LuanTable) ) throw new LuanException( "header '"+name+"' must be string or table" ); LuanTable t = (LuanTable)val; if( !t.isList() ) throw new LuanException( "header '"+name+"' table must be list" ); headers.put(name,t.asList()); } } } Map auth = getMap(map,"authorization"); if( auth != null ) { if( headers!=null && headers.containsKey("authorization") ) throw new LuanException( "can't define authorization with header 'authorization' defined" ); String user = getString(auth,"user"); String password = getString(auth,"password"); if( !auth.isEmpty() ) throw new LuanException( "unrecognized authorization options: "+auth ); StringBuilder sb = new StringBuilder(); if( user != null ) sb.append(user); sb.append(':'); if( password != null ) sb.append(password); String val = "Basic " + Base64.getEncoder().encodeToString(sb.toString().getBytes()); if( headers == null ) headers = new HashMap(); headers.put("authorization",val); } Map params = getMap(map,"parameters"); String enctype = getString(map,"enctype"); if( enctype != null ) { if( !enctype.equals("multipart/form-data") ) throw new LuanException( "unrecognized enctype: "+enctype ); if( this.method!=Method.POST ) throw new LuanException( "multipart/form-data can only be used with POST" ); if( params==null ) throw new LuanException( "multipart/form-data requires parameters" ); if( params.isEmpty() ) throw new LuanException( "multipart/form-data parameters can't be empty" ); multipart = new MultipartClient(params); } else if( params != null ) { StringBuilder sb = new StringBuilder(); for( Object hack : params.entrySet() ) { Map.Entry entry = (Map.Entry)hack; String key = (String)entry.getKey(); Object val = entry.getValue(); String keyEnc = encode(key); if( val instanceof String ) { and(sb); sb.append( keyEnc ).append( '=' ).append( encode((String)val) ); } else { if( !(val instanceof LuanTable) ) throw new LuanException( "parameter '"+key+"' must be string or table" ); LuanTable t = (LuanTable)val; if( !t.isList() ) throw new LuanException( "parameter '"+key+"' table must be list" ); for( Object obj : t.asList() ) { if( !(obj instanceof String) ) throw new LuanException( "parameter '"+key+"' values must be strings" ); and(sb); sb.append( keyEnc ).append( '=' ).append( encode((String)obj) ); } } } if( this.method==Method.POST ) { content = sb.toString(); } else { String urlS = this.url.toString(); if( urlS.indexOf('?') == -1 ) { urlS += '?'; } else { urlS += '&'; } urlS += sb; try { this.url = new URL(urlS); } catch(IOException e) { throw new RuntimeException(e); } } } Integer timeout = getInt(map,"time_out"); if( timeout != null ) this.timeout = timeout; if( !map.isEmpty() ) throw new LuanException( "unrecognized options: "+map ); } } private static void and(StringBuilder sb) { if( sb.length() > 0 ) sb.append('&'); } private static String encode(String s) { try { return URLEncoder.encode(s,"UTF-8"); } catch(UnsupportedEncodingException e) { throw new RuntimeException(e); } } private static String getString(Map map,String key) throws LuanException { Object val = map.remove(key); if( val!=null && !(val instanceof String) ) throw new LuanException( "parameter '"+key+"' must be a string" ); return (String)val; } private static Integer getInt(Map map,String key) throws LuanException { Object val = map.remove(key); if( val==null ) return null; Integer i = Luan.asInteger(val); if( i==null ) throw new LuanException( "parameter '"+key+"' must be an integer" ); return i; } private static LuanTable getTable(Map map,String key) throws LuanException { Object val = map.remove(key); if( val!=null && !(val instanceof LuanTable) ) throw new LuanException( "parameter '"+key+"' must be a table" ); return (LuanTable)val; } private static Map getMap(Map map,String key) throws LuanException { LuanTable t = getTable(map,key); return t==null ? null : t.asMap(); } @Override public InputStream inputStream(LuanState luan) throws IOException, LuanException { URLConnection con = url.openConnection(); if( timeout != 0 ) { con.setConnectTimeout(timeout); con.setReadTimeout(timeout); } if( headers != null ) { for( Object hack : headers.entrySet() ) { Map.Entry entry = (Map.Entry)hack; String key = (String)entry.getKey(); Object val = entry.getValue(); if( val instanceof String ) { con.addRequestProperty(key,(String)val); } else { List list = (List)val; for( Object obj : list ) { con.addRequestProperty(key,(String)obj); } } } } if( !(con instanceof HttpURLConnection) ) { if( method!=Method.GET ) throw new LuanException("method must be GET but is "+method); return con.getInputStream(); } HttpURLConnection httpCon = (HttpURLConnection)con; if( method==Method.GET ) { return getInputStream(luan,httpCon); } if( method==Method.DELETE ) { httpCon.setRequestMethod("DELETE"); return getInputStream(luan,httpCon); } // POST // httpCon.setRequestProperty("content-type","application/x-www-form-urlencoded"); httpCon.setDoOutput(true); httpCon.setRequestMethod("POST"); OutputStream out; if( multipart != null ) { out = multipart.write(httpCon); } else { byte[] post = content.getBytes(); // httpCon.setRequestProperty("Content-Length",Integer.toString(post.length)); out = httpCon.getOutputStream(); out.write(post); } out.flush(); try { return getInputStream(luan,httpCon); } finally { out.close(); } } private static InputStream getInputStream(LuanState luan,HttpURLConnection httpCon) throws IOException, LuanException { try { return httpCon.getInputStream(); } catch(IOException e) { int responseCode = httpCon.getResponseCode(); String responseMessage = httpCon.getResponseMessage(); InputStream is = httpCon.getErrorStream(); if( is == null ) throw e; Reader in = new InputStreamReader(is); String msg = Utils.readAll(in); in.close(); LuanException le = new LuanException(msg,e); LuanTable tbl = le.table(luan); tbl.rawPut("response_code",responseCode); tbl.rawPut("response_message",responseMessage); throw le; } } @Override public String to_string() { return url.toString(); } @Override public String to_uri_string() { return url.toString(); } }