David Daney wrote:
PR libgcj/26487 shows problems with our existing header handling.
I hacked up the attached patch, which needs more testing (please test it!)
The basic problem is that the headers were being held in a map which
scrambled them up if there were more than one header of the same name.
My change was to just hold the headers in a list. This makes some
searching operations less efficient, but seems the best way to keep the
headers from being combined.
New version of the patch. Just cleaned up and javadocs added.
I guess I will commit it tomorrow if no one objects.
2006-03-02 David Daney <[EMAIL PROTECTED]>
* gnu/java/net/protocol/http/HTTPURLConnection.java
(getRequestProperties): Rewrote.
(addRequestProperty): Rewrote.
(getHeaderFields): Rewrote.
(getHeaderField): Rewrote.
(getHeaderFieldKey): Rewrote.
(getHeaderField): Removed useless cast.
* gnu/java/net/protocol/http/Headers.java: Entire class rewritten.
* gnu/java/net/protocol/http/Request.java (dispatch): Use new
Headers
interface.
(notifyHeaderHandlers): Use new Headers interface.
Index: gnu/java/net/protocol/http/HTTPURLConnection.java
===================================================================
RCS file:
/sources/classpath/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java,v
retrieving revision 1.22
diff -c -p -r1.22 HTTPURLConnection.java
*** gnu/java/net/protocol/http/HTTPURLConnection.java 27 Feb 2006 23:37:20
-0000 1.22
--- gnu/java/net/protocol/http/HTTPURLConnection.java 2 Mar 2006 20:40:32
-0000
*************** public class HTTPURLConnection
*** 422,441 ****
if (connected)
throw new IllegalStateException("Already connected");
! HashMap m = new HashMap(requestHeaders);
! Iterator it = m.entrySet().iterator();
! while (it.hasNext())
! {
! Map.Entry e = (Map.Entry)it.next();
! String s = (String)e.getValue();
! String sa[] = s.split(",");
! ArrayList l = new ArrayList(sa.length);
! for (int i = 0; i < sa.length; i++)
! {
! l.add(sa[i].trim());
! }
! e.setValue(Collections.unmodifiableList(l));
! }
return Collections.unmodifiableMap(m);
}
--- 422,428 ----
if (connected)
throw new IllegalStateException("Already connected");
! Map m = requestHeaders.getAsMap();
return Collections.unmodifiableMap(m);
}
*************** public class HTTPURLConnection
*** 449,464 ****
public void addRequestProperty(String key, String value)
{
super.addRequestProperty(key, value);
!
! String old = requestHeaders.getValue(key);
! if (old == null)
! {
! requestHeaders.put(key, value);
! }
! else
! {
! requestHeaders.put(key, old + "," + value);
! }
}
public OutputStream getOutputStream()
--- 436,442 ----
public void addRequestProperty(String key, String value)
{
super.addRequestProperty(key, value);
! requestHeaders.addValue(key, value);
}
public OutputStream getOutputStream()
*************** public class HTTPURLConnection
*** 533,549 ****
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);
}
String getStatusLine(Response response)
--- 511,519 ----
return null;
}
}
! Map m = response.getHeaders().getAsMap();
! m.put(null, Collections.singletonList(getStatusLine(response)));
! return Collections.unmodifiableMap(m);
}
String getStatusLine(Response response)
*************** public class HTTPURLConnection
*** 571,590 ****
{
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();
}
public String getHeaderFieldKey(int index)
--- 541,547 ----
{
return getStatusLine(response);
}
! return response.getHeaders().getHeaderValue(index - 1);
}
public String getHeaderFieldKey(int index)
*************** public class HTTPURLConnection
*** 600,623 ****
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();
}
public String getHeaderField(String name)
--- 557,564 ----
return null;
}
}
! // index of zero is the status line.
! return response.getHeaders().getHeaderName(index - 1);
}
public String getHeaderField(String name)
*************** public class HTTPURLConnection
*** 633,639 ****
return null;
}
}
! return (String) response.getHeader(name);
}
public long getHeaderFieldDate(String name, long def)
--- 574,580 ----
return null;
}
}
! return response.getHeader(name);
}
public long getHeaderFieldDate(String name, long def)
Index: gnu/java/net/protocol/http/Headers.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/net/protocol/http/Headers.java,v
retrieving revision 1.6
diff -c -p -r1.6 Headers.java
*** gnu/java/net/protocol/http/Headers.java 2 Mar 2006 00:26:57 -0000
1.6
--- gnu/java/net/protocol/http/Headers.java 2 Mar 2006 20:40:32 -0000
***************
*** 1,5 ****
/* Headers.java --
! Copyright (C) 2004 Free Software Foundation, Inc.
This file is part of GNU Classpath.
--- 1,5 ----
/* Headers.java --
! Copyright (C) 2004, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
*************** import java.io.IOException;
*** 44,167 ****
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
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
* returns the header names in the order they were received.
*
* @author Chris Burdess ([EMAIL PROTECTED])
*/
! public class Headers
! extends LinkedHashMap
{
!
static final DateFormat dateFormat = new HTTPDateFormat();
! static class Header
{
! final String name;
!
! Header(String name)
{
- if (name == null || name.length() == 0)
- {
- throw new IllegalArgumentException(name);
- }
this.name = name;
}
-
- 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.
*/
public String getValue(String header)
{
! return (String) super.get(new Header(header));
}
/**
--- 44,118 ----
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
+ import java.util.ArrayList;
+ import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
! * A collection of HTTP header names and associated values. The
! * values are [EMAIL PROTECTED] 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 ([EMAIL PROTECTED])
+ * @author David Daney ([EMAIL PROTECTED])
*/
! class Headers
{
! /**
! * A list of HeaderElements
! *
! */
! private final ArrayList headers = new ArrayList();
!
static final DateFormat dateFormat = new HTTPDateFormat();
! static class HeaderElement
{
+ String name;
+ String value;
! HeaderElement(String name, String value)
{
this.name = name;
+ this.value = value;
}
}
public Headers()
{
}
! /**
! * Return an Iterator over this collection of headers.
! * Iterator.getNext() returns objects of type [EMAIL PROTECTED]
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)
{
! 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;
}
/**
*************** public class Headers
*** 227,277 ****
}
}
! public Object put(Object key, Object 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;
}
/**
! * Parse the specified input stream, adding headers to this collection.
*/
public void parse(InputStream in)
throws IOException
--- 178,239 ----
}
}
! /**
! * 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)
{
! remove(name);
! headers.add(headers.size(), new HeaderElement(name, value));
}
! /**
! * 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
*************** public class Headers
*** 333,350 ****
}
}
! private void addValue(String name, String value)
{
! Header key = new Header(name);
! String old = (String) super.get(key);
! if (old == null)
{
! super.put(key, value);
}
! else
{
! super.put(key, old + ", " + value);
}
}
}
--- 295,385 ----
}
}
!
! /**
! * 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()
{
! LinkedHashMap m = new LinkedHashMap();
! for (Iterator it = headers.iterator(); it.hasNext(); )
{
! HeaderElement e = (HeaderElement)it.next();
! String k = e.name.toLowerCase();
! ArrayList l = (ArrayList)m.get(k);
! if (l == null)
! {
! l = new ArrayList(1);
! l.add(e.value);
! m.put(k, l);
! }
! else
! l.add(e.value);
}
! for (Iterator it = m.entrySet().iterator(); it.hasNext(); )
{
! 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;
}
}
Index: gnu/java/net/protocol/http/Request.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/net/protocol/http/Request.java,v
retrieving revision 1.8
diff -c -p -r1.8 Request.java
*** gnu/java/net/protocol/http/Request.java 9 Feb 2006 09:23:38 -0000
1.8
--- gnu/java/net/protocol/http/Request.java 2 Mar 2006 20:40:32 -0000
*************** public class Request
*** 302,313 ****
String line = method + ' ' + requestUri + ' ' + version + CRLF;
out.write(line.getBytes(US_ASCII));
// Request headers
! for (Iterator i = requestHeaders.keySet().iterator();
! i.hasNext(); )
{
! String name =(String) i.next();
! String value =(String) requestHeaders.get(name);
! line = name + HEADER_SEP + value + CRLF;
out.write(line.getBytes(US_ASCII));
}
out.write(CRLF.getBytes(US_ASCII));
--- 302,311 ----
String line = method + ' ' + requestUri + ' ' + version + CRLF;
out.write(line.getBytes(US_ASCII));
// Request headers
! for (Iterator i = requestHeaders.iterator(); i.hasNext(); )
{
! 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));
*************** public class Request
*** 438,460 ****
void notifyHeaderHandlers(Headers headers)
{
! for (Iterator i = headers.entrySet().iterator(); i.hasNext(); )
{
! Map.Entry entry = (Map.Entry) i.next();
! String name =(String) entry.getKey();
// Handle Set-Cookie
! if ("Set-Cookie".equalsIgnoreCase(name))
! {
! String value = (String) entry.getValue();
! handleSetCookie(value);
! }
ResponseHeaderHandler handler =
! (ResponseHeaderHandler) responseHeaderHandlers.get(name);
if (handler != null)
! {
! String value = (String) entry.getValue();
! handler.setValue(value);
! }
}
}
--- 436,452 ----
void notifyHeaderHandlers(Headers headers)
{
! for (Iterator i = headers.iterator(); i.hasNext(); )
{
! Headers.HeaderElement entry = (Headers.HeaderElement) i.next();
// Handle Set-Cookie
! if ("Set-Cookie".equalsIgnoreCase(entry.name))
! handleSetCookie(entry.value);
!
ResponseHeaderHandler handler =
! (ResponseHeaderHandler) responseHeaderHandlers.get(entry.name);
if (handler != null)
! handler.setValue(entry.value);
}
}