diff core/src/luan/modules/LuanUrl.java @ 722:647602e8291a

add url options
author Franklin Schmidt <fschmidt@gmail.com>
date Fri, 03 Jun 2016 17:51:58 -0600
parents
children eaf30d5aaf6a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/LuanUrl.java	Fri Jun 03 17:51:58 2016 -0600
@@ -0,0 +1,222 @@
+package luan.modules;
+
+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.LuanState;
+import luan.LuanTable;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+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;
+
+	LuanUrl(LuanState luan,URL url,LuanTable options) throws LuanException {
+		this.url = url;
+		if( options != null ) {
+			Map map = options.asMap(luan);
+			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 auth = getMap(luan,map,"authorization");
+			if( auth != null ) {
+				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(luan,map,"parameters");
+			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.DELETE )
+					throw new LuanException( "the DELETE method cannot take parameters" );
+				if( this.method==Method.POST ) {
+					content = sb.toString();
+				} else { // GET
+					String urlS = url.toString();
+					if( urlS.indexOf('?') == -1 ) {
+						urlS += '?';
+					} else {
+						urlS += '&';
+					}
+					urlS += sb;
+					try {
+						url = new URL(urlS);
+					} catch(IOException e) {
+						throw new RuntimeException(e);
+					}
+				}
+			}
+			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 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(LuanState luan,Map map,String key) throws LuanException {
+		LuanTable t = getTable(map,key);
+		return t==null ? null : t.asMap(luan);
+	}
+
+	@Override InputStream inputStream() throws IOException, LuanException {
+		URLConnection con = url.openConnection();
+		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( method==Method.GET ) {
+			return con.getInputStream();
+		}
+
+		HttpURLConnection httpCon = (HttpURLConnection)con;
+
+		if( method==Method.DELETE ) {
+			httpCon.setRequestMethod("DELETE");
+			return httpCon.getInputStream();
+		}
+
+		// POST
+
+//		httpCon.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
+		httpCon.setDoOutput(true);
+		httpCon.setRequestMethod("POST");
+
+		byte[] post = content.getBytes();
+//		httpCon.setRequestProperty("Content-Length",Integer.toString(post.length));
+		OutputStream out = httpCon.getOutputStream();
+		out.write(post);
+		out.flush();
+		try {
+			try {
+				return httpCon.getInputStream();
+			} catch(IOException e) {
+				InputStream is = httpCon.getErrorStream();
+				if( is == null )
+					throw e;
+				Reader in = new InputStreamReader(is);
+				String msg = Utils.readAll(in);
+				in.close();
+				throw new LuanException(msg,e);
+			}
+		} finally {
+			out.close();
+		}
+	}
+
+	@Override public String to_string() {
+		return url.toString();
+	}
+
+	@Override public String to_uri_string() {
+		return url.toString();
+	}
+/*
+	public String post(String postS) throws IOException {
+		return new UrlCall(url).post(postS);
+	}
+
+	@Override public LuanTable table() {
+		LuanTable tbl = super.table();
+		try {
+			tbl.rawPut( "post", new LuanJavaFunction(
+				LuanUrl.class.getMethod( "post", String.class ), this
+			) );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+		return tbl;
+	}
+*/
+}