You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by js...@apache.org on 2002/12/03 06:46:16 UTC
cvs commit: jakarta-commons/httpclient/src/test/org/apache/commons/httpclient TestLocalHostBase.java TestHttpClientLocalHost.java TestHttpConnection.java TestHttpConnectionManager.java TestLocalHost.java
jsdever 2002/12/02 21:46:16
Modified: httpclient/src/java/org/apache/commons/httpclient
HttpClient.java HttpConnection.java
HttpConnectionManager.java HttpMethod.java
HttpMethodBase.java HttpState.java
HttpUrlMethod.java
httpclient/src/test/org/apache/commons/httpclient
TestHttpClientLocalHost.java
TestHttpConnection.java
TestHttpConnectionManager.java TestLocalHost.java
Added: httpclient/src/java/org/apache/commons/httpclient
MultiThreadedHttpConnectionManager.java
SimpleHttpConnectionManager.java
httpclient/src/test/org/apache/commons/httpclient
TestLocalHostBase.java
Removed: httpclient/src/java/org/apache/commons/httpclient
HttpMultiClient.java HttpSharedState.java
Log:
Massive merger of HttpClient and HttpMultiClient.
Contributed by: Michael Becke
Revision Changes Path
1.61 +264 -138 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpClient.java
Index: HttpClient.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpClient.java,v
retrieving revision 1.60
retrieving revision 1.61
diff -u -r1.60 -r1.61
--- HttpClient.java 12 Nov 2002 09:58:22 -0000 1.60
+++ HttpClient.java 3 Dec 2002 05:46:15 -0000 1.61
@@ -62,13 +62,14 @@
package org.apache.commons.httpclient;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
import java.io.IOException;
import java.net.URL;
+
import javax.net.ssl.SSLSocketFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
/**
* <p>
@@ -81,6 +82,8 @@
* @author Sean C. Sullivan
* @author <a href="mailto:dion@apache.org">dIon Gillard</a>
* @author Ortwin Gl�ck
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ *
* @version $Revision$ $Date$
*/
@@ -95,17 +98,33 @@
// ----------------------------------------------------------- Constructors
/**
- * Constructor.
+ * Creates an HttpClient using <code>SimpleHttpConnectionManager</code>.
+ *
+ * @see SimpleHttpConnectionManager
*/
public HttpClient() {
+ this( new SimpleHttpConnectionManager() );
}
- // ----------------------------------------------------- Instance Variables
-
/**
- * My {@link HttpConnection connection}.
+ * Creates an HttpClient with a user specified connection manager.
+ *
+ * @since 2.0
*/
- private HttpConnection connection = null;
+ public HttpClient( HttpConnectionManager httpConnectionManager ) {
+
+ if ( httpConnectionManager == null ) {
+ throw new IllegalArgumentException("httpConnectionManager cannot be null");
+ }
+
+ this.state = new HttpState();
+ this.state.setHttpConnectionManager( httpConnectionManager );
+
+ this.hostConfiguration = new HostConfiguration();
+
+ }
+
+ // ----------------------------------------------------- Instance Variables
/**
* My {@link HttpState state}.
@@ -114,10 +133,17 @@
private SSLSocketFactory sslSocketFactory = null;
+ /** the timout when waiting for a connection from the connectionManager */
+ private long httpConnectionTimeout = 0;
+
private int timeoutInMilliseconds = 0;
private int connectionTimeout = 0;
+ private HostConfiguration hostConfiguration;
+
+ private boolean strictMode = false;
+
// ------------------------------------------------------------- Properties
/**
@@ -126,10 +152,7 @@
* @see #setState(HttpState)
* @return the shared client state
*/
- public HttpState getState() {
- if (null == state) {
- state = new HttpState();
- }
+ public synchronized HttpState getState() {
return state;
}
@@ -139,16 +162,38 @@
* @see #getState()
* @param state the new state for the client
*/
- public void setState(HttpState state) {
+ public synchronized void setState(HttpState state) {
this.state = state;
}
/**
+ *
+ * @param strictMode <code>true</code> if strict mode should be used
+ *
+ * @see #isStrictMode()
+ *
+ */
+ public synchronized void setStrictMode(boolean strictMode) {
+ this.strictMode = strictMode;
+ }
+
+ /**
+ *
+ * @return <code>true</code> if strict mode being used
+ *
+ * @see #setStrictMode(boolean)
+ *
+ */
+ public synchronized boolean isStrictMode() {
+ return strictMode;
+ }
+
+ /**
* Specifies an alternative factory for SSL sockets.
* @see HttpConnection#setSSLSocketFactory(SSLSocketFactory) HttpConnection.setSSLSocketFactory
* @param sslSocketFactory a living instance of the alternative SSLSocketFactory
*/
- public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
+ public synchronized void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
this.sslSocketFactory = sslSocketFactory;
}
@@ -160,34 +205,44 @@
* @param newTimeoutInMilliseconds Timeout in milliseconds
*
*/
- public void setTimeout(int newTimeoutInMilliseconds) {
+ public synchronized void setTimeout(int newTimeoutInMilliseconds) {
this.timeoutInMilliseconds = newTimeoutInMilliseconds;
}
/**
+ * Sets the timeout used when retrieving an HttpConnection from the
+ * HttpConnectionManager.
+ *
+ * @param timeout the timeout in milliseconds
+ *
+ * @see HttpConnectionManager#getConnection(String, long)
+ */
+ public synchronized void setHttpConnectionFactoryTimeout( long timeout ) {
+ this.httpConnectionTimeout = timeout;
+ }
+
+ /**
* Sets the timeout until a connection is etablished. A value of 0 means
* the timeout is not used. The default value is 0.
* @see HttpConnection#setConnectionTimeout(int)
* @param newTimeoutInMilliseconds Timeout in milliseconds.
*/
- public void setConnectionTimeout(int newTimeoutInMilliseconds) {
+ public synchronized void setConnectionTimeout(int newTimeoutInMilliseconds) {
this.connectionTimeout = newTimeoutInMilliseconds;
}
// --------------------------------------------------------- Public Methods
/**
- * Start an HTTP session with the server at the given
- * <i>host</i> and <i>port</i>.
+ * @deprecated use hostConfiguration
+ *
+ * Sets the host, port and protocol(http) to be used when executing a
+ * method.
+ *
* @param host the host to connect to
* @param port the port to connect to
*
- * @see #startSession(String, int, boolean)
- * @see #startSession(String, int, Credentials)
- * @see #startSession(String, int, Credentials, boolean)
- * @see #startSession(String, int, String, int)
- * @see #endSession()
- *
+ * @see #getHostConfiguration()
*/
public void startSession(String host, int port) {
log.trace("enter HttpClient.startSession(String, int)");
@@ -195,19 +250,16 @@
}
/**
- * Start an HTTP or HTTPS session with the server at the given
- * <i>host</i> and <i>port</i>.
+ * @deprecated use hostConfiguration
+ *
+ * Sets the host, port and protocol to be used when executing a method.
+ *
* @param host the host to connect to
* @param port the port to connect to
* @param https when <code>true</code>, create an HTTPS session
*
- * @see #startSession(String, int)
- * @see #startSession(String, int, Credentials)
- * @see #startSession(String, int, Credentials, boolean)
- * @see #startSession(String, int, String, int)
- * @see #endSession()
- *
- */
+ * @see #getHostConfiguration()
+ */
public void startSession(String host, int port, boolean https) {
log.trace("enter HttpClient.startSession(String, int, boolean)");
@@ -215,48 +267,47 @@
log.debug("HttpClient.startSession(String,int,boolean): Host:"
+ host + " Port:" + port + " HTTPS:" + https);
}
- connection = new HttpConnection(host, port, https);
+
+ this.hostConfiguration.setHost(
+ host,
+ port,
+ https ? "https" : "http"
+ );
}
/**
- * Start an HTTP session with the server at the given
- * <i>host</i> and <i>port</i> using the given default
- * default credentials.
+ * @deprecated use hostConfiguration and httpState
+ *
+ * Sets the host, port, protocol(http) and credentials to be used when
+ * executing a method.
*
* @param host the host to connect to
* @param port the port to connect to
* @param creds the default credentials to use
*
- * @see #startSession(String, int, boolean)
- * @see #startSession(String, int)
+ * @see #getHostConfiguration()
+ * @see #getState()
* @see #startSession(String, int, Credentials, boolean)
- * @see #startSession(String, int, String, int)
- * @see #endSession()
- *
*/
public void startSession(String host, int port, Credentials creds) {
log.trace("enter HttpClient.startSession(String, int, Credentials)");
startSession(host, port, creds, false);
}
-
/**
- * Start an HTTP or HTTPS session with the server at the given
- * <i>host</i> and <i>port</i> using the given default
- * default credentials.
+ * @deprecated use hostConfiguration and httpState
+ *
+ * Sets the host, port, protocol and credentials to be used when executing a
+ * method.
*
* @param host the host to connect to
* @param port the port to connect to
* @param creds the default credentials to use
* @param https when <code>true</code>, create an HTTPS session
*
- * @see #startSession(String, int, boolean)
- * @see #startSession(String, int)
- * @see #startSession(String, int, Credentials)
- * @see #startSession(String, int, String, int)
- * @see #endSession()
- *
- */
+ * @see #getHostConfiguration()
+ * @see #getState()
+ */
public void startSession(String host, int port, Credentials creds, boolean https) {
log.trace("enter HttpClient.startSession(String, int, Credentials, boolean)");
@@ -268,22 +319,30 @@
+ " HTTPS:" + https);
}
getState().setCredentials(null, creds);
- connection = new HttpConnection(host, port, https);
+ this.hostConfiguration.setHost(
+ host,
+ port,
+ https ? "https" : "http"
+ );
}
-
/**
- * Start an HTTP or HTTPS session with the server specified by the scheme,
- * userinfo, host and port of the given <i>uri</i>.
+ * @deprecated use hostConfiguration and httpState
+ *
+ * Sets the host, port, protocol and credentials to be used when executing a
+ * method using the server specified by the scheme, userinfo, host and port
+ * of the given <i>uri</i>.
* <p>
* Note that the path component is not utilized.
* <p>
* @param uri an <code>HttpURL</code> or <code>HttpsURL</code> instance; the
* {@link URI URI} from which the scheme, userinfo, host and port of the
* session are determined
+ *
* @exception IllegalStateException not enough information to process
- * @see #startSession
- * @see #endSession
+ *
+ * @see #getHostConfiguration()
+ * @see #getState()
*/
public void startSession(URI uri) throws URIException {
log.trace("enter HttpClient.startSession(URI)");
@@ -315,29 +374,27 @@
throw new IllegalStateException
("HttpURL or HttpsURL instance required");
}
- connection = new HttpConnection(host, port, secure);
+ this.hostConfiguration.setHost(
+ host,
+ port,
+ secure ? "https" : "http"
+ );
}
-
/**
- * Start an HTTP or HTTPS session with the server specified
- * by the protocol, host and port of the given
- * <i>url</i>.
+ * @deprecated use hostConfiguration
+ *
+ * Sets the host, port and protocol to be used when executing a method.
* <p>
* Note that everything but the protocol, host and port of the
* given <i>url</i> is ignored.
* </p>
- * @param url the {@link URL URL} from which the protocol, host,
- * and port of the session are determined
- *
- * @see #startSession(String, int, boolean)
- * @see #startSession(String, int)
- * @see #startSession(String, int, Credentials)
- * @see #startSession(String, int, Credentials, boolean)
- * @see #startSession(java.net.URL, Credentials)
- * @see #startSession(String, int, String, int)
- * @see #endSession()
+ * @param url the {@link URL URL} from which the protocol, host, and port of
+ * the session are determined
+ *
+ * @exception IllegalArgumentException if the protocol is not http or https
*
+ * @see #getHostConfiguration()
*/
public void startSession(URL url) {
log.trace("enter HttpClient.startSession(String, int, Credentials, boolean)");
@@ -355,25 +412,22 @@
}
/**
- * Start an HTTP or HTTPS session with the server specified
- * by the protocol, host and port of the given
- * <i>url</i>, using the given credentials by default.
+ * @deprecated use hostConfiguration and httpState
+ *
+ * Sets the host, port, protocol and credentials to be used when executing a
+ * method.
* <p>
* Note that everything but the protocol, host and port of the
* given <i>url</i> is ignored.
* </p>
+ * @param url the {@link URL URL} from which the protocol, host, and port of
+ * the session are determined
* @param creds the default credentials to use
- * @param url the {@link URL URL} from which the protocol, host,
- * and port of the session are determined
- *
- * @see #startSession(java.net.URL)
- * @see #startSession(String, int, boolean)
- * @see #startSession(String, int)
- * @see #startSession(String, int, Credentials)
- * @see #startSession(String, int, Credentials, boolean)
- * @see #startSession(String, int, String, int)
- * @see #endSession()
+ *
+ * @exception IllegalArgumentException if the protocol is not http or https
*
+ * @see #getHostConfiguration()
+ * @see #getState()
*/
public void startSession(URL url, Credentials creds) {
log.trace("enter HttpClient.startSession(URL, Credentials)");
@@ -382,100 +436,151 @@
}
/**
- * Start an HTTP session with the server specified
- * by the given <i>host</i> and <i>port</i>
- * via the given <i>proxyhost</i> and <i>proxyport</i>.
+ * @deprecated use hostConfiguration
+ *
+ * Sets the host, port, protocol(http) and proxy to be used when executing a
+ * method.
+ *
* @param host the host to connect to
* @param port the port to connect to
* @param proxyhost the proxy host to connect via
* @param proxyport the proxy port to connect via
*
- * @see #endSession()
- *
+ * @see #getHostConfiguration()
*/
public void startSession(String host, int port, String proxyhost, int proxyport) {
log.trace("enter HttpClient.startSession(String, int, String, int)");
startSession(host, port, proxyhost, proxyport, false);
}
-
/**
- * Start an HTTP session with the server specified
- * by the given <i>host</i> and <i>port</i>
- * via the given <i>proxyhost</i> and <i>proxyport</i>.
- *
+ * @deprecated use hostConfiguration
+ *
+ * Sets the host, port, protocol and proxy to be used when executing a
+ * method.
+ *
* @param host the host to connect to
* @param port the port to connect to
* @param proxyhost the proxy host to connect via
* @param proxyport the proxy port to connect via
* @param secure whether or not to connect using HTTPS
- *
- * @see #endSession()
- *
+ *
+ * @see #getHostConfiguration()
*/
public void startSession(String host, int port, String proxyhost, int proxyport, boolean secure) {
log.trace("enter HttpClient.startSession(String, int, String, int, boolean)");
- connection = new HttpConnection(proxyhost, proxyport, host, port, secure);
+ this.hostConfiguration.setHost(
+ host,
+ port,
+ secure ? "https" : "http"
+ );
+ this.hostConfiguration.setProxy( proxyhost, proxyport );
}
/**
- * Execute the given {@link HttpMethod} using my current
- * {@link HttpConnection connection} and {@link HttpState}.
+ * Executes the given method.
*
- * @param method the {@link HttpMethod} to execute. This parameter must be non-null.
+ * @param method the {@link HttpMethod} to execute.
* @return the method's response code
*
* @throws java.io.IOException if an I/O error occurs
* @throws HttpException if a protocol exception occurs
- * @throws IllegalStateException if the session has not been started
- *
*/
- public synchronized int executeMethod(HttpMethod method)
- throws IOException, HttpException, IllegalStateException {
+ public int executeMethod(HttpMethod method)
+ throws IOException, HttpException {
+
log.trace("enter HttpClient.executeMethod(HttpMethod)");
+ return executeMethod(getHostConfiguration(), method);
+
+ }
+
+ /**
+ * Executes the given method.
+ *
+ * @param method the {@link HttpMethod} to execute.
+ * @return the method's response code
+ *
+ * @throws java.io.IOException if an I/O error occurs
+ * @throws HttpException if a protocol exception occurs
+ *
+ * @since 2.0
+ */
+ public int executeMethod(HostConfiguration hostConfiguration, HttpMethod method)
+ throws IOException, HttpException {
+ log.trace("enter HttpClient.executeMethod(HostConfiguration,HttpMethod)");
if (null == method) {
throw new NullPointerException("HttpMethod parameter");
}
- if (null == connection) {
- throw new IllegalStateException(
- "The startSession method must be called before executeMethod");
+ int soTimeout = 0;
+ boolean strictMode = false;
+ SSLSocketFactory socketFactory = null;
+ int connectionTimeout = 0;
+ long httpConnectionTimeout = 0;
+ HttpState state = null;
+ HostConfiguration defaultHostConfiguration = null;
+
+ /* 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;
+ socketFactory = this.sslSocketFactory;
+ connectionTimeout = this.connectionTimeout;
+ httpConnectionTimeout = this.httpConnectionTimeout;
+ state = getState();
+ defaultHostConfiguration = getHostConfiguration();
+ }
+
+ HostConfiguration methodConfiguration = new HostConfiguration(hostConfiguration);
+
+ if ( hostConfiguration != defaultHostConfiguration ) {
+ // we may need to appy some defaults
+ if ( !methodConfiguration.isHostSet() ) {
+ methodConfiguration.setHost(
+ defaultHostConfiguration.getHost(),
+ defaultHostConfiguration.getPort(),
+ defaultHostConfiguration.getProtocol()
+ );
+ }
+ if ( !methodConfiguration.isProxySet() && defaultHostConfiguration.isProxySet() ) {
+ methodConfiguration.setProxy(
+ defaultHostConfiguration.getProxyHost(),
+ defaultHostConfiguration.getProxyPort()
+ );
+ }
}
+ HttpConnection connection = state.getHttpConnectionManager().getConnection(
+ methodConfiguration,
+ httpConnectionTimeout
+ );
+
+ method.setStrictMode(strictMode);
+
if (!connection.isOpen()) {
- connection.setSSLSocketFactory(sslSocketFactory);
- connection.setSoTimeout(timeoutInMilliseconds);
+ connection.setSSLSocketFactory(socketFactory);
+ connection.setSoTimeout(soTimeout);
connection.setConnectionTimeout(connectionTimeout);
connection.open();
if (connection.isProxied() && connection.isSecure()) {
method = new ConnectMethod(method);
}
}
- return method.execute(getState(), connection);
+
+ return method.execute(state, connection);
}
/**
- * End the current session, closing my the associated
- * {@link HttpConnection connection} if any.
- *
- * @see #startSession(String, int)
- * @see #startSession(String, int, boolean)
- * @see #startSession(String, int, Credentials)
- * @see #startSession(String, int, Credentials, boolean)
- * @see #startSession(java.net.URL)
- * @see #startSession(java.net.URL, Credentials)
- * @see #startSession(String, int, String, int)
- *
- * @throws java.io.IOException when i/o errors occur closing the connection
- *
+ * @deprecated this method has no effect. HttpMethod.releaseConnection()
+ * should be used to release resources after a HttpMethod has been executed.
+ *
+ * @see HttpMethod#releaseConnection()
*/
public void endSession() throws IOException {
- log.trace("enter HttpClient.endSession()");
- if (null != connection) {
- connection.close();
- connection = null;
- }
}
/**
@@ -485,7 +590,7 @@
* the session has not been started via startSession.
*/
public String getHost() {
- return connection != null ? connection.getHost() : null;
+ return hostConfiguration.getHost();
}
/**
@@ -495,6 +600,27 @@
* has not been started via startSession().
*/
public int getPort() {
- return connection != null ? connection.getPort() : -1;
+ return hostConfiguration.getPort();
}
+
+ /**
+ * Returns the hostConfiguration.
+ * @return HostConfiguration
+ *
+ * @since 2.0
+ */
+ public synchronized HostConfiguration getHostConfiguration() {
+ return hostConfiguration;
+ }
+
+ /**
+ * Sets the hostConfiguration.
+ * @param hostConfiguration The hostConfiguration to set
+ *
+ * @since 2.0
+ */
+ public synchronized void setHostConfiguration(HostConfiguration hostConfiguration) {
+ this.hostConfiguration = hostConfiguration;
+ }
+
}
1.26 +31 -4 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java
Index: HttpConnection.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -r1.25 -r1.26
--- HttpConnection.java 21 Nov 2002 09:54:52 -0000 1.25
+++ HttpConnection.java 3 Dec 2002 05:46:15 -0000 1.26
@@ -744,6 +744,29 @@
closeSocketAndStreams();
}
+ /**
+ * Returns the httpConnectionManager.
+ * @return HttpConnectionManager
+ */
+ public HttpConnectionManager getHttpConnectionManager() {
+ return httpConnectionManager;
+ }
+
+ /**
+ * Sets the httpConnectionManager.
+ * @param httpConnectionManager The httpConnectionManager to set
+ */
+ public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) {
+ this.httpConnectionManager = httpConnectionManager;
+ }
+
+ public void releaseConnection() {
+ log.trace("enter HttpConnection.releaseConnection()");
+ if ( httpConnectionManager != null ) {
+ httpConnectionManager.releaseConnection( this );
+ }
+ }
+
// ------------------------------------------------------ Protected Methods
@@ -847,6 +870,7 @@
return sslSocketFactory.createSocket(host, port);
}
}
+
// ------------------------------------------------------------- Attributes
/** Log object for this class. */
@@ -883,4 +907,7 @@
private boolean _tunnelEstablished = false;
/** Timeout until connection established (Socket created). 0 means no timeout. */
private int connect_timeout = 0;
+ /** the connection manager that created this connection or null */
+ private HttpConnectionManager httpConnectionManager;
+
}
1.12 +43 -346 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnectionManager.java
Index: HttpConnectionManager.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnectionManager.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- HttpConnectionManager.java 29 Sep 2002 21:59:47 -0000 1.11
+++ HttpConnectionManager.java 3 Dec 2002 05:46:15 -0000 1.12
@@ -28,7 +28,7 @@
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
- * 4. The names "The Jakarta Project", "HttpClient", and "Apache Software
+ * 4. The names "The Jakarta Project", "Tomcat", 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.
@@ -62,366 +62,63 @@
package org.apache.commons.httpclient;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.LinkedList;
/**
- * Manages a set of HttpConnections for various host:ports. This class is
- * used by HttpMultiClient.
- *
- * @author Marc A. Saegesser
+ * An interface for classes that manage HttpConnections.
+ *
+ * @see org.apache.commons.httpclient.HttpConnection
+ * @see org.apache.commons.httpclient.HttpClient#HttpClient(HttpConnectionManager)
+ *
+ * @since 2.0
*/
-public class HttpConnectionManager
-{
- // -------------------------------------------------------- Class Variables
- /** Log object for this class. */
- private static final Log log = LogFactory.getLog(HttpConnectionManager.class);
-
- // ----------------------------------------------------- Instance Variables
- private Map mapHosts = new HashMap();
- private Map mapNumConnections = new HashMap();
- private int maxConnections = 2; // Per RFC 2616 sec 8.1.4
- private String proxyHost = null;
- private int proxyPort = -1;
-
- /**
- * No-args constructor
- */
- public HttpConnectionManager() {
- }
-
- /**
- * Set the proxy host to use for all connections.
- *
- * @param proxyHost - the proxy host name
- */
- public void setProxyHost(String proxyHost) {
- this.proxyHost = proxyHost;
- }
-
- /**
- * Get the proxy host.
- *
- * @return the proxy host name
- */
- public String getProxyHost() {
- return proxyHost;
- }
-
- /**
- * Set the proxy port to use for all connections.
- *
- * @param proxyPort - the proxy port number
- */
- public void setProxyPort(int proxyPort) {
- this.proxyPort = proxyPort;
- }
-
- /**
- * Get the proxy port number.
- *
- * @return the proxy port number
- */
- public int getProxyPort() {
- return proxyPort;
- }
-
- /**
- * Set the maximum number of connections allowed for a given host:port.
- * Per RFC 2616 section 8.1.4, this value defaults to 2.
- *
- * @param maxConnections - number of connections allowed for each host:port
- */
- public void setMaxConnectionsPerHost(int maxConnections) {
- this.maxConnections = maxConnections;
- }
-
- /**
- * Get the maximum number of connections allowed for a given host:port.
- *
- * @return The maximum number of connections allowed for a given host:port.
- */
- public int getMaxConnectionsPerHost() {
- return maxConnections;
- }
-
- /**
- * Get an HttpConnection for a given URL. The URL must be fully
- * specified (i.e. contain a protocol and a host (and optional port number).
- * If the maximum number of connections for the host has been reached, this
- * method will block forever until a connection becomes available.
- *
- * @param sURL - a fully specified URL.
- * @return an HttpConnection for the given host:port
- * @exception java.net.MalformedURLException
- * @exception org.apache.commons.httpclient.HttpException -
- * If no connection becomes available before the timeout expires
- */
- public HttpConnection getConnection(String sURL)
- throws HttpException, MalformedURLException {
- return getConnection(sURL, 0);
- }
+public interface HttpConnectionManager {
/**
- * Return the port provided if not -1 (default), return 443 if the
- * protocol is HTTPS, otherwise 80.
+ * Gets an HttpConnection for a given host configuration. If a connection is
+ * not available this method will block until one is.
*
- * This functionality is a URLUtil and may be better off in URIUtils
+ * The connection manager should be registered with any HttpConnection that
+ * is created.
*
- * @param protocol the protocol to use to get the port for, e.g. http or
- * https
- * @param port the port provided with the url (could be -1)
- * @return the port for the specified port and protocol.
+ * @param hostConfiguration the host configuration to use to configure the
+ * connection
+ *
+ * @return an HttpConnection for the given configuration
+ *
+ * @see HttpConnection#setHttpConnectionManager(HttpConnectionManager)
*/
- private static int getPort(String protocol, int port) {
- log.trace("HttpConnectionManager.getPort(String, port)");
+ public HttpConnection getConnection(HostConfiguration hostConfiguration)
+ throws MalformedURLException;
- // default to provided port
- int portForProtocol = port;
- if (portForProtocol == -1) {
- if (protocol.equalsIgnoreCase("HTTPS")) {
- portForProtocol = 443;
- } else {
- portForProtocol = 80;
- }
- }
- return portForProtocol;
- }
-
/**
- * Get an HttpConnection for a given URL. The URL must be fully
- * specified (i.e. contain a protocol and a host (and optional port number).
- * If the maximum number of connections for the host has been reached, this
- * method will block for <code>timeout</code> milliseconds or until a
- * connection becomes available. If no connection becomes available before
- * the timeout expires an HttpException exception will be thrown.
+ * Gets an HttpConnection for a given host configuration. If a connection is
+ * not available, this method will block for at most the specified number of
+ * milliseconds or until a connection becomes available.
*
- * @param sURL - a fully specified URL.
- * @param timeout - the time (in milliseconds) to wait for a connection
- * to become available
- * @return an HttpConnection for the given host:port
- * @exception java.net.MalformedURLException
- * @exception org.apache.commons.httpclient.HttpException -
- * If no connection becomes available before the timeout expires
- */
- public HttpConnection getConnection(String sURL, long timeout)
- throws HttpException, MalformedURLException {
- log.trace("enter HttpConnectionManager.getConnection(String, long)");
-
- // FIXME: This method is too big
- if (sURL == null) {
- throw new MalformedURLException("URL is null");
- }
-
- URL url = new URL(sURL);
- // Get the protocol and port (use default port if not specified)
- String protocol = url.getProtocol();
- String host = url.getHost();
- int port = HttpConnectionManager.getPort(protocol, url.getPort());
- final String hostAndPort = host + ":" + port;
-
- if (log.isDebugEnabled()) {
- log.debug("HttpConnectionManager.getConnection: key = "
- + hostAndPort);
- }
-
- // Look for a list of connections for the given host:port
- LinkedList listConnections = getConnections(hostAndPort);
-
- HttpConnection conn = null;
- // get a connection from the 'pool', waiting for 'timeout' if no
- // connections are currently available
- synchronized(listConnections) {
- if (listConnections.size() > 0) {
- conn = (HttpConnection)listConnections.removeFirst();
- } else {
- // get number of connections to host:port
- Integer numConnections = getConnectionsInUse(hostAndPort);
- if (numConnections.intValue() < maxConnections) {
- // Create a new connection
- boolean isSecure = protocol.equalsIgnoreCase("HTTPS");
- conn = new HttpConnection(proxyHost, proxyPort, host, port,
- isSecure);
- numConnections = new Integer(numConnections.intValue() + 1);
- mapNumConnections.put(hostAndPort, numConnections);
- } else {
- conn = waitForConnection(listConnections, timeout);
- }
- }
- }
-
- return conn;
- }
-
- /**
- * Get the pool (list) of connections available for the given host and port
+ * The connection manager should be registered with any HttpConnection that
+ * is created.
*
- * @param hostAndPort the key for the connection pool
- * @return a pool (list) of connections available for the given key
+ * @param hostConfiguration the host configuration to use to configure the
+ * connection
+ * @param timeout - the time (in milliseconds) to wait for a connection to
+ * become available, 0 to specify an infinite timeout
+ *
+ * @return an HttpConnection for the given configuraiton
+ *
+ * @exception org.apache.commons.httpclient.HttpException if no connection
+ * becomes available before the timeout expires
+ *
+ * @see HttpConnection#setHttpConnectionManager(HttpConnectionManager)
*/
- private LinkedList getConnections(String hostAndPort) {
- log.trace("enter HttpConnectionManager.getConnections(String)");
+ public HttpConnection getConnection(HostConfiguration hostConfiguration, long timeout)
+ throws HttpException, MalformedURLException;
- // Look for a list of connections for the given host:port
- LinkedList listConnections = null;
- synchronized (mapHosts) {
- listConnections = (LinkedList) mapHosts.get(hostAndPort);
- if (listConnections == null) {
- // First time for this host:port
- listConnections = new LinkedList();
- mapHosts.put(hostAndPort, listConnections);
- mapNumConnections.put(hostAndPort, new Integer(0));
- }
- }
- return listConnections;
- }
-
/**
- * Get the number of connections in use for the key
+ * Releases the given HttpConnection for use by other requests.
*
- * @param hostAndPort the key that connections are tracked on
- * @return the number of connections in use for the given key
- */
- public Integer getConnectionsInUse(String hostAndPort) {
- log.trace("enter HttpConnectionManager.getConnectionsInUse(String)");
- // FIXME: Shouldn't this be synchronized on mapNumConnections? or
- // mapHosts?
-
- Integer numConnections = (Integer)mapNumConnections.get(hostAndPort);
- if (numConnections == null) {
- log.error("HttpConnectionManager.getConnection: "
- + "No connection count for " + hostAndPort);
- // This should never happen, but just in case we'll try to recover.
- numConnections = new Integer(0);
- mapNumConnections.put(hostAndPort, numConnections);
- }
- return numConnections;
- }
-
- /**
- * wait for a connection from the pool
- */
- private HttpConnection waitForConnection(LinkedList pool, long timeout)
- throws HttpException {
- log.trace("enter HttpConnectionManager.waitForConnection(LinkedList, long)");
-
- // No connections available, so wait
- // Start the timeout thread
- TimeoutThread threadTimeout = new TimeoutThread();
- threadTimeout.setTimeout(timeout);
- threadTimeout.setWakeupThread(Thread.currentThread());
- threadTimeout.start();
-
- HttpConnection conn = null;
- // wait for the connection to be available
- while(conn == null){ // spin lock
- try {
- log.debug("HttpConnectionManager.getConnection: waiting for "
- + "connection from " + pool);
- pool.wait();
- } catch (InterruptedException e) {
- throw new HttpException("Timeout waiting for connection.");
- }
- if (pool.size() > 0) {
- conn = (HttpConnection)pool.removeFirst();
- threadTimeout.interrupt();
- }
- }
- return conn;
- }
-
- /**
- * Make the given HttpConnection available for use by other requests.
- * If another thread is blocked in getConnection() waiting for a connection
- * for this host:port, they will be woken up.
- *
* @param conn - The HttpConnection to make available.
*/
- public void releaseConnection(HttpConnection conn) {
- log.trace("enter HttpConnectionManager.releaseConnection(HttpConnection)");
-
- String host = conn.getHost();
- int port = conn.getPort();
- String key = host + ":" + port;
-
- if(log.isDebugEnabled()){
- log.debug("HttpConnectionManager.releaseConnection: Release connection for " + host + ":" + port);
- }
-
- LinkedList listConnections = null;
- synchronized(mapHosts){
- listConnections = (LinkedList)mapHosts.get(key);
- if(listConnections == null){
- // This is an error, but we'll try to recover
- log.error("HttpConnectionManager.releaseConnection: No connect list for " + key);
- listConnections = new LinkedList();
- mapHosts.put(key, listConnections);
- mapNumConnections.put(key, new Integer(1));
- }
- }
-
- synchronized(listConnections){
- // Put the connect back in the available list and notify a waiter
- listConnections.addFirst(conn);
- listConnections.notify();
- }
- }
-
- /**
- * In getConnection, if the maximum number of connections has already
- * been reached the call will block. This class is used to help provide
- * a timeout facility for this wait. Because Java does not provide a way to
- * determine if wait() returned due to a notify() or a timeout, we need
- * an outside mechanism to interrupt the waiting thread after the specified
- * timeout interval.
- */
- private static class TimeoutThread extends Thread {
-
- private long timeout = 0;
- private Thread thrdWakeup = null;
-
- public void setTimeout(long timeout)
- {
- this.timeout = timeout;
- }
-
- public long getTimeout()
- {
- return timeout;
- }
-
- public void setWakeupThread(Thread thrdWakeup)
- {
- this.thrdWakeup = thrdWakeup;
- }
-
- public Thread getWakeupThread()
- {
- return thrdWakeup;
- }
-
- public void run() {
- log.trace("TimeoutThread.run()");
- if(timeout == 0){
- return;
- }
- if(thrdWakeup == null){
- return;
- }
+ public void releaseConnection(HttpConnection conn);
- try{
- sleep(timeout);
- thrdWakeup.interrupt();
- }catch(InterruptedException e){
- log.debug("InterruptedException caught as expected");
- // This is expected
- }
- }
- }
}
1.20 +11 -4 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.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- HttpMethod.java 7 Sep 2002 01:07:47 -0000 1.19
+++ HttpMethod.java 3 Dec 2002 05:46:15 -0000 1.20
@@ -303,6 +303,13 @@
public void recycle();
/**
+ * Releases the connection being used by this method. In particular the
+ * connection is used to read the response(if there is one) and will be held
+ * until the response has been read.
+ */
+ public void releaseConnection();
+
+ /**
* Use this method internally to add footers.
* @since 2.0
*/
1.81 +110 -12 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.80
retrieving revision 1.81
diff -u -r1.80 -r1.81
--- HttpMethodBase.java 2 Dec 2002 06:14:56 -0000 1.80
+++ HttpMethodBase.java 3 Dec 2002 05:46:15 -0000 1.81
@@ -64,23 +64,21 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.URL;
+import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
+import java.net.URL;
import java.util.Date;
-import java.util.Map;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
+import org.apache.commons.httpclient.util.URIUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.commons.httpclient.util.URIUtil;
/**
* <p>
@@ -210,6 +208,9 @@
/** The response body, assuming it has not be intercepted by a sub-class. */
private InputStream responseStream = null;
+
+ /** The connection that the response stream was read from. */
+ private HttpConnection responseConnection = null;
/** Buffer for the response */
private byte[] responseBody = null;
@@ -758,6 +759,15 @@
return false;
}
+ private void wrapResponseStream( HttpConnection connection ) {
+
+ if ( responseStream != null ) {
+ this.responseConnection = connection;
+ this.responseStream = new ResponseAutoReleaseInputStream(responseStream);
+ }
+
+ }
+
/**
* Execute this method. Note that we cannot currently support redirects
* that change the connection parameters (host, port, protocol) because
@@ -831,9 +841,11 @@
//if the authentication is successful, return the statusCode
//otherwise, drop through the switch and try again.
if (processAuthenticationResponse(state, conn)) {
+ wrapResponseStream(conn);
return statusCode;
}
} else { //let the client handle the authenticaiton
+ wrapResponseStream(conn);
return statusCode;
}
break;
@@ -844,12 +856,14 @@
log.debug("Redirect required");
if (! processRedirectResponse(state, conn)) {
+ wrapResponseStream(conn);
return statusCode;
}
break;
default:
// neither an unauthorized nor a redirect response
+ wrapResponseStream(conn);
return statusCode;
} //end of switch
@@ -872,6 +886,8 @@
}
} //end of loop
+ wrapResponseStream(conn);
+
log.error("Narrowly avoided an infinite loop in execute");
throw new HttpRecoverableException("Maximum redirects ("+ maxForwards +") exceeded");
}
@@ -1027,6 +1043,8 @@
public void recycle() {
log.trace("enter HttpMethodBase.recycle()");
+ releaseConnection();
+
path = null;
followRedirects = false;
doAuthentication = true;
@@ -1041,6 +1059,21 @@
}
/**
+ * @see org.apache.commons.httpclient.HttpMethod#releaseConnection()
+ *
+ * @since 2.0
+ */
+ public void releaseConnection() {
+
+ if ( responseConnection != null ) {
+ responseConnection.releaseConnection();
+ this.responseConnection = null;
+ this.responseStream = null;
+ }
+
+ }
+
+ /**
* Remove the request header associated with the given name. Note that
* header-name matching is case insensitive.
*
@@ -1586,7 +1619,7 @@
responseBody = null; // is this desired?
Header lengthHeader = getResponseHeader("Content-Length");
Header transferEncodingHeader = getResponseHeader("Transfer-Encoding");
- InputStream is = conn.getResponseInputStream(this);
+ InputStream is = conn.getResponseInputStream();
if (wireLog.isDebugEnabled()) {
is = new WireLogInputStream(is);
}
@@ -1616,6 +1649,8 @@
try {
int expectedLength = Integer.parseInt(lengthValue);
+ // FIXME: what if the content length is 0, perhaps we should
+ // just return an empty stream in that case
result = new ContentLengthInputStream(is, expectedLength);
} catch(NumberFormatException e) {
throw new HttpException(
@@ -1629,11 +1664,14 @@
&& !getName().equals(ConnectMethod.NAME)){
result = is;
}
- if (result == null) return null;
+ if (result == null) {
+ return null;
+ }
if (shouldCloseConnection()) {
result = new AutoCloseInputStream(result, conn);
}
+
return result;
}
@@ -2263,5 +2301,65 @@
return getContentCharSet(getResponseHeader("Content-Type"));
}
+ /**
+ * Releases this connection from its connectionManager when the response has
+ * been read.
+ */
+ private class ResponseAutoReleaseInputStream extends InputStream {
+
+ private InputStream is;
+
+ public ResponseAutoReleaseInputStream(InputStream is) {
+ this.is = is;
+ }
+
+ /**
+ * @see java.io.InputStream#close()
+ */
+ public void close() throws IOException {
+ is.close();
+ releaseConnection();
+ }
+
+ /**
+ * @see java.io.InputStream#read()
+ */
+ public int read() throws IOException {
+ int b = is.read();
+
+ if ( b == -1 ) {
+ releaseConnection();
+ }
+
+ return b;
+ }
+
+ /**
+ * @see java.io.InputStream#read(byte, int, int)
+ */
+ public int read(byte[] array, int off, int len) throws IOException {
+ int b = is.read(array, off, len);
+
+ if ( b == -1 ) {
+ releaseConnection();
+ }
+
+ return b;
+ }
+
+ /**
+ * @see java.io.InputStream#read(byte)
+ */
+ public int read(byte[] array) throws IOException {
+ int b = is.read(array);
+
+ if ( b == -1 ) {
+ releaseConnection();
+ }
+
+ return b;
+ }
+
+ }
}
1.13 +58 -15 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpState.java
Index: HttpState.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpState.java,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -r1.12 -r1.13
--- HttpState.java 5 Sep 2002 04:57:00 -0000 1.12
+++ HttpState.java 3 Dec 2002 05:46:15 -0000 1.13
@@ -82,6 +82,7 @@
* @author Rodney Waldhoff
* @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
* @author Sean C. Sullivan
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
*
* @version $Revision$ $Date$
*
@@ -105,11 +106,24 @@
*/
private ArrayList cookies = new ArrayList();
+ private HttpConnectionManager httpConnectionManager;
+
// -------------------------------------------------------- Class Variables
/** Log object for this class. */
private static final Log log = LogFactory.getLog(HttpState.class);
+ /**
+ * Constructor for HttpState.
+ */
+ public HttpState() {
+
+ super();
+
+ this.httpConnectionManager = new SimpleHttpConnectionManager();
+
+ }
+
// ------------------------------------------------------------- Properties
/**
@@ -122,7 +136,7 @@
* @see #addCookies(Cookie[])
*
*/
- public void addCookie(Cookie cookie) {
+ public synchronized void addCookie(Cookie cookie) {
log.trace("enter HttpState.addCookie(Cookie)");
if (cookie != null) {
@@ -151,7 +165,7 @@
*
*
*/
- public void addCookies(Cookie[] newcookies) {
+ public synchronized void addCookies(Cookie[] newcookies) {
log.trace("enter HttpState.addCookies(Cookie[])");
if (newcookies != null) {
@@ -169,7 +183,7 @@
* @see #getCookies(String, int, String, boolean, java.util.Date)
*
*/
- public Cookie[] getCookies() {
+ public synchronized Cookie[] getCookies() {
log.trace("enter HttpState.getCookies()");
return (Cookie[])(cookies.toArray(new Cookie[cookies.size()]));
}
@@ -189,7 +203,13 @@
* @see #getCookies()
*
*/
- public Cookie[] getCookies(String domain, int port, String path, boolean secure, Date now) {
+ public synchronized Cookie[] getCookies(
+ String domain,
+ int port,
+ String path,
+ boolean secure,
+ Date now
+ ) {
log.trace("enter HttpState.getCookies(String, int, String, boolean, Date)");
ArrayList list = new ArrayList(cookies.size());
@@ -211,7 +231,7 @@
* @see #purgeExpiredCookies(java.util.Date)
*
*/
- public boolean purgeExpiredCookies() {
+ public synchronized boolean purgeExpiredCookies() {
log.trace("enter HttpState.purgeExpiredCookies()");
return purgeExpiredCookies(new Date());
}
@@ -224,7 +244,7 @@
* @see #purgeExpiredCookies()
*
*/
- public boolean purgeExpiredCookies(Date date) {
+ public synchronized boolean purgeExpiredCookies(Date date) {
log.trace("enter HttpState.purgeExpiredCookies(Date)");
boolean removed = false;
Iterator it = cookies.iterator();
@@ -256,7 +276,7 @@
* @see #setProxyCredentials(String, Credentials)
*
*/
- public void setCredentials(String realm, Credentials credentials) {
+ public synchronized void setCredentials(String realm, Credentials credentials) {
log.trace("enter HttpState.setCredentials(String, Credentials)");
credMap.put(realm,credentials);
}
@@ -275,7 +295,7 @@
* @see #setCredentials(String, Credentials)
*
*/
- public Credentials getCredentials(String realm) {
+ public synchronized Credentials getCredentials(String realm) {
log.trace("enter HttpState.getCredentials(String)");
Credentials creds = (Credentials) credMap.get(realm);
@@ -305,7 +325,7 @@
* @see #setCredentials(String, Credentials)
*
*/
- public void setProxyCredentials(String realm, Credentials credentials) {
+ public synchronized void setProxyCredentials(String realm, Credentials credentials) {
log.trace("enter HttpState.setProxyCredentials(String, credentials)");
proxyCred.put(realm, credentials);
}
@@ -322,7 +342,7 @@
* @return the credentials
* @see #setProxyCredentials
*/
- public Credentials getProxyCredentials(String realm) {
+ public synchronized Credentials getProxyCredentials(String realm) {
log.trace("enter HttpState.getProxyCredentials(String)");
Credentials creds = (Credentials) proxyCred.get(realm);
if (creds == null) {
@@ -331,7 +351,7 @@
return creds;
}
- public String toString()
+ public synchronized String toString()
{
StringBuffer sbResult = new StringBuffer();
@@ -401,4 +421,27 @@
}
return sbResult;
}
+
+ /**
+ * Returns the httpConnectionManager.
+ * @return HttpConnectionManager
+ *
+ * @since 2.0
+ */
+ public synchronized HttpConnectionManager getHttpConnectionManager() {
+ return httpConnectionManager;
+ }
+
+ /**
+ * Sets the httpConnectionManager.
+ * @param httpConnectionManager The httpConnectionManager to set
+ *
+ * @since 2.0
+ */
+ public synchronized void setHttpConnectionManager(
+ HttpConnectionManager httpConnectionManager
+ ) {
+ this.httpConnectionManager = httpConnectionManager;
+ }
+
}
1.6 +4 -4 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpUrlMethod.java
Index: HttpUrlMethod.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpUrlMethod.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- HttpUrlMethod.java 3 Sep 2002 01:36:26 -0000 1.5
+++ HttpUrlMethod.java 3 Dec 2002 05:46:15 -0000 1.6
@@ -68,7 +68,7 @@
* of a URL and gets the host:port from the connection maintained in
* HttpCleint. HttpUrlMethod is initialized with a fully specified URL and is
* used with HttpMultiClient. HttpMultiClient chooses the appropriate
- * HttpConnectoin (via HttpConnectionManager) based on the host and port in
+ * HttpConnectoin (via MultiThreadedHttpConnectionManager) based on the host and port in
* the URL.
*
* @author Marc A. Saegesser
1.1 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java
Index: MultiThreadedHttpConnectionManager.java
===================================================================
/*
* $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java,v 1.1 2002/12/03 05:46:15 jsdever Exp $
* $Revision: 1.1 $
* $Date: 2002/12/03 05:46:15 $
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 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", "Tomcat", 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
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.commons.httpclient;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Manages a set of HttpConnections for various host:ports.
*
* @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
*
* @since 2.0
*/
public class MultiThreadedHttpConnectionManager implements HttpConnectionManager {
// -------------------------------------------------------- Class Variables
/** Log object for this class. */
private static final Log log = LogFactory.getLog(MultiThreadedHttpConnectionManager.class);
// ----------------------------------------------------- Instance Variables
private Map mapHosts = new HashMap();
private int maxConnections = 2; // Per RFC 2616 sec 8.1.4
// mapping from reference to hostPort
private Map referenceToHostPort;
// the reference queue used to track when HttpConnections are lost to the
// garbage collector
private ReferenceQueue referenceQueue;
/**
* No-args constructor
*/
public MultiThreadedHttpConnectionManager() {
this.referenceToHostPort = Collections.synchronizedMap( new HashMap() );
this.referenceQueue = new ReferenceQueue();
new ReferenceQueueThread().start();
}
/**
* Set the maximum number of connections allowed for a given host:port.
* Per RFC 2616 section 8.1.4, this value defaults to 2.
*
* @param maxConnections - number of connections allowed for each host:port
*/
public void setMaxConnectionsPerHost(int maxConnections) {
this.maxConnections = maxConnections;
}
/**
* Get the maximum number of connections allowed for a given host:port.
*
* @return The maximum number of connections allowed for a given host:port.
*/
public int getMaxConnectionsPerHost() {
return maxConnections;
}
/**
* @see HttpConnectionManager#getConnection(HostConfiguration)
*/
public HttpConnection getConnection(HostConfiguration hostConfiguration)
throws MalformedURLException {
while( true ) {
try {
return getConnection(hostConfiguration, 0);
} catch ( HttpException e ) {
log.debug(
"Unexpected exception while waiting for connection",
e
);
};
}
}
/**
* Return the port provided if not -1 (default), return 443 if the
* protocol is HTTPS, otherwise 80.
*
* This functionality is a URLUtil and may be better off in URIUtils
*
* @param protocol the protocol to use to get the port for, e.g. http or
* https
* @param port the port provided with the url (could be -1)
* @return the port for the specified port and protocol.
*/
private static int getPort(String protocol, int port) {
log.trace("HttpConnectionManager.getPort(String, port)");
// default to provided port
int portForProtocol = port;
if (portForProtocol == -1) {
if (protocol.equalsIgnoreCase("HTTPS")) {
portForProtocol = 443;
} else {
portForProtocol = 80;
}
}
return portForProtocol;
}
/**
* @see HttpConnectionManager#getConnection(HostConfiguration, long)
*/
public HttpConnection getConnection(HostConfiguration hostConfiguration, long timeout)
throws HttpException, MalformedURLException {
log.trace("enter HttpConnectionManager.getConnection(HostConfiguration, long)");
if (hostConfiguration == null) {
throw new MalformedURLException("hostConfiguration is null");
}
// Get the protocol and port (use default port if not specified)
String protocol = hostConfiguration.getProtocol();
String host = hostConfiguration.getHost();
int port = MultiThreadedHttpConnectionManager.getPort(
protocol,
hostConfiguration.getPort()
);
String hostAndPort = host + ":" + port;
if (log.isDebugEnabled()) {
log.debug("HttpConnectionManager.getConnection: key = "
+ hostAndPort);
}
// Look for a list of connections for the given host:port
HostConnectionPool connectionPool = getConnectionPool(hostAndPort);
HttpConnection conn = getConnection(
connectionPool,
host,
port,
protocol.equalsIgnoreCase("HTTPS"),
hostConfiguration.getProxyHost(),
hostConfiguration.getProxyPort(),
timeout
);
return conn;
}
/**
* Gets a connection or waits if one is not available. A connection is
* available if one exists that is not being used or if fewer than
* maxConnections have been created in the connectionPool.
*
* @param connectionPool
* @param host
* @param port
* @param useHttps
* @param proxyHost
* @param proxyPort
* @param timeout the number of milliseconds to wait for a connection, 0 to
* wait indefinitely
*
* @return HttpConnection an available connection
*
* @throws HttpException if a connection does not available in 'timeout'
* milliseconds
*/
private HttpConnection getConnection(
HostConnectionPool connectionPool,
String host,
int port,
boolean useHttps,
String proxyHost,
int proxyPort,
long timeout
) throws HttpException {
HttpConnection connection = null;
synchronized( connectionPool ) {
// keep trying until a connection is available, should happen at
// most twice
while ( connection == null ) {
if (connectionPool.freeConnections.size() > 0) {
connection = (HttpConnection)connectionPool.freeConnections.removeFirst();
} else {
// get number of connections to host:port
if (connectionPool.numConnections < maxConnections) {
// Create a new connection
connection = new HttpConnection(
proxyHost,
proxyPort,
host,
port,
useHttps
);
connection.setHttpConnectionManager( this );
connectionPool.numConnections++;
// add a weak reference to this connection
referenceToHostPort.put(
new WeakReference( connection, referenceQueue ),
host + ":" + port
);
} else {
TimeoutThread threadTimeout = new TimeoutThread();
threadTimeout.setTimeout(timeout);
threadTimeout.setWakeupThread(Thread.currentThread());
threadTimeout.start();
try {
log.debug(
"HttpConnectionManager.getConnection: waiting for "
+ "connection from " + connectionPool
);
connectionPool.wait();
// we were woken up before the timeout occurred, so
// there should be a connection available
threadTimeout.interrupt();
} catch (InterruptedException e) {
throw new HttpException("Timeout waiting for connection.");
}
}
}
}
}
return connection;
}
/**
* Get the pool (list) of connections available for the given host and port
*
* @param hostAndPort the key for the connection pool
* @return a pool (list) of connections available for the given key
*/
private HostConnectionPool getConnectionPool(String hostAndPort) {
log.trace("enter HttpConnectionManager.getConnections(String)");
// Look for a list of connections for the given host:port
HostConnectionPool listConnections = null;
synchronized (mapHosts) {
listConnections = (HostConnectionPool) mapHosts.get(hostAndPort);
if (listConnections == null) {
// First time for this host:port
listConnections = new HostConnectionPool();
mapHosts.put(hostAndPort, listConnections);
}
}
return listConnections;
}
/**
* Get the number of connections in use for the key
*
* @param hostAndPort the key that connections are tracked on
* @return the number of connections in use for the given key
*/
public int getConnectionsInUse(String hostAndPort) {
log.trace("enter HttpConnectionManager.getConnectionsInUse(String)");
HostConnectionPool connectionPool = getConnectionPool(hostAndPort);
synchronized( connectionPool ) {
return connectionPool.numConnections;
}
}
/**
* Make the given HttpConnection available for use by other requests.
* If another thread is blocked in getConnection() waiting for a connection
* for this host:port, they will be woken up.
*
* @param conn - The HttpConnection to make available.
*/
public void releaseConnection(HttpConnection conn) {
log.trace("enter HttpConnectionManager.releaseConnection(HttpConnection)");
String host = conn.getHost();
int port = conn.getPort();
String key = host + ":" + port;
if(log.isDebugEnabled()){
log.debug("HttpConnectionManager.releaseConnection: Release connection for " + host + ":" + port);
}
HostConnectionPool listConnections = getConnectionPool(key);
synchronized(listConnections){
// Put the connect back in the available list and notify a waiter
listConnections.freeConnections.addFirst(conn);
if ( listConnections.numConnections == 0 ) {
// for some reason this connection pool didn't already exist
log.error("connection pool not found for host: " + key);
listConnections.numConnections = 1;
}
listConnections.notify();
}
}
/**
* A simple struct-link class to combine the connection list and the count
* of created connections.
*/
private class HostConnectionPool {
public LinkedList freeConnections = new LinkedList();
public int numConnections = 0;
}
/**
* A thread for listening for HttpConnections reclaimed by the garbage
* collector.
*/
private class ReferenceQueueThread extends Thread {
public ReferenceQueueThread() {
setDaemon(true);
}
/**
* @see java.lang.Runnable#run()
*/
public void run() {
while(true) {
try {
Reference ref = referenceQueue.remove();
if ( ref != null ) {
String hostPort = (String)referenceToHostPort.get(ref);
referenceToHostPort.remove(ref);
HostConnectionPool connectionPool = getConnectionPool(hostPort);
synchronized( connectionPool ) {
connectionPool.numConnections--;
connectionPool.notify();
}
}
} catch (InterruptedException e) {
log.debug("ReferenceQueueThread interrupted", e);
}
}
}
}
/**
* In getConnection, if the maximum number of connections has already
* been reached the call will block. This class is used to help provide
* a timeout facility for this wait. Because Java does not provide a way to
* determine if wait() returned due to a notify() or a timeout, we need
* an outside mechanism to interrupt the waiting thread after the specified
* timeout interval.
*/
private static class TimeoutThread extends Thread {
private long timeout = 0;
private Thread thrdWakeup = null;
public void setTimeout(long timeout)
{
this.timeout = timeout;
}
public long getTimeout()
{
return timeout;
}
public void setWakeupThread(Thread thrdWakeup)
{
this.thrdWakeup = thrdWakeup;
}
public Thread getWakeupThread()
{
return thrdWakeup;
}
public void run() {
log.trace("TimeoutThread.run()");
if(timeout == 0){
return;
}
if(thrdWakeup == null){
return;
}
try{
sleep(timeout);
thrdWakeup.interrupt();
}catch(InterruptedException e){
log.debug("InterruptedException caught as expected");
// This is expected
}
}
}
}
1.1 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/SimpleHttpConnectionManager.java
Index: SimpleHttpConnectionManager.java
===================================================================
/*
* $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/SimpleHttpConnectionManager.java,v 1.1 2002/12/03 05:46:15 jsdever Exp $
* $Revision: 1.1 $
* $Date: 2002/12/03 05:46:15 $
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 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", "Tomcat", 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
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.commons.httpclient;
import java.net.MalformedURLException;
/**
* A connection manager that provides access to a single HttpConnection. This
* manager makes no attempt to provide exclusive access to the contained
* HttpConnection.
*
* @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
*
* @since 2.0
*/
public class SimpleHttpConnectionManager implements HttpConnectionManager {
private HttpConnection httpConnection;
/**
* Constructor for SimpleHttpConnectionManager.
*/
public SimpleHttpConnectionManager() {
super();
}
/**
* @see org.apache.commons.httpclient.HttpConnectionManager#getConnection(HostConfiguration)
*/
public HttpConnection getConnection(HostConfiguration hostConfiguration)
throws MalformedURLException {
return getConnection(hostConfiguration, 0);
}
/**
* @see org.apache.commons.httpclient.HttpConnectionManager#getConnection(HostConfiguration, long)
*/
public HttpConnection getConnection(HostConfiguration hostConfiguration, long timeout)
throws MalformedURLException {
String protocol = hostConfiguration.getProtocol();
String host = hostConfiguration.getHost();
int port = hostConfiguration.getPort();
boolean isSecure = protocol.equalsIgnoreCase("HTTPS");
if ( httpConnection == null ) {
if ( hostConfiguration.isProxySet() ) {
httpConnection = new HttpConnection(
hostConfiguration.getProxyHost(),
hostConfiguration.getProxyPort(),
host,
port,
isSecure
);
} else {
httpConnection = new HttpConnection(host, port, isSecure);
}
} else {
// make sure the host and proxy are correct for this connection
// close it and set the values if they are not
if (
!hostConfiguration.hostEquals(httpConnection)
|| !hostConfiguration.proxyEquals(httpConnection)
) {
if ( httpConnection.isOpen() ) {
httpConnection.close();
}
httpConnection.setHost(host);
httpConnection.setPort(port);
httpConnection.setSecure(isSecure);
httpConnection.setProxyHost(hostConfiguration.getProxyHost());
httpConnection.setProxyPort(hostConfiguration.getProxyPort());
}
}
return httpConnection;
}
/**
* @see org.apache.commons.httpclient.HttpConnectionManager#releaseConnection(org.apache.commons.httpclient.HttpConnection)
*/
public void releaseConnection(HttpConnection conn) {
}
}
1.4 +5 -9 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpClientLocalHost.java
Index: TestHttpClientLocalHost.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpClientLocalHost.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- TestHttpClientLocalHost.java 1 Nov 2002 09:51:05 -0000 1.3
+++ TestHttpClientLocalHost.java 3 Dec 2002 05:46:16 -0000 1.4
@@ -78,14 +78,10 @@
* @author Rodney Waldhoff
* @version $Id$
*/
-public class TestHttpClientLocalHost extends TestCase {
+public class TestHttpClientLocalHost extends TestLocalHostBase {
// -------------------------------------------------------------- Constants
-
-
- private static final String host = "127.0.0.1";
- private static final int port = 8080;
// ------------------------------------------------------------ Constructor
1.3 +7 -8 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnection.java
Index: TestHttpConnection.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnection.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- TestHttpConnection.java 12 Nov 2002 09:58:23 -0000 1.2
+++ TestHttpConnection.java 3 Dec 2002 05:46:16 -0000 1.3
@@ -76,9 +76,9 @@
* @version $Id$
*
*/
-public class TestHttpConnection extends TestCase {
+public class TestHttpConnection extends TestLocalHostBase {
// ----------------------------------------------------- Instance Variables
-
+
// ------------------------------------------------------------ Constructor
public TestHttpConnection(String testName) {
super(testName);
@@ -100,13 +100,13 @@
// ----------------------------------------------------------- Test Methods
public void testConstructThenClose() {
- HttpConnection conn = new HttpConnection("localhost", 8080);
+ HttpConnection conn = new HttpConnection(host,port);
conn.close();
assertTrue( ! conn.isOpen() );
}
public void testConnTimeout() {
- HttpConnection conn = new HttpConnection("localhost", 8080);
+ HttpConnection conn = new HttpConnection(host,port);
// 1 ms is short enough to make this fail
conn.setConnectionTimeout(1);
try {
@@ -118,9 +118,8 @@
}
}
-
public void testForIllegalStateExceptions() {
- HttpConnection conn = new HttpConnection("localhost", 8080);
+ HttpConnection conn = new HttpConnection(host,port);
try {
OutputStream out = conn.getRequestOutputStream();
@@ -134,7 +133,7 @@
}
try {
- OutputStream out = conn.getRequestOutputStream(true);
+ OutputStream out = new ChunkedOutputStream(conn.getRequestOutputStream());
fail("getRequestOutputStream(true) did not throw the expected exception");
}
catch (IllegalStateException expected) {
@@ -145,7 +144,7 @@
}
try {
- InputStream in = conn.getResponseInputStream(new PostMethod());
+ InputStream in = conn.getResponseInputStream();
fail("getResponseInputStream() did not throw the expected exception");
}
catch (IllegalStateException expected) {
1.2 +194 -39 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java
Index: TestHttpConnectionManager.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- TestHttpConnectionManager.java 12 Apr 2002 21:17:06 -0000 1.1
+++ TestHttpConnectionManager.java 3 Dec 2002 05:46:16 -0000 1.2
@@ -6,7 +6,7 @@
*
* The Apache Software License, Version 1.1
*
- * Copyright (c) 1999 The Apache Software Foundation. All rights
+ * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -62,11 +62,13 @@
package org.apache.commons.httpclient;
-import junit.framework.*;
+import java.io.IOException;
import java.net.MalformedURLException;
-import java.lang.reflect.*;
-import org.apache.commons.httpclient.methods.*;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.commons.httpclient.methods.GetMethod;
/**
*
@@ -76,7 +78,7 @@
* @author Marc A. Saegesser
* @version $Id$
*/
-public class TestHttpConnectionManager extends TestCase {
+public class TestHttpConnectionManager extends TestLocalHostBase {
// ----------------------------------------------------- Instance Variables
// ------------------------------------------------------------ Constructor
@@ -100,22 +102,8 @@
// ----------------------------------------------------------- Test Methods
// Test the accessor methods
- public void testProxyHostAccessors() {
- HttpConnectionManager mgr = new HttpConnectionManager();
-
- mgr.setProxyHost("proxyhost");
- assertEquals("Proxy Host", "proxyhost", mgr.getProxyHost());
- }
-
- public void testProxyPortAccessors() {
- HttpConnectionManager mgr = new HttpConnectionManager();
-
- mgr.setProxyPort(8888);
- assertEquals("Proxy Port", 8888, mgr.getProxyPort());
- }
-
public void testMaxConnectionsAccessors() {
- HttpConnectionManager mgr = new HttpConnectionManager();
+ MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
// First test the default value
assertEquals("Default MaxConnections", 2, mgr.getMaxConnectionsPerHost());
@@ -125,11 +113,14 @@
}
public void testGetConnection() {
- HttpConnectionManager mgr = new HttpConnectionManager();
+ MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
+
+ HostConfiguration hostConfiguration = new HostConfiguration();
+ hostConfiguration.setHost("www.nosuchserver.com", 80, "http");
try{
// Create a new connection
- HttpConnection conn = mgr.getConnection("http://www.nosuchserver.com/path/path?query=string");
+ HttpConnection conn = mgr.getConnection(hostConfiguration);
// Validate the connection properties
assertEquals("Host", "www.nosuchserver.com", conn.getHost());
assertEquals("Port", 80, conn.getPort());
@@ -137,7 +128,8 @@
mgr.releaseConnection(conn);
// Create a new connection
- conn = mgr.getConnection("https://www.nosuchserver.com/path/path?query=string");
+ hostConfiguration.setHost("www.nosuchserver.com", -1, "https");
+ conn = mgr.getConnection(hostConfiguration);
// Validate the connection properties
assertEquals("Host", "www.nosuchserver.com", conn.getHost());
assertEquals("Port", 443, conn.getPort());
@@ -145,7 +137,8 @@
mgr.releaseConnection(conn);
// Create a new connection
- conn = mgr.getConnection("http://www.nowhere.org:8080/path/path?query=string");
+ hostConfiguration.setHost("www.nowhere.org", 8080, "http");
+ conn = mgr.getConnection(hostConfiguration);
// Validate the connection properties
assertEquals("Host", "www.nowhere.org", conn.getHost());
assertEquals("Port", 8080, conn.getPort());
@@ -154,47 +147,209 @@
}catch(MalformedURLException e){
fail("Caught unexpected MalformedURLException (" + e.toString() + ")");
- }catch(HttpException e){
- fail("Caught unexpected HttpException (" + e.toString() + ")");
}
+ }
+
+ public void testReleaseConnection() {
+
+ MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
+ connectionManager.setMaxConnectionsPerHost(1);
+
+ HttpClient client = new HttpClient(connectionManager);
+ client.getHostConfiguration().setHost(host, port, "http");
+ // we shouldn't have to wait if a connection is available
+ client.setHttpConnectionFactoryTimeout( 1 );
+
+ GetMethod getMethod = new GetMethod("/");
+ getMethod.setFollowRedirects(true);
+
+ try {
+ client.executeMethod(getMethod);
+ } catch (Exception e) {
+ fail("error reading from server: " + e);
+ }
+
+ try {
+ // this should fail quickly since the connection has not been released
+ client.executeMethod(getMethod);
+ fail("a httpConnection should not be available");
+ } catch (HttpException e) {
+ } catch (IOException e) {
+ fail("error reading from server; " + e);
+ }
+
+ // this should release the connection
+ getMethod.releaseConnection();
+
+ getMethod = new GetMethod("/");
+ getMethod.setFollowRedirects(true);
+
+ try {
+ // this should fail quickly if the connection has not been released
+ client.executeMethod(getMethod);
+ } catch (HttpException e) {
+ fail("httpConnection does not appear to have been released: " + e);
+ } catch (IOException e) {
+ fail("error reading from server; " + e);
+ }
}
+ /**
+ * Makes sure that a connection gets released after the content of the body
+ * is read.
+ */
+ public void testResponseAutoRelease() {
+
+ MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
+ connectionManager.setMaxConnectionsPerHost(1);
+
+ HttpClient client = new HttpClient(connectionManager);
+ client.getHostConfiguration().setHost(host, port, "http");
+ // we shouldn't have to wait if a connection is available
+ client.setHttpConnectionFactoryTimeout( 1 );
+
+ GetMethod getMethod = new GetMethod("/");
+ getMethod.setFollowRedirects(true);
+
+ try {
+ client.executeMethod(getMethod);
+ } catch (Exception e) {
+ fail("error reading from server: " + e);
+ }
+
+ // this should release the connection
+ getMethod.getResponseBody();
+
+ getMethod = new GetMethod("/");
+ getMethod.setFollowRedirects(true);
+
+ try {
+ // this should fail quickly if the connection has not been released
+ client.executeMethod(getMethod);
+ } catch (HttpException e) {
+ fail("httpConnection does not appear to have been released: " + e);
+ } catch (IOException e) {
+ fail("error reading from server; " + e);
+ }
+
+ }
+
+ public void testMaxConnectionsPerServer() {
+
+ MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
+ connectionManager.setMaxConnectionsPerHost(1);
+
+ HttpClient client = new HttpClient(connectionManager);
+ client.getHostConfiguration().setHost(host, port, "http");
+ // we shouldn't have to wait if a connection is available
+ client.setHttpConnectionFactoryTimeout( 1 );
+
+ GetMethod getMethod = new GetMethod("/");
+ getMethod.setFollowRedirects(true);
+
+ try {
+ client.executeMethod(getMethod);
+ } catch (Exception e) {
+ fail("error reading from server: " + e);
+ }
+
+ GetMethod getMethod2 = new GetMethod("/");
+ getMethod2.setFollowRedirects(true);
+
+ try {
+ // this should fail quickly since the connection has not been released
+ client.executeMethod(getMethod2);
+ fail("a httpConnection should not be available");
+ } catch (HttpException e) {
+ } catch (IOException e) {
+ fail("error reading from server; " + e);
+ }
+
+ }
+
+ public void testReclaimUnusedConnection() {
+
+ MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
+ connectionManager.setMaxConnectionsPerHost(1);
+
+ HttpClient client = new HttpClient(connectionManager);
+ client.getHostConfiguration().setHost(host, port, "http");
+ // we shouldn't have to wait if a connection is available
+ client.setHttpConnectionFactoryTimeout( 30000 );
+
+ GetMethod getMethod = new GetMethod("/");
+ getMethod.setFollowRedirects(true);
+
+ try {
+ client.executeMethod(getMethod);
+ } catch (Exception e) {
+ fail("error reading from server: " + e);
+ }
+
+ getMethod = new GetMethod("/");
+ getMethod.setFollowRedirects(true);
+
+ Runtime.getRuntime().gc();
+
+ try {
+ // we didn't explicitly release the connection, but it should be
+ // reclaimed by the garbage collector, we hope:)
+ client.executeMethod(getMethod);
+ } catch (HttpException e) {
+ fail("httpConnection does not appear to have been reclaimed by the GC: " + e);
+ } catch (IOException e) {
+ fail("error reading from server; " + e);
+ }
+
+ }
+
public void testGetMultipleConnections() {
- HttpConnectionManager mgr = new HttpConnectionManager();
+ HttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
+
+ HostConfiguration hostConfig1 = new HostConfiguration();
+ hostConfig1.setHost("www.nosuchserver.com", 80, "http");
+
+ HostConfiguration hostConfig2 = new HostConfiguration();
+ hostConfig2.setHost("www.nosuchserver.com", -1, "http");
+
+ HostConfiguration hostConfig3 = new HostConfiguration();
+ hostConfig3.setHost("www.nosuchserver.com", -1, "http");
try{
// Create a new connection
- HttpConnection conn1 = mgr.getConnection("http://www.nosuchserver.com/path/path?query=string");
+ HttpConnection conn1 = mgr.getConnection(hostConfig1);
// Release the connection
mgr.releaseConnection(conn1);
// Get the same connection again
- HttpConnection conn2 = mgr.getConnection("http://www.nosuchserver.com/");
+ HttpConnection conn2 = mgr.getConnection(hostConfig2);
assertEquals("Same connection", conn1, conn2);
// don't release yet
// Get another new connection
- HttpConnection conn3 = mgr.getConnection("http://www.nosuchserver.com/");
+ HttpConnection conn3 = mgr.getConnection(hostConfig3);
assertTrue(conn2 != conn3);
}catch(MalformedURLException e){
fail("Caught unexpected MalformedURLException (" + e.toString() + ")");
- }catch(HttpException e){
- fail("Caught unexpected HttpException (" + e.toString() + ")");
}
}
public void testTimeout()
{
- HttpConnectionManager mgr = new HttpConnectionManager();
+ MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
try{
- HttpConnection conn1 = mgr.getConnection("http://www.nosuchserver.com/path/path?query=string");
- HttpConnection conn2 = mgr.getConnection("http://www.nosuchserver.com/path/path?query=string");
- HttpConnection conn3 = mgr.getConnection("http://www.nosuchserver.com/path/path?query=string", 5000);
+ HostConfiguration hostConfig = new HostConfiguration();
+ hostConfig.setHost("www.nosuchserver.com", 80, "http");
+
+ HttpConnection conn1 = mgr.getConnection(hostConfig);
+ HttpConnection conn2 = mgr.getConnection(hostConfig);
+ HttpConnection conn3 = mgr.getConnection(hostConfig, 5000);
fail("Expected an HttpException.");
+
}catch(MalformedURLException e){
fail("Caught unexpected MalformedURLException (" + e.toString() + ")");
}catch(HttpException e){
1.5 +5 -4 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestLocalHost.java
Index: TestLocalHost.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestLocalHost.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- TestLocalHost.java 3 Sep 2002 01:24:52 -0000 1.4
+++ TestLocalHost.java 3 Dec 2002 05:46:16 -0000 1.5
@@ -81,6 +81,7 @@
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(TestHttpClientLocalHost.suite());
+ suite.addTest(TestHttpConnection.suite());
suite.addTest(TestMethodsLocalHost.suite());
suite.addTest(TestGetMethodLocal.suite());
suite.addTest(TestTraceMethodLocal.suite());
1.1 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestLocalHostBase.java
Index: TestLocalHostBase.java
===================================================================
/*
* $Header: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestLocalHostBase.java,v 1.1 2002/12/03 05:46:16 jsdever Exp $
* $Revision: 1.1 $
* $Date: 2002/12/03 05:46:16 $
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 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", "Tomcat", 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
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.commons.httpclient;
import junit.framework.TestCase;
/**
*
* @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
*/
public abstract class TestLocalHostBase extends TestCase {
/**
* Constructor for TestLocalHostBase.
* @param testName
*/
public TestLocalHostBase(String testName) {
super(testName);
}
protected static final String host = System.getProperty("httpclient.test.localHost","localhost");
protected static final int port;
static {
String portString = System.getProperty("httpclient.test.localPort","8080");
int tempPort = 8080;
try {
tempPort = Integer.parseInt(portString);
} catch(Exception e) {
tempPort = 8080;
}
port = tempPort;
}
}
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>