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

Reply via email to