722
|
1 package luan.modules;
|
|
2
|
|
3 import java.io.InputStream;
|
|
4 import java.io.InputStreamReader;
|
|
5 import java.io.OutputStream;
|
|
6 import java.io.Reader;
|
|
7 import java.io.IOException;
|
|
8 import java.io.UnsupportedEncodingException;
|
|
9 import java.net.URL;
|
|
10 import java.net.URLConnection;
|
|
11 import java.net.HttpURLConnection;
|
|
12 import java.net.URLEncoder;
|
|
13 import java.util.Map;
|
|
14 import java.util.HashMap;
|
|
15 import java.util.List;
|
|
16 import java.util.Base64;
|
|
17 import luan.LuanState;
|
|
18 import luan.LuanTable;
|
|
19 import luan.LuanJavaFunction;
|
|
20 import luan.LuanException;
|
|
21
|
|
22
|
|
23 public final class LuanUrl extends IoLuan.LuanIn {
|
|
24 private static enum Method { GET, POST, DELETE }
|
|
25
|
|
26 private URL url;
|
|
27 private Method method = Method.GET;
|
|
28 private Map headers;
|
|
29 private String content;
|
|
30
|
|
31 LuanUrl(LuanState luan,URL url,LuanTable options) throws LuanException {
|
|
32 this.url = url;
|
|
33 if( options != null ) {
|
|
34 Map map = options.asMap(luan);
|
|
35 String methodStr = getString(map,"method");
|
|
36 if( methodStr != null ) {
|
|
37 methodStr = methodStr.toUpperCase();
|
|
38 try {
|
|
39 this.method = Method.valueOf(methodStr);
|
|
40 } catch(IllegalArgumentException e) {
|
|
41 throw new LuanException( "invalid method: "+methodStr );
|
|
42 }
|
|
43 }
|
|
44 Map auth = getMap(luan,map,"authorization");
|
|
45 if( auth != null ) {
|
|
46 String user = getString(auth,"user");
|
|
47 String password = getString(auth,"password");
|
|
48 if( !auth.isEmpty() )
|
|
49 throw new LuanException( "unrecognized authorization options: "+auth );
|
|
50 StringBuilder sb = new StringBuilder();
|
|
51 if( user != null )
|
|
52 sb.append(user);
|
|
53 sb.append(':');
|
|
54 if( password != null )
|
|
55 sb.append(password);
|
|
56 String val = "Basic " + Base64.getEncoder().encodeToString(sb.toString().getBytes());
|
|
57 if( headers == null )
|
|
58 headers = new HashMap();
|
|
59 headers.put("Authorization",val);
|
|
60 }
|
|
61 Map params = getMap(luan,map,"parameters");
|
|
62 if( params != null ) {
|
|
63 StringBuilder sb = new StringBuilder();
|
|
64 for( Object hack : params.entrySet() ) {
|
|
65 Map.Entry entry = (Map.Entry)hack;
|
|
66 String key = (String)entry.getKey();
|
|
67 Object val = entry.getValue();
|
|
68 String keyEnc = encode(key);
|
|
69 if( val instanceof String ) {
|
|
70 and(sb);
|
|
71 sb.append( keyEnc ).append( '=' ).append( encode((String)val) );
|
|
72 } else {
|
|
73 if( !(val instanceof LuanTable) )
|
|
74 throw new LuanException( "parameter '"+key+"' must be string or table" );
|
|
75 LuanTable t = (LuanTable)val;
|
|
76 if( !t.isList() )
|
|
77 throw new LuanException( "parameter '"+key+"' table must be list" );
|
|
78 for( Object obj : t.asList() ) {
|
|
79 if( !(obj instanceof String) )
|
|
80 throw new LuanException( "parameter '"+key+"' values must be strings" );
|
|
81 and(sb);
|
|
82 sb.append( keyEnc ).append( '=' ).append( encode((String)obj) );
|
|
83 }
|
|
84 }
|
|
85 }
|
|
86 if( this.method==Method.DELETE )
|
|
87 throw new LuanException( "the DELETE method cannot take parameters" );
|
|
88 if( this.method==Method.POST ) {
|
|
89 content = sb.toString();
|
|
90 } else { // GET
|
|
91 String urlS = url.toString();
|
|
92 if( urlS.indexOf('?') == -1 ) {
|
|
93 urlS += '?';
|
|
94 } else {
|
|
95 urlS += '&';
|
|
96 }
|
|
97 urlS += sb;
|
|
98 try {
|
|
99 url = new URL(urlS);
|
|
100 } catch(IOException e) {
|
|
101 throw new RuntimeException(e);
|
|
102 }
|
|
103 }
|
|
104 }
|
|
105 if( !map.isEmpty() )
|
|
106 throw new LuanException( "unrecognized options: "+map );
|
|
107 }
|
|
108 }
|
|
109
|
|
110 private static void and(StringBuilder sb) {
|
|
111 if( sb.length() > 0 )
|
|
112 sb.append('&');
|
|
113 }
|
|
114
|
|
115 private static String encode(String s) {
|
|
116 try {
|
|
117 return URLEncoder.encode(s,"UTF-8");
|
|
118 } catch(UnsupportedEncodingException e) {
|
|
119 throw new RuntimeException(e);
|
|
120 }
|
|
121 }
|
|
122
|
|
123 private static String getString(Map map,String key) throws LuanException {
|
|
124 Object val = map.remove(key);
|
|
125 if( val!=null && !(val instanceof String) )
|
|
126 throw new LuanException( "parameter '"+key+"' must be a string" );
|
|
127 return (String)val;
|
|
128 }
|
|
129
|
|
130 private static LuanTable getTable(Map map,String key) throws LuanException {
|
|
131 Object val = map.remove(key);
|
|
132 if( val!=null && !(val instanceof LuanTable) )
|
|
133 throw new LuanException( "parameter '"+key+"' must be a table" );
|
|
134 return (LuanTable)val;
|
|
135 }
|
|
136
|
|
137 private static Map getMap(LuanState luan,Map map,String key) throws LuanException {
|
|
138 LuanTable t = getTable(map,key);
|
|
139 return t==null ? null : t.asMap(luan);
|
|
140 }
|
|
141
|
|
142 @Override InputStream inputStream() throws IOException, LuanException {
|
|
143 URLConnection con = url.openConnection();
|
|
144 if( headers != null ) {
|
|
145 for( Object hack : headers.entrySet() ) {
|
|
146 Map.Entry entry = (Map.Entry)hack;
|
|
147 String key = (String)entry.getKey();
|
|
148 Object val = entry.getValue();
|
|
149 if( val instanceof String ) {
|
|
150 con.addRequestProperty(key,(String)val);
|
|
151 } else {
|
|
152 List list = (List)val;
|
|
153 for( Object obj : list ) {
|
|
154 con.addRequestProperty(key,(String)obj);
|
|
155 }
|
|
156 }
|
|
157 }
|
|
158 }
|
|
159 if( method==Method.GET ) {
|
|
160 return con.getInputStream();
|
|
161 }
|
|
162
|
|
163 HttpURLConnection httpCon = (HttpURLConnection)con;
|
|
164
|
|
165 if( method==Method.DELETE ) {
|
|
166 httpCon.setRequestMethod("DELETE");
|
|
167 return httpCon.getInputStream();
|
|
168 }
|
|
169
|
|
170 // POST
|
|
171
|
|
172 // httpCon.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
|
|
173 httpCon.setDoOutput(true);
|
|
174 httpCon.setRequestMethod("POST");
|
|
175
|
|
176 byte[] post = content.getBytes();
|
|
177 // httpCon.setRequestProperty("Content-Length",Integer.toString(post.length));
|
|
178 OutputStream out = httpCon.getOutputStream();
|
|
179 out.write(post);
|
|
180 out.flush();
|
|
181 try {
|
|
182 try {
|
|
183 return httpCon.getInputStream();
|
|
184 } catch(IOException e) {
|
|
185 InputStream is = httpCon.getErrorStream();
|
|
186 if( is == null )
|
|
187 throw e;
|
|
188 Reader in = new InputStreamReader(is);
|
|
189 String msg = Utils.readAll(in);
|
|
190 in.close();
|
|
191 throw new LuanException(msg,e);
|
|
192 }
|
|
193 } finally {
|
|
194 out.close();
|
|
195 }
|
|
196 }
|
|
197
|
|
198 @Override public String to_string() {
|
|
199 return url.toString();
|
|
200 }
|
|
201
|
|
202 @Override public String to_uri_string() {
|
|
203 return url.toString();
|
|
204 }
|
|
205 /*
|
|
206 public String post(String postS) throws IOException {
|
|
207 return new UrlCall(url).post(postS);
|
|
208 }
|
|
209
|
|
210 @Override public LuanTable table() {
|
|
211 LuanTable tbl = super.table();
|
|
212 try {
|
|
213 tbl.rawPut( "post", new LuanJavaFunction(
|
|
214 LuanUrl.class.getMethod( "post", String.class ), this
|
|
215 ) );
|
|
216 } catch(NoSuchMethodException e) {
|
|
217 throw new RuntimeException(e);
|
|
218 }
|
|
219 return tbl;
|
|
220 }
|
|
221 */
|
|
222 }
|