rwaldhoff 01/08/13 15:37:12 Modified: httpclient/src/java/org/apache/commons/httpclient Tag: rlwrefactoring HttpMethod.java HttpMethodBase.java httpclient/src/java/org/apache/commons/httpclient/methods Tag: rlwrefactoring PostMethod.java PutMethod.java Log: * have writeRequestBody return boolean, removing the need for "used" depedency between HttpMethodBase and subclasses * javadoc and reorder HttpMethod and HttpMethodBase methods Revision Changes Path No revision No revision 1.5.2.3 +34 -17 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethod.java Index: HttpMethod.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethod.java,v retrieving revision 1.5.2.2 retrieving revision 1.5.2.3 diff -u -r1.5.2.2 -r1.5.2.3 --- HttpMethod.java 2001/08/13 15:54:31 1.5.2.2 +++ HttpMethod.java 2001/08/13 22:37:12 1.5.2.3 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethod.java,v 1.5.2.2 2001/08/13 15:54:31 rwaldhoff Exp $ - * $Revision: 1.5.2.2 $ - * $Date: 2001/08/13 15:54:31 $ + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethod.java,v 1.5.2.3 2001/08/13 22:37:12 rwaldhoff Exp $ + * $Revision: 1.5.2.3 $ + * $Date: 2001/08/13 22:37:12 $ * ==================================================================== * Copyright (C) The Apache Software Foundation. All rights reserved. * @@ -16,7 +16,7 @@ import java.io.OutputStream; import java.io.IOException; import java.util.Iterator; -import java.util.HashMap; +import java.util.Collection; import org.apache.commons.httpclient.log.*; @@ -29,10 +29,19 @@ */ public interface HttpMethod { + // -------------------------------------------------------------- Constants + + /** <tt>HTTP/1.1</tt> */ public static final String PROTOCOL = "HTTP/1.1"; + + /** <tt>org.apache.commons.httpclient.HttpMethod</tt> log. */ public static final Log log = LogSource.getInstance("org.apache.commons.httpclient.HttpMethod"); + + /** <tt>httpclient.wire</tt> log. */ public static final Log wireLog = LogSource.getInstance("httpclient.wire"); + // ------------------------------------------- Property Setters and Getters + /** * Obtain name of this method, suitable for use in the "request line", * for example <tt>GET</tt> or <tt>POST</tt>. @@ -90,7 +99,7 @@ public boolean followRedirects(); /** - * Set whether or not this method should automatically follow + * Set whether or not I should automatically follow * HTTP redirects (status code 302, etc.) */ public void setFollowRedirects(boolean followRedirects); @@ -149,21 +158,14 @@ */ public Iterator getRequestHeaders(); + // ---------------------------------------------------------------- Queries + /** * Confirm that I am ready to execute. */ public boolean validate(); /** - * Execute this method. - * @param state state information to associate with this request - * @param request the stream to write the request to - * @param response the stream to read the response from - * @param baseHeaders an intitial set of headers to use - */ - public int execute(State state, HttpConnection connection, HashMap baseHeaders) throws HttpException, IOException; - - /** * Return the status code associated with the latest response. */ public int getStatusCode(); @@ -184,14 +186,29 @@ public Header getResponseHeader(String headerName); /** - * Return <tt>true</tt> if I have been used but not - * recycled. + * Return <tt>true</tt> if I have been {@link #execute executed} + * but not recycled. */ public boolean hasBeenUsed(); + // --------------------------------------------------------- Action Methods + + /** + * Execute this method. + * + * @param state state information to associate with this request + * @param request the stream to write the request to + * @param response the stream to read the response from + * @param baseHeaders an intitial set of headers to use + * + * @return the integer status code if one was obtained, or <tt>-1</tt> + */ + public int execute(State state, HttpConnection connection, Collection baseHeaders) throws HttpException, IOException; + /** * Recycle this method so that it can be used again. - * This method clears my path and query string. + * All of my instances variables will be reset + * once this method has been called. */ public void recycle(); } 1.10.2.7 +524 -152 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java Index: HttpMethodBase.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v retrieving revision 1.10.2.6 retrieving revision 1.10.2.7 diff -u -r1.10.2.6 -r1.10.2.7 --- HttpMethodBase.java 2001/08/13 17:54:27 1.10.2.6 +++ HttpMethodBase.java 2001/08/13 22:37:12 1.10.2.7 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v 1.10.2.6 2001/08/13 17:54:27 rwaldhoff Exp $ - * $Revision: 1.10.2.6 $ - * $Date: 2001/08/13 17:54:27 $ + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v 1.10.2.7 2001/08/13 22:37:12 rwaldhoff Exp $ + * $Revision: 1.10.2.7 $ + * $Date: 2001/08/13 22:37:12 $ * ==================================================================== * Copyright (C) The Apache Software Foundation. All rights reserved. * @@ -22,6 +22,7 @@ import java.util.List; import java.util.Vector; import java.net.URLEncoder; +import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -35,78 +36,140 @@ * <dt>{@link #getName}</dt> * <dd>to return the approriate name for this method</dd> * <dt>{@link #getRequestContentLength}</dt> - * <dd>when the request has a body</dd> + * <dd>when the request may have a body</dd> + * <dt>{@link #writeRequestBody}</dt> + * <dd>when the request may have a body</dd> * <dt>...</dt> * </dl> + * + * @author <a href="mailto:[EMAIL PROTECTED]">Remy Maucherat</a> + * @author Rodney Waldhoff + * @version $Id: HttpMethodBase.java,v 1.10.2.7 2001/08/13 22:37:12 rwaldhoff Exp $ */ public abstract class HttpMethodBase implements HttpMethod { - protected String path = null; - protected boolean followRedirects = false; - protected HashMap parameters = new HashMap(); - protected String queryString = null; - protected HashMap requestHeaders = new HashMap(); - protected HashMap responseHeaders = new HashMap(); - protected int statusCode = -1; - protected String statusText = "Not executed"; - protected boolean used = false; - protected boolean http11 = true; - protected boolean bodySent = false; + // ----------------------------------------------------------- Constructors + + /** + * No-arg constructor. + */ public HttpMethodBase() { } + /** + * Path-specifying constructor. + * + * @param path my path + */ public HttpMethodBase(String path) { setPath(path); } - public abstract String getName(); - - protected int getRequestContentLength() { - return 0; - } + // ------------------------------------------- Property Setters and Getters - protected void writeRequestBody(State state, HttpConnection conn) throws IOException, HttpException { - bodySent = true; - return; - } + /** + * Obtain name of this method, suitable for use in the "request line", + * for example <tt>GET</tt> or <tt>POST</tt>. + * @return the name of this method + */ + public abstract String getName(); + /** + * Set the path part of my request. + * @param path the path to request + */ public void setPath(String path) { this.path = path; } + /** + * Get the path part my request. + * @return the path to request + */ public String getPath() { return this.path; } + /** + * Set the specified request header, overwriting any + * previous value. + * @param headerName the header's name + * @param headerValue the header's value + */ public void setRequestHeader(String headerName, String headerValue) { Header header = new Header(headerName, headerValue); requestHeaders.put(headerName.toLowerCase(),header); } + /** + * Set the specified request header, overwriting any + * previous value. + * @param header the header + */ public void setRequestHeader(Header header) { requestHeaders.put(header.getName().toLowerCase(),header); } + /** + * 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 + */ public Header getRequestHeader(String headerName) { return (Header)(requestHeaders.get(headerName.toLowerCase())); } + /** + * Remove the request header associated with the given name. + * Note that header-name matching is case insensitive. + * @param headerName the header name + * @return the header + */ public void removeRequestHeader(String headerName) { requestHeaders.remove(headerName.toLowerCase()); } + /** + * Whether or not I should automatically follow + * HTTP redirects (status code 302, etc.) + */ public boolean followRedirects() { return this.followRedirects; } + /** + * Set whether or not I should automatically follow + * HTTP redirects (status code 302, etc.) + */ public void setFollowRedirects(boolean followRedirects) { this.followRedirects = followRedirects; } + /** + * Set the specified request (for GET requests, query string) + * parameter, overwriting any previous value associated with the + * given <i>parameterName</i>. + * + * @param parameterName the name of the parameter, which MUST NOT + * be <tt>null</tt> + * @param parameterValue the name of the parameter, which MAY + * be <tt>null</tt> + */ public void setParameter(String parameterName, String parameterValue) { parameters.put(parameterName,parameterValue); } + /** + * Set the specified request (for GET requests, query string) + * parameter, NOT overwriting any previous value associated + * with the given <i>parameterName</i>. + * + * @param parameterName the name of the parameter, which MUST NOT + * be <tt>null</tt> + * @param parameterValue the name of the parameter, which MAY + * be <tt>null</tt> + */ public void addParameter(String parameterName, String parameterValue) { Object old = parameters.put(parameterName,parameterValue); if(null != old) { @@ -127,10 +190,26 @@ } } + /** + * Remove all request parameters associated with + * the given <i>parameterName</i>. + * + * @param parameterName the name of the parameter, which MUST NOT + * be <tt>null</tt> + */ public void removeParameter(String paramName) { parameters.remove(paramName); } + /** + * Remove a single instance of the specified name-value pair + * from my request parameters + * + * @param parameterName the name of the parameter, which MUST NOT + * be <tt>null</tt> + * @param parameterValue the name of the parameter, which MAY + * be <tt>null</tt> + */ public void removeParameter(String paramName, String paramValue) { Object old = parameters.get(paramName); if(null != old) { @@ -151,59 +230,85 @@ } } + /** + * Set my query string. + */ public void setQueryString(String queryString) { this.queryString = queryString; } + /** + * Return an iterator over my headers. + */ public Iterator getRequestHeaders() { - return requestHeaders.entrySet().iterator(); + return requestHeaders.values().iterator(); } + // ---------------------------------------------------------------- Queries + + /** + * Confirm that I am ready to execute. + * <p> + * This implementation always returns <tt>true</tt>. + * @return <tt>true</tt> + */ public boolean validate() { return true; } + /** + * Return the status code associated with the latest response. + */ public int getStatusCode() { return statusCode; } + /** + * Return the status text (or "reason phrase") associated with the latest response. + */ public String getStatusText() { return statusText; } + /** + * Return an interator over my response headers. + */ public Iterator getResponseHeaders() { return responseHeaders.entrySet().iterator(); } + /** + * Return the specified response headers. + */ public Header getResponseHeader(String headerName) { return (Header)(responseHeaders.get(headerName.toLowerCase())); } + /** + * Return <tt>true</tt> if I have been {@link #execute executed} + * but not recycled. + */ public boolean hasBeenUsed() { return used; } - public void recycle() { - path = null; - followRedirects = false; - parameters.clear(); - queryString = null; - requestHeaders.clear(); - responseHeaders.clear(); - statusCode = -1; - statusText = "Not executed"; - used = false; - http11 = false; - bodySent = false; - } + + // --------------------------------------------------------- Action Methods /** + * Execute this method. + * * @param state state information to associate with this request * @param request the stream to write the request to * @param response the stream to read the response from * @param baseHeaders an intitial set of headers to use + * + * @return the integer status code if one was obtained, or <tt>-1</tt> + * @throws HttpException if I have been used but not recycled + * @throws HttpExcpetion if I am not {@link #validate valid} + * @throws NullPointerException if <i>state</i> or <i>connection</i> is <tt>null</tt> */ - public int execute(State state, HttpConnection connection, HashMap baseHeaders) throws HttpException, IOException { + public int execute(State state, HttpConnection connection, Collection baseHeaders) throws HttpException, IOException { log.debug("HttpMethodBase.execute(State,HttpConnection,HashMap)"); if(hasBeenUsed()) { @@ -252,7 +357,7 @@ if(statusCode < HttpStatus.SC_OK) { if(HttpStatus.SC_CONTINUE == statusCode) { if(!bodySent) { - writeRequestBody(state,connection); + bodySent = writeRequestBody(state,connection); } else { // error? 100 response, but I've already written the body return statusCode; @@ -373,48 +478,228 @@ return statusCode; } + // ------------------------------------------------------ Protected Methods + + /** + * Writes my request to the given {@link HttpConnection}. + * <p> + * The request is written according to the following logic: + * <ol> + * <li>{@link #writeRequestLine} is invoked to write the request line.</li> + * <li>{@link #writeRequestHeaders} is invoked to write the associated headers.</li> + * <li><tt>\r\n</tt> is sent to close the head part of the request.</li> + * <li>{@link #writeRequestBody} is invoked to write the body part of the request.</li> + * </ol> + * Subclasses may want to override one or more of the above methods to + * to customize the processing. (Or they may choose to override this method + * if dramatically different processing is required.) + * + * @param state the client state + * @param conn the {@link HttpConnection} to write the request to + */ protected void writeRequest(State state, HttpConnection conn) throws IOException, HttpException { log.debug("HttpMethodBase.writeRequest(State,HttpConnection)"); writeRequestLine(state,conn); writeRequestHeaders(state,conn); conn.writeLine(); // close head - writeRequestBody(state,conn); + bodySent = writeRequestBody(state,conn); } - protected void readResponse(State state, HttpConnection conn) throws IOException, HttpException { - log.debug("HttpMethodBase.readResponse(State,HttpConnection)"); - readStatusLine(state,conn); - processStatusLine(state,conn); - readResponseHeaders(state,conn); - processResponseHeaders(state,conn); - readResponseBody(state,conn); - processResponseBody(state,conn); + + /** + * Writes the "request line" to the given {@link HttpConnection}. + * <p> + * Subclasses may want to override this method to + * to customize the processing. + * + * @see #generateRequestLine + * + * @param state the client state + * @param conn the {@link HttpConnection} to write to + */ + protected void writeRequestLine(State state, HttpConnection conn) throws IOException, HttpException { + log.debug("HttpMethodBase.writeRequestLine(State,HttpConnection)"); + String requestLine = HttpMethodBase.generateRequestLine(conn, getName(),getPath(),queryString,parameters,(http11 ? "HTTP/1.1" : "HTTP/1.0")); + conn.print(requestLine); } - protected void processStatusLine(State state, HttpConnection conn) { + /** + * Writes the request headers to the given {@link HttpConnection}. + * <p> + * This implementation invokes {@link #generateRequestHeaders}, + * and then writes each header to the request stream. + * <p> + * Subclasses may want to override this method to + * to customize the processing. + * + * @see #generateRequestHeaders + * @see #getRequestHeaders + * + * @param state the client state + * @param conn the {@link HttpConnection} to write to + */ + protected void writeRequestHeaders(State state, HttpConnection conn) throws IOException, HttpException { + log.debug("HttpMethodBase.writeRequestHeaders(State,HttpConnection)"); + generateRequestHeaders(state,conn); + Iterator it = getRequestHeaders(); + while(it.hasNext()) { + conn.print(it.next().toString()); + } } - protected void processResponseHeaders(State state, HttpConnection conn) { - // add cookies, if any - // should we set cookies? - Header setCookieHeader = getResponseHeader("set-cookie2"); - if(null == setCookieHeader) { //ignore old-style if new is supported - setCookieHeader = getResponseHeader("set-cookie"); + /** + * Populates the {@link #requestHeaders} map to + * with additional {@link Header headers} to be + * submitted to the given {@link HttpConnection}. + * <p> + * This implementation adds <tt>User-Agent</tt>, + * <tt>Host</tt>, <tt>Cookie</tt>, <tt>Content-Length</tt> + * <tt>Transfer-Encoding</tt>, and <tt>Authorization</tt> + * headers, where appropriate. + * <p> + * Subclasses may want to override this method to + * to add additional headers, and may choose to + * invoke this implementation (via <tt>super</tt>) + * to add the "standard" headers. + * + * @see #writeRequestHeaders + * + * @param state the client state + * @param conn the {@link HttpConnection} the headers will eventually be written to + */ + protected void generateRequestHeaders(State state, HttpConnection conn) throws IOException, HttpException { + + // add default user agent + if (!requestHeaders.containsKey("user-agent")) { + setRequestHeader(HttpClient.USER_AGENT); } - if(setCookieHeader != null) { - try { - Cookie[] cookies = Cookie.parse(conn.getHost(), setCookieHeader); - state.addCookies(cookies); - } catch (Exception e) { - log.error("processResponseHeaders(State,HttpConnection)",e); + // add host (should do this conditionally?, i.e., don't send to http/1.0?) + if (!requestHeaders.containsKey("host")) { + setRequestHeader("Host",conn.getHost()); + } + + // add cookies + if (!requestHeaders.containsKey("cookie")) { + Vector cookies = state.getCookies(); + if (cookies != null && cookies.size() > 0) { + setRequestHeader(Cookie.createCookieHeader(conn.getHost(), getPath(), cookies)); + } + } + + // add content length or chunking + int len = getRequestContentLength(); + if(!requestHeaders.containsKey("content-length")) { + if(-1 < len) { + setRequestHeader("Content-Length",String.valueOf(len)); + } else if(http11 && len < 0) { + // XXX should this be an "add" rather than a "set"? + setRequestHeader("Transfer-Encoding","chunked"); } } + + // add authorization header, if needed + if(!requestHeaders.containsKey("authorization")) { + Header wwwAuthenticateHeader = (Header)(responseHeaders.get("www-authenticate")); + if(null != wwwAuthenticateHeader) { + String challengeResponse = Authenticator.challengeResponse(wwwAuthenticateHeader.getValue(),state); + if (challengeResponse != null) { + setRequestHeader("Authorization",challengeResponse); + } + } + } } - protected void processResponseBody(State state, HttpConnection conn) { + /** + * Return the length (in bytes) of + * my request body, suitable for use in + * a <tt>Content-Length</tt> header. + * <p> + * Return <tt>-1</tt> when the content-length + * is unknown. + * <p> + * This implementation returns <tt>0</tt>, + * indicating that the request has no + * body. + * @return <tt>0</tt>, indicating that the request has no body. + */ + protected int getRequestContentLength() { + return 0; + } + + /** + * Write the request body to the given {@link HttpConnection} + * <p> + * If an expectation is required, this method should + * ensure that is has been sent by checking the + * {@link #getStatusCode status code}. + * <p> + * This method should return <tt>true</tt> + * if the request body was actually sent (or is empty), + * or <tt>false</tt> if it could not be sent for + * some reason (for example, expectation required but + * not present). + * <p> + * This implementation always returns <tt>true</tt>. + * @return <tt>true</tt> + */ + protected boolean writeRequestBody(State state, HttpConnection conn) throws IOException, HttpException { + return true; } + /** + * Reads the response from the given {@link HttpConnection}. + * <p> + * The response is written according to the following logic: + * <ol> + * <li>{@link #readStatusLine} is invoked to read the request line.</li> + * <li> + * {@link #processStatusLine} is invoked, allowing the method + * to respond to the status line if desired. + * </li> + * <li>{@link #readResponseHeaders} is invoked to read the associated headers.</li> + * <li> + * {@link #processStatusLine} is invoked, allowing the method + * to respond to the headers if desired. + * </li> + * <li> + * {@link #readResponseBody} is invoked to read the associated body (if any). + * </li> + * <li> + * {@link #processResponseBody} is invoked, allowing the method + * to respond to the body if desired. + * </li> + * </ol> + * Subclasses may want to override one or more of the above methods to + * to customize the processing. (Or they may choose to override this method + * if dramatically different processing is required.) + * + * @param state the client state + * @param conn the {@link HttpConnection} to read the response from + */ + protected void readResponse(State state, HttpConnection conn) throws IOException, HttpException { + log.debug("HttpMethodBase.readResponse(State,HttpConnection)"); + readStatusLine(state,conn); + processStatusLine(state,conn); + readResponseHeaders(state,conn); + processResponseHeaders(state,conn); + readResponseBody(state,conn); + processResponseBody(state,conn); + } + + /** + * Read the status line from the given {@link HttpConnection}, + * setting {@link #statusCode} and {@link #statusText}. + * <p> + * Subclasses may want to override this method to + * to customize the processing. + * + * @see #readResponse + * @see #processStatusLine + * + * @param state the client state + * @param conn the {@link HttpConnection} to read the response from + */ protected void readStatusLine(State state, HttpConnection conn) throws IOException, HttpException { log.debug("HttpMethodBase.readStatusLine(State,HttpConnection)"); statusCode = -1; @@ -461,6 +746,36 @@ } } + /** + * When this method is invoked, the {@link #statusCode} + * and {@link #statusText} values will have been set (in other + * words, {@link #readStatusLine} will have been invoked). + * <p> + * Subclasses may want to override this method to respond to these value. + * This implementation does nothing. + * + * @see #readResponse + * @see #readStatusLine + * + * @param state the client state + * @param conn the {@link HttpConnection} to read the response from + */ + protected void processStatusLine(State state, HttpConnection conn) { + } + + /** + * Read response headers from the given {@link HttpConnection}, + * populating the {@link #responseHeaders} map. + * <p> + * Subclasses may want to override this method to + * to customize the processing. + * + * @see #readResponse + * @see #processResponseHeaders + * + * @param state the client state + * @param conn the {@link HttpConnection} to read the response from + */ protected void readResponseHeaders(State state, HttpConnection conn) throws IOException, HttpException { log.debug("HttpMethodBase.readResponseHeaders(State,HttpConnection)"); responseHeaders.clear(); @@ -484,10 +799,67 @@ } } + /** + * When this method is invoked, the {@link #responseHeaders} + * map will have been populated with the response headers + * (in other words, {@link #readResponseHeaders} will have + * been invoked). + * <p> + * This implementation will handle the <tt>Set-Cookie</tt> + * and <tt>Set-Cookie2</tt> headers, if any, adding the + * relevant cookies to the given {@link State}. + * <p> + * Subclasses may want to override this method to + * specially process additional headers, and/or + * invoke this method (via <tt>super</tt>) to process + * the <tt>Set-Cookie</tt> and <tt>Set-Cookie2</tt> headers. + * + * @see #readResponse + * @see #readResponseHeaders + * + * @param state the client state + * @param conn the {@link HttpConnection} to read the response from + */ + protected void processResponseHeaders(State state, HttpConnection conn) { + // add cookies, if any + // should we set cookies? + Header setCookieHeader = getResponseHeader("set-cookie2"); + if(null == setCookieHeader) { //ignore old-style if new is supported + setCookieHeader = getResponseHeader("set-cookie"); + } + + if(setCookieHeader != null) { + try { + Cookie[] cookies = Cookie.parse(conn.getHost(), setCookieHeader); + state.addCookies(cookies); + } catch (Exception e) { + log.error("processResponseHeaders(State,HttpConnection)",e); + } + } + } + + + /** + * Read the response body from the given {@link HttpConnection}. + * <p> + * The current implementation simply consumes the expected + * response body (according to the values of the + * <tt>Content-Length</tt> and <tt>Transfer-Encoding</tt> + * headers, if any). + * <p> + * Subclasses may want to override this method to + * to customize the processing. + * + * @see #readResponse + * @see #processResponseBody + * + * @param state the client state + * @param conn the {@link HttpConnection} to read the response from + */ protected void readResponseBody(State state, HttpConnection conn) throws IOException, HttpException { log.debug("HttpMethodBase.readResponseBody(State,HttpConnection)"); - OutputStream out = new ByteArrayOutputStream(); + //OutputStream out = new ByteArrayOutputStream(); int expectedLength = 0; int foundLength = 0; { @@ -512,12 +884,12 @@ nb = is.read(buffer); if (nb == -1) break; - if (out == null) - throw new IOException("Unable to buffer data"); + //if (out == null) + // throw new IOException("Unable to buffer data"); if(wireLog.isInfoEnabled()) { wireLog.info("<< \"" + new String(buffer,0,nb) + "\""); } - out.write(buffer, 0, nb); + //out.write(buffer, 0, nb); foundLength += nb; if(expectedLength > -1) { if(foundLength == expectedLength) { @@ -528,33 +900,85 @@ } } } - out.close(); - //is.close(); + //out.close(); } - protected void addRequestHeaders(HashMap headers) { - Iterator iter = headers.keySet().iterator(); - while(iter.hasNext()) { - Object name = iter.next(); - Object value = headers.get(name); - Header h = null; - if(value instanceof Header) { - h = (Header)value; - } else { - h = new Header(String.valueOf(name),String.valueOf(value)); - } - setRequestHeader(h); + /** + * When this method is invoked, {@link #readResponseBody} will + * have been invoked. + * <p> + * This implementation does nothing. + * <p> + * Subclasses may want to override this method. + * + * @see #readResponse + * @see #readResponseBody + * + * @param state the client state + * @param conn the {@link HttpConnection} to read the response from + */ + protected void processResponseBody(State state, HttpConnection conn) { + } + + /** + * Recycle this method so that it can be used again. + * All of my instances variables will be reset + * once this method has been called. + */ + public void recycle() { + path = null; + followRedirects = false; + parameters.clear(); + queryString = null; + requestHeaders.clear(); + responseHeaders.clear(); + statusCode = -1; + statusText = "Not executed"; + used = false; + http11 = false; + bodySent = false; + } + + // ---------------------------------------------- Protected Utility Methods + + /** + * Throws an {@link IllegalStateException} if + * {@link #used}. + */ + protected void checkNotUsed() { + if(used) { + throw new IllegalStateException("Already used."); } } - // override me if you'd like - protected void writeRequestLine(State state, HttpConnection conn) throws IOException, HttpException { - log.debug("HttpMethodBase.writeRequestLine(State,HttpConnection)"); - String requestLine = HttpMethodBase.generateRequestLine(conn, getName(),getPath(),queryString,parameters,(http11 ? "HTTP/1.1" : "HTTP/1.0")); - conn.print(requestLine); + /** + * Throws an {@link IllegalStateException} if + * not {@link #used}. + */ + protected void checkUsed() { + if(!used) { + throw new IllegalStateException("Not Used."); + } } - // a util method + /** + * Add the specified {@link Collection} of + * {@link Header}s to my set of request headers. + */ + protected void addRequestHeaders(Collection headers) { + Iterator iter = headers.iterator(); + while(iter.hasNext()) { + setRequestHeader((Header)(iter.next())); + } + } + + + // ------------------------------------------------- Static Utility Methods + + /** + * Generate an HTTP/S request line according to + * the specified attributes. + */ protected static String generateRequestLine(HttpConnection connection, String name, String path, String queryString, HashMap queryParams, String protocol) { boolean addedQmark = false; StringBuffer buf = new StringBuffer(); @@ -610,72 +1034,20 @@ } } } - - // override me if you'd like - protected void writeRequestHeaders(State state, HttpConnection conn) throws IOException, HttpException { - log.debug("HttpMethodBase.writeRequestHeaders(State,HttpConnection)"); - generateRequestHeaders(state,conn); - Iterator it = requestHeaders.keySet().iterator(); - while(it.hasNext()) { - Header header = (Header)(requestHeaders.get(it.next())); - conn.print(header.toString()); - } - } - - // use me if you'd like - protected void generateRequestHeaders(State state, HttpConnection conn) throws IOException, HttpException { - // add default user agent - if (!requestHeaders.containsKey("user-agent")) { - setRequestHeader(HttpClient.USER_AGENT); - } + // ----------------------------------------------------- Instance Variables - // add host (should do this conditionally?, i.e., don't send to http/1.0?) - if (!requestHeaders.containsKey("host")) { - setRequestHeader("Host",conn.getHost()); - } - - // add cookies - if (!requestHeaders.containsKey("cookie")) { - Vector cookies = state.getCookies(); - if (cookies != null && cookies.size() > 0) { - setRequestHeader(Cookie.createCookieHeader(conn.getHost(), getPath(), cookies)); - } - } - - // add content length or chunking - int len = getRequestContentLength(); - if(!requestHeaders.containsKey("content-length")) { - if(-1 < len) { - setRequestHeader("Content-Length",String.valueOf(len)); - } else if(http11 && len < 0) { - // XXX should this be an "add" rather than a "set"? - setRequestHeader("Transfer-Encoding","chunked"); - } - } - - // add authorization header, if needed - if(!requestHeaders.containsKey("authorization")) { - Header wwwAuthenticateHeader = (Header)(responseHeaders.get("www-authenticate")); - if(null != wwwAuthenticateHeader) { - String challengeResponse = Authenticator.challengeResponse(wwwAuthenticateHeader.getValue(),state); - if (challengeResponse != null) { - setRequestHeader("Authorization",challengeResponse); - } - } - } - } - - protected void checkNotUsed() { - if(used) { - throw new IllegalStateException("Already used."); - } - } - - protected void checkUsed() { - if(!used) { - throw new IllegalStateException("Not Used."); - } - } + protected String path = null; + protected boolean followRedirects = false; + protected HashMap parameters = new HashMap(); + protected String queryString = null; + protected HashMap requestHeaders = new HashMap(); + protected HashMap responseHeaders = new HashMap(); + protected int statusCode = -1; + protected String statusText = null; + protected boolean used = false; + protected boolean http11 = true; + protected boolean bodySent = false; } + No revision No revision 1.3.2.4 +6 -5 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java Index: PostMethod.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java,v retrieving revision 1.3.2.3 retrieving revision 1.3.2.4 diff -u -r1.3.2.3 -r1.3.2.4 --- PostMethod.java 2001/08/13 17:38:56 1.3.2.3 +++ PostMethod.java 2001/08/13 22:37:12 1.3.2.4 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java,v 1.3.2.3 2001/08/13 17:38:56 rwaldhoff Exp $ - * $Revision: 1.3.2.3 $ - * $Date: 2001/08/13 17:38:56 $ + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java,v 1.3.2.4 2001/08/13 22:37:12 rwaldhoff Exp $ + * $Revision: 1.3.2.4 $ + * $Date: 2001/08/13 22:37:12 $ * ==================================================================== * Copyright (C) The Apache Software Foundation. All rights reserved. * @@ -131,12 +131,13 @@ } } - protected void writeRequestBody(State state, HttpConnection conn) throws IOException, HttpException { + protected boolean writeRequestBody(State state, HttpConnection conn) throws IOException, HttpException { + log.debug("PostMethod.writeRequestBody(State,HttpConnection)"); if(null == requestBody) { requestBody = generateRequestBody(parameters); } conn.print(requestBody); - bodySent = true; + return true; } protected int getRequestContentLength() { 1.3.2.4 +7 -8 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PutMethod.java Index: PutMethod.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PutMethod.java,v retrieving revision 1.3.2.3 retrieving revision 1.3.2.4 diff -u -r1.3.2.3 -r1.3.2.4 --- PutMethod.java 2001/08/13 17:54:27 1.3.2.3 +++ PutMethod.java 2001/08/13 22:37:12 1.3.2.4 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PutMethod.java,v 1.3.2.3 2001/08/13 17:54:27 rwaldhoff Exp $ - * $Revision: 1.3.2.3 $ - * $Date: 2001/08/13 17:54:27 $ + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PutMethod.java,v 1.3.2.4 2001/08/13 22:37:12 rwaldhoff Exp $ + * $Revision: 1.3.2.4 $ + * $Date: 2001/08/13 22:37:12 $ * * Copyright (C) The Apache Software Foundation. All rights reserved. * @@ -193,9 +193,9 @@ } } - protected void writeRequestBody(State state, HttpConnection conn) throws IOException, HttpException { + protected boolean writeRequestBody(State state, HttpConnection conn) throws IOException, HttpException { if(requestHeaders.containsKey("expect") && statusCode != HttpStatus.SC_CONTINUE) { - return; + return false; } RequestOutputStream out = conn.getRequestOutputStream(); if((http11) && (getRequestHeader("Content-Length") == null)) { @@ -210,8 +210,7 @@ } else if(data != null){ inputStream = new ByteArrayInputStream(data); } else { - bodySent = true; - return; + return true; } byte[] buffer = new byte[4096]; @@ -225,7 +224,7 @@ } inputStream.close(); out.close(); - bodySent = true; + return true; } protected int getRequestContentLength() {