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