Index: java/org/apache/commons/httpclient/HttpClient.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpClient.java,v retrieving revision 1.79 diff -u -r1.79 HttpClient.java --- java/org/apache/commons/httpclient/HttpClient.java 16 Jul 2003 20:48:27 -0000 1.79 +++ java/org/apache/commons/httpclient/HttpClient.java 22 Jul 2003 12:32:52 -0000 @@ -98,6 +98,7 @@ private static final Log LOG = LogFactory.getLog(HttpClient.class); static { + if (LOG.isDebugEnabled()) { LOG.debug("Java version: " + System.getProperty("java.version")); LOG.debug("Java vendor: " + System.getProperty("java.vendor")); @@ -319,29 +320,32 @@ throw new IllegalArgumentException("HttpMethod parameter may not be null"); } - int soTimeout = 0; - boolean strictMode = false; - int connectionTimeout = 0; - long httpConnectionTimeout = 0; + if (hostConfiguration == null) { + hostConfiguration = ( + method.getHostConfiguration() != null + ? method.getHostConfiguration() + : getHostConfiguration() + ); + } + HostConfiguration defaultHostConfiguration = null; + HttpMethodSession methodSession = new HttpMethodSession(); /* access all synchronized data in a single block, this will keeps us * from accessing data asynchronously as well having to regain the lock * for each item. */ synchronized (this) { - soTimeout = this.timeoutInMilliseconds; - strictMode = this.strictMode; - connectionTimeout = this.connectionTimeout; - httpConnectionTimeout = this.httpConnectionTimeout; - if (state == null) { - state = getState(); - } + methodSession.setSoTimeout(this.timeoutInMilliseconds); + methodSession.setStrictMode(this.strictMode); + methodSession.setConnectionTimeout(this.connectionTimeout); + methodSession.setHttpConnectionFactoryTimeout(this.httpConnectionTimeout); + methodSession.setState(state == null ? getState() : state); + methodSession.setConnectionManager(this.httpConnectionManager); defaultHostConfiguration = getHostConfiguration(); } - HostConfiguration methodConfiguration - = new HostConfiguration(hostConfiguration); + HostConfiguration methodConfiguration = new HostConfiguration(hostConfiguration); if (hostConfiguration != defaultHostConfiguration) { // we may need to apply some defaults @@ -368,41 +372,15 @@ } } - HttpConnectionManager connmanager = this.httpConnectionManager; - - HttpConnection connection = connmanager.getConnectionWithTimeout( - methodConfiguration, - httpConnectionTimeout - ); - - try { - // Catch all possible exceptions to make sure to release the - // connection, as although the user may call - // Method->releaseConnection(), the method doesn't know about the - // connection until HttpMethod.execute() is called. - - method.setStrictMode(strictMode); + methodSession.setHostConfiguration(methodConfiguration); + methodSession.setMethod(method); - if (!connection.isOpen()) { - connection.setSoTimeout(soTimeout); - connection.setConnectionTimeout(connectionTimeout); - connection.open(); - if (connection.isProxied() && connection.isSecure()) { - method = new ConnectMethod(method); - } - } - } catch (IOException e) { - connection.releaseConnection(); - throw e; - } catch (RuntimeException e) { - connection.releaseConnection(); - throw e; - } + methodSession.executeMethod(); - return method.execute(state, connection); + return methodSession.getMethod().getStatusCode(); } - /** + /** * Return the host that the client is accessing. * * @return The host that the client is accessing, or null if Index: java/org/apache/commons/httpclient/HttpConnection.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java,v retrieving revision 1.69 diff -u -r1.69 HttpConnection.java --- java/org/apache/commons/httpclient/HttpConnection.java 16 Jul 2003 20:48:27 -0000 1.69 +++ java/org/apache/commons/httpclient/HttpConnection.java 22 Jul 2003 12:32:55 -0000 @@ -1020,11 +1020,30 @@ // we are assuming that the connection will only be released once used used = true; - if (httpConnectionManager != null) { + + if (locked) { + LOG.debug("Connection is locked. Call to releaseConnection() ignored."); + } else if (httpConnectionManager != null) { httpConnectionManager.releaseConnection(this); + } else { + LOG.warn("HttpConnectionManager is null. Connection cannot be released."); } } + /** + * @return + */ + boolean isLocked() { + return locked; + } + + /** + * @param locked + */ + void setLocked(boolean locked) { + this.locked = locked; + } + // ------------------------------------------------------ Protected Methods /** @@ -1316,6 +1335,10 @@ /** TCP_NODELAY socket value */ private boolean soNodelay = true; + + /** flag to indicate if this connection can be released, if locked the connection cannot be + * released */ + private boolean locked = false; /** Whether or not the _socket is a secure one. Note the difference to _ssl */ private boolean usingSecureSocket = false; 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 22 Jul 2003 12:32:56 -0000 @@ -271,6 +271,14 @@ Header getResponseHeader(String headerName); /** + * Returns the headers with the given name. Note that header-name matching is + * case insensitive. + * @param headerName the name of the headers to be returned. + * @return an array of zero or more headers + */ + Header[] getResponseHeaders(String headerName); + + /** * Return an array of my response footers * @return null if no footers are available */ 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.170 diff -u -r1.170 HttpMethodBase.java --- java/org/apache/commons/httpclient/HttpMethodBase.java 19 Jul 2003 09:41:37 -0000 1.170 +++ java/org/apache/commons/httpclient/HttpMethodBase.java 22 Jul 2003 12:33:00 -0000 @@ -68,13 +68,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; -import java.util.HashSet; -import java.util.Set; import org.apache.commons.httpclient.auth.AuthScheme; -import org.apache.commons.httpclient.auth.AuthenticationException; import org.apache.commons.httpclient.auth.HttpAuthenticator; -import org.apache.commons.httpclient.auth.MalformedChallengeException; import org.apache.commons.httpclient.cookie.CookiePolicy; import org.apache.commons.httpclient.cookie.CookieSpec; import org.apache.commons.httpclient.cookie.MalformedCookieException; @@ -141,9 +137,6 @@ */ public abstract class HttpMethodBase implements HttpMethod { - /** Maximum number of redirects and authentications that will be followed */ - private static final int MAX_FORWARDS = 100; - // -------------------------------------------------------------- Constants /** Log object for this class. */ @@ -173,15 +166,9 @@ /** My response trailer headers, if any. */ private HeaderGroup responseTrailerHeaders = new HeaderGroup(); - /** Realms that we tried to authenticate to */ - private Set realms = null; - /** Actual authentication realm */ private String realm = null; - /** Proxy Realms that we tried to authenticate to */ - private Set proxyRealms = null; - /** Actual proxy authentication realm */ private String proxyRealm = null; @@ -226,12 +213,6 @@ */ private MethodRetryHandler methodRetryHandler; - /** true if we are currently executing */ - private boolean inExecute = false; - - /** true if we are finished with the connection */ - private boolean doneWithConnection = false; - /** true if the connection must be closed when no longer needed */ private boolean connectionCloseForced = false; @@ -578,6 +559,13 @@ } /** + * @see org.apache.commons.httpclient.HttpMethod#getResponseHeaders(java.lang.String) + */ + public Header[] getResponseHeaders(String headerName) { + return getResponseHeaderGroup().getHeaders(headerName); + } + + /** * Convenience method top provide access to the status code. * * @return the status code associated with the latest response. @@ -903,48 +891,6 @@ } /** - * Return true if a retry is needed. - * @param statusCode The status code - * @param state The state. - * @param conn The connection - * @return boolean true if a retry is needed. - */ - private boolean isRetryNeeded(int statusCode, HttpState state, HttpConnection conn) { - switch (statusCode) { - case HttpStatus.SC_UNAUTHORIZED: - case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: - LOG.debug("Authorization required"); - if (doAuthentication) { //process authentication response - //if the authentication is successful, return the statusCode - //otherwise, drop through the switch and try again. - if (processAuthenticationResponse(state, conn)) { - return false; - } - } else { //let the client handle the authenticaiton - return false; - } - break; - - case HttpStatus.SC_MOVED_TEMPORARILY: - case HttpStatus.SC_MOVED_PERMANENTLY: - case HttpStatus.SC_SEE_OTHER: - case HttpStatus.SC_TEMPORARY_REDIRECT: - LOG.debug("Redirect required"); - - if (!processRedirectResponse(conn)) { - return false; - } - break; - - default: - // neither an unauthorized nor a redirect response - return false; - } //end of switch - - return true; - } - - /** * Check to see if the this method is ready to be executed. * * @param state The state. @@ -960,15 +906,13 @@ if (conn == null) { throw new IllegalArgumentException("HttpConnection parameter may not be null"); } - if (hasBeenUsed()) { - throw new IllegalStateException("Already used, but not recycled."); - } + // TODO: do we care +// if (hasBeenUsed()) { +// throw new IllegalStateException("Already used, but not recycled."); +// } if (!validate()) { throw new ProtocolException("HttpMethodBase object not valid"); } - if (inExecute) { - throw new IllegalStateException("Execute invoked recursively, or exited abnormally."); - } } /** @@ -1004,229 +948,22 @@ this.responseConnection = conn; checkExecuteConditions(state, conn); - inExecute = true; - - try { - //pre-emptively add the authorization header, if required. - if (state.isAuthenticationPreemptive()) { + this.statusLine = null; - LOG.debug("Preemptively sending default basic credentials"); - - try { - if (HttpAuthenticator.authenticateDefault(this, conn, state)) { - LOG.debug("Default basic credentials applied"); - } - if (conn.isProxied()) { - if (HttpAuthenticator.authenticateProxyDefault(this, conn, state)) { - LOG.debug("Default basic proxy credentials applied"); - } - } - } catch (AuthenticationException e) { - // Log error and move on - LOG.error(e.getMessage(), e); - } - } - - realms = new HashSet(); - proxyRealms = new HashSet(); - int forwardCount = 0; //protect from an infinite loop - - while (forwardCount++ < MAX_FORWARDS) { - // on every retry, reset this state information. - conn.setLastResponseInputStream(null); - - if (LOG.isDebugEnabled()) { - LOG.debug("Execute loop try " + forwardCount); - } + conn.setLastResponseInputStream(null); - // Discard status line - this.statusLine = null; - - //write the request and read the response, will retry - processRequest(state, conn); - - if (!isRetryNeeded(statusLine.getStatusCode(), state, conn)) { - // nope, no retry needed, exit loop. - break; - } - - // retry - close previous stream. Caution - this causes - // responseBodyConsumed to be called, which may also close the - // connection. - if (responseStream != null) { - responseStream.close(); - } - - } //end of retry loop - - if (forwardCount >= MAX_FORWARDS) { - LOG.error("Narrowly avoided an infinite loop in execute"); - throw new ProtocolException("Maximum redirects (" - + MAX_FORWARDS + ") exceeded"); - } - - } finally { - inExecute = false; - // If the response has been fully processed, return the connection - // to the pool. Use this flag, rather than other tests (like - // responseStream == null), as subclasses, might reset the stream, - // for example, reading the entire response into a file and then - // setting the file as the stream. - if (doneWithConnection) { - ensureConnectionRelease(); - } - } + // TODO: this needs to be exposed + boolean requestSent = false; + writeRequest(state, conn); + requestSent = true; + readResponse(state, conn); + // the method has successfully executed + used = true; return statusLine.getStatusCode(); } /** - * Process the redirect response. - * @param conn The connection to use. - * @return boolean true if the redirect was successful. - */ - private boolean processRedirectResponse(HttpConnection conn) { - - if (!getFollowRedirects()) { - LOG.info("Redirect requested but followRedirects is " - + "disabled"); - return false; - } - - //get the location header to find out where to redirect to - Header locationHeader = getResponseHeader("location"); - if (locationHeader == null) { - // got a redirect response, but no location header - LOG.error("Received redirect response " + getStatusCode() - + " but no location header"); - return false; - } - String location = locationHeader.getValue(); - if (LOG.isDebugEnabled()) { - LOG.debug("Redirect requested to location '" + location - + "'"); - } - - //rfc2616 demands the location value be a complete URI - //Location = "Location" ":" absoluteURI - URI redirectUri = null; - URI currentUri = null; - - try { - currentUri = new URI( - conn.getProtocol().getScheme(), - null, - conn.getHost(), - conn.getPort(), - this.getPath() - ); - redirectUri = new URI(location, true); - if (redirectUri.isRelativeURI()) { - if (isStrictMode()) { - LOG.warn("Redirected location '" + location - + "' is not acceptable in strict mode"); - return false; - } else { - //location is incomplete, use current values for defaults - LOG.debug("Redirect URI is not absolute - parsing as relative"); - redirectUri = new URI(currentUri, redirectUri); - } - } - } catch (URIException e) { - LOG.warn("Redirected location '" + location + "' is malformed"); - return false; - } - - //check for redirect to a different protocol, host or port - try { - checkValidRedirect(currentUri, redirectUri); - } catch (HttpException ex) { - //LOG the error and let the client handle the redirect - LOG.warn(ex.getMessage()); - return false; - } - - //invalidate the list of authentication attempts - this.realms.clear(); - //remove exisitng authentication headers - removeRequestHeader(HttpAuthenticator.WWW_AUTH_RESP); - //update the current location with the redirect location. - //avoiding use of URL.getPath() and URL.getQuery() to keep - //jdk1.2 comliance. - setPath(redirectUri.getEscapedPath()); - setQueryString(redirectUri.getEscapedQuery()); - - if (LOG.isDebugEnabled()) { - LOG.debug("Redirecting from '" + currentUri.getEscapedURI() - + "' to '" + redirectUri.getEscapedURI()); - } - - return true; - } - - /** - * Check for a valid redirect given the current conn and new URI. - * Redirect to a different protocol, host or port are checked for validity. - * - * @param currentUri The current URI (redirecting from) - * @param redirectUri The new URI to redirect to - * @throws HttpException if the redirect is invalid - * @since 2.0 - */ - private static void checkValidRedirect(URI currentUri, URI redirectUri) - throws HttpException { - LOG.trace("enter HttpMethodBase.checkValidRedirect(HttpConnection, URL)"); - - String oldProtocol = currentUri.getScheme(); - String newProtocol = redirectUri.getScheme(); - if (!oldProtocol.equals(newProtocol)) { - throw new ProtocolException("Redirect from protocol " + oldProtocol - + " to " + newProtocol + " is not supported"); - } - - String oldHost = currentUri.getHost(); - String newHost = redirectUri.getHost(); - if (!oldHost.equalsIgnoreCase(newHost)) { - // TODO: Add an HttpNotImplementedException or some such? - throw new ProtocolException("Redirect from host " + oldHost - + " to " + newHost + " is not supported"); - } - - int oldPort = currentUri.getPort(); - if (oldPort < 0) { - oldPort = getDefaultPort(oldProtocol); - } - int newPort = redirectUri.getPort(); - if (newPort < 0) { - newPort = getDefaultPort(newProtocol); - } - if (oldPort != newPort) { - throw new ProtocolException("Redirect from port " + oldPort - + " to " + newPort + " is not supported"); - } - } - - /** - * Returns the default port for the given protocol. - * - * @param protocol currently only http and https are recognized - * @return the default port of the given protocol or -1 if the - * protocol is not recognized. - * - * @since 2.0 - * - */ - private static int getDefaultPort(String protocol) { - String proto = protocol.toLowerCase().trim(); - if (proto.equals("http")) { - return 80; - } else if (proto.equals("https")) { - return 443; - } - return -1; - } - - /** * Whether the object has been used and not recycled. * * @return true if I have been {@link #execute executed} but not @@ -1259,8 +996,6 @@ http11 = true; responseBody = null; recoverableExceptionCount = 0; - inExecute = false; - doneWithConnection = false; connectionCloseForced = false; } @@ -1911,7 +1646,6 @@ "enter HttpMethodBase.readResponseBody(HttpState, HttpConnection)"); // assume we are not done with the connection if we get a stream - doneWithConnection = false; InputStream stream = readResponseBody(conn); if (stream == null) { // done using the connection! @@ -2356,109 +2090,8 @@ } /** - * process a response that requires authentication - * - * @param state the current state - * @param conn The connection - * - * @return true if the request has completed process, false if more - * attempts are needed - */ - private boolean processAuthenticationResponse(HttpState state, HttpConnection conn) { - LOG.trace("enter HttpMethodBase.processAuthenticationResponse(" - + "HttpState, HttpConnection)"); - - int statusCode = statusLine.getStatusCode(); - // handle authentication required - Header[] challenges = null; - Set realmsUsed = null; - switch (statusCode) { - case HttpStatus.SC_UNAUTHORIZED: - challenges = getResponseHeaderGroup().getHeaders(HttpAuthenticator.WWW_AUTH); - realmsUsed = realms; - break; - case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: - challenges = getResponseHeaderGroup().getHeaders(HttpAuthenticator.PROXY_AUTH); - realmsUsed = proxyRealms; - break; - } - boolean authenticated = false; - // if there was a header requesting authentication - if (challenges.length > 0) { - AuthScheme authscheme = null; - try { - authscheme = HttpAuthenticator.selectAuthScheme(challenges); - } catch (MalformedChallengeException e) { - if (LOG.isErrorEnabled()) { - LOG.error(e.getMessage(), e); - } - return true; - } catch (UnsupportedOperationException e) { - if (LOG.isErrorEnabled()) { - LOG.error(e.getMessage(), e); - } - return true; - } - - StringBuffer buffer = new StringBuffer(); - buffer.append(conn.getHost()); - int port = conn.getPort(); - if (conn.getProtocol().getDefaultPort() != port) { - buffer.append(':'); - buffer.append(port); - } - buffer.append('#'); - buffer.append(authscheme.getID()); - String realm = buffer.toString(); - - if (realmsUsed.contains(realm)) { - if (LOG.isInfoEnabled()) { - LOG.info("Already tried to authenticate to \"" - + realm + "\" but still receiving " - + statusCode + "."); - } - return true; - } else { - realmsUsed.add(realm); - } - - try { - //remove preemptive header and reauthenticate - switch (statusCode) { - case HttpStatus.SC_UNAUTHORIZED: - removeRequestHeader(HttpAuthenticator.WWW_AUTH_RESP); - authenticated = HttpAuthenticator.authenticate( - authscheme, this, conn, state); - this.realm = authscheme.getRealm(); - break; - case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: - removeRequestHeader(HttpAuthenticator.PROXY_AUTH_RESP); - authenticated = HttpAuthenticator.authenticateProxy( - authscheme, this, conn, state); - this.proxyRealm = authscheme.getRealm(); - break; - } - } catch (AuthenticationException e) { - LOG.warn(e.getMessage()); - return true; // finished request - } - if (!authenticated) { - // won't be able to authenticate to this challenge - // without additional information - LOG.debug("HttpMethodBase.execute(): Server demands " - + "authentication credentials, but none are " - + "available, so aborting."); - } else { - LOG.debug("HttpMethodBase.execute(): Server demanded " - + "authentication credentials, will try again."); - // let's try it again, using the credentials - } - } - - return !authenticated; // finished processing if we aren't authenticated - } - - /** + * @deprecated no longer used + * * Returns proxy authentication realm, if it has been used during authentication process. * Otherwise returns null. * @@ -2469,6 +2102,8 @@ } /** + * @deprecated no longer used + * * Returns authentication realm, if it has been used during authentication process. * Otherwise returns null. * @@ -2479,81 +2114,6 @@ } /** - * Write a request and read the response. Both the write to the server will - * be retried {@link #maxRetries} times if the operation fails with a - * HttpRecoverableException. The write will only be attempted if the read - * has succeeded. - * - *

- * The used is set to true if the write succeeds. - *

- * - * @param state the current state - * @param connection the connection for communication - * - * @throws HttpException when errors occur as part of the HTTP protocol - * conversation - * @throws IOException when an I/O error occurs communicating with the - * server - * - * @see #writeRequest(HttpState,HttpConnection) - * @see #readResponse(HttpState,HttpConnection) - */ - private void processRequest(HttpState state, HttpConnection connection) - throws HttpException, IOException { - LOG.trace("enter HttpMethodBase.processRequest(HttpState, HttpConnection)"); - - int execCount = 0; - boolean requestSent = false; - - // loop until the method is successfully processed, the retryHandler - // returns false or a non-recoverable exception is thrown - while (true) { - execCount++; - requestSent = false; - - if (LOG.isTraceEnabled()) { - LOG.trace("Attempt number " + execCount + " to process request"); - } - try { - if (!connection.isOpen()) { - LOG.debug("Opening the connection."); - connection.open(); - } - writeRequest(state, connection); - requestSent = true; - readResponse(state, connection); - // the method has successfully executed - used = true; - break; - } catch (HttpRecoverableException httpre) { - if (LOG.isDebugEnabled()) { - LOG.debug("Closing the connection."); - } - connection.close(); - LOG.info("Recoverable exception caught when processing request"); - // update the recoverable exception count. - recoverableExceptionCount++; - - // test if this method should be retried - if (!getMethodRetryHandler().retryMethod( - this, - connection, - httpre, - execCount, - requestSent) - ) { - LOG.warn( - "Recoverable exception caught but MethodRetryHandler.retryMethod() " - + "returned false, rethrowing exception" - ); - throw httpre; - } - } - } - } - - /** * Return the character set from the header. * @param contentheader The content header. * @return String The character set. @@ -2604,6 +2164,8 @@ } /** + * @deprecated no longer used + * * Returns the number of "recoverable" exceptions thrown and handled, to * allow for monitoring the quality of the connection. * @@ -2633,10 +2195,7 @@ responseConnection.close(); } - doneWithConnection = true; - if (!inExecute) { - ensureConnectionRelease(); - } + ensureConnectionRelease(); } /** Index: java/org/apache/commons/httpclient/HttpMethodSession.java =================================================================== RCS file: java/org/apache/commons/httpclient/HttpMethodSession.java diff -N java/org/apache/commons/httpclient/HttpMethodSession.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ java/org/apache/commons/httpclient/HttpMethodSession.java 22 Jul 2003 12:33:02 -0000 @@ -0,0 +1,637 @@ +/* + * $Header: $ + * $Revision: $ + * $Date: $ + * + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ + +package org.apache.commons.httpclient; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.httpclient.auth.AuthScheme; +import org.apache.commons.httpclient.auth.AuthenticationException; +import org.apache.commons.httpclient.auth.HttpAuthenticator; +import org.apache.commons.httpclient.auth.MalformedChallengeException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Michael Becke + */ +class HttpMethodSession { + + /** Maximum number of redirects and authentications that will be followed */ + private static final int MAX_FORWARDS = 100; + + private static final Log LOG = LogFactory.getLog(HttpMethodSession.class); + + private HttpMethod method; + + private HttpState state; + + private HostConfiguration hostConfiguration; + + private HttpConnectionManager connectionManager; + + private HttpConnection connection; + + private int soTimeout; + + private int connectionTimeout; + + private boolean strictMode; + + private long httpConnectionFactoryTimeout; + + /** How many times did this transparently handle a recoverable exception? */ + private int recoverableExceptionCount = 0; + + /** Realms that we tried to authenticate to */ + private Set realms = null; + + /** Proxy Realms that we tried to authenticate to */ + private Set proxyRealms = null; + + /** Actual authentication realm */ + private String realm = null; + + /** Actual proxy authentication realm */ + private String proxyRealm = null; + + public void executeMethod() throws IOException, HttpException { + + method.setStrictMode(strictMode); + + try { + int forwardCount = 0; //protect from an infinite loop + + while (forwardCount++ < MAX_FORWARDS) { + // on every retry, reset this state information. + if (LOG.isDebugEnabled()) { + LOG.debug("Execute loop try " + forwardCount); + } + + executeMethodForHost(); + + if (!isRetryNeeded()) { + // nope, no retry needed, exit loop. + break; + } + + // retry - close previous stream. Caution - this causes + // responseBodyConsumed to be called, which may also close the + // connection. + if (method.getResponseBodyAsStream() != null) { + method.getResponseBodyAsStream().close(); + } + + } //end of retry loop + + if (forwardCount >= MAX_FORWARDS) { + LOG.error("Narrowly avoided an infinite loop in execute"); + throw new ProtocolException("Maximum redirects (" + + MAX_FORWARDS + ") exceeded"); + } + + } finally { + if (connection != null) { + connection.setLocked(false); + } + // If the response has been fully processed, return the connection + // to the pool. Use this flag, rather than other tests (like + // responseStream == null), as subclasses, might reset the stream, + // for example, reading the entire response into a file and then + // setting the file as the stream. + if (method.getResponseBodyAsStream() == null) { + method.releaseConnection(); + } + } + + } + + /** + * Executes a method with the current hostConfiguration. + * + * @throws IOException + * @throws HttpException + */ + private void executeMethodForHost() throws IOException, HttpException { + + if (connection != null && !hostConfiguration.hostEquals(connection)) { + connection.setLocked(false); + connection.releaseConnection(); + connection = null; + } + if (connection == null) { + connection = connectionManager.getConnectionWithTimeout( + hostConfiguration, + httpConnectionFactoryTimeout + ); + connection.setLocked(true); + + realms = new HashSet(); + proxyRealms = new HashSet(); + + //pre-emptively add the authorization header, if required. + if (state.isAuthenticationPreemptive()) { + + LOG.debug("Preemptively sending default basic credentials"); + + try { + if (HttpAuthenticator.authenticateDefault(method, connection, state)) { + LOG.debug("Default basic credentials applied"); + } + if (connection.isProxied()) { + if (HttpAuthenticator.authenticateProxyDefault(method, connection, state)) { + LOG.debug("Default basic proxy credentials applied"); + } + } + } catch (AuthenticationException e) { + // Log error and move on + LOG.error(e.getMessage(), e); + } + } + + } + + try { + // Catch all possible exceptions to make sure to release the + // connection, as although the user may call + // Method->releaseConnection(), the method doesn't know about the + // connection until HttpMethod.execute() is called. + + if (!connection.isOpen()) { + connection.setSoTimeout(soTimeout); + connection.setConnectionTimeout(connectionTimeout); + connection.open(); + if (connection.isProxied() && connection.isSecure()) { + executeConnect(); + } + } + } catch (IOException e) { + connection.releaseConnection(); + throw e; + } catch (RuntimeException e) { + connection.releaseConnection(); + throw e; + } + + connection.setLastResponseInputStream(null); + executeMethodWithRetry(); + } + + private void executeMethodWithRetry() throws HttpException, IOException { + + int execCount = 0; + // TODO: how do we get requestSent? + boolean requestSent = false; + + // loop until the method is successfully processed, the retryHandler + // returns false or a non-recoverable exception is thrown + while (true) { + execCount++; + requestSent = false; + + if (LOG.isTraceEnabled()) { + LOG.trace("Attempt number " + execCount + " to process request"); + } + try { + if (!connection.isOpen()) { + LOG.debug("Opening the connection."); + connection.open(); + } + method.execute(state, connection); + break; + } catch (HttpRecoverableException httpre) { + if (LOG.isDebugEnabled()) { + LOG.debug("Closing the connection."); + } + connection.close(); + LOG.info("Recoverable exception caught when processing request"); + // update the recoverable exception count. + recoverableExceptionCount++; + + // test if this method should be retried + if (!getMethodRetryHandler().retryMethod( + method, + connection, + httpre, + execCount, + requestSent) + ) { + LOG.warn( + "Recoverable exception caught but MethodRetryHandler.retryMethod() " + + "returned false, rethrowing exception" + ); + throw httpre; + } + } + } + + } + + private MethodRetryHandler getMethodRetryHandler() { + + if (method instanceof HttpMethodBase) { + return ((HttpMethodBase) method).getMethodRetryHandler(); + } else { + return new DefaultMethodRetryHandler(); + } + } + + private void executeConnect() throws IOException, HttpException { + // TODO: implement me + } + + /** + * Process the redirect response. + * @return boolean true if the redirect was successful. + */ + private boolean processRedirectResponse() { + + if (!method.getFollowRedirects()) { + LOG.info("Redirect requested but followRedirects is " + + "disabled"); + return false; + } + + //get the location header to find out where to redirect to + Header locationHeader = method.getResponseHeader("location"); + if (locationHeader == null) { + // got a redirect response, but no location header + LOG.error("Received redirect response " + method.getStatusCode() + + " but no location header"); + return false; + } + String location = locationHeader.getValue(); + if (LOG.isDebugEnabled()) { + LOG.debug("Redirect requested to location '" + location + + "'"); + } + + //rfc2616 demands the location value be a complete URI + //Location = "Location" ":" absoluteURI + URI redirectUri = null; + URI currentUri = null; + + try { + currentUri = new URI( + connection.getProtocol().getScheme(), + null, + connection.getHost(), + connection.getPort(), + method.getPath() + ); + redirectUri = new URI(location, true); + if (redirectUri.isRelativeURI()) { + if (method.isStrictMode()) { + LOG.warn("Redirected location '" + location + + "' is not acceptable in strict mode"); + return false; + } else { + //location is incomplete, use current values for defaults + LOG.debug("Redirect URI is not absolute - parsing as relative"); + redirectUri = new URI(currentUri, redirectUri); + } + } + } catch (URIException e) { + LOG.warn("Redirected location '" + location + "' is malformed"); + return false; + } + + //invalidate the list of authentication attempts + this.realms.clear(); + //remove exisitng authentication headers + method.removeRequestHeader(HttpAuthenticator.WWW_AUTH_RESP); + //update the current location with the redirect location. + //avoiding use of URL.getPath() and URL.getQuery() to keep + //jdk1.2 comliance. + method.setPath(redirectUri.getEscapedPath()); + method.setQueryString(redirectUri.getEscapedQuery()); + hostConfiguration.setHost(redirectUri); + + if (LOG.isDebugEnabled()) { + LOG.debug("Redirecting from '" + currentUri.getEscapedURI() + + "' to '" + redirectUri.getEscapedURI()); + } + + return true; + } + + /** + * process a response that requires authentication + * + * @param state the current state + * @param conn The connection + * + * @return true if the request has completed process, false if more + * attempts are needed + */ + private boolean processAuthenticationResponse(HttpState state, HttpConnection conn) { + LOG.trace("enter HttpMethodBase.processAuthenticationResponse(" + + "HttpState, HttpConnection)"); + + int statusCode = method.getStatusCode(); + // handle authentication required + Header[] challenges = null; + Set realmsUsed = null; + switch (statusCode) { + case HttpStatus.SC_UNAUTHORIZED: + challenges = method.getResponseHeaders(HttpAuthenticator.WWW_AUTH); + realmsUsed = realms; + break; + case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: + challenges = method.getResponseHeaders(HttpAuthenticator.PROXY_AUTH); + realmsUsed = proxyRealms; + break; + } + boolean authenticated = false; + // if there was a header requesting authentication + if (challenges.length > 0) { + AuthScheme authscheme = null; + try { + authscheme = HttpAuthenticator.selectAuthScheme(challenges); + } catch (MalformedChallengeException e) { + if (LOG.isErrorEnabled()) { + LOG.error(e.getMessage(), e); + } + return true; + } catch (UnsupportedOperationException e) { + if (LOG.isErrorEnabled()) { + LOG.error(e.getMessage(), e); + } + return true; + } + + StringBuffer buffer = new StringBuffer(); + buffer.append(conn.getHost()); + int port = conn.getPort(); + if (conn.getProtocol().getDefaultPort() != port) { + buffer.append(':'); + buffer.append(port); + } + buffer.append('#'); + buffer.append(authscheme.getID()); + String realm = buffer.toString(); + + if (realmsUsed.contains(realm)) { + if (LOG.isInfoEnabled()) { + LOG.info("Already tried to authenticate to \"" + + realm + "\" but still receiving " + + statusCode + "."); + } + return true; + } else { + realmsUsed.add(realm); + } + + try { + //remove preemptive header and reauthenticate + switch (statusCode) { + case HttpStatus.SC_UNAUTHORIZED: + method.removeRequestHeader(HttpAuthenticator.WWW_AUTH_RESP); + authenticated = HttpAuthenticator.authenticate( + authscheme, method, conn, state); + this.realm = authscheme.getRealm(); + break; + case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: + method.removeRequestHeader(HttpAuthenticator.PROXY_AUTH_RESP); + authenticated = HttpAuthenticator.authenticateProxy( + authscheme, method, conn, state); + this.proxyRealm = authscheme.getRealm(); + break; + } + } catch (AuthenticationException e) { + LOG.warn(e.getMessage()); + return true; // finished request + } + if (!authenticated) { + // won't be able to authenticate to this challenge + // without additional information + LOG.debug("HttpMethodBase.execute(): Server demands " + + "authentication credentials, but none are " + + "available, so aborting."); + } else { + LOG.debug("HttpMethodBase.execute(): Server demanded " + + "authentication credentials, will try again."); + // let's try it again, using the credentials + } + } + + return !authenticated; // finished processing if we aren't authenticated + } + + /** + * Return true if a retry is needed. + * @param statusCode The status code + * @param state The state. + * @param conn The connection + * @return boolean true if a retry is needed. + */ + private boolean isRetryNeeded() { + switch (method.getStatusCode()) { + case HttpStatus.SC_UNAUTHORIZED: + case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: + LOG.debug("Authorization required"); + if (method.getDoAuthentication()) { //process authentication response + //if the authentication is successful, return the statusCode + //otherwise, drop through the switch and try again. + if (processAuthenticationResponse(state, connection)) { + return false; + } + } else { //let the client handle the authenticaiton + return false; + } + break; + + case HttpStatus.SC_MOVED_TEMPORARILY: + case HttpStatus.SC_MOVED_PERMANENTLY: + case HttpStatus.SC_SEE_OTHER: + case HttpStatus.SC_TEMPORARY_REDIRECT: + LOG.debug("Redirect required"); + + if (!processRedirectResponse()) { + return false; + } + break; + + default: + // neither an unauthorized nor a redirect response + return false; + } //end of switch + + return true; + } + + /** + * @return + */ + public HostConfiguration getHostConfiguration() { + return hostConfiguration; + } + + /** + * @param hostConfiguration + */ + public void setHostConfiguration(HostConfiguration hostConfiguration) { + this.hostConfiguration = hostConfiguration; + } + + /** + * @return + */ + public HttpMethod getMethod() { + return method; + } + + /** + * @param method + */ + public void setMethod(HttpMethod method) { + this.method = method; + } + + /** + * @return + */ + public HttpState getState() { + return state; + } + + /** + * @param state + */ + public void setState(HttpState state) { + this.state = state; + } + + /** + * @return + */ + public HttpConnectionManager getConnectionManager() { + return connectionManager; + } + + /** + * @param connectionManager + */ + public void setConnectionManager(HttpConnectionManager connectionManager) { + this.connectionManager = connectionManager; + } + + /** + * @return + */ + public int getConnectionTimeout() { + return connectionTimeout; + } + + /** + * @param connectionTimeout + */ + public void setConnectionTimeout(int connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + /** + * @return + */ + public long getHttpConnectionFactoryTimeout() { + return httpConnectionFactoryTimeout; + } + + /** + * @param httpConnectionFactoryTimeout + */ + public void setHttpConnectionFactoryTimeout(long httpConnectionTimeout) { + this.httpConnectionFactoryTimeout = httpConnectionTimeout; + } + + /** + * @return + */ + public int getSoTimeout() { + return soTimeout; + } + + /** + * @param soTimeout + */ + public void setSoTimeout(int soTimeout) { + this.soTimeout = soTimeout; + } + + /** + * @return + */ + public boolean isStrictMode() { + return strictMode; + } + + /** + * @param strictMode + */ + public void setStrictMode(boolean strictMode) { + this.strictMode = strictMode; + } + +} Index: test/org/apache/commons/httpclient/NoHostHttpConnectionManager.java =================================================================== RCS file: test/org/apache/commons/httpclient/NoHostHttpConnectionManager.java diff -N test/org/apache/commons/httpclient/NoHostHttpConnectionManager.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/org/apache/commons/httpclient/NoHostHttpConnectionManager.java 22 Jul 2003 12:33:04 -0000 @@ -0,0 +1,77 @@ +/* + * Created on Jul 21, 2003 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package org.apache.commons.httpclient; + +import java.io.IOException; +import java.io.InputStream; + +/** + * @author mbecke + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class NoHostHttpConnectionManager implements HttpConnectionManager { + + private HttpConnection connection; + + public NoHostHttpConnectionManager() { + this.connection = new SimpleHttpConnection(); + } + + /** + * @param connection + */ + public void setConnection(HttpConnection connection) { + this.connection = connection; + } + + public HttpConnection getConnection(HostConfiguration hostConfiguration) { + return connection; + } + + public HttpConnection getConnection(HostConfiguration hostConfiguration, long timeout) + throws HttpException { + return connection; + } + + public HttpConnection getConnectionWithTimeout( + HostConfiguration hostConfiguration, + long timeout) + throws ConnectTimeoutException { + return connection; + } + + public void releaseConnection(HttpConnection conn) { + if (conn != connection) { + throw new IllegalStateException("Unexpected close on a different connection."); + } + + finishLastResponse(connection); + } + + /** + * Since the same connection is about to be reused, make sure the + * previous request was completely processed, and if not + * consume it now. + * @param conn The connection + */ + static void finishLastResponse(HttpConnection conn) { + InputStream lastResponse = conn.getLastResponseInputStream(); + if (lastResponse != null) { + conn.setLastResponseInputStream(null); + try { + lastResponse.close(); + } catch (IOException ioe) { + //FIXME: badness - close to force reconnect. + conn.close(); + } + } + } + + +} Index: test/org/apache/commons/httpclient/TestAuthenticator.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestAuthenticator.java,v retrieving revision 1.26 diff -u -r1.26 TestAuthenticator.java --- test/org/apache/commons/httpclient/TestAuthenticator.java 5 Jul 2003 22:31:21 -0000 1.26 +++ test/org/apache/commons/httpclient/TestAuthenticator.java 22 Jul 2003 12:33:07 -0000 @@ -78,7 +78,7 @@ * @author Jeff Dever * @version $Id: TestAuthenticator.java,v 1.26 2003/07/05 22:31:21 olegk Exp $ */ -public class TestAuthenticator extends TestCase { +public class TestAuthenticator extends TestNoHostBase { // ------------------------------------------------------------ Constructor public TestAuthenticator(String testName) { @@ -487,10 +487,8 @@ public void testNTLMAuthenticationRetry() throws Exception { NTCredentials cred = new NTCredentials("username", "password", "host", "domain"); - HttpState state = new HttpState(); - state.setCredentials(null, null, cred); + client.getState().setCredentials(null, null, cred); HttpMethod method = new SimpleHttpMethod(); - SimpleHttpConnection conn = new SimpleHttpConnection(); conn.addResponse( "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: NTLM\r\n" + @@ -506,7 +504,7 @@ "Connection: close\r\n" + "Server: HttpClient Test/2.0\r\n\r\n" + "stuff\r\n"); - method.execute(state, conn); + client.executeMethod(method); assertNull(method.getResponseHeader("WWW-Authenticate")); assertEquals(200, method.getStatusCode()); } @@ -515,12 +513,10 @@ * Test that the Unauthorized response is returned when doAuthentication is false. */ public void testDoAuthenticateFalse() throws Exception { - HttpState state = new HttpState(); - state.setCredentials(null, "Protected", + client.getState().setCredentials(null, "Protected", new UsernamePasswordCredentials("name", "pass")); HttpMethod method = new SimpleHttpMethod(); method.setDoAuthentication(false); - SimpleHttpConnection conn = new SimpleHttpConnection(); conn.addResponse( "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Basic realm=\"Protected\"\r\n" + @@ -530,7 +526,7 @@ "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Server: HttpClient Test/2.0\r\n"); - method.execute(state, conn); + client.executeMethod(method); assertNotNull(method.getResponseHeader("WWW-Authenticate")); assertNull(method.getRequestHeader("Authorization")); assertEquals(401, method.getStatusCode()); @@ -541,18 +537,16 @@ /** */ public void testInvalidCredentials() throws Exception { - HttpState state = new HttpState(); - state.setCredentials(null, "Protected", new UsernamePasswordCredentials("name", "pass")); + client.getState().setCredentials(null, "Protected", new UsernamePasswordCredentials("name", "pass")); HttpMethod method = new SimpleHttpMethod(); method.setDoAuthentication(false); - SimpleHttpConnection conn = new SimpleHttpConnection(); conn.addResponse( "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Basic realm=\"Protected\"\r\n" + "Connection: close\r\n" + "Server: HttpClient Test/2.0\r\n" ); - method.execute(state, conn); + client.executeMethod(method); assertEquals(401, method.getStatusCode()); } @@ -560,10 +554,8 @@ // --------------------------------- Test Methods for Multiple Authentication public void testMultipleChallengeBasic() throws Exception { - HttpState state = new HttpState(); - state.setCredentials("Protected", null, new UsernamePasswordCredentials("name", "pass")); + client.getState().setCredentials("Protected", null, new UsernamePasswordCredentials("name", "pass")); HttpMethod method = new SimpleHttpMethod(); - SimpleHttpConnection conn = new SimpleHttpConnection(); conn.addResponse( "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Unsupported\r\n" + @@ -575,8 +567,8 @@ "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Server: HttpClient Test/2.0\r\n" - ); - method.execute(state, conn); + ); + client.executeMethod(method); Header authHeader = method.getRequestHeader("Authorization"); assertNotNull(authHeader); @@ -585,10 +577,8 @@ } public void testMultipleChallengeBasicLongRealm() throws Exception { - HttpState state = new HttpState(); - state.setCredentials(null, null, new UsernamePasswordCredentials("name", "pass")); + client.getState().setCredentials(null, null, new UsernamePasswordCredentials("name", "pass")); HttpMethod method = new SimpleHttpMethod(); - SimpleHttpConnection conn = new SimpleHttpConnection(); conn.addResponse( "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Unsupported\r\n" + @@ -601,7 +591,7 @@ "Connection: close\r\n" + "Server: HttpClient Test/2.0\r\n" ); - method.execute(state, conn); + client.executeMethod(method); Header authHeader = method.getRequestHeader("Authorization"); assertNotNull(authHeader); @@ -613,10 +603,8 @@ public void testMultipleChallengeDigest() throws Exception { - HttpState state = new HttpState(); - state.setCredentials("Protected", null, new UsernamePasswordCredentials("name", "pass")); + client.getState().setCredentials("Protected", null, new UsernamePasswordCredentials("name", "pass")); HttpMethod method = new SimpleHttpMethod(); - SimpleHttpConnection conn = new SimpleHttpConnection(); conn.addResponse( "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Unsupported\r\n" + @@ -630,7 +618,7 @@ "Connection: close\r\n" + "Server: HttpClient Test/2.0\r\n" ); - method.execute(state, conn); + client.executeMethod(method); Header authHeader = method.getRequestHeader("Authorization"); assertNotNull(authHeader); @@ -640,10 +628,8 @@ public void testMultipleProxyChallengeBasic() throws Exception { - HttpState state = new HttpState(); - state.setProxyCredentials("Protected", null, new UsernamePasswordCredentials("name", "pass")); + client.getState().setProxyCredentials("Protected", null, new UsernamePasswordCredentials("name", "pass")); HttpMethod method = new SimpleHttpMethod(); - SimpleHttpConnection conn = new SimpleHttpConnection(); conn.addResponse( "HTTP/1.1 407 Proxy Authentication Required\r\n" + "Proxy-Authenticate: Basic realm=\"Protected\"\r\n" + @@ -656,7 +642,7 @@ "Connection: close\r\n" + "Server: HttpClient Test/2.0\r\n" ); - method.execute(state, conn); + client.executeMethod(method); Header authHeader = method.getRequestHeader("Proxy-Authorization"); assertNotNull(authHeader); @@ -666,10 +652,8 @@ public void testMultipleProxyChallengeDigest() throws Exception { - HttpState state = new HttpState(); - state.setProxyCredentials("Protected", null, new UsernamePasswordCredentials("name", "pass")); + client.getState().setProxyCredentials("Protected", null, new UsernamePasswordCredentials("name", "pass")); HttpMethod method = new SimpleHttpMethod(); - SimpleHttpConnection conn = new SimpleHttpConnection(); conn.addResponse( "HTTP/1.1 407 Proxy Authentication Required\r\n" + "Proxy-Authenticate: Basic realm=\"Protected\"\r\n" + @@ -683,7 +667,7 @@ "Connection: close\r\n" + "Server: HttpClient Test/2.0\r\n" ); - method.execute(state, conn); + client.executeMethod(method); Header authHeader = method.getRequestHeader("Proxy-Authorization"); assertNotNull(authHeader); Index: test/org/apache/commons/httpclient/TestHttpConnectionManager.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java,v retrieving revision 1.9 diff -u -r1.9 TestHttpConnectionManager.java --- test/org/apache/commons/httpclient/TestHttpConnectionManager.java 16 Jul 2003 20:48:28 -0000 1.9 +++ test/org/apache/commons/httpclient/TestHttpConnectionManager.java 22 Jul 2003 12:33:08 -0000 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java,v 1.9 2003/07/16 20:48:28 olegk Exp $ - * $Revision: 1.9 $ - * $Date: 2003/07/16 20:48:28 $ + * $Header: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java,v 1.8 2003/04/28 23:19:58 mbecke Exp $ + * $Revision: 1.8 $ + * $Date: 2003/04/28 23:19:58 $ * ==================================================================== * * The Apache Software License, Version 1.1 @@ -73,7 +73,7 @@ * Unit tests for {@link HttpConnectionManager}. * * @author Marc A. Saegesser - * @version $Id: TestHttpConnectionManager.java,v 1.9 2003/07/16 20:48:28 olegk Exp $ + * @version $Id: TestHttpConnectionManager.java,v 1.8 2003/04/28 23:19:58 mbecke Exp $ */ public class TestHttpConnectionManager extends TestLocalHostBase { @@ -128,7 +128,7 @@ HttpConnection connection = mgr.getConnection(hostConfiguration); ConnectMethod connect = new ConnectMethod(get); assertTrue(connect.execute(new HttpState(), connection) != 200); - } catch (Exception e) { + } catch (IOException e) { e.printStackTrace(); fail("Error executing connect: " + e); } @@ -137,7 +137,7 @@ try { get.releaseConnection(); mgr.getConnectionWithTimeout(hostConfiguration, 1).releaseConnection(); - } catch (Exception e1) { + } catch (ConnectTimeoutException e1) { fail("Connection should have been available."); } @@ -147,7 +147,7 @@ HttpConnection connection = mgr.getConnection(hostConfiguration); ConnectMethod connect = new ConnectMethod(get); assertTrue(connect.execute(new HttpState(), connection) != 200); - } catch (Exception e) { + } catch (IOException e) { e.printStackTrace(); fail("Error executing connect: " + e); } @@ -166,7 +166,7 @@ HttpConnection connection = mgr.getConnection(hostConfiguration); ConnectMethod connect = new ConnectMethod(get); assertTrue(connect.execute(new HttpState(), connection) != 200); - } catch (Exception e) { + } catch (IOException e) { e.printStackTrace(); fail("Error executing connect: " + e); } @@ -224,7 +224,7 @@ HttpClient client = createHttpClient(connectionManager); // we shouldn't have to wait if a connection is available - client.setHttpConnectionFactoryTimeout( 1 ); + client.setHttpConnectionFactoryTimeout(1); GetMethod getMethod = new GetMethod("/"); @@ -242,6 +242,7 @@ } catch (HttpException e) { fail("error reading from server; " + e); } catch (IOException e) { + e.printStackTrace(); fail("error reading from server; " + e); } Index: test/org/apache/commons/httpclient/TestMethodsNoHost.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestMethodsNoHost.java,v retrieving revision 1.19 diff -u -r1.19 TestMethodsNoHost.java --- test/org/apache/commons/httpclient/TestMethodsNoHost.java 19 Jun 2003 20:52:07 -0000 1.19 +++ test/org/apache/commons/httpclient/TestMethodsNoHost.java 22 Jul 2003 12:33:10 -0000 @@ -67,11 +67,11 @@ import java.io.Reader; import junit.framework.Test; -import junit.framework.TestCase; import junit.framework.TestSuite; + import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.HeadMethod; +import org.apache.commons.httpclient.methods.PostMethod; /** * @author Rodney Waldhoff @@ -80,7 +80,7 @@ * @author Oleg Kalnichevski * @version $Revision: 1.19 $ $Date: 2003/06/19 20:52:07 $ */ -public class TestMethodsNoHost extends TestCase { +public class TestMethodsNoHost extends TestNoHostBase { static final String NAME = "name", VALUE = "value"; static final String NAME0 = "name0", VALUE0 = "value0"; @@ -186,7 +186,6 @@ } public void testConnectionAutoClose() throws Exception { - SimpleHttpConnection conn = new SimpleHttpConnection(); String headers = "HTTP/1.1 200 OK\r\n" +"Date: Wed, 28 Mar 2001 05:05:04 GMT\r\n" +"Connection: close\r\n"; @@ -199,7 +198,7 @@ conn.addResponse(headers, body); conn.open(); HttpMethodBase method = new GetMethod("/"); - method.execute(new HttpState(), conn); + client.executeMethod(method); Reader response = new InputStreamReader(method.getResponseBodyAsStream()); int c; while ((c = response.read()) != -1) { @@ -214,7 +213,7 @@ conn.addResponse(headers, ""); try { - headMethod.execute(new HttpState(), conn); + client.executeMethod(headMethod); conn.assertNotOpen(); } catch (Throwable t) { @@ -250,7 +249,6 @@ * Make sure that its OK to call releaseConnection if the connection has not been. */ public void testReleaseConnection() { - HttpClient client = new HttpClient(); HttpMethod method = new GetMethod("http://bogus.url/path/"); method.releaseConnection(); } Index: test/org/apache/commons/httpclient/TestMethodsRedirectNoHost.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestMethodsRedirectNoHost.java,v retrieving revision 1.6 diff -u -r1.6 TestMethodsRedirectNoHost.java --- test/org/apache/commons/httpclient/TestMethodsRedirectNoHost.java 20 Apr 2003 23:26:23 -0000 1.6 +++ test/org/apache/commons/httpclient/TestMethodsRedirectNoHost.java 22 Jul 2003 12:33:11 -0000 @@ -63,21 +63,17 @@ package org.apache.commons.httpclient; import junit.framework.Test; -import junit.framework.TestCase; import junit.framework.TestSuite; -import org.apache.commons.httpclient.methods.*; +import org.apache.commons.httpclient.methods.PostMethod; /** * @author Jeff Dever * @version $Revision: 1.6 $ */ -public class TestMethodsRedirectNoHost extends TestCase { +public class TestMethodsRedirectNoHost extends TestNoHostBase { - SimpleHttpConnection conn; - - // ------------------------------------------------------------ Constructor public TestMethodsRedirectNoHost(String testName) { @@ -90,11 +86,6 @@ return new TestSuite(TestMethodsRedirectNoHost.class); } - public void setUp() throws Exception{ - conn = new SimpleHttpConnection(); - } - - private void addRedirectResponse(String location) { String headers = "HTTP/1.1 302 Redirect\r\n" +"Date: Wed, 28 Mar 2002 05:05:04 GMT\r\n" @@ -118,9 +109,9 @@ addOkResponse(); conn.open(); - HttpMethod method = new SimpleHttpMethod("/oldfile"); + HttpMethod method = new SimpleHttpMethod("http://localhost/oldfile"); method.setFollowRedirects(true); - method.execute(new HttpState(), conn); + client.executeMethod(method); Header locationHeader = method.getResponseHeader("Location"); assertEquals(200, method.getStatusCode()); assertEquals("/newfile", method.getPath()); @@ -135,7 +126,7 @@ HttpMethod method = new SimpleHttpMethod("/oldfile"); method.setFollowRedirects(true); - method.execute(new HttpState(), conn); + client.executeMethod(method); Header locationHeader = method.getResponseHeader("Location"); assertEquals(200, method.getStatusCode()); assertEquals("/newfile", method.getPath()); @@ -150,7 +141,7 @@ PostMethod method = new PostMethod("/oldfile"); method.setRequestBody(new NameValuePair[] { new NameValuePair("name", "value") } ); - method.execute(new HttpState(), conn); + client.executeMethod(method); Header locationHeader = method.getResponseHeader("Location"); assertEquals(302, method.getStatusCode()); assertEquals("/oldfile", method.getPath()); @@ -182,7 +173,7 @@ HttpMethod method = new SimpleHttpMethod("/oldfile"); method.setFollowRedirects(true); method.setStrictMode(false); - method.execute(new HttpState(), conn); + client.executeMethod(method); Header locationHeader = method.getResponseHeader("Location"); assertEquals(200, method.getStatusCode()); assertEquals("/newfile", method.getPath()); @@ -266,13 +257,11 @@ addOkResponse(); conn.open(); - HttpState state = new HttpState(); - state.addCookie( - new Cookie("localhost", "name", "value", "/", -1, false)); + client.getState().addCookie(new Cookie("localhost", "name", "value", "/", -1, false)); HttpMethod method = new SimpleHttpMethod("/oldfile"); method.setFollowRedirects(true); - method.execute(state, conn); + client.executeMethod(method); Header locationHeader = method.getResponseHeader("Location"); assertEquals(200, method.getStatusCode()); Index: test/org/apache/commons/httpclient/TestNoHostBase.java =================================================================== RCS file: test/org/apache/commons/httpclient/TestNoHostBase.java diff -N test/org/apache/commons/httpclient/TestNoHostBase.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/org/apache/commons/httpclient/TestNoHostBase.java 22 Jul 2003 12:33:11 -0000 @@ -0,0 +1,37 @@ +package org.apache.commons.httpclient; + +import junit.framework.TestCase; + +/** + */ +public abstract class TestNoHostBase extends TestCase { + + protected SimpleHttpConnection conn; + + protected HttpClient client; + + protected NoHostHttpConnectionManager connectionManager; + + /** + * + */ + public TestNoHostBase() { + super(); + } + + /** + * @param arg0 + */ + public TestNoHostBase(String arg0) { + super(arg0); + } + + public void setUp() throws Exception{ + conn = new SimpleHttpConnection(); + connectionManager = new NoHostHttpConnectionManager(); + connectionManager.setConnection(conn); + client = new HttpClient(connectionManager); + client.getHostConfiguration().setHost("localhost", 80, "http"); + } + +} 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.8 diff -u -r1.8 TestResponseHeaders.java --- test/org/apache/commons/httpclient/TestResponseHeaders.java 20 Jun 2003 13:33:09 -0000 1.8 +++ test/org/apache/commons/httpclient/TestResponseHeaders.java 22 Jul 2003 12:33:12 -0000 @@ -76,7 +76,7 @@ * @author Adrian Sutton * @version $Id: TestResponseHeaders.java,v 1.8 2003/06/20 13:33:09 adrian Exp $ */ -public class TestResponseHeaders extends TestCase { +public class TestResponseHeaders extends TestNoHostBase { // ------------------------------------------------------------ Constructor public TestResponseHeaders(String testName) { @@ -94,20 +94,6 @@ return new TestSuite(TestResponseHeaders.class); } - - - /** - * Simple extension of HttpMethodBase. - */ - private class SimpleHttpMethod extends HttpMethodBase { - public SimpleHttpMethod() { - super(""); - } - public String getName() { - return "simple"; - } - } - // ----------------------------------------------------------- Test Methods public void testHeaders() throws Exception { String body = "XXX\r\nYYY\r\nZZZ"; @@ -118,10 +104,9 @@ "Content-Type: text/xml; charset=utf-8\r\n" + "Date: Wed, 28 Mar 2001 05:05:04 GMT\r\n" + "Server: UserLand Frontier/7.0-WinNT\r\n"; - HttpState state = new HttpState(); HttpMethod method = new SimpleHttpMethod(); - SimpleHttpConnection conn = new SimpleHttpConnection(headers, body); - method.execute(state, conn); + conn.addResponse(headers, body); + client.executeMethod(method); 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()); @@ -139,17 +124,15 @@ "HTTP/1.1 200 OK\r\n" + "Content-Length: " + body.length() + "\r\n" + "Content-Length: " + body.length() + "\r\n"; - HttpState state = new HttpState(); HttpMethod method = new SimpleHttpMethod(); - SimpleHttpConnection conn = new SimpleHttpConnection(headers, body); - method.execute(state, conn); + conn.addResponse(headers, body); + client.executeMethod(method); assertNotNull( "Response body is null.", method.getResponseBodyAsStream() ); } public void testDuplicateProxyConnection() throws Exception { - SimpleHttpConnection conn = new SimpleHttpConnection(); String headers = "HTTP/1.1 200 OK\r\n" + "proxy-connection: close\r\n" @@ -161,12 +144,11 @@ conn.setProxyHost("proxy"); conn.setProxyPort(1); GetMethod method = new GetMethod("/"); - method.execute(new HttpState(), conn); + client.executeMethod(method); method.getResponseBodyAsString(); assertFalse(conn.isOpen()); - conn = new SimpleHttpConnection(); headers = "HTTP/1.0 200 OK\r\n" + "proxy-connection: keep-alive\r\n" @@ -178,7 +160,7 @@ conn.setProxyHost("proxy"); conn.setProxyPort(1); method = new GetMethod("/"); - method.execute(new HttpState(), conn); + client.executeMethod(method); method.getResponseBodyAsString(); assertTrue(conn.isOpen()); @@ -186,21 +168,19 @@ public void testDuplicateConnection() throws Exception { - SimpleHttpConnection conn = new SimpleHttpConnection(); String headers = "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Connection: close\r\n" + "\r\n"; - conn.addResponse(headers, ""); GetMethod method = new GetMethod("/"); - method.execute(new HttpState(), conn); + conn.addResponse(headers, ""); + client.executeMethod(method); method.getResponseBodyAsString(); assertFalse(conn.isOpen()); - conn = new SimpleHttpConnection(); headers = "HTTP/1.0 200 OK\r\n" +"Connection: keep-alive\r\n" @@ -208,9 +188,9 @@ + "Content-Length: 0\r\n" +"\r\n"; - conn.addResponse(headers, ""); method = new GetMethod("/"); - method.execute(new HttpState(), conn); + conn.addResponse(headers, ""); + client.executeMethod(method); method.getResponseBodyAsString(); assertTrue(conn.isOpen()); @@ -218,27 +198,25 @@ public void testNoContentLength() throws Exception { // test with connection header - SimpleHttpConnection conn = new SimpleHttpConnection(); String headers = "HTTP/1.1 200 OK\r\n" + "Connection: keep-alive\r\n" + "\r\n"; - conn.addResponse(headers, "12345"); GetMethod method = new GetMethod("/"); - method.execute(new HttpState(), conn); + conn.addResponse(headers, "12345"); + client.executeMethod(method); method.getResponseBodyAsString(); assertFalse(conn.isOpen()); // test without connection header - conn = new SimpleHttpConnection(); headers = "HTTP/1.1 200 OK\r\n\r\n"; // test with connection header - conn.addResponse(headers, "12345"); method = new GetMethod("/"); - method.execute(new HttpState(), conn); + conn.addResponse(headers, "12345"); + client.executeMethod(method); method.getResponseBodyAsString(); assertFalse(conn.isOpen()); @@ -246,30 +224,28 @@ public void testProxyNoContentLength() throws Exception { // test with proxy-connection header - SimpleHttpConnection conn = new SimpleHttpConnection(); String headers = "HTTP/1.1 200 OK\r\n" + "proxy-connection: keep-alive\r\n" + "\r\n"; - conn.addResponse(headers, "12345"); conn.setProxyHost("proxy"); conn.setProxyPort(1); GetMethod method = new GetMethod("/"); - method.execute(new HttpState(), conn); + conn.addResponse(headers, "12345"); + client.executeMethod(method); method.getResponseBodyAsString(); assertFalse(conn.isOpen()); // test without proxy-connection header - conn = new SimpleHttpConnection(); headers = "HTTP/1.1 200 OK\r\n\r\n"; - conn.addResponse(headers, "12345"); conn.setProxyHost("proxy"); conn.setProxyPort(1); method = new GetMethod("/"); - method.execute(new HttpState(), conn); + conn.addResponse(headers, "12345"); + client.executeMethod(method); method.getResponseBodyAsString(); assertFalse(conn.isOpen()); @@ -280,10 +256,9 @@ String headers = "HTTP/1.1 200 OK\r\n" + "Content-Length: " + body.length() + "\r\n"; - HttpState state = new HttpState(); HttpMethod method = new SimpleHttpMethod(); - SimpleHttpConnection conn = new SimpleHttpConnection(headers, body); - method.execute(state, conn); + conn.addResponse(headers, body); + client.executeMethod(method); assertEquals(null, method.getResponseHeader(null)); assertEquals(null, method.getResponseHeader("bogus")); } @@ -299,10 +274,9 @@ "Date: Wed, 28 Mar 2001\r\n" + " 05:05:04 GMT\r\n" + "Server: UserLand Frontier/7.0-WinNT\r\n"; - HttpState state = new HttpState(); HttpMethod method = new SimpleHttpMethod(); - SimpleHttpConnection conn = new SimpleHttpConnection(headers, body); - method.execute(state, conn); + conn.addResponse(headers, body); + client.executeMethod(method); 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());