Mercurial Hosting > luan
comparison src/luan/modules/url/LuanUrl.java @ 775:1a68fc55a80c
simplify dir structure
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 26 Aug 2016 14:36:40 -0600 |
parents | core/src/luan/modules/url/LuanUrl.java@ffbbe25dab09 |
children | 6a87d51ae0ed |
comparison
equal
deleted
inserted
replaced
774:3e30cf310e56 | 775:1a68fc55a80c |
---|---|
1 package luan.modules.url; | |
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.Luan; | |
18 import luan.LuanState; | |
19 import luan.LuanTable; | |
20 import luan.LuanJavaFunction; | |
21 import luan.LuanException; | |
22 import luan.modules.IoLuan; | |
23 import luan.modules.Utils; | |
24 | |
25 | |
26 public final class LuanUrl extends IoLuan.LuanIn { | |
27 | |
28 private static enum Method { GET, POST, DELETE } | |
29 | |
30 private URL url; | |
31 private Method method = Method.GET; | |
32 private Map headers; | |
33 private String content = null; | |
34 private MultipartClient multipart = null; | |
35 private int timeout = 0; | |
36 | |
37 public LuanUrl(LuanState luan,URL url,LuanTable options) throws LuanException { | |
38 this.url = url; | |
39 if( options != null ) { | |
40 Map map = options.asMap(luan); | |
41 String methodStr = getString(map,"method"); | |
42 if( methodStr != null ) { | |
43 methodStr = methodStr.toUpperCase(); | |
44 try { | |
45 this.method = Method.valueOf(methodStr); | |
46 } catch(IllegalArgumentException e) { | |
47 throw new LuanException( "invalid method: "+methodStr ); | |
48 } | |
49 } | |
50 Map headerMap = getMap(luan,map,"headers"); | |
51 if( headerMap != null ) { | |
52 headers = new HashMap(); | |
53 for( Object hack : headerMap.entrySet() ) { | |
54 Map.Entry entry = (Map.Entry)hack; | |
55 String key = (String)entry.getKey(); | |
56 Object val = entry.getValue(); | |
57 String name = toHttpHeaderName(key); | |
58 if( val instanceof String ) { | |
59 headers.put(name,val); | |
60 } else { | |
61 if( !(val instanceof LuanTable) ) | |
62 throw new LuanException( "header '"+key+"' must be string or table" ); | |
63 LuanTable t = (LuanTable)val; | |
64 if( !t.isList() ) | |
65 throw new LuanException( "header '"+key+"' table must be list" ); | |
66 headers.put(name,t.asList()); | |
67 } | |
68 } | |
69 } | |
70 Map auth = getMap(luan,map,"authorization"); | |
71 if( auth != null ) { | |
72 if( headers!=null && headers.containsKey("Authorization") ) | |
73 throw new LuanException( "can't define authorization with header 'Authorization' defined" ); | |
74 String user = getString(auth,"user"); | |
75 String password = getString(auth,"password"); | |
76 if( !auth.isEmpty() ) | |
77 throw new LuanException( "unrecognized authorization options: "+auth ); | |
78 StringBuilder sb = new StringBuilder(); | |
79 if( user != null ) | |
80 sb.append(user); | |
81 sb.append(':'); | |
82 if( password != null ) | |
83 sb.append(password); | |
84 String val = "Basic " + Base64.getEncoder().encodeToString(sb.toString().getBytes()); | |
85 if( headers == null ) | |
86 headers = new HashMap(); | |
87 headers.put("Authorization",val); | |
88 } | |
89 Map params = getMap(luan,map,"parameters"); | |
90 String enctype = getString(map,"enctype"); | |
91 if( enctype != null ) { | |
92 if( !enctype.equals("multipart/form-data") ) | |
93 throw new LuanException( "unrecognized enctype: "+enctype ); | |
94 if( this.method!=Method.POST ) | |
95 throw new LuanException( "multipart/form-data can only be used with POST" ); | |
96 if( params==null ) | |
97 throw new LuanException( "multipart/form-data requires parameters" ); | |
98 if( params.isEmpty() ) | |
99 throw new LuanException( "multipart/form-data parameters can't be empty" ); | |
100 multipart = new MultipartClient(params); | |
101 } | |
102 else if( params != null ) { | |
103 StringBuilder sb = new StringBuilder(); | |
104 for( Object hack : params.entrySet() ) { | |
105 Map.Entry entry = (Map.Entry)hack; | |
106 String key = (String)entry.getKey(); | |
107 Object val = entry.getValue(); | |
108 String keyEnc = encode(key); | |
109 if( val instanceof String ) { | |
110 and(sb); | |
111 sb.append( keyEnc ).append( '=' ).append( encode((String)val) ); | |
112 } else { | |
113 if( !(val instanceof LuanTable) ) | |
114 throw new LuanException( "parameter '"+key+"' must be string or table" ); | |
115 LuanTable t = (LuanTable)val; | |
116 if( !t.isList() ) | |
117 throw new LuanException( "parameter '"+key+"' table must be list" ); | |
118 for( Object obj : t.asList() ) { | |
119 if( !(obj instanceof String) ) | |
120 throw new LuanException( "parameter '"+key+"' values must be strings" ); | |
121 and(sb); | |
122 sb.append( keyEnc ).append( '=' ).append( encode((String)obj) ); | |
123 } | |
124 } | |
125 } | |
126 if( this.method==Method.DELETE ) | |
127 throw new LuanException( "the DELETE method cannot take parameters" ); | |
128 if( this.method==Method.POST ) { | |
129 content = sb.toString(); | |
130 } else { // GET | |
131 String urlS = this.url.toString(); | |
132 if( urlS.indexOf('?') == -1 ) { | |
133 urlS += '?'; | |
134 } else { | |
135 urlS += '&'; | |
136 } | |
137 urlS += sb; | |
138 try { | |
139 this.url = new URL(urlS); | |
140 } catch(IOException e) { | |
141 throw new RuntimeException(e); | |
142 } | |
143 } | |
144 } | |
145 Integer timeout = getInt(map,"time_out"); | |
146 if( timeout != null ) | |
147 this.timeout = timeout; | |
148 if( !map.isEmpty() ) | |
149 throw new LuanException( "unrecognized options: "+map ); | |
150 } | |
151 } | |
152 | |
153 public static String toHttpHeaderName(String luanName) { | |
154 luanName = luanName.toLowerCase(); | |
155 StringBuilder buf = new StringBuilder(); | |
156 boolean capitalize = true; | |
157 char[] a = luanName.toCharArray(); | |
158 for( int i=0; i<a.length; i++ ) { | |
159 char c = a[i]; | |
160 if( c == '_' || c == '-' ) { | |
161 a[i] = '-'; | |
162 capitalize = true; | |
163 } else if( capitalize ) { | |
164 a[i] = Character.toUpperCase(c); | |
165 capitalize = false; | |
166 } | |
167 } | |
168 return String.valueOf(a); | |
169 } | |
170 | |
171 private static void and(StringBuilder sb) { | |
172 if( sb.length() > 0 ) | |
173 sb.append('&'); | |
174 } | |
175 | |
176 private static String encode(String s) { | |
177 try { | |
178 return URLEncoder.encode(s,"UTF-8"); | |
179 } catch(UnsupportedEncodingException e) { | |
180 throw new RuntimeException(e); | |
181 } | |
182 } | |
183 | |
184 private static String getString(Map map,String key) throws LuanException { | |
185 Object val = map.remove(key); | |
186 if( val!=null && !(val instanceof String) ) | |
187 throw new LuanException( "parameter '"+key+"' must be a string" ); | |
188 return (String)val; | |
189 } | |
190 | |
191 private static Integer getInt(Map map,String key) throws LuanException { | |
192 Object val = map.remove(key); | |
193 if( val==null ) | |
194 return null; | |
195 Integer i = Luan.asInteger(val); | |
196 if( i==null ) | |
197 throw new LuanException( "parameter '"+key+"' must be an integer" ); | |
198 return i; | |
199 } | |
200 | |
201 private static LuanTable getTable(Map map,String key) throws LuanException { | |
202 Object val = map.remove(key); | |
203 if( val!=null && !(val instanceof LuanTable) ) | |
204 throw new LuanException( "parameter '"+key+"' must be a table" ); | |
205 return (LuanTable)val; | |
206 } | |
207 | |
208 private static Map getMap(LuanState luan,Map map,String key) throws LuanException { | |
209 LuanTable t = getTable(map,key); | |
210 return t==null ? null : t.asMap(luan); | |
211 } | |
212 | |
213 @Override public InputStream inputStream() throws IOException, LuanException { | |
214 URLConnection con = url.openConnection(); | |
215 if( timeout != 0 ) { | |
216 con.setConnectTimeout(timeout); | |
217 con.setReadTimeout(timeout); | |
218 } | |
219 if( headers != null ) { | |
220 for( Object hack : headers.entrySet() ) { | |
221 Map.Entry entry = (Map.Entry)hack; | |
222 String key = (String)entry.getKey(); | |
223 Object val = entry.getValue(); | |
224 if( val instanceof String ) { | |
225 con.addRequestProperty(key,(String)val); | |
226 } else { | |
227 List list = (List)val; | |
228 for( Object obj : list ) { | |
229 con.addRequestProperty(key,(String)obj); | |
230 } | |
231 } | |
232 } | |
233 } | |
234 if( method==Method.GET ) { | |
235 return con.getInputStream(); | |
236 } | |
237 | |
238 HttpURLConnection httpCon = (HttpURLConnection)con; | |
239 | |
240 if( method==Method.DELETE ) { | |
241 httpCon.setRequestMethod("DELETE"); | |
242 return httpCon.getInputStream(); | |
243 } | |
244 | |
245 // POST | |
246 | |
247 // httpCon.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); | |
248 httpCon.setDoOutput(true); | |
249 httpCon.setRequestMethod("POST"); | |
250 | |
251 OutputStream out; | |
252 if( multipart != null ) { | |
253 out = multipart.write(httpCon); | |
254 } else { | |
255 byte[] post = content.getBytes(); | |
256 // httpCon.setRequestProperty("Content-Length",Integer.toString(post.length)); | |
257 out = httpCon.getOutputStream(); | |
258 out.write(post); | |
259 } | |
260 out.flush(); | |
261 try { | |
262 try { | |
263 return httpCon.getInputStream(); | |
264 } catch(IOException e) { | |
265 InputStream is = httpCon.getErrorStream(); | |
266 if( is == null ) | |
267 throw e; | |
268 Reader in = new InputStreamReader(is); | |
269 String msg = Utils.readAll(in); | |
270 in.close(); | |
271 throw new LuanException(msg,e); | |
272 } | |
273 } finally { | |
274 out.close(); | |
275 } | |
276 } | |
277 | |
278 @Override public String to_string() { | |
279 return url.toString(); | |
280 } | |
281 | |
282 @Override public String to_uri_string() { | |
283 return url.toString(); | |
284 } | |
285 /* | |
286 public String post(String postS) throws IOException { | |
287 return new UrlCall(url).post(postS); | |
288 } | |
289 | |
290 @Override public LuanTable table() { | |
291 LuanTable tbl = super.table(); | |
292 try { | |
293 tbl.rawPut( "post", new LuanJavaFunction( | |
294 LuanUrl.class.getMethod( "post", String.class ), this | |
295 ) ); | |
296 } catch(NoSuchMethodException e) { | |
297 throw new RuntimeException(e); | |
298 } | |
299 return tbl; | |
300 } | |
301 */ | |
302 } |