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:
Mark Wielaard 2006-03-10 21:46:48 +00:00
parent 27079765d0
commit 8aa540d2f7
1367 changed files with 188789 additions and 22762 deletions

View file

@ -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();
}
}

View file

@ -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.
}
}
}

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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 &lt;=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;
}

View file

@ -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.

View file

@ -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)
*/