chiark / gitweb /
rename com.myjavatools.web to net.chiark.yarrg
[jarrg-ian.git] / src / net / chiark / yarrg / ClientHttpRequest.java
1 package net.chiark.yarrg;
2
3 import java.net.URLConnection;
4 import java.net.URL;
5 import java.io.IOException;
6 import java.util.HashMap;
7 import java.util.Map;
8 import java.io.File;
9 import java.io.InputStream;
10 import java.util.Random;
11 import java.io.OutputStream;
12 import java.io.FileInputStream;
13 import java.util.Iterator;
14
15 /**
16  * <p>Title: Client HTTP Request class</p>
17  * <p>Description: this class helps to send POST HTTP requests with various form data,
18  * including files. Cookies can be added to be included in the request.</p>
19  *
20  * <p>Modified by Ted Pearson(ted@tedpearson.com) 5/4/09</p>
21  * <p>Modified by Owen Dunn and Ian Jackson 2009, 2010<p>
22  *
23  * Originally this was called "com.myjavatools.web.ClientHttpRequest"
24  * but since we have changed it to be incompatible and it's a very
25  * old version, we have renamed it.  - Ian Jackson September 2010.
26  *
27  * @author Vlad Patryshev
28  * @version 1.0
29  *
30  */
31 public class ClientHttpRequest {
32   URLConnection connection;
33   OutputStream os = null;
34   Map cookies = new HashMap();
35
36   protected void connect() throws IOException {
37     if (os == null) os = connection.getOutputStream();
38   }
39
40   protected void write(char c) throws IOException {
41     connect();
42     os.write(c);
43   }
44
45   protected void write(String s) throws IOException {
46     connect();
47     os.write(s.getBytes());
48   }
49
50   protected void newline() throws IOException {
51     connect();
52     write("\r\n");
53   }
54
55   protected void writeln(String s) throws IOException {
56     connect();
57     write(s);
58     newline();
59   }
60
61   private static Random random = new Random();
62
63   protected static String randomString() {
64     return Long.toString(random.nextLong(), 36);
65   }
66
67   String boundary = "---------------------------" + randomString() + randomString() + randomString();
68
69   private void boundary() throws IOException {
70     write("--");
71     write(boundary);
72   }
73
74   /**
75    * Creates a new multipart POST HTTP request on a freshly opened URLConnection
76    *
77    * @param connection an already open URL connection
78    * @throws IOException
79    */
80   public ClientHttpRequest(URLConnection connection) throws IOException {
81     this.connection = connection;
82     connection.setDoOutput(true);
83     connection.setRequestProperty("Content-Type",
84                                   "multipart/form-data; boundary=" + boundary);
85   }
86
87   /**
88    * Creates a new multipart POST HTTP request for a specified URL
89    *
90    * @param url the URL to send request to
91    * @throws IOException
92    */
93   public ClientHttpRequest(URL url) throws IOException {
94     this(url.openConnection());
95   }
96
97   /**
98    * Creates a new multipart POST HTTP request for a specified URL string
99    *
100    * @param urlString the string representation of the URL to send request to
101    * @throws IOException
102    */
103   public ClientHttpRequest(String urlString) throws IOException {
104     this(new URL(urlString));
105   }
106
107
108   private void postCookies() {
109     StringBuffer cookieList = new StringBuffer();
110
111     for (Iterator i = cookies.entrySet().iterator(); i.hasNext();) {
112       Map.Entry entry = (Map.Entry)(i.next());
113       cookieList.append(entry.getKey().toString() + "=" + entry.getValue());
114
115       if (i.hasNext()) {
116         cookieList.append("; ");
117       }
118     }
119     if (cookieList.length() > 0) {
120       connection.setRequestProperty("Cookie", cookieList.toString());
121     }
122   }
123
124   /**
125    * adds a cookie to the requst
126    * @param name cookie name
127    * @param value cookie value
128    * @throws IOException
129    */
130   public void setCookie(String name, String value) throws IOException {
131     cookies.put(name, value);
132   }
133
134   /**
135    * adds cookies to the request
136    * @param cookies the cookie "name-to-value" map
137    * @throws IOException
138    */
139   public void setCookies(Map cookies) throws IOException {
140     if (cookies == null) return;
141     this.cookies.putAll(cookies);
142   }
143
144   /**
145    * adds cookies to the request
146    * @param cookies array of cookie names and values (cookies[2*i] is a name, cookies[2*i + 1] is a value)
147    * @throws IOException
148    */
149   public void setCookies(String[] cookies) throws IOException {
150     if (cookies == null) return;
151     for (int i = 0; i < cookies.length - 1; i+=2) {
152       setCookie(cookies[i], cookies[i+1]);
153     }
154   }
155
156   private void writeName(String name) throws IOException {
157     newline();
158     write("Content-Disposition: form-data; name=\"");
159     write(name);
160     write('"');
161   }
162
163   /**
164    * adds a string parameter to the request
165    * @param name parameter name
166    * @param value parameter value
167    * @throws IOException
168    */
169   public void setParameter(String name, String value) throws IOException {
170     boundary();
171     writeName(name);
172     newline(); newline();
173     writeln(value);
174   }
175
176   private static void pipe(InputStream in, OutputStream out) throws IOException {
177     byte[] buf = new byte[500000];
178     int nread;
179     int navailable;
180     int total = 0;
181     synchronized (in) {
182       while((nread = in.read(buf, 0, buf.length)) >= 0) {
183         out.write(buf, 0, nread);
184         total += nread;
185       }
186     }
187     out.flush();
188     buf = null;
189   }
190
191   /**
192    * adds a file parameter to the request
193    * @param name parameter name
194    * @param filename the name of the file
195    * @param is input stream to read the contents of the file from
196    * @throws IOException
197    */
198   public void setParameter(String name, String filename, InputStream is) throws IOException {
199     String type = connection.guessContentTypeFromName(filename);
200     if (type == null) type = "application/octet-stream";
201     setParameter(name, filename, is, type);
202   }
203   
204   /**
205    * adds a file parameter to the request
206    * @param name parameter name
207    * @param filename the name of the file
208    * @param is input stream to read the contents of the file from
209    * @param type Content-Type of the file
210    * @throws IOException
211    */
212   public void setParameter(String name, String filename, InputStream is, String type) throws IOException {
213     boundary();
214     writeName(name);
215     write("; filename=\"");
216     write(filename);
217     write('"');
218     newline();
219     write("Content-Type: ");
220     writeln(type);
221     newline();
222     pipe(is, os);
223     newline();
224   }
225
226   /**
227    * adds a file parameter to the request
228    * @param name parameter name
229    * @param file the file to upload
230    * @throws IOException
231    */
232   public void setParameter(String name, File file) throws IOException {
233     setParameter(name, file.getPath(), new FileInputStream(file));
234   }
235
236   /**
237    * adds a parameter to the request; if the parameter is a File, the file is uploaded, otherwise the string value of the parameter is passed in the request
238    * @param name parameter name
239    * @param object parameter value, a File or anything else that can be stringified
240    * @throws IOException
241    */
242   public void setParameter(String name, Object object) throws IOException {
243     if (object instanceof File) {
244       setParameter(name, (File) object);
245     } else {
246       setParameter(name, object.toString());
247     }
248   }
249
250   /**
251    * adds parameters to the request
252    * @param parameters "name-to-value" map of parameters; if a value is a file, the file is uploaded, otherwise it is stringified and sent in the request
253    * @throws IOException
254    */
255   public void setParameters(Map parameters) throws IOException {
256     if (parameters == null) return;
257     for (Iterator i = parameters.entrySet().iterator(); i.hasNext();) {
258       Map.Entry entry = (Map.Entry)i.next();
259       setParameter(entry.getKey().toString(), entry.getValue());
260     }
261   }
262
263   /**
264    * adds parameters to the request
265    * @param parameters array of parameter names and values (parameters[2*i] is a name, parameters[2*i + 1] is a value); if a value is a file, the file is uploaded, otherwise it is stringified and sent in the request
266    * @throws IOException
267    */
268   public void setParameters(Object[] parameters) throws IOException {
269     if (parameters == null) return;
270     for (int i = 0; i < parameters.length - 1; i+=2) {
271       setParameter(parameters[i].toString(), parameters[i+1]);
272     }
273   }
274
275   /**
276    * posts the requests to the server, with all the cookies and parameters that were added
277    * @return input stream with the server response
278    * @throws IOException
279    */
280   public InputStream post() throws IOException {
281     boundary();
282     writeln("--");
283     os.close();
284     InputStream tis;
285     try {
286         tis = connection.getInputStream();
287     } catch (IOException e) {
288         tis = ((java.net.HttpURLConnection) connection).getErrorStream();
289     }
290     return tis;
291   }
292
293   /**
294    * posts the requests to the server, with all the cookies and parameters that were added before (if any), and with parameters that are passed in the argument
295    * @param parameters request parameters
296    * @return input stream with the server response
297    * @throws IOException
298    * @see setParameters
299    */
300   public InputStream post(Map parameters) throws IOException {
301     setParameters(parameters);
302     return post();
303   }
304
305   /**
306    * posts the requests to the server, with all the cookies and parameters that were added before (if any), and with parameters that are passed in the argument
307    * @param parameters request parameters
308    * @return input stream with the server response
309    * @throws IOException
310    * @see setParameters
311    */
312   public InputStream post(Object[] parameters) throws IOException {
313     setParameters(parameters);
314     return post();
315   }
316
317   /**
318    * posts the requests to the server, with all the cookies and parameters that were added before (if any), and with cookies and parameters that are passed in the arguments
319    * @param cookies request cookies
320    * @param parameters request parameters
321    * @return input stream with the server response
322    * @throws IOException
323    * @see setParameters
324    * @see setCookies
325    */
326   public InputStream post(Map cookies, Map parameters) throws IOException {
327     setCookies(cookies);
328     setParameters(parameters);
329     return post();
330   }
331
332   /**
333    * posts the requests to the server, with all the cookies and parameters that were added before (if any), and with cookies and parameters that are passed in the arguments
334    * @param cookies request cookies
335    * @param parameters request parameters
336    * @return input stream with the server response
337    * @throws IOException
338    * @see setParameters
339    * @see setCookies
340    */
341   public InputStream post(String[] cookies, Object[] parameters) throws IOException {
342     setCookies(cookies);
343     setParameters(parameters);
344     return post();
345   }
346
347   /**
348    * post the POST request to the server, with the specified parameter
349    * @param name parameter name
350    * @param value parameter value
351    * @return input stream with the server response
352    * @throws IOException
353    * @see setParameter
354    */
355   public InputStream post(String name, Object value) throws IOException {
356     setParameter(name, value);
357     return post();
358   }
359
360   /**
361    * post the POST request to the server, with the specified parameters
362    * @param name1 first parameter name
363    * @param value1 first parameter value
364    * @param name2 second parameter name
365    * @param value2 second parameter value
366    * @return input stream with the server response
367    * @throws IOException
368    * @see setParameter
369    */
370   public InputStream post(String name1, Object value1, String name2, Object value2) throws IOException {
371     setParameter(name1, value1);
372     return post(name2, value2);
373   }
374
375   /**
376    * post the POST request to the server, with the specified parameters
377    * @param name1 first parameter name
378    * @param value1 first parameter value
379    * @param name2 second parameter name
380    * @param value2 second parameter value
381    * @param name3 third parameter name
382    * @param value3 third parameter value
383    * @return input stream with the server response
384    * @throws IOException
385    * @see setParameter
386    */
387   public InputStream post(String name1, Object value1, String name2, Object value2, String name3, Object value3) throws IOException {
388     setParameter(name1, value1);
389     return post(name2, value2, name3, value3);
390   }
391
392   /**
393    * post the POST request to the server, with the specified parameters
394    * @param name1 first parameter name
395    * @param value1 first parameter value
396    * @param name2 second parameter name
397    * @param value2 second parameter value
398    * @param name3 third parameter name
399    * @param value3 third parameter value
400    * @param name4 fourth parameter name
401    * @param value4 fourth parameter value
402    * @return input stream with the server response
403    * @throws IOException
404    * @see setParameter
405    */
406   public InputStream post(String name1, Object value1, String name2, Object value2, String name3, Object value3, String name4, Object value4) throws IOException {
407     setParameter(name1, value1);
408     return post(name2, value2, name3, value3, name4, value4);
409   }
410
411   /**
412    * posts a new request to specified URL, with parameters that are passed in the argument
413    * @param parameters request parameters
414    * @return input stream with the server response
415    * @throws IOException
416    * @see setParameters
417    */
418   public static InputStream post(URL url, Map parameters) throws IOException {
419     return new ClientHttpRequest(url).post(parameters);
420   }
421
422   /**
423    * posts a new request to specified URL, with parameters that are passed in the argument
424    * @param parameters request parameters
425    * @return input stream with the server response
426    * @throws IOException
427    * @see setParameters
428    */
429   public static InputStream post(URL url, Object[] parameters) throws IOException {
430     return new ClientHttpRequest(url).post(parameters);
431   }
432
433   /**
434    * posts a new request to specified URL, with cookies and parameters that are passed in the argument
435    * @param cookies request cookies
436    * @param parameters request parameters
437    * @return input stream with the server response
438    * @throws IOException
439    * @see setCookies
440    * @see setParameters
441    */
442   public static InputStream post(URL url, Map cookies, Map parameters) throws IOException {
443     return new ClientHttpRequest(url).post(cookies, parameters);
444   }
445
446   /**
447    * posts a new request to specified URL, with cookies and parameters that are passed in the argument
448    * @param cookies request cookies
449    * @param parameters request parameters
450    * @return input stream with the server response
451    * @throws IOException
452    * @see setCookies
453    * @see setParameters
454    */
455   public static InputStream post(URL url, String[] cookies, Object[] parameters) throws IOException {
456     return new ClientHttpRequest(url).post(cookies, parameters);
457   }
458
459   /**
460    * post the POST request specified URL, with the specified parameter
461    * @param name parameter name
462    * @param value parameter value
463    * @return input stream with the server response
464    * @throws IOException
465    * @see setParameter
466    */
467   public static InputStream post(URL url, String name1, Object value1) throws IOException {
468     return new ClientHttpRequest(url).post(name1, value1);
469   }
470
471   /**
472    * post the POST request to specified URL, with the specified parameters
473    * @param name1 first parameter name
474    * @param value1 first parameter value
475    * @param name2 second parameter name
476    * @param value2 second parameter value
477    * @return input stream with the server response
478    * @throws IOException
479    * @see setParameter
480    */
481   public static InputStream post(URL url, String name1, Object value1, String name2, Object value2) throws IOException {
482     return new ClientHttpRequest(url).post(name1, value1, name2, value2);
483   }
484
485   /**
486    * post the POST request to specified URL, with the specified parameters
487    * @param name1 first parameter name
488    * @param value1 first parameter value
489    * @param name2 second parameter name
490    * @param value2 second parameter value
491    * @param name3 third parameter name
492    * @param value3 third parameter value
493    * @return input stream with the server response
494    * @throws IOException
495    * @see setParameter
496    */
497   public static InputStream post(URL url, String name1, Object value1, String name2, Object value2, String name3, Object value3) throws IOException {
498     return new ClientHttpRequest(url).post(name1, value1, name2, value2, name3, value3);
499   }
500
501   /**
502    * post the POST request to specified URL, with the specified parameters
503    * @param name1 first parameter name
504    * @param value1 first parameter value
505    * @param name2 second parameter name
506    * @param value2 second parameter value
507    * @param name3 third parameter name
508    * @param value3 third parameter value
509    * @param name4 fourth parameter name
510    * @param value4 fourth parameter value
511    * @return input stream with the server response
512    * @throws IOException
513    * @see setParameter
514    */
515   public static InputStream post(URL url, String name1, Object value1, String name2, Object value2, String name3, Object value3, String name4, Object value4) throws IOException {
516     return new ClientHttpRequest(url).post(name1, value1, name2, value2, name3, value3, name4, value4);
517   }
518 }