Index: java/org/apache/commons/httpclient/Authenticator.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Authenticator.java,v retrieving revision 1.39 diff -u -r1.39 Authenticator.java --- java/org/apache/commons/httpclient/Authenticator.java 28 Jan 2003 04:40:20 -0000 1.39 +++ java/org/apache/commons/httpclient/Authenticator.java 4 Feb 2003 03:13:06 -0000 @@ -246,7 +246,7 @@ LOG.trace("enter Authenticator.authenticate(HttpMethod, HttpState)"); - Header challengeHeader = method.getResponseHeader(WWW_AUTH); + Header challengeHeader = method.getResponseHeaderGroup().getCondensedHeader(WWW_AUTH); return authenticate(method, state, challengeHeader, WWW_AUTH_RESP); } @@ -270,7 +270,7 @@ LOG.trace("enter Authenticator.authenticateProxy(HttpMethod, " + "HttpState)"); - Header challengeHeader = method.getResponseHeader(PROXY_AUTH); + Header challengeHeader = method.getResponseHeaderGroup().getCondensedHeader(PROXY_AUTH); return authenticate(method, state, challengeHeader, PROXY_AUTH_RESP); } @@ -356,8 +356,8 @@ NTCredentials credentials = null; - if (method.getRequestHeader("Host") != null) { - String host = method.getRequestHeader("Host").getValue(); + if (method.getRequestHeaderGroup().containsHeader("Host")) { + String host = method.getRequestHeaderGroup().getFirstHeader("Host").getValue(); try { credentials = (NTCredentials) (proxy ? state.getProxyCredentials(host) @@ -518,7 +518,7 @@ //Get the authorization header value try { - authHeader = method.getResponseHeader(authName).getValue(); + authHeader = method.getResponseHeaderGroup().getCondensedHeader(authName).getValue(); authHeader = authHeader.substring(7).trim(); } catch (NullPointerException npe) { return (Map) (new java.util.Hashtable(0)); Index: java/org/apache/commons/httpclient/ChunkedInputStream.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/ChunkedInputStream.java,v retrieving revision 1.12 diff -u -r1.12 ChunkedInputStream.java --- java/org/apache/commons/httpclient/ChunkedInputStream.java 28 Jan 2003 04:40:20 -0000 1.12 +++ java/org/apache/commons/httpclient/ChunkedInputStream.java 4 Feb 2003 03:12:59 -0000 @@ -84,6 +84,7 @@ * @author Martin Elwin * @author Eric Johnson * @author Mike Bowler + * @author Michael Becke * * @since 2.0 * @@ -306,46 +307,12 @@ } /** - * Stores the footers into map of Headers + * Stores the footers to the method. * @throws IOException If an IO problem occurs */ private void parseFooters() throws IOException { - String line = readLine(); - while ((line != null) && (!line.equals(""))) { - int colonPos = line.indexOf(':'); - if (colonPos != -1) { - String key = line.substring(0, colonPos).trim(); - String val = line.substring(colonPos + 1).trim(); - Header footer = new Header(key, val); - method.addResponseFooter(footer); - } - line = readLine(); - } - } - - /** - * Read the next line from {@link #in}. - * @return String The next line. - * @throws IOException If an IO problem occurs. - */ - private String readLine() throws IOException { - StringBuffer buf = new StringBuffer(); - while (true) { - int ch = in.read(); - if (ch < 0) { - if (buf.length() == 0) { - return null; - } else { - break; - } - } else if (ch == '\r') { - continue; - } else if (ch == '\n') { - break; - } - buf.append((char) ch); - } - return (buf.toString()); + method.getResponseFooterGroup().clear(); + HeaderParser.parseHeaders(in, method.getResponseFooterGroup()); } /** Index: java/org/apache/commons/httpclient/ConnectMethod.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/ConnectMethod.java,v retrieving revision 1.7 diff -u -r1.7 ConnectMethod.java --- java/org/apache/commons/httpclient/ConnectMethod.java 28 Jan 2003 04:40:20 -0000 1.7 +++ java/org/apache/commons/httpclient/ConnectMethod.java 4 Feb 2003 03:13:00 -0000 @@ -138,8 +138,10 @@ if (method instanceof HttpMethodBase) { ((HttpMethodBase) method).addRequestHeaders(state, conn); } - conn.print(method.getRequestHeader("Host").toExternalForm()); - Header header = method.getRequestHeader(Authenticator.PROXY_AUTH_RESP); + conn.print(method.getRequestHeaderGroup().getFirstHeader("Host").toExternalForm()); + Header header = method.getRequestHeaderGroup().getFirstHeader( + Authenticator.PROXY_AUTH_RESP + ); if (header == null) { header = getRequestHeader(Authenticator.PROXY_AUTH_RESP); } Index: java/org/apache/commons/httpclient/HeaderGroup.java =================================================================== RCS file: java/org/apache/commons/httpclient/HeaderGroup.java diff -N java/org/apache/commons/httpclient/HeaderGroup.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ java/org/apache/commons/httpclient/HeaderGroup.java 4 Feb 2003 03:13:06 -0000 @@ -0,0 +1,187 @@ +package org.apache.commons.httpclient; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * A class for combining a set of headers. This class allows for multiple + * headers with the same name and keeps track of the order in which headers were + * added. + * + * @author Michael Becke + */ +public class HeaderGroup { + + private List headers; + + /** + * Constructor for HeaderGroup. + */ + public HeaderGroup() { + this.headers = new ArrayList(); + } + + /** + * Removes any contained headers. + */ + public void clear() { + headers.clear(); + } + + /** + * Adds the given header to the group. The order in which this header was + * added is preserved. + * + * @param header the header to add + */ + public void addHeader(Header header) { + headers.add(header); + } + + /** + * Removes the given header. + * + * @param header the header to remove + */ + public void removeHeader(Header header) { + headers.remove(header); + } + + /** + * Sets all of the headers contained within this group overriding any + * existing headers. The headers are added in the order in which they appear + * in the array. + * + * @param headers the headers to set + */ + public void setHeaders(Header[] headers) { + clear(); + + for (int i = 0; i < headers.length; i++) { + addHeader(headers[i]); + } + } + + /** + * Gets a header representing all of the header values with the given name. + * If more that one header with the given name exists the values will be + * combined with a "," as per RFC 1945. + * + *

Header name comparison is case insensitive. + * + * @param name the name of the header(s) to get + * @return a header with a condensed value or null if no + * headers by the given name are present + */ + public Header getCondensedHeader(String name) { + Header[] headers = getHeaders(name); + + if (headers.length == 0) { + return null; + } else if (headers.length == 1) { + return headers[0]; + } else { + StringBuffer valueBuffer = new StringBuffer(headers[0].getValue()); + + for (int i = 1; i < headers.length; i++) { + valueBuffer.append(", "); + valueBuffer.append(headers[i].getValue()); + } + + return new Header(name.toLowerCase(), valueBuffer.toString()); + } + } + + /** + * Gets all of the headers with the given name. The returned array + * maintains the relative order in which the headers were added. + * + *

Header name comparison is case insensitive. + * + * @param name the name of the header(s) to get + * + * @return an array of length >= 0 + */ + public Header[] getHeaders(String name) { + ArrayList headersFound = new ArrayList(); + + for (Iterator headerIter = headers.iterator(); headerIter.hasNext();) { + Header header = (Header) headerIter.next(); + if (header.getName().equalsIgnoreCase(name)) { + headersFound.add(header); + } + } + + return (Header[]) headersFound.toArray(new Header[headersFound.size()]); + } + + /** + * Gets the first header with the given name. + * + *

Header name comparison is case insensitive. + * + * @param name the name of the header to get + * @return the first header or null + */ + public Header getFirstHeader(String name) { + for (Iterator headerIter = headers.iterator(); headerIter.hasNext();) { + Header header = (Header) headerIter.next(); + if (header.getName().equalsIgnoreCase(name)) { + return header; + } + } + + return null; + } + + /** + * Gets the last header with the given name. + * + *

Header name comparison is case insensitive. + * + * @param name the name of the header to get + * @return the last header or null + */ + public Header getLastHeader(String name) { + // start at the end of the list and work backwards + for (int i = headers.size() - 1; i >= 0; i--) { + Header header = (Header) headers.get(i); + if (header.getName().equalsIgnoreCase(name)) { + return header; + } + } + + return null; + } + + /** + * Gets all of the headers contained within this group. + * + * @return an array of length >= 0 + */ + public Header[] getAllHeaders() { + return (Header[]) headers.toArray(new Header[headers.size()]); + } + + /** + * Tests if headers with the given name are contained within this group. + * + *

Header name comparison is case insensitive. + * + * @param name the header name to test for + * @return true if at least one header with the name is + * contained, false otherwise + */ + public boolean containsHeader(String name) { + for (Iterator headerIter = headers.iterator(); headerIter.hasNext();) { + Header header = (Header) headerIter.next(); + if (header.getName().equalsIgnoreCase(name)) { + return true; + } + } + + return false; + } + +} Index: java/org/apache/commons/httpclient/HeaderParser.java =================================================================== RCS file: java/org/apache/commons/httpclient/HeaderParser.java diff -N java/org/apache/commons/httpclient/HeaderParser.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ java/org/apache/commons/httpclient/HeaderParser.java 4 Feb 2003 03:12:59 -0000 @@ -0,0 +1,115 @@ +package org.apache.commons.httpclient; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A utility class for parsing http header values. + * + * @author Michael Becke + */ +public class HeaderParser { + + /** Log object for this class. */ + private static final Log LOG = LogFactory.getLog(HeaderParser.class); + /** Log for any wire messages. */ + private static final Log WIRE_LOG = LogFactory.getLog("httpclient.wire"); + + /** + * Constructor for HeaderParser. + */ + private HeaderParser() {} + + /** + * Parses headers from the given stream and adds them to the headerGroup. + * + * @param is the stream to read headers from + * @param headerGroup the group to add the headers to + * @throws IOException if an IO error occurs while reading from the stream + * @throws HttpException if there is an error parsing a header value + */ + public static void parseHeaders( + InputStream is, + HeaderGroup headerGroup + ) throws IOException, HttpException { + LOG.trace("enter HeaderParser.parseHeaders(HttpConnection, HeaderGroup)"); + + String name = null; + StringBuffer value = null; + for (; ;) { + String line = readLine(is); + if ((line == null) || (line.length() < 1)) { + break; + } + + // Parse the header name and value + // Check for folded headers first + // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2 + // discussion on folded headers + if ((line.charAt(0) == ' ') || (line.charAt(0) == '\t')) { + // we have continuation folded header + // so append value + value.append(' '); + value.append(line.trim()); + } else { + // make sure we save the previous name,value pair if present + if (name != null) { + headerGroup.addHeader(new Header(name, value.toString())); + } + + // Otherwise we should have normal HTTP header line + // Parse the header name and value + int colon = line.indexOf(":"); + if (colon < 0) { + throw new HttpException("Unable to parse header: " + line); + } + name = line.substring(0, colon).trim(); + value = new StringBuffer(line.substring(colon + 1).trim()); + } + + } + + // make sure we save the last name,value pair if present + if (name != null) { + headerGroup.addHeader(new Header(name, value.toString())); + } + } + + /** + * Reads a single line from a stream. + * + * @param is the stream to read from + * + * @return the line read or null if the line was empty + * + * @throws IOException if an error occurs while reading from the stream + */ + public static String readLine(InputStream is) throws IOException { + LOG.trace("enter HeaderParser.readLine()"); + + StringBuffer buf = new StringBuffer(); + while (true) { + int ch = is.read(); + if (ch < 0) { + if (buf.length() == 0) { + return null; + } else { + break; + } + } else if (ch == '\r') { + continue; + } else if (ch == '\n') { + break; + } + buf.append((char) ch); + } + if (WIRE_LOG.isDebugEnabled() && buf.length() > 0) { + WIRE_LOG.debug("<< \"" + buf.toString() + "\" [\\r\\n]"); + } + return (buf.toString()); + } + +} Index: java/org/apache/commons/httpclient/HttpMethod.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethod.java,v retrieving revision 1.23 diff -u -r1.23 HttpMethod.java --- java/org/apache/commons/httpclient/HttpMethod.java 28 Jan 2003 04:40:20 -0000 1.23 +++ java/org/apache/commons/httpclient/HttpMethod.java 4 Feb 2003 03:13:04 -0000 @@ -75,6 +75,7 @@ * @author Rod Waldhoff * @author Jeff Dever * @author Mike Bowler + * @author Michael Becke * * @version $Revision: 1.23 $ $Date: 2003/01/28 04:40:20 $ * @since 1.0 @@ -146,53 +147,77 @@ * @return true if strict mode is enabled. */ boolean isStrictMode(); - + /** + * @deprecated use request HeaderGroup + * * Set the specified request header, overwriting any * previous value. * Note that header-name matching is case insensitive. * @param headerName the header's name * @param headerValue the header's value + * + * @see #getRequestHeaderGroup() + * @see HeaderGroup#addHeader(Header) */ void setRequestHeader(String headerName, String headerValue); /** + * @deprecated use request HeaderGroup + * * Set the specified request header, overwriting any * previous value. * Note that header-name matching is case insensitive. * @param header the header + * + * @see #getRequestHeaderGroup() + * @see HeaderGroup#addHeader(Header) */ void setRequestHeader(Header header); /** - * Adds the specified request header, NOT overwriting any - * previous value. + * Adds the specified request header, NOT overwriting any previous value. * Note that header-name matching is case insensitive. * @param headerName the header's name * @param headerValue the header's value + * + * @see #getRequestHeaderGroup() + * @see HeaderGroup#addHeader(Header) */ void addRequestHeader(String headerName, String headerValue); /** - * Adds the specified request header, NOT overwriting any - * previous value. + * Adds the specified request header, NOT overwriting any previous value. * Note that header-name matching is case insensitive. * @param header the header + * + * @see #getRequestHeaderGroup() + * @see HeaderGroup#addHeader(Header) */ void addRequestHeader(Header header); /** + * @deprecated use request HeaderGroup + * * Get the request header associated with the given name. * Note that header-name matching is case insensitive. * @param headerName the header name * @return the header + * + * @see #getRequestHeaderGroup() + * @see HeaderGroup#getHeaders(String) */ Header getRequestHeader(String headerName); /** + * @deprecated use request HeaderGroup + * * Remove all request headers associated with the given name. * Note that header-name matching is case insensitive. * @param headerName the header name + * + * @see #getRequestHeaderGroup() + * @see HeaderGroup#removeHeader(Header) */ void removeRequestHeader(String headerName); @@ -232,9 +257,20 @@ /** * Return an array of my request headers. * @return an array of request headers. + * + * @see #getRequestHeaderGroup() + * @see HeaderGroup#getAllHeaders() */ Header[] getRequestHeaders(); + /** + * Gets the HeaderGroup representing the request headers. + * @return the request headers + * + * @since 2.0 + */ + HeaderGroup getRequestHeaderGroup(); + // ---------------------------------------------------------------- Queries /** @@ -259,28 +295,60 @@ /** * Return an array of my response headers. * @return An array of all the response headers. + * + * @see #getRequestHeaderGroup() + * @see HeaderGroup#getAllHeaders() */ Header[] getResponseHeaders(); /** + * Gets the HeaderGroup representing the response headers. + * @return the response headers + * + * @since 2.0 + */ + HeaderGroup getResponseHeaderGroup(); + + /** + * @deprecated use response HeaderGroup + * * Return the specified response header. Note that header-name matching is * case insensitive. * @param headerName The name of the header to be returned. * @return The specified response header. + * + * @see #getRequestHeaderGroup() + * @see HeaderGroup#getHeaders(String) */ Header getResponseHeader(String headerName); /** * Return an array of my response footers * @return null if no footers are available + * + * @see #getResponseFooterGroup() + * @see HeaderGroup#getAllHeaders() */ Header[] getResponseFooters(); /** + * Gets the HeaderGroup representing the response footers. + * @return the response footers + * + * @since 2.0 + */ + HeaderGroup getResponseFooterGroup(); + + /** + * @deprecated use response footer HeaderGroup + * * Return the specified response footer. Note that footer-name matching is * case insensitive. * @param footerName The name of the footer. * @return The response footer. + * + * @see #getResponseFooterGroup() + * @see HeaderGroup#getHeaders(String) */ Header getResponseFooter(String footerName); Index: java/org/apache/commons/httpclient/HttpMethodBase.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v retrieving revision 1.108 diff -u -r1.108 HttpMethodBase.java --- java/org/apache/commons/httpclient/HttpMethodBase.java 2 Feb 2003 04:30:13 -0000 1.108 +++ java/org/apache/commons/httpclient/HttpMethodBase.java 4 Feb 2003 03:13:12 -0000 @@ -69,10 +69,7 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; -import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; import java.util.Set; import java.util.StringTokenizer; @@ -165,16 +162,16 @@ // ----------------------------------------------------- Instance variables /** My request headers, if any. */ - private Map requestHeaders = new HashMap(); + private HeaderGroup requestHeaders = new HeaderGroup(); /** The Status-Line from the response. */ private StatusLine statusLine = null; /** My response headers, if any. */ - private Map responseHeaders = new HashMap(); + private HeaderGroup responseHeaders = new HeaderGroup(); /** My response footers, if any. */ - private Map responseFooters = null; + private HeaderGroup responseFooters = new HeaderGroup(); /** Realms that we tried to authenticate to */ private Set realms = null; @@ -440,10 +437,7 @@ * @param footer The new footer to add. */ public void addResponseFooter(Header footer) { - if (responseFooters == null) { - responseFooters = new HashMap(); - } - responseFooters.put(footer.getName().toLowerCase(), footer); + responseFooters.addHeader(footer); } /** @@ -537,7 +531,15 @@ * @param header the header */ public void setRequestHeader(Header header) { - requestHeaders.put(header.getName().toLowerCase(), header); + + Header[] headers = requestHeaders.getHeaders(header.getName()); + + for (int i = 0; i < headers.length; i++) { + requestHeaders.removeHeader(headers[i]); + } + + requestHeaders.addHeader(header); + } /** @@ -551,8 +553,11 @@ * @return the matching header */ public Header getRequestHeader(String headerName) { - return (headerName == null) - ? null : (Header) (requestHeaders.get(headerName.toLowerCase())); + if (headerName == null) { + return null; + } else { + return requestHeaders.getCondensedHeader(headerName); + } } /** @@ -561,11 +566,31 @@ * @return an array of my request headers. */ public Header[] getRequestHeaders() { - return (Header[]) (requestHeaders.values().toArray( - new Header[requestHeaders.size()])); + return requestHeaders.getAllHeaders(); + } + + /** + * @see org.apache.commons.httpclient.HttpMethod#getRequestHeaderGroup() + */ + public HeaderGroup getRequestHeaderGroup() { + return requestHeaders; + } + + /** + * @see org.apache.commons.httpclient.HttpMethod#getResponseFooterGroup() + */ + public HeaderGroup getResponseFooterGroup() { + return responseFooters; } /** + * @see org.apache.commons.httpclient.HttpMethod#getResponseHeaderGroup() + */ + public HeaderGroup getResponseHeaderGroup() { + return responseHeaders; + } + + /** * Convenience method top provide access to the status code. * * @return the status code associated with the latest response. @@ -598,8 +623,7 @@ * @return an array of my response headers. */ public Header[] getResponseHeaders() { - return (Header[]) (responseHeaders.values().toArray( - new Header[responseHeaders.size()])); + return responseHeaders.getAllHeaders(); } /** @@ -612,10 +636,12 @@ * * @return the matching header */ - public Header getResponseHeader(String headerName) { - return (headerName == null) - ? null - : (Header) (responseHeaders.get(headerName.toLowerCase())); + public Header getResponseHeader(String headerName) { + if (headerName == null) { + return null; + } else { + return responseHeaders.getCondensedHeader(headerName); + } } /** @@ -694,11 +720,7 @@ * @return null if no footers are available */ public Header[] getResponseFooters() { - if (responseFooters == null) { - return null; - } - return (Header[]) (responseFooters.values().toArray( - new Header[responseFooters.size()])); + return responseFooters.getAllHeaders(); } /** @@ -711,11 +733,11 @@ * @return the matching footer */ public Header getResponseFooter(String footerName) { - if (responseFooters == null) { + if (footerName == null) { return null; + } else { + return responseFooters.getCondensedHeader(footerName); } - return (footerName == null) ? null - : (Header) (responseFooters.get(footerName.toLowerCase())); } /** @@ -1139,6 +1161,7 @@ queryString = null; requestHeaders.clear(); responseHeaders.clear(); + responseFooters.clear(); statusLine = null; used = false; http11 = true; @@ -1174,7 +1197,12 @@ * @param headerName the header name */ public void removeRequestHeader(String headerName) { - requestHeaders.remove(headerName.toLowerCase()); + + Header[] headers = requestHeaders.getHeaders(headerName); + for (int i = 0; i < headers.length; i++) { + requestHeaders.removeHeader(headers[i]); + } + } // ---------------------------------------------------------------- Queries @@ -1682,6 +1710,7 @@ readResponseBody(state, conn); processResponseBody(state, conn); } catch (IOException e) { + LOG.warn("error reading response", e); throw new HttpRecoverableException(e.toString()); } } @@ -1836,54 +1865,7 @@ + "HttpConnection)"); responseHeaders.clear(); - - String name = null; - String value = null; - for (; ;) { - String line = conn.readLine(); - if ((line == null) || (line.length() < 1)) { - break; - } - - // Parse the header name and value - // Check for folded headers first - // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2 - // discussion on folded headers - boolean isFolded = false; - if ((line.charAt(0) == ' ') || (line.charAt(0) == '\t')) { - // we have continuation folded header - // so append value - isFolded = true; - value = line.substring(1).trim(); - } else { - // Otherwise we should have normal HTTP header line - // Parse the header name and value - int colon = line.indexOf(":"); - if (colon < 0) { - throw new HttpException("Unable to parse header: " + line); - } - name = line.substring(0, colon).trim(); - value = line.substring(colon + 1).trim(); - } - Header header = getResponseHeader(name); - if (null == header) { - header = new Header(name, value); - } else { - String oldvalue = header.getValue(); - if (null != oldvalue) { - if (isFolded) { - // LWS becomes space plus extended value - header = new Header(name, oldvalue + " " + value); - } else { - // Append additional header value - header = new Header(name, oldvalue + ", " + value); - } - } else { - header = new Header(name, value); - } - } - setResponseHeader(header); - } + HeaderParser.parseHeaders(conn.getResponseInputStream(), responseHeaders); } /** @@ -2049,9 +2031,10 @@ LOG.trace("enter HttpMethodBase.writeRequestHeaders(HttpState," + "HttpConnection)"); addRequestHeaders(state, conn); - Iterator it = requestHeaders.values().iterator(); - while (it.hasNext()) { - conn.print(((Header) it.next()).toExternalForm()); + + Header[] headers = getRequestHeaders(); + for (int i = 0; i < headers.length; i++) { + conn.print(headers[i].toExternalForm()); } } @@ -2162,20 +2145,6 @@ newValue = ""; } return existingValue + ", " + newValue; - } - - /** - * Sets the specified response header. - * - * @param header the header to set. - * - * @since 2.0 - */ - private void setResponseHeader(Header header) { - if (header == null) { - return; - } - responseHeaders.put(header.getName().toLowerCase(), header); } /** Index: java/org/apache/commons/httpclient/ResponseInputStream.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/ResponseInputStream.java,v retrieving revision 1.22 diff -u -r1.22 ResponseInputStream.java --- java/org/apache/commons/httpclient/ResponseInputStream.java 30 Jan 2003 05:01:54 -0000 1.22 +++ java/org/apache/commons/httpclient/ResponseInputStream.java 4 Feb 2003 03:13:01 -0000 @@ -137,14 +137,18 @@ count = 0; // Retrieving transfer encoding header - Header transferEncoding = method.getResponseHeader("transfer-encoding"); + Header transferEncoding = method.getResponseHeaderGroup().getFirstHeader( + "transfer-encoding" + ); if ((null != transferEncoding) && (transferEncoding.getValue(). toLowerCase().indexOf("chunked") != -1)) { chunk = true; } // Retrieving content length header - Header contentLengthHeader = method.getResponseHeader("content-length"); + Header contentLengthHeader = method.getResponseHeaderGroup().getFirstHeader( + "content-length" + ); if (null != contentLengthHeader) { try { this.contentLength = Index: java/org/apache/commons/httpclient/util/HttpURLConnection.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/util/HttpURLConnection.java,v retrieving revision 1.11 diff -u -r1.11 HttpURLConnection.java --- java/org/apache/commons/httpclient/util/HttpURLConnection.java 31 Jan 2003 00:33:37 -0000 1.11 +++ java/org/apache/commons/httpclient/util/HttpURLConnection.java 4 Feb 2003 03:13:15 -0000 @@ -247,14 +247,8 @@ LOG.trace("enter HttpURLConnection.getHeaderField(String)"); // Note: Return the last matching header in the Header[] array, as in // the JDK implementation. - Header[] headers = this.method.getResponseHeaders(); - for (int i = headers.length - 1; i >= 0; i--) { - if (headers[i].getName().equalsIgnoreCase(name)) { - return headers[i].getValue(); - } - } - - return null; + Header header = this.method.getRequestHeaderGroup().getLastHeader(name); + return (header == null ? null : header.getValue()); } /** @@ -275,10 +269,7 @@ return null; } - // Note: HttpClient does not currently keep headers in the same order - // that they are read from the HTTP server. - - Header[] headers = this.method.getResponseHeaders(); + Header[] headers = this.method.getResponseHeaderGroup().getAllHeaders(); if (keyPosition < 0 || keyPosition > headers.length) { return null; } @@ -307,7 +298,7 @@ // Note: HttpClient does not currently keep headers in the same order // that they are read from the HTTP server. - Header[] headers = this.method.getResponseHeaders(); + Header[] headers = this.method.getResponseHeaderGroup().getAllHeaders(); if (position < 0 || position > headers.length) { return null; } Index: test/org/apache/commons/httpclient/SimpleHttpConnection.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/SimpleHttpConnection.java,v retrieving revision 1.8 diff -u -r1.8 SimpleHttpConnection.java --- test/org/apache/commons/httpclient/SimpleHttpConnection.java 31 Jan 2003 23:23:17 -0000 1.8 +++ test/org/apache/commons/httpclient/SimpleHttpConnection.java 4 Feb 2003 03:12:58 -0000 @@ -63,24 +63,24 @@ package org.apache.commons.httpclient; -import org.apache.commons.httpclient.protocol.Protocol; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.StringReader; +import java.io.OutputStreamWriter; import java.util.Vector; +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + /** * For test-nohost testing purposes only. * * @author Jeff Dever + * @author Michael Becke */ class SimpleHttpConnection extends HttpConnection { @@ -90,8 +90,9 @@ Vector headers = new Vector(); Vector bodies = new Vector(); - BufferedReader headerReader = null; - ByteArrayInputStream bodyInputStream = null; + + ByteArrayInputStream inputStream; + ByteArrayOutputStream bodyOutputStream = null; public void addResponse(String header) { @@ -122,26 +123,40 @@ } public void assertOpen() throws IllegalStateException { - if (bodyInputStream == null) { + if (inputStream == null) { throw new IllegalStateException(); } } public void assertNotOpen() throws IllegalStateException{ - if (bodyInputStream != null) { + if (inputStream != null) { throw new IllegalStateException(); } } public void open() throws IOException { - if (headerReader != null) return; + if (inputStream != null) return; try{ log.debug("hit: " + hits); - headerReader = new BufferedReader( - new StringReader((String)headers.elementAt(hits))); - bodyInputStream = new ByteArrayInputStream( - HttpConstants.getContentBytes((String)bodies.elementAt(hits))); + + // write the header to a byte array + ByteArrayOutputStream headerOutputStream = new ByteArrayOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter( headerOutputStream ); + writer.write((String) headers.elementAt(hits)); + // terminate the headers + writer.write("\r\n"); + writer.close(); + + byte[] headerContent = headerOutputStream.toByteArray(); + byte[] bodyContent = HttpConstants.getContentBytes((String)bodies.elementAt(hits)); + + // combine the header and body content so they can be read from one steam + byte[] content = new byte[headerContent.length + bodyContent.length]; + System.arraycopy(headerContent, 0, content, 0, headerContent.length); + System.arraycopy(bodyContent, 0, content, headerContent.length, bodyContent.length); + + inputStream = new ByteArrayInputStream( content ); bodyOutputStream = new ByteArrayOutputStream(); hits++; } catch (ArrayIndexOutOfBoundsException aiofbe) { @@ -151,13 +166,9 @@ } public void close() { - if (headerReader != null) { - try { headerReader.close(); } catch(IOException e) {} - headerReader = null; - } - if (bodyInputStream != null) { - try { bodyInputStream.close(); } catch(IOException e) {} - bodyInputStream = null; + if (inputStream != null) { + try { inputStream.close(); } catch(IOException e) {} + inputStream = null; } if (bodyOutputStream != null) { try { bodyOutputStream.close(); } catch(IOException e) {} @@ -175,7 +186,7 @@ public String readLine() throws IOException, IllegalStateException { - String str = headerReader.readLine(); + String str = HeaderParser.readLine(inputStream); log.debug("read: " + str); return str; } @@ -187,7 +198,7 @@ public InputStream getResponseInputStream() { - return bodyInputStream; + return inputStream; } public OutputStream getRequestOutputStream() { Index: test/org/apache/commons/httpclient/SimpleHttpMethod.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/SimpleHttpMethod.java,v retrieving revision 1.4 diff -u -r1.4 SimpleHttpMethod.java --- test/org/apache/commons/httpclient/SimpleHttpMethod.java 23 Jan 2003 22:48:25 -0000 1.4 +++ test/org/apache/commons/httpclient/SimpleHttpMethod.java 4 Feb 2003 03:12:57 -0000 @@ -96,26 +96,48 @@ return "Simple"; } - public Header getResponseHeader(String name) { - try { - if(name.equalsIgnoreCase(header.getName())) { - return header; - } else { - return super.getResponseHeader(name); - } - } catch(NullPointerException e) { - return super.getResponseHeader(name); + /** + * Makes sure any respose header that exists has been added to the response + * header group. + */ + private void ensureResponseHeaderIsSet() { + if ( header != null ) { + super.getResponseHeaderGroup().addHeader(header); + header = null; } } + /** + * @see org.apache.commons.httpclient.HttpMethod#execute(org.apache.commons.httpclient.HttpState, org.apache.commons.httpclient.HttpConnection) + */ + public int execute(HttpState state, HttpConnection connection) + throws HttpException, IOException { + return super.execute(state, connection); + } + + /** + * @see org.apache.commons.httpclient.HttpMethod#getResponseHeader(java.lang.String) + * @deprecated + */ + public Header getResponseHeader(String headerName) { + ensureResponseHeaderIsSet(); + return super.getResponseHeader(headerName); + } - public int execute(HttpState state, HttpConnection conn) - throws HttpException, IOException{ - return super.execute(state, conn); - } + /** + * @see org.apache.commons.httpclient.HttpMethod#getResponseHeaderGroup() + */ + public HeaderGroup getResponseHeaderGroup() { + ensureResponseHeaderIsSet(); + return super.getResponseHeaderGroup(); + } + + /** + * @see org.apache.commons.httpclient.HttpMethod#getResponseHeaders() + */ + public Header[] getResponseHeaders() { + ensureResponseHeaderIsSet(); + return super.getResponseHeaders(); + } - public void addRequestHeaders(HttpState state, HttpConnection conn) - throws HttpException, IOException{ - super.addRequestHeaders(state, conn); - } } Index: test/org/apache/commons/httpclient/TestResponseHeaders.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestResponseHeaders.java,v retrieving revision 1.6 diff -u -r1.6 TestResponseHeaders.java --- test/org/apache/commons/httpclient/TestResponseHeaders.java 23 Jan 2003 22:48:27 -0000 1.6 +++ test/org/apache/commons/httpclient/TestResponseHeaders.java 4 Feb 2003 03:12:57 -0000 @@ -119,11 +119,14 @@ HttpMethod method = new SimpleHttpMethod(); SimpleHttpConnection conn = new SimpleHttpConnection(headers, body); method.execute(state, conn); - assertEquals("close", method.getResponseHeader("Connection").getValue()); - assertEquals(body.length(), Integer.parseInt(method.getResponseHeader("Content-Length").getValue())); - assertEquals("text/xml; charset=utf-8", method.getResponseHeader("Content-Type").getValue()); - assertEquals("Wed, 28 Mar 2001 05:05:04 GMT", method.getResponseHeader("Date").getValue()); - assertEquals("UserLand Frontier/7.0-WinNT", method.getResponseHeader("Server").getValue()); + + HeaderGroup responseHeaders = method.getResponseHeaderGroup(); + + assertEquals("close", responseHeaders.getFirstHeader("Connection").getValue()); + assertEquals(body.length(), Integer.parseInt(responseHeaders.getFirstHeader("Content-Length").getValue())); + assertEquals("text/xml; charset=utf-8", responseHeaders.getFirstHeader("Content-Type").getValue()); + assertEquals("Wed, 28 Mar 2001 05:05:04 GMT", responseHeaders.getFirstHeader("Date").getValue()); + assertEquals("UserLand Frontier/7.0-WinNT", responseHeaders.getFirstHeader("Server").getValue()); } /** @@ -153,8 +156,8 @@ HttpMethod method = new SimpleHttpMethod(); SimpleHttpConnection conn = new SimpleHttpConnection(headers, body); method.execute(state, conn); - assertEquals(null, method.getResponseHeader(null)); - assertEquals(null, method.getResponseHeader("bogus")); + assertEquals(null, method.getResponseHeaderGroup().getFirstHeader(null)); + assertEquals(null, method.getResponseHeaderGroup().getFirstHeader("bogus")); } public void testFoldedHeaders() throws Exception { @@ -172,11 +175,28 @@ HttpMethod method = new SimpleHttpMethod(); SimpleHttpConnection conn = new SimpleHttpConnection(headers, body); method.execute(state, conn); - assertEquals("close", method.getResponseHeader("Connection").getValue()); - assertEquals(body.length(), Integer.parseInt(method.getResponseHeader("Content-Length").getValue())); - assertEquals("text/xml; charset=utf-8 boundary=XXXX", method.getResponseHeader("Content-Type").getValue()); - assertEquals("Wed, 28 Mar 2001 05:05:04 GMT", method.getResponseHeader("Date").getValue()); - assertEquals("UserLand Frontier/7.0-WinNT", method.getResponseHeader("Server").getValue()); - assertTrue(method.getResponseHeader("Content-Type").toString().indexOf("boundary") != -1); + + HeaderGroup responseHeaders = method.getResponseHeaderGroup(); + + assertEquals("close", responseHeaders.getFirstHeader("Connection").getValue()); + assertEquals( + body.length(), + Integer.parseInt(responseHeaders.getFirstHeader("Content-Length").getValue()) + ); + assertEquals( + "text/xml; charset=utf-8 boundary=XXXX", + responseHeaders.getFirstHeader("Content-Type").getValue() + ); + assertEquals( + "Wed, 28 Mar 2001 05:05:04 GMT", + responseHeaders.getFirstHeader("Date").getValue() + ); + assertEquals( + "UserLand Frontier/7.0-WinNT", + responseHeaders.getFirstHeader("Server").getValue() + ); + assertTrue( + responseHeaders.getFirstHeader("Content-Type").toString().indexOf("boundary") != -1 + ); } }