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
This commit is contained in:
parent
27079765d0
commit
8aa540d2f7
1367 changed files with 188789 additions and 22762 deletions
|
@ -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,29 +38,47 @@ 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;
|
||||
|
||||
int size;
|
||||
int count;
|
||||
boolean meta;
|
||||
boolean eof;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param in the response socket input stream
|
||||
|
@ -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;
|
||||
return 0xff & buf[0];
|
||||
}
|
||||
|
||||
public int read(byte[] buffer)
|
||||
throws IOException
|
||||
{
|
||||
return read(buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
@ -330,28 +331,228 @@ public class HTTPConnection
|
|||
return cookieManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 super.get(new Header((String) key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the specified header as a string.
|
||||
* Return an Iterator over this collection of headers.
|
||||
* Iterator.getNext() returns objects of type {@link HeaderElement}.
|
||||
*
|
||||
* @return the Iterator.
|
||||
*/
|
||||
Iterator iterator()
|
||||
{
|
||||
return headers.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
/**
|
||||
* 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.put(new Header((String) key), value);
|
||||
}
|
||||
|
||||
public Object remove(Object key)
|
||||
{
|
||||
return super.remove(new Header((String) key));
|
||||
}
|
||||
|
||||
public void putAll(Map t)
|
||||
{
|
||||
for (Iterator i = t.keySet().iterator(); i.hasNext(); )
|
||||
{
|
||||
String key = (String) i.next();
|
||||
String value = (String) t.get(key);
|
||||
put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Set keySet()
|
||||
{
|
||||
Set keys = super.keySet();
|
||||
Set ret = new LinkedHashSet();
|
||||
for (Iterator i = keys.iterator(); i.hasNext(); )
|
||||
{
|
||||
ret.add(((Header) i.next()).name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Set entrySet()
|
||||
{
|
||||
Set entries = super.entrySet();
|
||||
Set ret = new LinkedHashSet();
|
||||
for (Iterator i = entries.iterator(); i.hasNext(); )
|
||||
{
|
||||
Map.Entry entry = (Map.Entry) i.next();
|
||||
ret.add(new HeaderEntry(entry));
|
||||
}
|
||||
return ret;
|
||||
remove(name);
|
||||
headers.add(headers.size(), new HeaderElement(name, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the specified input stream, adding headers to this collection.
|
||||
* 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 it = o.iterator(); it.hasNext(); )
|
||||
{
|
||||
HeaderElement e = (HeaderElement)it.next();
|
||||
remove(e.name);
|
||||
}
|
||||
for (Iterator it = o.iterator(); it.hasNext(); )
|
||||
{
|
||||
HeaderElement e = (HeaderElement)it.next();
|
||||
addValue(e.name, e.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
for (Iterator it = headers.iterator(); it.hasNext(); )
|
||||
{
|
||||
HeaderElement e = (HeaderElement)it.next();
|
||||
if (e.name.equalsIgnoreCase(name))
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
Header key = new Header(name);
|
||||
String old = (String) super.get(key);
|
||||
if (old == null)
|
||||
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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -93,11 +93,6 @@ public class Request
|
|||
*/
|
||||
protected RequestBodyWriter requestBodyWriter;
|
||||
|
||||
/**
|
||||
* Request body negotiation threshold for 100-continue expectations.
|
||||
*/
|
||||
protected int requestBodyNegotiationThreshold;
|
||||
|
||||
/**
|
||||
* Map of response header handlers.
|
||||
*/
|
||||
|
@ -127,7 +122,6 @@ public class Request
|
|||
this.path = path;
|
||||
requestHeaders = new Headers();
|
||||
responseHeaderHandlers = new HashMap();
|
||||
requestBodyNegotiationThreshold = 4096;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -250,21 +244,6 @@ public class Request
|
|||
this.authenticator = authenticator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue