You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ri...@apache.org on 2007/12/12 11:34:00 UTC
svn commit: r603540 - in /geronimo/sandbox/AsyncHttpClient/src:
main/java/org/apache/ahc/ main/java/org/apache/ahc/codec/
test/java/org/apache/ahc/
Author: rickmcguire
Date: Wed Dec 12 02:33:59 2007
New Revision: 603540
URL: http://svn.apache.org/viewvc?rev=603540&view=rev
Log:
GERONIMO-3686 AsyncHttpClient does not reuse connection even if connections are persistent
Patch submitted by Sangjin Lee.
Modified:
geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java
geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java
geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java
geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpMessage.java
geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AsyncHttpClientTest.java
geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AsyncHttpClientWithFutureTest.java
geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AuthTest.java
geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/TimeoutTest.java
Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java?rev=603540&r1=603539&r2=603540&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java Wed Dec 12 02:33:59 2007
@@ -27,10 +27,12 @@
import javax.net.ssl.SSLContext;
+import org.apache.ahc.codec.HttpDecoder;
import org.apache.ahc.codec.HttpIoHandler;
import org.apache.ahc.codec.HttpProtocolCodecFactory;
import org.apache.ahc.codec.HttpRequestMessage;
import org.apache.ahc.codec.ResponseFuture;
+import org.apache.ahc.codec.SessionCache;
import org.apache.ahc.ssl.TrustManagerFactoryImpl;
import org.apache.ahc.util.AsyncHttpClientException;
import org.apache.mina.common.ConnectFuture;
@@ -38,6 +40,7 @@
import org.apache.mina.common.IoFutureListener;
import org.apache.mina.common.IoSession;
import org.apache.mina.common.RuntimeIOException;
+import org.apache.mina.common.support.DefaultConnectFuture;
import org.apache.mina.filter.SSLFilter;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.nio.SocketConnector;
@@ -51,6 +54,9 @@
*/
public class AsyncHttpClient {
+ /** The Constant DEFAULT_REUSE_CONNECTION. */
+ public static final boolean DEFAULT_REUSE_CONNECTION = false;
+
/** The Constant DEFAULT_CONNECTION_TIMEOUT. */
public static final int DEFAULT_CONNECTION_TIMEOUT = 30000;
@@ -92,13 +98,16 @@
/** The thread pool. */
private final ExecutorService threadPool;
-
+
/** The HttpIoHandler handler. */
private final HttpIoHandler handler;
/** The ssl filter. */
private SSLFilter sslFilter;
-
+
+ /** connection reuse option */
+ private volatile boolean reuseConnection = DEFAULT_REUSE_CONNECTION;
+
/** The Reuse Address Socket Parameter. */
private boolean reuseAddress = DEFAULT_REUSE_ADDRESS;
@@ -124,6 +133,24 @@
private boolean tcpNoDelay = DEFAULT_TCP_NO_DELAY;
/**
+ * Returns if it reuses established connections for more requests.
+ *
+ * @return true if it reuses connections
+ */
+ public boolean isReuseConnection() {
+ return reuseConnection;
+ }
+
+ /**
+ * Sets if it should reuse established connections for more requests.
+ *
+ * @param reuseConnection the new value
+ */
+ public void setReuseConnection(boolean reuseConnection) {
+ this.reuseConnection = reuseConnection;
+ }
+
+ /**
* Checks if is reuse address.
*
* @return true, if is reuse address
@@ -266,7 +293,7 @@
public void setTcpNoDelay(boolean tcpNoDelay) {
this.tcpNoDelay = tcpNoDelay;
}
-
+
/**
* Instantiates a new AsyncHttpClient. It will use a single threaded model and is good for
* use in one-off connections.
@@ -336,7 +363,7 @@
* @see HttpRequestMessage
*/
public ResponseFuture sendRequest(HttpRequestMessage message) {
- return sendRequest(message, null);
+ return sendRequest(message, null);
}
/**
@@ -357,7 +384,7 @@
* returned.
*/
public ResponseFuture sendRequest(HttpRequestMessage message,
- BlockingQueue<ResponseFuture> queue) {
+ BlockingQueue<ResponseFuture> queue) {
if (threadPool != null && threadPool.isShutdown()) {
throw new IllegalStateException("AsyncHttpClient has been destroyed and cannot be reused.");
}
@@ -366,15 +393,43 @@
// request unless it already exists (i.e. sendRequest() is called
// multiple times for the request)
if (message.getResponseFuture() == null) {
- message.setResponseFuture(new ResponseFuture(message, queue));
+ message.setResponseFuture(new ResponseFuture(message, queue));
}
- String host = message.getHost();
- int port = message.getPort();
- ConnectFuture future = connector.connect(new InetSocketAddress(host, port), handler);
+ // *IF* connection reuse is enabled, we should see if we have a cached
+ // connection first; if not, always open a new one
+ ConnectFuture future = null;
+ if (reuseConnection) {
+ future = getCachedConnection(message);
+ } else {
+ // add the Connection close header explicitly
+ message.setHeader(HttpDecoder.CONNECTION, HttpDecoder.CLOSE);
+ }
+
+ // if no cached connection is found or keep-alive is disabled, force a
+ // new connection
+ if (future == null) {
+ future = openConnection(message);
+ }
future.addListener(new FutureListener(message));
return message.getResponseFuture();
}
+
+ private ConnectFuture openConnection(HttpRequestMessage message) {
+ return connector.connect(new InetSocketAddress(message.getHost(), message.getPort()), handler);
+ }
+
+ private ConnectFuture getCachedConnection(HttpRequestMessage message) {
+ IoSession cached = SessionCache.getInstance().getActiveSession(message);
+ if (cached == null) {
+ return null;
+ }
+
+ // create a containing future object and set the session right away
+ ConnectFuture future = new DefaultConnectFuture();
+ future.setSession(cached);
+ return future;
+ }
/**
* Gets the connection timeout.
@@ -424,6 +479,8 @@
* connection occurs, it is also responsible for sending the request.
*/
class FutureListener implements IoFutureListener {
+ static final String SSL_FILTER = "SSL";
+ static final String PROTOCOL_FILTER = "protocolFilter";
/** The request. */
final HttpRequestMessage request;
@@ -449,28 +506,16 @@
ConnectFuture connFuture = (ConnectFuture) future;
if (connFuture.isConnected()) {
IoSession sess = future.getSession();
- String scheme = request.getUrl().getProtocol();
- //Add the https filter
- if (scheme.toLowerCase().equals("https")) {
- if (sslFilter == null) {
- try {
- sslFilter = new SSLFilter(createClientSSLContext());
- sslFilter.setUseClientMode(true);
- } catch (GeneralSecurityException e) {
- try {
- sess.getHandler().exceptionCaught(sess, e);
- } catch (Exception e1) {
- //Do nothing...we just reported it
- }
- }
- }
- sess.getFilterChain().addLast("SSL", sslFilter);
+ // see if we need to add the SSL filter
+ addSSLFilter(sess);
+ // add the protocol filter (if it's not there already like in a
+ // reused session)
+ if (!sess.getFilterChain().contains(PROTOCOL_FILTER)) {
+ sess.getFilterChain().addLast(PROTOCOL_FILTER, new ProtocolCodecFilter(
+ new HttpProtocolCodecFactory()));
}
-
- sess.getFilterChain().addLast("protocolFilter", new ProtocolCodecFilter(
- new HttpProtocolCodecFactory()));
-
+
sess.setAttribute(HttpIoHandler.CURRENT_REQUEST, request);
sess.setAttachment(AsyncHttpClient.this);
@@ -494,6 +539,31 @@
} catch (RuntimeIOException e) {
//Set the callback exception
request.getCallback().onException(e);
+ }
+ }
+ }
+
+ private void addSSLFilter(IoSession sess) {
+ String scheme = request.getUrl().getProtocol();
+
+ //Add the https filter
+ if (scheme.toLowerCase().equals("https")) {
+ // add the SSL filter if it's not there already like in a reused
+ // session
+ if (!sess.getFilterChain().contains(SSL_FILTER)) {
+ if (sslFilter == null) {
+ try {
+ sslFilter = new SSLFilter(createClientSSLContext());
+ sslFilter.setUseClientMode(true);
+ } catch (GeneralSecurityException e) {
+ try {
+ sess.getHandler().exceptionCaught(sess, e);
+ } catch (Exception e1) {
+ //Do nothing...we just reported it
+ }
+ }
+ }
+ sess.getFilterChain().addLast(SSL_FILTER, sslFilter);
}
}
}
Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java?rev=603540&r1=603539&r2=603540&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java Wed Dec 12 02:33:59 2007
@@ -26,10 +26,6 @@
import org.apache.ahc.util.DateUtil;
import org.apache.ahc.util.NameValuePair;
-import org.apache.ahc.auth.AuthChallengeParser;
-import org.apache.ahc.auth.AuthScheme;
-import org.apache.ahc.auth.AuthPolicy;
-import org.apache.ahc.auth.AuthState;
import org.apache.mina.common.ByteBuffer;
/**
@@ -42,6 +38,9 @@
/** The Constant CONNECTION. */
public static final String CONNECTION = "Connection";
+
+ /** The Constant CLOSE as a value for the Connection header */
+ public static final String CLOSE = "close";
/** The Constant COOKIE_COMMENT. */
public static final String COOKIE_COMMENT = "comment";
Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java?rev=603540&r1=603539&r2=603540&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java Wed Dec 12 02:33:59 2007
@@ -52,7 +52,7 @@
* The Constant CURRENT_RESPONSE.
*/
public static final String CURRENT_RESPONSE = "CURRENT_RESPONSE";
-
+
/**
* The Constant CONNECTION_CLOSE.
*/
@@ -123,8 +123,8 @@
//Send the redirect
client.sendRequest(request);
- //Close the current session since we are done with it
- ioSession.close();
+ // cache the session before we return
+ SessionCache.getInstance().cacheSession(ioSession);
return;
}
@@ -150,8 +150,8 @@
request.setAuthCount(authCount);
client.sendRequest(request);
- //Close the current session since we are done with it
- ioSession.close();
+ // cache the session before we return
+ SessionCache.getInstance().cacheSession(ioSession);
return;
}
}
@@ -161,8 +161,9 @@
// complete the future which will also fire the callback
ResponseFuture result = request.getResponseFuture();
result.set(response);
-
- ioSession.close();
+
+ // cache the session before we return
+ SessionCache.getInstance().cacheSession(ioSession);
}
/**
@@ -196,11 +197,13 @@
public void sessionClosed(IoSession ioSession) throws Exception {
//Clean up if any in-proccess decoding was occurring
ioSession.removeAttribute(CURRENT_RESPONSE);
+ // remove it from the cache
+ SessionCache.getInstance().removeSession(ioSession);
HttpRequestMessage request = (HttpRequestMessage) ioSession.getAttribute(CURRENT_REQUEST);
cancelTasks(request);
AsyncHttpClientCallback callback = request.getCallback();
if (callback != null) {
- callback.onClosed();
+ callback.onClosed();
}
}
Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpMessage.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpMessage.java?rev=603540&r1=603539&r2=603540&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpMessage.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpMessage.java Wed Dec 12 02:33:59 2007
@@ -22,6 +22,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import org.apache.ahc.util.NameValuePair;
@@ -129,16 +130,17 @@
/**
- * Gets the headers.
+ * Returns all headers.
*
- * @return the headers
+ * @return all headers
*/
public List<NameValuePair> getHeaders() {
return headers;
}
/**
- * Sets the headers.
+ * Sets the headers. It removes any headers that were stored before the
+ * call.
*
* @param headers the new headers
*/
@@ -164,6 +166,87 @@
*/
public void addHeader(String name, String value) {
headers.add(new NameValuePair(name, value));
+ }
+
+ /**
+ * Removes the headers that have the given name.
+ *
+ * @param name the name
+ */
+ public void removeHeader(String name) {
+ Iterator<NameValuePair> it = headers.iterator();
+ while (it.hasNext()) {
+ NameValuePair header = it.next();
+ if (header.getName().equals(name)) {
+ it.remove();
+ }
+ }
+ }
+
+ /**
+ * Sets the header with the given name and the value. This differs from
+ * <code>addHeader()</code> in that it removes any existing header under the
+ * name and adds the new one.
+ *
+ * @param name the name
+ * @param value the value
+ * @throws IllegalArgumentException if either the name or the value is null.
+ */
+ public void setHeader(String name, String value) {
+ if (name == null || value == null) {
+ throw new IllegalArgumentException("null name or value was passed in");
+ }
+
+ // we're resetting the value, so remove it first
+ removeHeader(name);
+ addHeader(name, value);
+ }
+
+ /**
+ * Returns the value for the header with the given name. If there are more
+ * than one header stored, it returns the first entry it finds in the list.
+ *
+ * @param name the name
+ * @return the value for the header, or null if it is not found
+ * @throws IllegalArgumentException if the name is null
+ */
+ public String getHeader(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("null name was passed in");
+ }
+
+ Iterator<NameValuePair> it = headers.iterator();
+ while (it.hasNext()) {
+ NameValuePair header = it.next();
+ if (header.getName().equals(name)) {
+ return header.getValue();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns an array of values for the header with the given name.
+ *
+ * @param name the name
+ * @return the value for the header. If there is no entry under the name,
+ * an empty array is returned.
+ * @throws IllegalArgumentException if the name is null
+ */
+ public String[] getHeaders(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("null name was passed in");
+ }
+
+ List<String> values = new ArrayList<String>();
+ Iterator<NameValuePair> it = headers.iterator();
+ while (it.hasNext()) {
+ NameValuePair header = it.next();
+ if (header.getName().equals(name)) {
+ values.add(header.getValue());
+ }
+ }
+ return values.toArray(new String[]{});
}
/**
Modified: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AsyncHttpClientTest.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AsyncHttpClientTest.java?rev=603540&r1=603539&r2=603540&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AsyncHttpClientTest.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AsyncHttpClientTest.java Wed Dec 12 02:33:59 2007
@@ -137,6 +137,7 @@
boolean testForException,
TestCallback callback) throws Exception {
AsyncHttpClient ahc = new AsyncHttpClient();
+ ahc.setTcpNoDelay(true);
ahc.sendRequest(request);
Modified: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AsyncHttpClientWithFutureTest.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AsyncHttpClientWithFutureTest.java?rev=603540&r1=603539&r2=603540&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AsyncHttpClientWithFutureTest.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AsyncHttpClientWithFutureTest.java Wed Dec 12 02:33:59 2007
@@ -123,6 +123,7 @@
BlockingQueue<ResponseFuture> queue)
throws Exception {
AsyncHttpClient ahc = new AsyncHttpClient();
+ ahc.setTcpNoDelay(true);
return ahc.sendRequest(request, queue);
}
}
Modified: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AuthTest.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AuthTest.java?rev=603540&r1=603539&r2=603540&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AuthTest.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AuthTest.java Wed Dec 12 02:33:59 2007
@@ -79,6 +79,7 @@
protected void setUp() throws Exception {
super.setUp();
ahc = new AsyncHttpClient();
+ ahc.setTcpNoDelay(true);
}
protected void tearDown() throws Exception {
Modified: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/TimeoutTest.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/TimeoutTest.java?rev=603540&r1=603539&r2=603540&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/TimeoutTest.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/TimeoutTest.java Wed Dec 12 02:33:59 2007
@@ -32,6 +32,7 @@
TestCallback callback = new TestCallback();
AsyncHttpClient ahc = new AsyncHttpClient();
+ ahc.setTcpNoDelay(true);
HttpRequestMessage request = new HttpRequestMessage(new URL("http://localhost:8282/timeout.jsp"), callback);