OSDN Git Service

* java/lang/System.java (static): Set http.agent system property when
[pf3gnuchains/gcc-fork.git] / libjava / gnu / java / net / protocol / http / Connection.java
1 /* HttpURLConnection.java -- URLConnection class for HTTP protocol
2    Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Classpath.
6
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11  
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA.
21
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38
39
40 package gnu.java.net.protocol.http;
41
42 import java.io.BufferedInputStream;
43 import java.io.BufferedOutputStream;
44 import java.io.ByteArrayOutputStream;
45 import java.io.DataInputStream;
46 import java.io.InputStream;
47 import java.io.IOException;
48 import java.io.OutputStream;
49 import java.io.OutputStreamWriter;
50 import java.io.PrintWriter;
51 import java.net.HttpURLConnection;
52 import java.net.ProtocolException;
53 import java.net.Socket;
54 import java.net.URL;
55 import java.net.URLConnection;
56 import java.security.AccessController;
57 import java.security.PrivilegedAction;
58 import java.util.HashMap;
59 import java.util.Iterator;
60 import java.util.Map;
61 import gnu.java.net.HeaderFieldHelper;
62
63 /**
64  * This subclass of java.net.URLConnection models a URLConnection via
65  * the HTTP protocol.
66  *
67  * Status: Minimal subset of functionality.  Proxies only partially
68  * handled; Redirects not yet handled.  FileNameMap handling needs to
69  * be considered.  useCaches, ifModifiedSince, and
70  * allowUserInteraction need consideration as well as doInput and
71  * doOutput.
72  * 
73  * @author Aaron M. Renn <arenn@urbanophile.com>
74  * @author Warren Levy <warrenl@cygnus.com>
75  */
76 public final class Connection extends HttpURLConnection
77 {
78   /**
79    * The socket we are connected to
80    */
81   private Socket socket;
82   
83   // Properties depeending on system properties settings
84   static int proxyPort = 80;
85   static boolean proxyInUse = false;
86   static String proxyHost = null;
87   static String userAgent;
88
89   static 
90   {
91     // Make sure access control for system properties depends only on
92     // our class ProtectionDomain, not on any (indirect) callers.
93     AccessController.doPrivileged(new PrivilegedAction() {
94         public Object run()
95         {
96           // Recognize some networking properties listed at
97           // http://java.sun.com/j2se/1.4/docs/guide/net/properties.html.
98           String port = null;
99           proxyHost = System.getProperty("http.proxyHost");
100           if (proxyHost != null)
101             {
102               proxyInUse = true;
103               if ((port = System.getProperty("http.proxyPort")) != null)
104                 {
105                   try
106                     {
107                       proxyPort = Integer.parseInt(port);
108                     }
109                   catch (Throwable t)
110                     {
111                       // Nothing.  
112                     }
113                 }
114             }
115           
116           userAgent = System.getProperty("http.agent");
117
118           return null;
119         }
120       });
121   }
122
123   /**
124    * The InputStream for this connection.
125    */
126   private DataInputStream inputStream;
127
128   /**
129    * The OutputStream for this connection
130    */
131   private OutputStream outputStream;
132
133   /**
134    * bufferedOutputStream is a buffer to contain content of the HTTP request,
135    * and will be written to outputStream all at once
136    */
137   private ByteArrayOutputStream bufferedOutputStream;
138
139   /**
140    * This object holds the request properties.
141    */
142   private HashMap requestProperties = new HashMap();
143
144   /**
145    * This is the object that holds the header field information
146    */
147   private HeaderFieldHelper headers = new HeaderFieldHelper();
148
149   /**
150    * Calls superclass constructor to initialize
151    */
152   protected Connection(URL url)
153   {
154     super(url);
155
156     /* Set up some variables */
157     doOutput = false;
158   }
159
160   /**
161    * Connects to the remote host, sends the request, and parses the reply
162    * code and header information returned
163    */
164   public void connect() throws IOException
165   {
166     // Call is ignored if already connected.
167     if (connected)
168       return;
169
170     // Get address and port number.
171     int port;
172     if (proxyInUse)
173       {
174         port = proxyPort;
175         socket = new Socket(proxyHost, port);
176       }
177     else
178       {
179         if ((port = url.getPort()) == -1)
180           port = 80;
181         // Open socket and output stream.
182         socket = new Socket(url.getHost(), port);
183       }
184
185     inputStream =
186       new DataInputStream(new BufferedInputStream(socket.getInputStream()));
187     outputStream = new BufferedOutputStream (socket.getOutputStream());
188
189     sendRequest();
190     receiveReply();
191
192     connected = true;
193   }
194
195   /**
196    * Disconnects from the remote server.
197    */
198   public void disconnect()
199   {
200     if (socket != null)
201       {
202         try
203           {
204             socket.close();
205           }
206         catch (IOException e)
207           {
208             // Ignore errors in closing socket.
209           }
210         socket = null;
211       }
212   }
213
214   /**
215    * Write HTTP request header and content to outputWriter.
216    */
217   void sendRequest() throws IOException
218   {
219     // Create PrintWriter for easier sending of headers.
220     PrintWriter outputWriter =
221       new PrintWriter(new OutputStreamWriter(outputStream, "8859_1")); 
222     
223     // Send request including any request properties that were set.
224     outputWriter.print (getRequestMethod() + " " + url.getFile()
225                         + " HTTP/1.1\r\n");
226
227     // Set additional HTTP headers.
228     if (getRequestProperty ("Host") == null)
229       setRequestProperty ("Host", url.getHost());
230     
231     if (getRequestProperty ("Connection") == null)
232       setRequestProperty ("Connection", "Close");
233     
234     if (getRequestProperty ("user-agent") == null)
235       setRequestProperty ("user-agent", userAgent);
236     
237     if (getRequestProperty ("accept") == null)
238       setRequestProperty ("accept", "*/*");
239     
240     if (getRequestProperty ("Content-type") == null)
241       setRequestProperty ("Content-type", "application/x-www-form-urlencoded");
242
243     // Set correct content length.
244     if (bufferedOutputStream != null)
245       setRequestProperty ("Content-length", String.valueOf (bufferedOutputStream.size()));
246
247     // Write all req_props name-value pairs to the output writer.
248     Iterator itr = getRequestProperties().entrySet().iterator();
249
250     while (itr.hasNext())
251       {
252         Map.Entry e = (Map.Entry) itr.next();
253         outputWriter.print (e.getKey() + ": " + e.getValue() + "\r\n");
254       }
255
256     // One more CR-LF indicates end of header.
257     outputWriter.print ("\r\n");
258     outputWriter.flush();
259
260     // Write content
261     if (bufferedOutputStream != null)
262       {
263         bufferedOutputStream.writeTo (outputStream);
264         outputStream.flush();
265       }
266   }
267
268   /**
269    * Read HTTP reply from inputStream.
270    */
271   private void receiveReply() throws IOException
272   {
273     // Parse the reply
274     String line = inputStream.readLine();
275     String saveline = line;
276     int idx = line.indexOf (" ");
277
278     if ((idx == -1)
279         || (line.length() < (idx + 6)))
280       throw new IOException ("Server reply was unparseable: " + saveline);
281
282     headers.addHeaderField (null, line);
283
284     line = line.substring (idx + 1);
285     String code = line.substring (0, 3);
286     
287     try
288       {
289         responseCode = Integer.parseInt (code);
290       }
291     catch (NumberFormatException e)
292       {
293         throw new IOException ("Server reply was unparseable: " + saveline);
294       }
295     
296     responseMessage = line.substring (4);
297
298     // Now read the header lines
299     String key = null, value = null;
300     
301     while (true)
302       {
303         line = inputStream.readLine();
304         
305         if (line.equals(""))
306           break;
307
308         // Check for folded lines
309         if (line.startsWith (" ")
310             || line.startsWith("\t"))
311           {
312             // Trim off leading space
313             do
314               {
315                 if (line.length() == 1)
316                   throw new IOException("Server header lines were unparseable: "
317                                         + line);
318
319                 line = line.substring (1);
320               }
321             while (line.startsWith(" ")
322                    || line.startsWith("\t"));
323
324             value = value + " " + line;
325           }
326         else 
327           {
328             if (key != null)
329               {
330                 headers.addHeaderField (key.toLowerCase(), value);
331                 key = null;
332                 value = null;
333               }
334
335             // Parse out key and value
336             idx = line.indexOf (":");
337             if ((idx == -1)
338                 || (line.length() < (idx + 2)))
339               throw new IOException ("Server header lines were unparseable: "
340                                      + line);
341
342             key = line.substring (0, idx);
343             value = line.substring (idx + 1);
344
345             // Trim off leading space
346             while (value.startsWith (" ")
347                    || value.startsWith ("\t"))
348               {
349                 if (value.length() == 1)
350                   throw new IOException ("Server header lines were unparseable: "
351                                          + line);
352
353                 value = value.substring (1);
354               }
355           }
356       }
357     
358     if (key != null)
359       {
360         headers.addHeaderField (key.toLowerCase(), value.toLowerCase());
361       }
362   }
363
364   /**
365    * Return a boolean indicating whether or not this connection is
366    * going through a proxy
367    *
368    * @return true if using a proxy, false otherwise
369    */
370   public boolean usingProxy()
371   {
372     return proxyInUse;
373   }
374
375   /**
376    * Returns an InputStream for reading from this connection.  This stream
377    * will be "queued up" for reading just the contents of the requested file.
378    * Overrides URLConnection.getInputStream()
379    *
380    * @return An InputStream for this connection.
381    *
382    * @exception IOException If an error occurs
383    */
384   public InputStream getInputStream() throws IOException
385   {
386     if (!connected)
387       connect();
388
389     if (!doInput)
390       throw new ProtocolException("Can't open InputStream if doInput is false");
391     
392     return inputStream;
393   }
394
395   /**
396    * Returns on OutputStream for writing to this connection.
397    *
398    * @return An OutputStream for this connection.
399    *
400    * @exception IOException If an error occurs
401    */
402   public OutputStream getOutputStream() throws IOException
403   {
404     if (connected)
405       throw new ProtocolException
406         ("You cannot get an output stream for an existing http connection");
407
408     if (!doOutput)
409       throw new ProtocolException
410         ("Want output stream while haven't setDoOutput(true)");
411     
412     if (bufferedOutputStream == null)
413       bufferedOutputStream = new ByteArrayOutputStream (256); //default is too small
414     
415     return bufferedOutputStream;
416   }
417
418   /**
419    * Overrides java.net.HttpURLConnection.setRequestMethod() in order to
420    * restrict the available methods to only those we support.
421    *
422    * @param method The RequestMethod to use
423    *
424    * @exception ProtocolException If the specified method is not valid
425    */
426   public void setRequestMethod (String method) throws ProtocolException
427   {
428     method = method.toUpperCase();
429     
430     if (method.equals("GET")
431         || method.equals("HEAD")
432         || method.equals("POST"))
433       super.setRequestMethod (method);
434     else
435       throw new ProtocolException ("Unsupported or unknown request method " +
436                                    method);
437   }
438
439   public void addRequestProperty(String key, String value)
440   {
441     if (connected)
442       throw new IllegalStateException("Already connected");
443     
444     String old = (String) requestProperties.put(key, value);
445
446     if (old != null)
447       requestProperties.put(key, old + "," + value);
448   }
449
450   public String getRequestProperty(String key)
451   {
452     if (connected)
453       throw new IllegalStateException("Already connected");
454     
455     return (String) requestProperties.get(key);
456   }
457
458   public void setRequestProperty(String key, String value)
459   {
460     if (connected)
461       throw new IllegalStateException("Already connected");
462     
463     requestProperties.put(key, value);
464   }
465
466   public Map getRequestProperties()
467   {
468     if (connected)
469       throw new IllegalStateException("Already connected");
470     
471     return requestProperties;
472   }
473
474   public String getHeaderField(String name)
475   {
476     if (!connected)
477       try
478         {
479           connect();
480         }
481       catch (IOException x)
482         {
483           return null;
484         }
485
486     return (String) headers.getHeaderFieldValueByKey(name.toLowerCase());
487   }
488
489   public Map getHeaderFields()
490   {
491     if (!connected)
492       try
493         {
494           connect();
495         }
496       catch (IOException x)
497         {
498           return null;
499         }
500
501     return headers.getHeaderFields();
502   }
503
504   /**
505    * This method returns the header field value at the specified numeric
506    * index.
507    *
508    * @param n The index into the header field array
509    *
510    * @return The value of the specified header field, or <code>null</code>
511    * if the specified index is not valid.
512    */
513   public String getHeaderField(int n)
514   {
515     if (!connected)
516       try
517         {
518           connect();
519         }
520       catch (IOException x)
521         {
522           return null;
523         }
524
525     return headers.getHeaderFieldValueByIndex (n);
526   }
527
528   /**
529    * This method returns the header field key at the specified numeric
530    * index.
531    *
532    * @param n The index into the header field array
533    *
534    * @return The name of the header field key, or <code>null</code> if the
535    * specified index is not valid.
536    */
537   public String getHeaderFieldKey(int n)
538   {
539     if (!connected)
540       try
541         {
542           connect();
543         }
544       catch (IOException x)
545         {
546           return null;
547         }
548
549     return headers.getHeaderFieldKeyByIndex (n);
550   }
551 }