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