You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ro...@apache.org on 2007/12/08 17:55:13 UTC
svn commit: r602506 - in /jakarta/httpcomponents/httpcore/trunk:
RELEASE_NOTES.txt
module-main/src/main/java/org/apache/http/impl/DefaultConnectionReuseStrategy.java
module-main/src/test/java/org/apache/http/impl/TestDefaultConnectionReuseStrategy.java
Author: rolandw
Date: Sat Dec 8 08:55:13 2007
New Revision: 602506
URL: http://svn.apache.org/viewvc?rev=602506&view=rev
Log:
HTTPCORE-112: patch committed with testcases
Modified:
jakarta/httpcomponents/httpcore/trunk/RELEASE_NOTES.txt
jakarta/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultConnectionReuseStrategy.java
jakarta/httpcomponents/httpcore/trunk/module-main/src/test/java/org/apache/http/impl/TestDefaultConnectionReuseStrategy.java
Modified: jakarta/httpcomponents/httpcore/trunk/RELEASE_NOTES.txt
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/RELEASE_NOTES.txt?rev=602506&r1=602505&r2=602506&view=diff
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/RELEASE_NOTES.txt (original)
+++ jakarta/httpcomponents/httpcore/trunk/RELEASE_NOTES.txt Sat Dec 8 08:55:13 2007
@@ -1,5 +1,8 @@
Changes since 4.0 Alpha 6
+* [HTTPCORE-112] DefaultConnectionReuseStrategy interprets token sequences
+ Contributed by Roland Weber <rolandw at apache.org>
+
* [HTTPCORE-122] new interface TokenIterator and basic implementation
Contributed by Roland Weber <rolandw at apache.org>
Modified: jakarta/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultConnectionReuseStrategy.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultConnectionReuseStrategy.java?rev=602506&r1=602505&r2=602506&view=diff
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultConnectionReuseStrategy.java (original)
+++ jakarta/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultConnectionReuseStrategy.java Sat Dec 8 08:55:13 2007
@@ -34,36 +34,46 @@
import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.HttpConnection;
import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
+import org.apache.http.ParseException;
import org.apache.http.ProtocolVersion;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.TokenIterator;
+import org.apache.http.message.BasicTokenIterator;
+
/**
* Default implementation of a strategy deciding about connection re-use.
*
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
*
* @version $Revision$
*
* @since 4.0
*/
-public class DefaultConnectionReuseStrategy implements ConnectionReuseStrategy {
+public class DefaultConnectionReuseStrategy
+ implements ConnectionReuseStrategy {
public DefaultConnectionReuseStrategy() {
super();
}
// see interface ConnectionReuseStrategy
- public boolean keepAlive(final HttpResponse response, final HttpContext context) {
+ public boolean keepAlive(final HttpResponse response,
+ final HttpContext context) {
if (response == null) {
- throw new IllegalArgumentException("HTTP response may not be null");
+ throw new IllegalArgumentException
+ ("HTTP response may not be null.");
}
if (context == null) {
- throw new IllegalArgumentException("HTTP context may not be null");
+ throw new IllegalArgumentException
+ ("HTTP context may not be null.");
}
HttpConnection conn = (HttpConnection)
@@ -73,34 +83,90 @@
return false;
// do NOT check for stale connection, that is an expensive operation
+ // Check for a self-terminating entity. If the end of the entity will
+ // be indicated by closing the connection, there is no keep-alive.
HttpEntity entity = response.getEntity();
ProtocolVersion ver = response.getStatusLine().getProtocolVersion();
if (entity != null) {
if (entity.getContentLength() < 0) {
- if (!entity.isChunked() || ver.lessEquals(HttpVersion.HTTP_1_0)) {
+ if (!entity.isChunked() ||
+ ver.lessEquals(HttpVersion.HTTP_1_0)) {
// if the content length is not known and is not chunk
// encoded, the connection cannot be reused
return false;
}
}
}
- // Check for 'Connection' directive
- Header connheader = response.getFirstHeader(HTTP.CONN_DIRECTIVE);
- if (connheader == null) {
- connheader = response.getFirstHeader("Proxy-Connection");
- }
- if (connheader != null) {
- String conndirective = connheader.getValue();
- if (HTTP.CONN_CLOSE.equalsIgnoreCase(conndirective)) {
+
+ // Check for the "Connection" header. If that is absent, check for
+ // the "Proxy-Connection" header. The latter is an unspecified and
+ // broken but unfortunately common extension of HTTP.
+ HeaderIterator hit = response.headerIterator(HTTP.CONN_DIRECTIVE);
+ if (!hit.hasNext())
+ hit = response.headerIterator("Proxy-Connection");
+
+ // Experimental usage of the "Connection" header in HTTP/1.0 is
+ // documented in RFC 2068, section 19.7.1. A token "keep-alive" is
+ // used to indicate that the connection should be persistent.
+ // Note that the final specification of HTTP/1.1 in RFC 2616 does not
+ // include this information. Neither is the "Connection" header
+ // mentioned in RFC 1945, which informally describes HTTP/1.0.
+ //
+ // RFC 2616 specifies "close" as the only connection token with a
+ // specific meaning: it disables persistent connections.
+ //
+ // The "Proxy-Connection" header is not formally specified anywhere,
+ // but is commonly used to carry one token, "close" or "keep-alive".
+ // The "Connection" header, on the other hand, is defined as a
+ // sequence of tokens, where each token is a header name, and the
+ // token "close" has the above-mentioned additional meaning.
+ //
+ // To get through this mess, we treat the "Proxy-Connection" header
+ // in exactly the same way as the "Connection" header, but only if
+ // the latter is missing. We scan the sequence of tokens for both
+ // "close" and "keep-alive". As "close" is specified by RFC 2068,
+ // it takes precedence and indicates a non-persistent connection.
+ // If there is no "close" but a "keep-alive", we take the hint.
+
+ if (hit.hasNext()) {
+ try {
+ TokenIterator ti = createTokenIterator(hit);
+ boolean keepalive = false;
+ while (ti.hasNext()) {
+ final String token = ti.nextToken();
+ if (HTTP.CONN_CLOSE.equalsIgnoreCase(token)) {
+ return false;
+ } else if (HTTP.CONN_KEEP_ALIVE.equalsIgnoreCase(token)) {
+ // continue the loop, there may be a "close" afterwards
+ keepalive = true;
+ }
+ }
+ if (keepalive)
+ return true;
+ // neither "close" nor "keep-alive", use default policy
+
+ } catch (ParseException px) {
+ // invalid connection header means no persistent connection
+ // we don't have logging in HttpCore, so the exception is lost
return false;
- } else if (HTTP.CONN_KEEP_ALIVE.equalsIgnoreCase(conndirective)) {
- return true;
- } else {
- // log unknown directive
}
}
- // Resorting to protocol version default close connection policy
+
+ // default since HTTP/1.1 is persistent, before it was non-persistent
return !ver.lessEquals(HttpVersion.HTTP_1_0);
}
-
+
+
+ /**
+ * Creates a token iterator from a header iterator.
+ * This method can be overridden to replace the implementation of
+ * the token iterator.
+ *
+ * @param hit the header iterator
+ *
+ * @return the token iterator
+ */
+ protected TokenIterator createTokenIterator(HeaderIterator hit) {
+ return new BasicTokenIterator(hit);
+ }
}
Modified: jakarta/httpcomponents/httpcore/trunk/module-main/src/test/java/org/apache/http/impl/TestDefaultConnectionReuseStrategy.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-main/src/test/java/org/apache/http/impl/TestDefaultConnectionReuseStrategy.java?rev=602506&r1=602505&r2=602506&view=diff
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-main/src/test/java/org/apache/http/impl/TestDefaultConnectionReuseStrategy.java (original)
+++ jakarta/httpcomponents/httpcore/trunk/module-main/src/test/java/org/apache/http/impl/TestDefaultConnectionReuseStrategy.java Sat Dec 8 08:55:13 2007
@@ -223,6 +223,78 @@
assertFalse(reuseStrategy.keepAlive(response, context));
}
+ public void testConnectionTokens1() throws Exception {
+ // Use HTTP 1.1
+ HttpResponse response =
+ createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1);
+ response.addHeader("Connection", "yadda, cLOSe, dumdy");
+
+ assertFalse(reuseStrategy.keepAlive(response, context));
+ }
+
+ public void testConnectionTokens2() throws Exception {
+ // Use HTTP 1.1
+ HttpResponse response =
+ createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1);
+ response.addHeader("Connection", "yadda, kEEP-alive, dumdy");
+
+ assertTrue(reuseStrategy.keepAlive(response, context));
+ }
+
+ public void testConnectionTokens3() throws Exception {
+ // Use HTTP 1.1
+ HttpResponse response =
+ createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1);
+ response.addHeader("Connection", "yadda, keep-alive, close, dumdy");
+
+ assertFalse(reuseStrategy.keepAlive(response, context));
+ }
+
+ public void testConnectionTokens4() throws Exception {
+ // Use HTTP 1.1
+ HttpResponse response =
+ createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1);
+ response.addHeader("Connection", "yadda, close, dumdy");
+ response.addHeader("Proxy-Connection", "keep-alive");
+
+ // Connection takes precedence over Proxy-Connection
+ assertFalse(reuseStrategy.keepAlive(response, context));
+ }
+
+ public void testConnectionTokens5() throws Exception {
+ // Use HTTP 1.1
+ HttpResponse response =
+ createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1);
+ response.addHeader("Connection", "yadda, dumdy");
+ response.addHeader("Proxy-Connection", "close");
+
+ // Connection takes precedence over Proxy-Connection,
+ // even if it doesn't contain a recognized token.
+ // Default for HTTP/1.1 is to keep alive.
+ assertTrue(reuseStrategy.keepAlive(response, context));
+ }
+
+ public void testConnectionTokens6() throws Exception {
+ // Use HTTP 1.1
+ HttpResponse response =
+ createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1);
+ response.addHeader("Connection", "");
+ response.addHeader("Proxy-Connection", "close");
+
+ // Connection takes precedence over Proxy-Connection,
+ // even if it is empty. Default for HTTP/1.1 is to keep alive.
+ assertTrue(reuseStrategy.keepAlive(response, context));
+ }
+
+ public void testConnectionTokensInvalid() throws Exception {
+ // Use HTTP 1.1
+ HttpResponse response =
+ createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1);
+ response.addHeader("Connection", "keep-alive=true");
+
+ assertFalse(reuseStrategy.keepAlive(response, context));
+ }
+
/**
* Creates a response without an entity.