diff options
Diffstat (limited to 'libjava/gnu/java/net/protocol/http/Connection.java')
-rw-r--r-- | libjava/gnu/java/net/protocol/http/Connection.java | 494 |
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); } } |