1 /* HttpURLConnection.java -- URLConnection class for HTTP protocol
2 Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004
3 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
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)
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.
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
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
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. */
40 package gnu.java.net.protocol.http;
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;
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;
61 import gnu.java.net.HeaderFieldHelper;
64 * This subclass of java.net.URLConnection models a URLConnection via
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
73 * @author Aaron M. Renn <arenn@urbanophile.com>
74 * @author Warren Levy <warrenl@cygnus.com>
76 public final class Connection extends HttpURLConnection
79 * The socket we are connected to
81 private Socket socket;
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;
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() {
96 // Recognize some networking properties listed at
97 // http://java.sun.com/j2se/1.4/docs/guide/net/properties.html.
99 proxyHost = System.getProperty("http.proxyHost");
100 if (proxyHost != null)
103 if ((port = System.getProperty("http.proxyPort")) != null)
107 proxyPort = Integer.parseInt(port);
116 userAgent = System.getProperty("http.agent");
124 * The InputStream for this connection.
126 private DataInputStream inputStream;
129 * The OutputStream for this connection
131 private OutputStream outputStream;
134 * bufferedOutputStream is a buffer to contain content of the HTTP request,
135 * and will be written to outputStream all at once
137 private ByteArrayOutputStream bufferedOutputStream;
140 * This object holds the request properties.
142 private HashMap requestProperties = new HashMap();
145 * This is the object that holds the header field information
147 private HeaderFieldHelper headers = new HeaderFieldHelper();
150 * Calls superclass constructor to initialize
152 protected Connection(URL url)
156 /* Set up some variables */
161 * Connects to the remote host, sends the request, and parses the reply
162 * code and header information returned
164 public void connect() throws IOException
166 // Call is ignored if already connected.
170 // Get address and port number.
175 socket = new Socket(proxyHost, port);
179 if ((port = url.getPort()) == -1)
181 // Open socket and output stream.
182 socket = new Socket(url.getHost(), port);
186 new DataInputStream(new BufferedInputStream(socket.getInputStream()));
187 outputStream = new BufferedOutputStream (socket.getOutputStream());
196 * Disconnects from the remote server.
198 public void disconnect()
206 catch (IOException e)
208 // Ignore errors in closing socket.
215 * Write HTTP request header and content to outputWriter.
217 void sendRequest() throws IOException
219 // Create PrintWriter for easier sending of headers.
220 PrintWriter outputWriter =
221 new PrintWriter(new OutputStreamWriter(outputStream, "8859_1"));
223 // Send request including any request properties that were set.
224 outputWriter.print (getRequestMethod() + " " + url.getFile()
227 // Set additional HTTP headers.
228 if (getRequestProperty ("Host") == null)
229 setRequestProperty ("Host", url.getHost());
231 if (getRequestProperty ("Connection") == null)
232 setRequestProperty ("Connection", "Close");
234 if (getRequestProperty ("user-agent") == null)
235 setRequestProperty ("user-agent", userAgent);
237 if (getRequestProperty ("accept") == null)
238 setRequestProperty ("accept", "*/*");
240 if (getRequestProperty ("Content-type") == null)
241 setRequestProperty ("Content-type", "application/x-www-form-urlencoded");
243 // Set correct content length.
244 if (bufferedOutputStream != null)
245 setRequestProperty ("Content-length", String.valueOf (bufferedOutputStream.size()));
247 // Write all req_props name-value pairs to the output writer.
248 Iterator itr = getRequestProperties().entrySet().iterator();
250 while (itr.hasNext())
252 Map.Entry e = (Map.Entry) itr.next();
253 outputWriter.print (e.getKey() + ": " + e.getValue() + "\r\n");
256 // One more CR-LF indicates end of header.
257 outputWriter.print ("\r\n");
258 outputWriter.flush();
261 if (bufferedOutputStream != null)
263 bufferedOutputStream.writeTo (outputStream);
264 outputStream.flush();
269 * Read HTTP reply from inputStream.
271 private void receiveReply() throws IOException
274 String line = inputStream.readLine();
275 String saveline = line;
276 int idx = line.indexOf (" ");
279 || (line.length() < (idx + 6)))
280 throw new IOException ("Server reply was unparseable: " + saveline);
282 headers.addHeaderField (null, line);
284 line = line.substring (idx + 1);
285 String code = line.substring (0, 3);
289 responseCode = Integer.parseInt (code);
291 catch (NumberFormatException e)
293 throw new IOException ("Server reply was unparseable: " + saveline);
296 responseMessage = line.substring (4);
298 // Now read the header lines
299 String key = null, value = null;
303 line = inputStream.readLine();
308 // Check for folded lines
309 if (line.startsWith (" ")
310 || line.startsWith("\t"))
312 // Trim off leading space
315 if (line.length() == 1)
316 throw new IOException("Server header lines were unparseable: "
319 line = line.substring (1);
321 while (line.startsWith(" ")
322 || line.startsWith("\t"));
324 value = value + " " + line;
330 headers.addHeaderField (key.toLowerCase(), value);
335 // Parse out key and value
336 idx = line.indexOf (":");
338 || (line.length() < (idx + 2)))
339 throw new IOException ("Server header lines were unparseable: "
342 key = line.substring (0, idx);
343 value = line.substring (idx + 1);
345 // Trim off leading space
346 while (value.startsWith (" ")
347 || value.startsWith ("\t"))
349 if (value.length() == 1)
350 throw new IOException ("Server header lines were unparseable: "
353 value = value.substring (1);
360 headers.addHeaderField (key.toLowerCase(), value.toLowerCase());
365 * Return a boolean indicating whether or not this connection is
366 * going through a proxy
368 * @return true if using a proxy, false otherwise
370 public boolean usingProxy()
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()
380 * @return An InputStream for this connection.
382 * @exception IOException If an error occurs
384 public InputStream getInputStream() throws IOException
390 throw new ProtocolException("Can't open InputStream if doInput is false");
396 * Returns on OutputStream for writing to this connection.
398 * @return An OutputStream for this connection.
400 * @exception IOException If an error occurs
402 public OutputStream getOutputStream() throws IOException
405 throw new ProtocolException
406 ("You cannot get an output stream for an existing http connection");
409 throw new ProtocolException
410 ("Want output stream while haven't setDoOutput(true)");
412 if (bufferedOutputStream == null)
413 bufferedOutputStream = new ByteArrayOutputStream (256); //default is too small
415 return bufferedOutputStream;
419 * Overrides java.net.HttpURLConnection.setRequestMethod() in order to
420 * restrict the available methods to only those we support.
422 * @param method The RequestMethod to use
424 * @exception ProtocolException If the specified method is not valid
426 public void setRequestMethod (String method) throws ProtocolException
428 method = method.toUpperCase();
430 if (method.equals("GET")
431 || method.equals("HEAD")
432 || method.equals("POST"))
433 super.setRequestMethod (method);
435 throw new ProtocolException ("Unsupported or unknown request method " +
439 public void addRequestProperty(String key, String value)
442 throw new IllegalStateException("Already connected");
444 String old = (String) requestProperties.put(key, value);
447 requestProperties.put(key, old + "," + value);
450 public String getRequestProperty(String key)
453 throw new IllegalStateException("Already connected");
455 return (String) requestProperties.get(key);
458 public void setRequestProperty(String key, String value)
461 throw new IllegalStateException("Already connected");
463 requestProperties.put(key, value);
466 public Map getRequestProperties()
469 throw new IllegalStateException("Already connected");
471 return requestProperties;
474 public String getHeaderField(String name)
481 catch (IOException x)
486 return (String) headers.getHeaderFieldValueByKey(name.toLowerCase());
489 public Map getHeaderFields()
496 catch (IOException x)
501 return headers.getHeaderFields();
505 * This method returns the header field value at the specified numeric
508 * @param n The index into the header field array
510 * @return The value of the specified header field, or <code>null</code>
511 * if the specified index is not valid.
513 public String getHeaderField(int n)
520 catch (IOException x)
525 return headers.getHeaderFieldValueByIndex (n);
529 * This method returns the header field key at the specified numeric
532 * @param n The index into the header field array
534 * @return The name of the header field key, or <code>null</code> if the
535 * specified index is not valid.
537 public String getHeaderFieldKey(int n)
544 catch (IOException x)
549 return headers.getHeaderFieldKeyByIndex (n);