diff options
author | Mark Wielaard <mark@gcc.gnu.org> | 2006-03-10 21:46:48 +0000 |
---|---|---|
committer | Mark Wielaard <mark@gcc.gnu.org> | 2006-03-10 21:46:48 +0000 |
commit | 8aa540d2f783474d1d2e06f16744bf67b9c1facc (patch) | |
tree | ea38c56431c5d4528fb54254c3f8e50f517bede3 /libjava/classpath/gnu/java/net/protocol | |
parent | 27079765d00123f8e53d0e1ef7f9d46559266e6d (diff) | |
download | gcc-8aa540d2f783474d1d2e06f16744bf67b9c1facc.zip gcc-8aa540d2f783474d1d2e06f16744bf67b9c1facc.tar.gz gcc-8aa540d2f783474d1d2e06f16744bf67b9c1facc.tar.bz2 |
Imported GNU Classpath 0.90
Imported GNU Classpath 0.90
* scripts/makemake.tcl: Set gnu/java/awt/peer/swing to ignore.
* gnu/classpath/jdwp/VMFrame.java (SIZE): New constant.
* java/lang/VMCompiler.java: Use gnu.java.security.hash.MD5.
* java/lang/Math.java: New override file.
* java/lang/Character.java: Merged from Classpath.
(start, end): Now 'int's.
(canonicalName): New field.
(CANONICAL_NAME, NO_SPACES_NAME, CONSTANT_NAME): New constants.
(UnicodeBlock): Added argument.
(of): New overload.
(forName): New method.
Updated unicode blocks.
(sets): Updated.
* sources.am: Regenerated.
* Makefile.in: Likewise.
From-SVN: r111942
Diffstat (limited to 'libjava/classpath/gnu/java/net/protocol')
11 files changed, 609 insertions, 392 deletions
diff --git a/libjava/classpath/gnu/java/net/protocol/file/Connection.java b/libjava/classpath/gnu/java/net/protocol/file/Connection.java index 8e4a413..f7253b0 100644 --- a/libjava/classpath/gnu/java/net/protocol/file/Connection.java +++ b/libjava/classpath/gnu/java/net/protocol/file/Connection.java @@ -135,21 +135,18 @@ public class Connection extends URLConnection * @exception MalformedURLException If the given string contains invalid * escape sequences. * - * Sadly the same as URI.unquote, but there's nothing we can do to - * make it accessible. - * */ public static String unquote(String str) throws MalformedURLException { if (str == null) return null; - byte[] buf = new byte[str.length()]; + + final int MAX_BYTES_PER_UTF_8_CHAR = 3; + byte[] buf = new byte[str.length()*MAX_BYTES_PER_UTF_8_CHAR]; int pos = 0; for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); - if (c > 127) - throw new MalformedURLException(str + " : Invalid character"); if (c == '%') { if (i + 2 >= str.length()) @@ -160,6 +157,15 @@ public class Connection extends URLConnection throw new MalformedURLException(str + " : Invalid quoted character"); buf[pos++] = (byte) (hi * 16 + lo); } + else if (c > 127) { + try { + byte [] c_as_bytes = Character.toString(c).getBytes("utf-8"); + System.arraycopy(c_as_bytes, 0, buf, pos, c_as_bytes.length); + } + catch (java.io.UnsupportedEncodingException x2) { + throw (Error) new InternalError().initCause(x2); + } + } else buf[pos++] = (byte) c; } diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/ActiveModeDTP.java b/libjava/classpath/gnu/java/net/protocol/ftp/ActiveModeDTP.java index 3755e8b..aa3c412 100644 --- a/libjava/classpath/gnu/java/net/protocol/ftp/ActiveModeDTP.java +++ b/libjava/classpath/gnu/java/net/protocol/ftp/ActiveModeDTP.java @@ -84,6 +84,7 @@ final class ActiveModeDTP } this.connectionTimeout = connectionTimeout; acceptThread = new Thread(this, "ActiveModeDTP"); + acceptThread.setDaemon(true); acceptThread.start(); } diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/FTPURLConnection.java b/libjava/classpath/gnu/java/net/protocol/ftp/FTPURLConnection.java index 62c40f1..f937e51 100644 --- a/libjava/classpath/gnu/java/net/protocol/ftp/FTPURLConnection.java +++ b/libjava/classpath/gnu/java/net/protocol/ftp/FTPURLConnection.java @@ -1,5 +1,5 @@ /* FTPURLConnection.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,10 +38,9 @@ exception statement from your version. */ package gnu.java.net.protocol.ftp; +import gnu.classpath.SystemProperties; import gnu.java.net.GetLocalHostAction; -import gnu.java.security.action.GetPropertyAction; -import java.io.FileNotFoundException; import java.io.FilterInputStream; import java.io.FilterOutputStream; import java.io.IOException; @@ -51,7 +50,6 @@ import java.net.InetAddress; import java.net.URL; import java.net.URLConnection; import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Map; @@ -113,11 +111,9 @@ public class FTPURLConnection else { username = "anonymous"; - PrivilegedAction a = new GetPropertyAction("user.name"); - String systemUsername =(String) AccessController.doPrivileged(a); - a = new GetLocalHostAction(); + GetLocalHostAction a = new GetLocalHostAction(); InetAddress localhost =(InetAddress) AccessController.doPrivileged(a); - password = systemUsername + "@" + + password = SystemProperties.getProperty("user.name") + "@" + ((localhost == null) ? "localhost" : localhost.getHostName()); } connection = new FTPConnection(host, port); @@ -167,24 +163,13 @@ public class FTPURLConnection connect(); } String path = url.getPath(); - String filename = null; - int lsi = path.lastIndexOf('/'); - if (lsi != -1) + if (connection.changeWorkingDirectory(path)) { - filename = path.substring(lsi + 1); - path = path.substring(0, lsi); - if (!connection.changeWorkingDirectory(path)) - { - throw new FileNotFoundException(path); - } - } - if (filename != null && filename.length() > 0) - { - return this.new ClosingInputStream(connection.retrieve(filename)); + return this.new ClosingInputStream(connection.list(null)); } else { - return this.new ClosingInputStream(connection.list(null)); + return this.new ClosingInputStream(connection.retrieve(path)); } } @@ -198,20 +183,8 @@ public class FTPURLConnection { connect(); } - String dir = url.getPath(); - String filename = url.getFile(); - if (!connection.changeWorkingDirectory(dir)) - { - throw new FileNotFoundException(dir); - } - if (filename != null) - { - return this.new ClosingOutputStream(connection.store(filename)); - } - else - { - throw new FileNotFoundException(filename); - } + String path = url.getPath(); + return this.new ClosingOutputStream(connection.store(path)); } public String getRequestProperty(String key) diff --git a/libjava/classpath/gnu/java/net/protocol/http/ChunkedInputStream.java b/libjava/classpath/gnu/java/net/protocol/http/ChunkedInputStream.java index a4487d1..9870412 100644 --- a/libjava/classpath/gnu/java/net/protocol/http/ChunkedInputStream.java +++ b/libjava/classpath/gnu/java/net/protocol/http/ChunkedInputStream.java @@ -1,5 +1,5 @@ /* ChunkedInputStream.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,28 +38,46 @@ exception statement from your version. */ package gnu.java.net.protocol.http; -import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.net.ProtocolException; + +// +// Note that we rely on the implemtation of skip() in the super class +// (InputStream) calling our read methods to account for chunk headers +// while skipping. +// + + /** * Input stream wrapper for the "chunked" transfer-coding. * * @author Chris Burdess (dog@gnu.org) */ public class ChunkedInputStream - extends FilterInputStream + extends InputStream { private static final byte CR = 0x0d; private static final byte LF = 0x0a; + Headers headers; + + /** The underlying stream. */ + private InputStream in; + + /** Size of the chunk we're reading. */ int size; + /** Number of bytes we've read in this chunk. */ int count; + /** + * True when we should read meta-information, false when we should + * read data. + */ boolean meta; + /** True when we've hit EOF. */ boolean eof; - Headers headers; /** * Constructor. @@ -68,7 +86,7 @@ public class ChunkedInputStream */ public ChunkedInputStream(InputStream in, Headers headers) { - super(in); + this.in = in; this.headers = headers; size = -1; count = 0; @@ -84,21 +102,10 @@ public class ChunkedInputStream { return -1; } - int ret = (int) buf[0]; - if (ret < 0) - { - ret += 0x100; - } - return ret; - } - - public int read(byte[] buffer) - throws IOException - { - return read(buffer, 0, buffer.length); + return 0xff & buf[0]; } - public int read(byte[] buffer, int offset, int length) + public synchronized int read(byte[] buffer, int offset, int length) throws IOException { if (eof) @@ -120,7 +127,18 @@ public class ChunkedInputStream } else if (c == 0x0a && last == 0x0d) // CRLF { - size = Integer.parseInt(buf.toString(), 16); + try + { + size = Integer.parseInt(buf.toString(), 16); + } + catch (NumberFormatException nfe) + { + IOException ioe = new IOException("Bad chunk header"); + ioe.initCause(nfe); + // Unrecoverable. Don't try to read more. + in.close(); + throw ioe; + } break; } else if (!seenSemi && c >= 0x30) @@ -142,17 +160,22 @@ public class ChunkedInputStream } else { - int diff = length - offset; - int max = size - count; - max = (diff < max) ? diff : max; - int len = (max > 0) ? in.read(buffer, offset, max) : 0; + int canRead = Math.min(size - count, length); + int len = in.read(buffer, offset, canRead); + if (len == -1) + { + // This is an error condition but it isn't clear what we + // should do with it. + eof = true; + return -1; + } count += len; if (count == size) { // Read CRLF int c1 = in.read(); int c2 = in.read(); - if (c1 == -1 && c2 == -1) + if (c1 == -1 || c2 == -1) { // EOF before CRLF: bad, but ignore eof = true; @@ -167,6 +190,37 @@ public class ChunkedInputStream return len; } } + + /** + * This method returns the number of bytes that can be read from + * this stream before a read might block. Even if the underlying + * InputStream has data available past the end of the current chunk, + * we have no way of knowing how large the next chunk header will + * be. So we cannot report available data past the current chunk. + * + * @return The number of bytes that can be read before a read might + * block + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + if (meta) + return 0; + + return Math.min(in.available(), size - count); + } + + /** + * This method closes the ChunkedInputStream by closing the underlying + * InputStream. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + in.close(); + } } diff --git a/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java b/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java index 573a791..33d9756a 100644 --- a/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java +++ b/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java @@ -1,5 +1,5 @@ /* HTTPConnection.java -- - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,7 +38,6 @@ exception statement from your version. */ package gnu.java.net.protocol.http; -import gnu.classpath.Configuration; import gnu.classpath.SystemProperties; import gnu.java.net.EmptyX509TrustManager; @@ -53,8 +52,9 @@ import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; +import java.util.ListIterator; import java.util.Map; import javax.net.ssl.HandshakeCompletedListener; @@ -164,7 +164,7 @@ public class HTTPConnection /** * The pool that this connection is a member of (if any). */ - private LinkedHashMap pool; + private Pool pool; /** * Creates a new HTTP connection. @@ -266,7 +266,8 @@ public class HTTPConnection /** * Returns the HTTP version string supported by this connection. - * @see #version + * @see #majorVersion + * @see #minorVersion */ public String getVersion() { @@ -331,27 +332,227 @@ public class HTTPConnection } /** + * Manages a pool of HTTPConections. The pool will have a maximum + * size determined by the value of the maxConn parameter passed to + * the {@link #get} method. This value inevitably comes from the + * http.maxConnections system property. If the + * classpath.net.http.keepAliveTTL system property is set, that will + * be the maximum time (in seconds) that an idle connection will be + * maintained. + */ + static class Pool + { + /** + * Singleton instance of the pool. + */ + static Pool instance = new Pool(); + + /** + * The pool + */ + final LinkedList connectionPool = new LinkedList(); + + /** + * Maximum size of the pool. + */ + int maxConnections; + + /** + * If greater than zero, the maximum time a connection will remain + * int the pool. + */ + int connectionTTL; + + /** + * A thread that removes connections older than connectionTTL. + */ + class Reaper + implements Runnable + { + public void run() + { + synchronized (Pool.this) + { + try + { + do + { + while (connectionPool.size() > 0) + { + long currentTime = System.currentTimeMillis(); + + HTTPConnection c = + (HTTPConnection)connectionPool.getFirst(); + + long waitTime = c.timeLastUsed + + connectionTTL - currentTime; + + if (waitTime <= 0) + removeOldest(); + else + try + { + Pool.this.wait(waitTime); + } + catch (InterruptedException _) + { + // Ignore the interrupt. + } + } + // After the pool is empty, wait TTL to see if it + // is used again. This is because in the + // situation where a single thread is making HTTP + // requests to the same server it can remove the + // connection from the pool before the Reaper has + // a chance to start. This would cause the Reaper + // to exit if it were not for this extra delay. + // The result would be starting a Reaper thread + // for each HTTP request. With the delay we get + // at most one Reaper created each TTL. + try + { + Pool.this.wait(connectionTTL); + } + catch (InterruptedException _) + { + // Ignore the interrupt. + } + } + while (connectionPool.size() > 0); + } + finally + { + reaper = null; + } + } + } + } + + Reaper reaper; + + /** + * Private constructor to ensure singleton. + */ + private Pool() + { + } + + /** + * Tests for a matching connection. + * + * @param c connection to match. + * @param h the host name. + * @param p the port. + * @param sec true if using https. + * + * @return true if c matches h, p, and sec. + */ + private static boolean matches(HTTPConnection c, + String h, int p, boolean sec) + { + return h.equals(c.hostname) && (p == c.port) && (sec == c.secure); + } + + /** + * Get a pooled HTTPConnection. If there is an existing idle + * connection to the requested server it is returned. Otherwise a + * new connection is created. + * + * @param host the name of the host to connect to + * @param port the port on the host to connect to + * @param secure whether to use a secure connection + * + * @return the HTTPConnection. + */ + synchronized HTTPConnection get(String host, + int port, + boolean secure) + { + String ttl = + SystemProperties.getProperty("classpath.net.http.keepAliveTTL"); + connectionTTL = (ttl != null && ttl.length() > 0) ? + 1000 * Math.max(1, Integer.parseInt(ttl)) : 10000; + + String mc = SystemProperties.getProperty("http.maxConnections"); + maxConnections = (mc != null && mc.length() > 0) ? + Math.max(Integer.parseInt(mc), 1) : 5; + if (maxConnections < 1) + maxConnections = 1; + + HTTPConnection c = null; + + ListIterator it = connectionPool.listIterator(0); + while (it.hasNext()) + { + HTTPConnection cc = (HTTPConnection)it.next(); + if (matches(cc, host, port, secure)) + { + c = cc; + it.remove(); + break; + } + } + if (c == null) + { + c = new HTTPConnection(host, port, secure); + c.setPool(this); + } + return c; + } + + /** + * Put an idle HTTPConnection back into the pool. If this causes + * the pool to be come too large, the oldest connection is removed + * and closed. + * + */ + synchronized void put(HTTPConnection c) + { + c.timeLastUsed = System.currentTimeMillis(); + connectionPool.addLast(c); + + // maxConnections must always be >= 1 + while (connectionPool.size() >= maxConnections) + removeOldest(); + + if (connectionTTL > 0 && null == reaper) { + // If there is a connectionTTL, then the reaper has removed + // any stale connections, so we don't have to check for stale + // now. We do have to start a reaper though, as there is not + // one running now. + reaper = new Reaper(); + Thread t = new Thread(reaper, "HTTPConnection.Reaper"); + t.setDaemon(true); + t.start(); + } + } + + /** + * Remove the oldest connection from the pool and close it. + */ + void removeOldest() + { + HTTPConnection cx = (HTTPConnection)connectionPool.removeFirst(); + try + { + cx.closeConnection(); + } + catch (IOException ioe) + { + // Ignore it. We are just cleaning up. + } + } + } + + /** * The number of times this HTTPConnection has be used via keep-alive. */ int useCount; /** - * Generates a key for connections in the connection pool. - * - * @param h the host name. - * @param p the port. - * @param sec true if using https. - * - * @return the key. + * If this HTTPConnection is in the pool, the time it was put there. */ - static Object getPoolKey(String h, int p, boolean sec) - { - StringBuilder buf = new StringBuilder(sec ? "https://" : "http://"); - buf.append(h); - buf.append(':'); - buf.append(p); - return buf.toString(); - } + long timeLastUsed; /** * Set the connection pool that this HTTPConnection is a member of. @@ -360,7 +561,7 @@ public class HTTPConnection * * @param p the pool. */ - void setPool(LinkedHashMap p) + void setPool(Pool p) { pool = p; } @@ -374,25 +575,20 @@ public class HTTPConnection { if (pool != null) { - synchronized (pool) + useCount++; + pool.put(this); + + } + else + { + // If there is no pool, just close. + try { - useCount++; - Object key = HTTPConnection.getPoolKey(hostname, port, secure); - pool.put(key, this); - while (pool.size() >= HTTPURLConnection.maxConnections) - { - // maxConnections must always be >= 1 - Object lru = pool.keySet().iterator().next(); - HTTPConnection c = (HTTPConnection)pool.remove(lru); - try - { - c.closeConnection(); - } - catch (IOException ioe) - { - // Ignore it. We are just cleaning up. - } - } + closeConnection(); + } + catch (IOException ioe) + { + // Ignore it. We are just cleaning up. } } } diff --git a/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java b/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java index d5da7d6..5c2af9e 100644 --- a/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java +++ b/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java @@ -1,5 +1,5 @@ /* HTTPURLConnection.java -- - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +38,8 @@ exception statement from your version. */ package gnu.java.net.protocol.http; +import gnu.classpath.SystemProperties; + import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -45,11 +47,11 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.ProtocolException; import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.security.cert.Certificate; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; @@ -70,13 +72,6 @@ public class HTTPURLConnection extends HttpsURLConnection implements HandshakeCompletedListener { - - /** - * Pool of reusable connections, used if keepAlive is true. - */ - private static final LinkedHashMap connectionPool = new LinkedHashMap(); - static int maxConnections; - /* * The underlying connection. */ @@ -108,38 +103,23 @@ public class HTTPURLConnection { super(url); requestHeaders = new Headers(); - AccessController.doPrivileged(this.new GetHTTPPropertiesAction()); - } - - class GetHTTPPropertiesAction - implements PrivilegedAction - { - - public Object run() - { - proxyHostname = System.getProperty("http.proxyHost"); - if (proxyHostname != null && proxyHostname.length() > 0) - { - String port = System.getProperty("http.proxyPort"); - if (port != null && port.length() > 0) - { - proxyPort = Integer.parseInt(port); - } - else - { - proxyHostname = null; - proxyPort = -1; - } - } - agent = System.getProperty("http.agent"); - String ka = System.getProperty("http.keepAlive"); - keepAlive = !(ka != null && "false".equals(ka)); - String mc = System.getProperty("http.maxConnections"); - maxConnections = (mc != null && mc.length() > 0) ? - Math.max(Integer.parseInt(mc), 1) : 5; - return null; - } - + proxyHostname = SystemProperties.getProperty("http.proxyHost"); + if (proxyHostname != null && proxyHostname.length() > 0) + { + String port = SystemProperties.getProperty("http.proxyPort"); + if (port != null && port.length() > 0) + { + proxyPort = Integer.parseInt(port); + } + else + { + proxyHostname = null; + proxyPort = -1; + } + } + agent = SystemProperties.getProperty("http.agent"); + String ka = SystemProperties.getProperty("http.keepAlive"); + keepAlive = !(ka != null && "false".equals(ka)); } public void connect() @@ -254,8 +234,24 @@ public class HTTPURLConnection } } - if (response.getCodeClass() == 3 && getInstanceFollowRedirects()) + if (response.isRedirect() && getInstanceFollowRedirects()) { + // Read the response body, if there is one. If the + // redirect points us back at the same server, we will use + // the cached connection, so we must make sure there is no + // pending data in it. + InputStream body = response.getBody(); + if (body != null) + { + byte[] ignore = new byte[1024]; + while (true) + { + int n = body.read(ignore, 0, ignore.length); + if (n == -1) + break; + } + } + // Follow redirect String location = response.getHeader("Location"); if (location != null) @@ -333,16 +329,13 @@ public class HTTPURLConnection { responseSink = response.getBody(); - if (response.getCode() == 404) - { - errorSink = responseSink; - throw new FileNotFoundException(url.toString()); - } + if (response.isError()) + errorSink = responseSink; } } while (retry); connected = true; - } + } /** * Returns a connection, from the pool if necessary. @@ -353,16 +346,7 @@ public class HTTPURLConnection HTTPConnection connection; if (keepAlive) { - Object key = HTTPConnection.getPoolKey(host, port, secure); - synchronized (connectionPool) - { - connection = (HTTPConnection) connectionPool.remove(key); - if (connection == null) - { - connection = new HTTPConnection(host, port, secure); - connection.setPool(connectionPool); - } - } + connection = HTTPConnection.Pool.instance.get(host, port, secure); } else { @@ -427,30 +411,32 @@ public class HTTPURLConnection public String getRequestProperty(String key) { + if (key == null) + return null; + return requestHeaders.getValue(key); } public Map getRequestProperties() { - return requestHeaders; + if (connected) + throw new IllegalStateException("Already connected"); + + Map m = requestHeaders.getAsMap(); + return Collections.unmodifiableMap(m); } public void setRequestProperty(String key, String value) { + super.setRequestProperty(key, value); + requestHeaders.put(key, value); } public void addRequestProperty(String key, String value) { - String old = requestHeaders.getValue(key); - if (old == null) - { - requestHeaders.put(key, value); - } - else - { - requestHeaders.put(key, old + "," + value); - } + super.addRequestProperty(key, value); + requestHeaders.addValue(key, value); } public OutputStream getOutputStream() @@ -493,6 +479,17 @@ public class HTTPURLConnection { throw new ProtocolException("doInput is false"); } + + if (response.isError()) + { + int code = response.getCode(); + if (code == 404 || code == 410) + throw new FileNotFoundException(url.toString()); + + throw new IOException("Server returned HTTP response code " + code + + " for URL " + url.toString()); + } + return responseSink; } @@ -514,17 +511,9 @@ public class HTTPURLConnection return null; } } - Headers headers = response.getHeaders(); - LinkedHashMap ret = new LinkedHashMap(); - ret.put(null, Collections.singletonList(getStatusLine(response))); - for (Iterator i = headers.entrySet().iterator(); i.hasNext(); ) - { - Map.Entry entry = (Map.Entry) i.next(); - String key = (String) entry.getKey(); - String value = (String) entry.getValue(); - ret.put(key, Collections.singletonList(value)); - } - return Collections.unmodifiableMap(ret); + Map m = response.getHeaders().getAsMap(); + m.put(null, Collections.singletonList(getStatusLine(response))); + return Collections.unmodifiableMap(m); } String getStatusLine(Response response) @@ -552,20 +541,7 @@ public class HTTPURLConnection { return getStatusLine(response); } - Iterator i = response.getHeaders().entrySet().iterator(); - Map.Entry entry; - int count = 1; - do - { - if (!i.hasNext()) - { - return null; - } - entry = (Map.Entry) i.next(); - count++; - } - while (count <= index); - return (String) entry.getValue(); + return response.getHeaders().getHeaderValue(index - 1); } public String getHeaderFieldKey(int index) @@ -581,24 +557,8 @@ public class HTTPURLConnection return null; } } - if (index == 0) - { - return null; - } - Iterator i = response.getHeaders().entrySet().iterator(); - Map.Entry entry; - int count = 1; - do - { - if (!i.hasNext()) - { - return null; - } - entry = (Map.Entry) i.next(); - count++; - } - while (count <= index); - return (String) entry.getKey(); + // index of zero is the status line. + return response.getHeaders().getHeaderName(index - 1); } public String getHeaderField(String name) @@ -614,7 +574,7 @@ public class HTTPURLConnection return null; } } - return (String) response.getHeader(name); + return response.getHeader(name); } public long getHeaderFieldDate(String name, long def) diff --git a/libjava/classpath/gnu/java/net/protocol/http/Headers.java b/libjava/classpath/gnu/java/net/protocol/http/Headers.java index 9306fc4..b42faaa 100644 --- a/libjava/classpath/gnu/java/net/protocol/http/Headers.java +++ b/libjava/classpath/gnu/java/net/protocol/http/Headers.java @@ -1,5 +1,5 @@ /* Headers.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -44,125 +44,75 @@ import java.io.IOException; import java.io.InputStream; import java.text.DateFormat; import java.text.ParseException; -import java.util.Collection; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; /** - * A collection of HTTP header names and associated values. - * Retrieval of values is case insensitive. An iteration over the keys + * A collection of HTTP header names and associated values. The + * values are {@link ArrayList ArrayLists} of Strings. Retrieval of + * values is case insensitive. An iteration over the collection * returns the header names in the order they were received. * * @author Chris Burdess (dog@gnu.org) + * @author David Daney (ddaney@avtrex.com) */ -public class Headers - extends LinkedHashMap +class Headers { - + /** + * A list of HeaderElements + * + */ + private final ArrayList headers = new ArrayList(); + static final DateFormat dateFormat = new HTTPDateFormat(); - static class Header + static class HeaderElement { + String name; + String value; - final String name; - - Header(String name) + HeaderElement(String name, String value) { - if (name == null || name.length() == 0) - { - throw new IllegalArgumentException(name); - } this.name = name; + this.value = value; } - - public int hashCode() - { - return name.toLowerCase().hashCode(); - } - - public boolean equals(Object other) - { - if (other instanceof Header) - { - return ((Header) other).name.equalsIgnoreCase(name); - } - return false; - } - - public String toString() - { - return name; - } - - } - - static class HeaderEntry - implements Map.Entry - { - - final Map.Entry entry; - - HeaderEntry(Map.Entry entry) - { - this.entry = entry; - } - - public Object getKey() - { - return ((Header) entry.getKey()).name; - } - - public Object getValue() - { - return entry.getValue(); - } - - public Object setValue(Object value) - { - return entry.setValue(value); - } - - public int hashCode() - { - return entry.hashCode(); - } - - public boolean equals(Object other) - { - return entry.equals(other); - } - - public String toString() - { - return getKey().toString() + "=" + getValue(); - } - } public Headers() { } - public boolean containsKey(Object key) - { - return super.containsKey(new Header((String) key)); - } - - public Object get(Object key) + /** + * Return an Iterator over this collection of headers. + * Iterator.getNext() returns objects of type {@link HeaderElement}. + * + * @return the Iterator. + */ + Iterator iterator() { - return super.get(new Header((String) key)); + return headers.iterator(); } - + /** - * Returns the value of the specified header as a string. + * Returns the value of the specified header as a string. If + * multiple values are present, the last one is returned. */ public String getValue(String header) { - return (String) super.get(new Header(header)); + for (int i = headers.size() - 1; i >= 0; i--) + { + HeaderElement e = (HeaderElement)headers.get(i); + if (e.name.equalsIgnoreCase(header)) + { + return e.value; + } + } + return null; } /** @@ -228,51 +178,62 @@ public class Headers } } - public Object put(Object key, Object value) - { - return super.put(new Header((String) key), value); - } - - public Object remove(Object key) + /** + * Add a header to this set of headers. If there is an existing + * header with the same name, it is discarded. + * + * @param name the header name + * @param value the header value + * + * @see #addValue + */ + public void put(String name, String value) { - return super.remove(new Header((String) key)); + remove(name); + headers.add(headers.size(), new HeaderElement(name, value)); } - public void putAll(Map t) + /** + * Add all headers from a set of headers to this set. If any of the + * headers to be added have the same name as existing headers, the + * existing headers will be discarded. + * + * @param o the headers to be added + */ + public void putAll(Headers o) { - for (Iterator i = t.keySet().iterator(); i.hasNext(); ) + for (Iterator it = o.iterator(); it.hasNext(); ) { - String key = (String) i.next(); - String value = (String) t.get(key); - put(key, value); + HeaderElement e = (HeaderElement)it.next(); + remove(e.name); } - } - - public Set keySet() - { - Set keys = super.keySet(); - Set ret = new LinkedHashSet(); - for (Iterator i = keys.iterator(); i.hasNext(); ) + for (Iterator it = o.iterator(); it.hasNext(); ) { - ret.add(((Header) i.next()).name); + HeaderElement e = (HeaderElement)it.next(); + addValue(e.name, e.value); } - return ret; } - public Set entrySet() + /** + * Remove a header from this set of headers. If there is more than + * one instance of a header of the given name, they are all removed. + * + * @param name the header name + */ + public void remove(String name) { - Set entries = super.entrySet(); - Set ret = new LinkedHashSet(); - for (Iterator i = entries.iterator(); i.hasNext(); ) + for (Iterator it = headers.iterator(); it.hasNext(); ) { - Map.Entry entry = (Map.Entry) i.next(); - ret.add(new HeaderEntry(entry)); + HeaderElement e = (HeaderElement)it.next(); + if (e.name.equalsIgnoreCase(name)) + it.remove(); } - return ret; } /** - * Parse the specified input stream, adding headers to this collection. + * Parse the specified InputStream, adding headers to this collection. + * + * @param in the InputStream. */ public void parse(InputStream in) throws IOException @@ -334,18 +295,90 @@ public class Headers } } - private void addValue(String name, String value) + + /** + * Add a header to this set of headers. If there is an existing + * header with the same name, it is not effected. + * + * @param name the header name + * @param value the header value + * + * @see #put + */ + public void addValue(String name, String value) + { + headers.add(headers.size(), new HeaderElement(name, value)); + } + + /** + * Get a new Map containing all the headers. The keys of the Map + * are Strings (the header names). The values of the Map are + * unmodifiable Lists containing Strings (the header values). + * + * <p> + * + * The returned map is modifiable. Changing it will not effect this + * collection of Headers in any way. + * + * @return a Map containing all the headers. + */ + public Map getAsMap() { - Header key = new Header(name); - String old = (String) super.get(key); - if (old == null) + LinkedHashMap m = new LinkedHashMap(); + for (Iterator it = headers.iterator(); it.hasNext(); ) { - super.put(key, value); + HeaderElement e = (HeaderElement)it.next(); + ArrayList l = (ArrayList)m.get(e.name); + if (l == null) + { + l = new ArrayList(1); + l.add(e.value); + m.put(e.name, l); + } + else + l.add(0, e.value); } - else + for (Iterator it = m.entrySet().iterator(); it.hasNext(); ) { - super.put(key, old + ", " + value); + Map.Entry me = (Map.Entry)it.next(); + ArrayList l = (ArrayList)me.getValue(); + me.setValue(Collections.unmodifiableList(l)); } + return m; + } + + /** + * Get the name of the Nth header. + * + * @param i the header index. + * + * @return the header name. + * + * @see #getHeaderValue + */ + public String getHeaderName(int i) + { + if (i >= headers.size() || i < 0) + return null; + + return ((HeaderElement)headers.get(i)).name; + } + + /** + * Get the value of the Nth header. + * + * @param i the header index. + * + * @return the header value. + * + * @see #getHeaderName + */ + public String getHeaderValue(int i) + { + if (i >= headers.size() || i < 0) + return null; + + return ((HeaderElement)headers.get(i)).value; } } diff --git a/libjava/classpath/gnu/java/net/protocol/http/Request.java b/libjava/classpath/gnu/java/net/protocol/http/Request.java index b9441b3..e15ec41 100644 --- a/libjava/classpath/gnu/java/net/protocol/http/Request.java +++ b/libjava/classpath/gnu/java/net/protocol/http/Request.java @@ -1,5 +1,5 @@ /* Request.java -- - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -94,11 +94,6 @@ public class Request protected RequestBodyWriter requestBodyWriter; /** - * Request body negotiation threshold for 100-continue expectations. - */ - protected int requestBodyNegotiationThreshold; - - /** * Map of response header handlers. */ protected Map responseHeaderHandlers; @@ -127,7 +122,6 @@ public class Request this.path = path; requestHeaders = new Headers(); responseHeaderHandlers = new HashMap(); - requestBodyNegotiationThreshold = 4096; } /** @@ -251,21 +245,6 @@ public class Request } /** - * Sets the request body negotiation threshold. - * If this is set, it determines the maximum size that the request body - * may be before body negotiation occurs(via the - * <code>100-continue</code> expectation). This ensures that a large - * request body is not sent when the server wouldn't have accepted it - * anyway. - * @param threshold the body negotiation threshold, or <=0 to disable - * request body negotation entirely - */ - public void setRequestBodyNegotiationThreshold(int threshold) - { - requestBodyNegotiationThreshold = threshold; - } - - /** * Dispatches this request. * A request can only be dispatched once; calling this method a second * time results in a protocol exception. @@ -291,10 +270,10 @@ public class Request if (requestBodyWriter != null) { contentLength = requestBodyWriter.getContentLength(); - if (contentLength > requestBodyNegotiationThreshold) + String expect = getHeader("Expect"); + if (expect != null && expect.equals("100-continue")) { expectingContinue = true; - setHeader("Expect", "100-continue"); } else { @@ -323,12 +302,10 @@ public class Request String line = method + ' ' + requestUri + ' ' + version + CRLF; out.write(line.getBytes(US_ASCII)); // Request headers - for (Iterator i = requestHeaders.keySet().iterator(); - i.hasNext(); ) + for (Iterator i = requestHeaders.iterator(); i.hasNext(); ) { - String name =(String) i.next(); - String value =(String) requestHeaders.get(name); - line = name + HEADER_SEP + value + CRLF; + Headers.HeaderElement elt = (Headers.HeaderElement)i.next(); + line = elt.name + HEADER_SEP + elt.value + CRLF; out.write(line.getBytes(US_ASCII)); } out.write(CRLF.getBytes(US_ASCII)); @@ -459,23 +436,17 @@ public class Request void notifyHeaderHandlers(Headers headers) { - for (Iterator i = headers.entrySet().iterator(); i.hasNext(); ) + for (Iterator i = headers.iterator(); i.hasNext(); ) { - Map.Entry entry = (Map.Entry) i.next(); - String name =(String) entry.getKey(); + Headers.HeaderElement entry = (Headers.HeaderElement) i.next(); // Handle Set-Cookie - if ("Set-Cookie".equalsIgnoreCase(name)) - { - String value = (String) entry.getValue(); - handleSetCookie(value); - } + if ("Set-Cookie".equalsIgnoreCase(entry.name)) + handleSetCookie(entry.value); + ResponseHeaderHandler handler = - (ResponseHeaderHandler) responseHeaderHandlers.get(name); + (ResponseHeaderHandler) responseHeaderHandlers.get(entry.name); if (handler != null) - { - String value = (String) entry.getValue(); - handler.setValue(value); - } + handler.setValue(entry.value); } } @@ -528,6 +499,9 @@ public class Request throw new ProtocolException("Unsupported Content-Encoding: " + contentCoding); } + // Remove the Content-Encoding header because the content is + // no longer compressed. + responseHeaders.remove("Content-Encoding"); } return in; } diff --git a/libjava/classpath/gnu/java/net/protocol/http/Response.java b/libjava/classpath/gnu/java/net/protocol/http/Response.java index 58d7454..76fac93 100644 --- a/libjava/classpath/gnu/java/net/protocol/http/Response.java +++ b/libjava/classpath/gnu/java/net/protocol/http/Response.java @@ -1,5 +1,5 @@ /* Response.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -188,6 +188,28 @@ public class Response { return headers.getDateValue(name); } + + /** + * Tests whether this response indicates a redirection. + * + * @return <code>true</code> if, <code>false</code> otherwise. + */ + public boolean isRedirect() + { + return (code != 304 && getCodeClass() == 3); + } + + /** + * Tests whether this response indicates an error. + * Errors are the response codes <code>4xx</code> - Client error and + * <code>5xx</code> - Server error. + * + * @return <code>true</code> if, <code>false</code> otherwise. + */ + public boolean isError() + { + return (getCodeClass() == 4 || getCodeClass() == 5); + } /** * Returns an InputStream that returns the body of the response. diff --git a/libjava/classpath/gnu/java/net/protocol/http/ResponseHeaderHandler.java b/libjava/classpath/gnu/java/net/protocol/http/ResponseHeaderHandler.java index 8e4e649..4c5261d 100644 --- a/libjava/classpath/gnu/java/net/protocol/http/ResponseHeaderHandler.java +++ b/libjava/classpath/gnu/java/net/protocol/http/ResponseHeaderHandler.java @@ -41,7 +41,7 @@ package gnu.java.net.protocol.http; /** * Callback interface for objects that wish to be notified of response * header values. - * @see Request#setHeaderHandler(String) + * @see Request#setResponseHeaderHandler(String, ResponseHeaderHandler) * * @author Chris Burdess (dog@gnu.org) */ diff --git a/libjava/classpath/gnu/java/net/protocol/jar/Connection.java b/libjava/classpath/gnu/java/net/protocol/jar/Connection.java index e2a052e..41c5d6d 100644 --- a/libjava/classpath/gnu/java/net/protocol/jar/Connection.java +++ b/libjava/classpath/gnu/java/net/protocol/jar/Connection.java @@ -1,5 +1,5 @@ /* Connection - jar url connection for java.net - Copyright (C) 1999, 2002, 2003, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2002, 2003, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,7 @@ exception statement from your version. */ package gnu.java.net.protocol.jar; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -145,7 +146,7 @@ public final class Connection extends JarURLConnection jar_entry = (JarEntry) jar_file.getEntry (entry_name); if(jar_entry == null) - throw new IOException ("No entry for " + entry_name + " exists."); + throw new FileNotFoundException("No entry for " + entry_name + " exists."); } connected = true; @@ -159,9 +160,6 @@ public final class Connection extends JarURLConnection if (! doInput) throw new ProtocolException("Can't open InputStream if doInput is false"); - if (jar_entry == null) - throw new IOException (jar_url + " couldn't be found."); - return jar_file.getInputStream (jar_entry); } |