aboutsummaryrefslogtreecommitdiff
path: root/libjava/gnu/java/net/protocol/http/Connection.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/gnu/java/net/protocol/http/Connection.java')
-rw-r--r--libjava/gnu/java/net/protocol/http/Connection.java494
1 files changed, 322 insertions, 172 deletions
diff --git a/libjava/gnu/java/net/protocol/http/Connection.java b/libjava/gnu/java/net/protocol/http/Connection.java
index 7794bc7f2f7..0bc50d147c0 100644
--- a/libjava/gnu/java/net/protocol/http/Connection.java
+++ b/libjava/gnu/java/net/protocol/http/Connection.java
@@ -39,44 +39,42 @@ exception statement from your version. */
package gnu.java.net.protocol.http;
import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
-import java.net.InetAddress;
import java.net.ProtocolException;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
-import java.util.Vector;
-import java.util.Hashtable;
-import java.util.Enumeration;
+import gnu.java.net.HeaderFieldHelper;
/**
- * Written using on-line Java Platform 1.2 API Specification, as well
- * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
+ * This subclass of java.net.URLConnection models a URLConnection via
+ * the HTTP protocol.
+ *
* Status: Minimal subset of functionality. Proxies only partially
* handled; Redirects not yet handled. FileNameMap handling needs to
* be considered. useCaches, ifModifiedSince, and
* allowUserInteraction need consideration as well as doInput and
* doOutput.
- */
-
-/**
+ *
+ * @author Aaron M. Renn <arenn@urbanophile.com>
* @author Warren Levy <warrenl@cygnus.com>
- * @date March 29, 1999.
*/
-class Connection extends HttpURLConnection
+public final class Connection extends HttpURLConnection
{
- protected Socket sock = null;
- private static Hashtable defRequestProperties = new Hashtable();
- private Hashtable requestProperties;
- private Hashtable hdrHash = new Hashtable();
- private Vector hdrVec = new Vector();
- private BufferedInputStream bufferedIn;
-
+ /**
+ * The socket we are connected to
+ */
+ private Socket socket;
private static int proxyPort = 80;
private static boolean proxyInUse = false;
private static String proxyHost = null;
@@ -104,43 +102,47 @@ class Connection extends HttpURLConnection
}
}
- public Connection(URL url)
+ /**
+ * The InputStream for this connection.
+ */
+ private DataInputStream inputStream;
+
+ /**
+ * The OutputStream for this connection
+ */
+ private OutputStream outputStream;
+
+ /**
+ * bufferedOutputStream is a buffer to contain content of the HTTP request,
+ * and will be written to outputStream all at once
+ */
+ private ByteArrayOutputStream bufferedOutputStream;
+
+ /**
+ * This object holds the request properties.
+ */
+ private HashMap requestProperties = new HashMap();
+
+ /**
+ * This is the object that holds the header field information
+ */
+ private HeaderFieldHelper headers = new HeaderFieldHelper();
+
+ /**
+ * Calls superclass constructor to initialize
+ */
+ protected Connection(URL url)
{
super(url);
- requestProperties = (Hashtable) defRequestProperties.clone();
- }
-
- // Override method in URLConnection.
- public static void setDefaultRequestProperty(String key, String value)
- {
- defRequestProperties.put(key, value);
- }
-
- // Override method in URLConnection.
- public static String getDefaultRequestProperty(String key)
- {
- return (String) defRequestProperties.get(key);
- }
-
- // Override method in URLConnection.
- public void setRequestProperty(String key, String value)
- {
- if (connected)
- throw new IllegalAccessError("Connection already established.");
-
- requestProperties.put(key, value);
- }
- // Override method in URLConnection.
- public String getRequestProperty(String key)
- {
- if (connected)
- throw new IllegalAccessError("Connection already established.");
-
- return (String) requestProperties.get(key);
+ /* Set up some variables */
+ doOutput = false;
}
- // Implementation of abstract method.
+ /**
+ * Connects to the remote host, sends the request, and parses the reply
+ * code and header information returned
+ */
public void connect() throws IOException
{
// Call is ignored if already connected.
@@ -152,55 +154,212 @@ class Connection extends HttpURLConnection
if (proxyInUse)
{
port = proxyPort;
- sock = new Socket(proxyHost, port);
+ socket = new Socket(proxyHost, port);
}
else
{
- InetAddress destAddr = InetAddress.getByName(url.getHost());
if ((port = url.getPort()) == -1)
port = 80;
// Open socket and output stream.
- sock = new Socket(destAddr, port);
+ socket = new Socket(url.getHost(), port);
}
- PrintWriter out = new PrintWriter(sock.getOutputStream());
+ inputStream =
+ new DataInputStream(new BufferedInputStream(socket.getInputStream()));
+ outputStream = new BufferedOutputStream (socket.getOutputStream());
+ bufferedOutputStream = new ByteArrayOutputStream (256); //default is too small
+
+ sendRequest();
+ receiveReply();
- // Send request including any request properties that were set.
- out.print(getRequestMethod() + " " + url.getFile() + " HTTP/1.0\r\n");
- out.print("Host: " + url.getHost() + ":" + port + "\r\n");
- Enumeration reqKeys = requestProperties.keys();
- Enumeration reqVals = requestProperties.elements();
- while (reqKeys.hasMoreElements())
- out.print(reqKeys.nextElement() + ": " + reqVals.nextElement() + "\r\n");
- out.print("\r\n");
- out.flush();
- getHttpHeaders();
connected = true;
}
- // Implementation of abstract method.
+ /**
+ * Disconnects from the remote server.
+ */
public void disconnect()
{
- if (sock != null)
+ if (socket != null)
{
try
{
- sock.close();
+ socket.close();
}
- catch (IOException ex)
+ catch (IOException e)
{
- ; // Ignore errors in closing socket.
+ // Ignore errors in closing socket.
}
- sock = null;
+ socket = null;
+ }
+ }
+
+ /**
+ * Write HTTP request header and content to outputWriter.
+ */
+ void sendRequest() throws IOException
+ {
+ // Create PrintWriter for easier sending of headers.
+ PrintWriter outputWriter = new PrintWriter(outputStream);
+
+ // Send request including any request properties that were set.
+ outputWriter.print (getRequestMethod() + " " + url.getFile()
+ + " HTTP/1.1\r\n");
+
+ // Set additional HTTP headers.
+ if (getRequestProperty ("Host") == null)
+ setRequestProperty ("Host", url.getHost());
+
+ if (getRequestProperty ("Connection") == null)
+ setRequestProperty ("Connection", "Close");
+
+ if (getRequestProperty ("user-agent") == null)
+ setRequestProperty ("user-agent", "gnu-libgcj/"
+ + System.getProperty ("classpath.version"));
+
+ if (getRequestProperty ("accept") == null)
+ setRequestProperty ("accept", "*/*");
+
+ if (getRequestProperty ("Content-type") == null)
+ setRequestProperty ("Content-type", "application/x-www-form-urlencoded");
+
+ // Set correct content length.
+ setRequestProperty ("Content-length", String.valueOf (bufferedOutputStream.size()));
+
+ // Write all req_props name-value pairs to the output writer.
+ Iterator itr = getRequestProperties().entrySet().iterator();
+
+ while (itr.hasNext())
+ {
+ Map.Entry e = (Map.Entry) itr.next();
+ outputWriter.print (e.getKey() + ": " + e.getValue() + "\r\n");
+ }
+
+ // One more CR-LF indicates end of header.
+ outputWriter.print ("\r\n");
+ outputWriter.flush();
+
+ // Write content
+ bufferedOutputStream.writeTo (outputStream);
+ outputStream.flush();
+ }
+
+ /**
+ * Read HTTP reply from inputStream.
+ */
+ private void receiveReply() throws IOException
+ {
+ // Parse the reply
+ String line = inputStream.readLine();
+ String saveline = line;
+ int idx = line.indexOf (" ");
+
+ if ((idx == -1)
+ || (line.length() < (idx + 6)))
+ throw new IOException ("Server reply was unparseable: " + saveline);
+
+ headers.addHeaderField (null, line);
+
+ line = line.substring (idx + 1);
+ String code = line.substring (0, 3);
+
+ try
+ {
+ responseCode = Integer.parseInt (code);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IOException ("Server reply was unparseable: " + saveline);
+ }
+
+ responseMessage = line.substring (4);
+
+ // Now read the header lines
+ String key = null, value = null;
+
+ while (true)
+ {
+ line = inputStream.readLine();
+
+ if (line.equals(""))
+ break;
+
+ // Check for folded lines
+ if (line.startsWith (" ")
+ || line.startsWith("\t"))
+ {
+ // Trim off leading space
+ do
+ {
+ if (line.length() == 1)
+ throw new IOException("Server header lines were unparseable: "
+ + line);
+
+ line = line.substring (1);
+ }
+ while (line.startsWith(" ")
+ || line.startsWith("\t"));
+
+ value = value + " " + line;
+ }
+ else
+ {
+ if (key != null)
+ {
+ headers.addHeaderField (key.toLowerCase(), value);
+ key = null;
+ value = null;
+ }
+
+ // Parse out key and value
+ idx = line.indexOf (":");
+ if ((idx == -1)
+ || (line.length() < (idx + 2)))
+ throw new IOException ("Server header lines were unparseable: "
+ + line);
+
+ key = line.substring (0, idx);
+ value = line.substring (idx + 1);
+
+ // Trim off leading space
+ while (value.startsWith (" ")
+ || value.startsWith ("\t"))
+ {
+ if (value.length() == 1)
+ throw new IOException ("Server header lines were unparseable: "
+ + line);
+
+ value = value.substring (1);
+ }
+ }
+ }
+
+ if (key != null)
+ {
+ headers.addHeaderField (key.toLowerCase(), value.toLowerCase());
}
}
+ /**
+ * Return a boolean indicating whether or not this connection is
+ * going through a proxy
+ *
+ * @return true if using a proxy, false otherwise
+ */
public boolean usingProxy()
{
return proxyInUse;
}
- // Override default method in URLConnection.
+ /**
+ * Returns an InputStream for reading from this connection. This stream
+ * will be "queued up" for reading just the contents of the requested file.
+ * Overrides URLConnection.getInputStream()
+ *
+ * @return An InputStream for this connection.
+ *
+ * @exception IOException If an error occurs
+ */
public InputStream getInputStream() throws IOException
{
if (!connected)
@@ -208,22 +367,85 @@ class Connection extends HttpURLConnection
if (!doInput)
throw new ProtocolException("Can't open InputStream if doInput is false");
- return bufferedIn;
+
+ return inputStream;
}
- // Override default method in URLConnection.
+ /**
+ * Returns on OutputStream for writing to this connection.
+ *
+ * @return An OutputStream for this connection.
+ *
+ * @exception IOException If an error occurs
+ */
public OutputStream getOutputStream() throws IOException
{
+ if (!doOutput)
+ throw new ProtocolException
+ ("Want output stream while haven't setDoOutput(true)");
+
if (!connected)
connect();
+
+ return bufferedOutputStream;
+ }
+
+ /**
+ * Overrides java.net.HttpURLConnection.setRequestMethod() in order to
+ * restrict the available methods to only those we support.
+ *
+ * @param method The RequestMethod to use
+ *
+ * @exception ProtocolException If the specified method is not valid
+ */
+ public void setRequestMethod (String method) throws ProtocolException
+ {
+ method = method.toUpperCase();
+
+ if (method.equals("GET")
+ || method.equals("HEAD")
+ || method.equals("POST"))
+ super.setRequestMethod (method);
+ else
+ throw new ProtocolException ("Unsupported or unknown request method " +
+ method);
+ }
+
+ public void addRequestProperty(String key, String value)
+ {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+
+ String old = (String) requestProperties.put(key, value);
+
+ if (old != null)
+ requestProperties.put(key, old + "," + value);
+ }
+
+ public String getRequestProperty(String key)
+ {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+
+ return (String) requestProperties.get(key);
+ }
+
+ public void setRequestProperty(String key, String value)
+ {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+
+ requestProperties.put(key, value);
+ }
- if (! doOutput)
- throw new
- ProtocolException("Can't open OutputStream if doOutput is false");
- return sock.getOutputStream();
+ public Map getRequestProperties()
+ {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+
+ return requestProperties;
}
- // Override default method in URLConnection.
public String getHeaderField(String name)
{
if (!connected)
@@ -236,10 +458,9 @@ class Connection extends HttpURLConnection
return null;
}
- return (String) hdrHash.get(name.toLowerCase());
+ return (String) headers.getHeaderFieldValueByKey(name.toLowerCase());
}
- // Override default method in URLConnection.
public Map getHeaderFields()
{
if (!connected)
@@ -252,10 +473,18 @@ class Connection extends HttpURLConnection
return null;
}
- return hdrHash;
+ return headers.getHeaderFields();
}
- // Override default method in URLConnection.
+ /**
+ * This method returns the header field value at the specified numeric
+ * index.
+ *
+ * @param n The index into the header field array
+ *
+ * @return The value of the specified header field, or <code>null</code>
+ * if the specified index is not valid.
+ */
public String getHeaderField(int n)
{
if (!connected)
@@ -268,12 +497,18 @@ class Connection extends HttpURLConnection
return null;
}
- if (n < hdrVec.size())
- return getField ((String) hdrVec.elementAt(n));
- return null;
+ return headers.getHeaderFieldValueByIndex (n);
}
- // Override default method in URLConnection.
+ /**
+ * This method returns the header field key at the specified numeric
+ * index.
+ *
+ * @param n The index into the header field array
+ *
+ * @return The name of the header field key, or <code>null</code> if the
+ * specified index is not valid.
+ */
public String getHeaderFieldKey(int n)
{
if (!connected)
@@ -286,91 +521,6 @@ class Connection extends HttpURLConnection
return null;
}
- if (n < hdrVec.size())
- return getKey ((String) hdrVec.elementAt(n));
- return null;
- }
-
- private String getKey(String str)
- {
- if (str == null)
- return null;
- int index = str.indexOf(':');
- if (index >= 0)
- return str.substring(0, index);
- else
- return null;
- }
-
- private String getField(String str)
- {
- if (str == null)
- return null;
- int index = str.indexOf(':');
- if (index >= 0)
- return str.substring(index + 1).trim();
- else
- return str;
- }
-
- private void getHttpHeaders() throws IOException
- {
- // Originally tried using a BufferedReader here to take advantage of
- // the readLine method and avoid the following, but the buffer read
- // past the end of the headers so the first part of the content was lost.
- // It is probably more robust than it needs to be, e.g. the byte[]
- // is unlikely to overflow and a '\r' should always be followed by a '\n',
- // but it is better to be safe just in case.
- bufferedIn = new BufferedInputStream(sock.getInputStream());
-
- int buflen = 100;
- byte[] buf = new byte[buflen];
- String line = "";
- boolean gotnl = false;
- byte[] ch = new byte[1];
- ch[0] = (byte) '\n';
-
- while (true)
- {
- // Check for leftover byte from non-'\n' after a '\r'.
- if (ch[0] != '\n')
- line = line + '\r' + new String(ch, 0, 1);
-
- int i;
- // FIXME: This is rather inefficient.
- for (i = 0; i < buflen; i++)
- {
- buf[i] = (byte) bufferedIn.read();
- if (buf[i] == -1)
- throw new IOException("Malformed HTTP header");
- if (buf[i] == '\r')
- {
- bufferedIn.read(ch, 0, 1);
- if (ch[0] == '\n')
- gotnl = true;
- break;
- }
- }
- line = line + new String(buf, 0, i);
-
- // A '\r' '\n' combo indicates the end of the header entry.
- // If it wasn't found, cycle back through the loop and append
- // to 'line' until one is found.
- if (gotnl)
- {
- // A zero length entry signals the end of the headers.
- if (line.length() == 0)
- break;
-
- // Store the header and reinitialize for next cycle.
- hdrVec.addElement(line);
- String key = getKey(line);
- if (key != null)
- hdrHash.put(key.toLowerCase(), getField(line));
- line = "";
- ch[0] = (byte) '\n';
- gotnl = false;
- }
- }
+ return headers.getHeaderFieldKeyByIndex (n);
}
}