001: /*
002: * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/httpcore/tags/4.0-beta1/module-main/src/main/java/org/apache/http/impl/DefaultConnectionReuseStrategy.java $
003: * $Revision: 602537 $
004: * $Date: 2007-12-08 20:42:06 +0100 (Sat, 08 Dec 2007) $
005: *
006: * ====================================================================
007: * Licensed to the Apache Software Foundation (ASF) under one
008: * or more contributor license agreements. See the NOTICE file
009: * distributed with this work for additional information
010: * regarding copyright ownership. The ASF licenses this file
011: * to you under the Apache License, Version 2.0 (the
012: * "License"); you may not use this file except in compliance
013: * with the License. You may obtain a copy of the License at
014: *
015: * http://www.apache.org/licenses/LICENSE-2.0
016: *
017: * Unless required by applicable law or agreed to in writing,
018: * software distributed under the License is distributed on an
019: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020: * KIND, either express or implied. See the License for the
021: * specific language governing permissions and limitations
022: * under the License.
023: * ====================================================================
024: *
025: * This software consists of voluntary contributions made by many
026: * individuals on behalf of the Apache Software Foundation. For more
027: * information on the Apache Software Foundation, please see
028: * <http://www.apache.org/>.
029: *
030: */
031:
032: package org.apache.http.impl;
033:
034: import org.apache.http.ConnectionReuseStrategy;
035: import org.apache.http.HttpConnection;
036: import org.apache.http.HeaderIterator;
037: import org.apache.http.HttpEntity;
038: import org.apache.http.HttpResponse;
039: import org.apache.http.HttpVersion;
040: import org.apache.http.ParseException;
041: import org.apache.http.ProtocolVersion;
042: import org.apache.http.protocol.HTTP;
043: import org.apache.http.protocol.HttpContext;
044: import org.apache.http.protocol.ExecutionContext;
045: import org.apache.http.TokenIterator;
046: import org.apache.http.message.BasicTokenIterator;
047:
048: /**
049: * Default implementation of a strategy deciding about connection re-use.
050: * The default implementation first checks some basics, for example
051: * whether the connection is still open or whether the end of the
052: * request entity can be determined without closing the connection.
053: * If these checks pass, the tokens in the "Connection" header will
054: * be examined. In the absence of a "Connection" header, the
055: * non-standard but commonly used "Proxy-Connection" header takes
056: * it's role. A token "close" indicates that the connection cannot
057: * be reused. If there is no such token, a token "keep-alive" indicates
058: * that the connection should be re-used. If neither token is found,
059: * or if there are no "Connection" headers, the default policy for
060: * the HTTP version is applied. Since HTTP/1.1, connections are re-used
061: * by default. Up until HTTP/1.0, connections are not re-used by default.
062: *
063: * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
064: * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
065: *
066: * @version $Revision: 602537 $
067: *
068: * @since 4.0
069: */
070: public class DefaultConnectionReuseStrategy implements
071: ConnectionReuseStrategy {
072:
073: public DefaultConnectionReuseStrategy() {
074: super ();
075: }
076:
077: // see interface ConnectionReuseStrategy
078: public boolean keepAlive(final HttpResponse response,
079: final HttpContext context) {
080: if (response == null) {
081: throw new IllegalArgumentException(
082: "HTTP response may not be null.");
083: }
084: if (context == null) {
085: throw new IllegalArgumentException(
086: "HTTP context may not be null.");
087: }
088:
089: HttpConnection conn = (HttpConnection) context
090: .getAttribute(ExecutionContext.HTTP_CONNECTION);
091:
092: if (conn != null && !conn.isOpen())
093: return false;
094: // do NOT check for stale connection, that is an expensive operation
095:
096: // Check for a self-terminating entity. If the end of the entity will
097: // be indicated by closing the connection, there is no keep-alive.
098: HttpEntity entity = response.getEntity();
099: ProtocolVersion ver = response.getStatusLine()
100: .getProtocolVersion();
101: if (entity != null) {
102: if (entity.getContentLength() < 0) {
103: if (!entity.isChunked()
104: || ver.lessEquals(HttpVersion.HTTP_1_0)) {
105: // if the content length is not known and is not chunk
106: // encoded, the connection cannot be reused
107: return false;
108: }
109: }
110: }
111:
112: // Check for the "Connection" header. If that is absent, check for
113: // the "Proxy-Connection" header. The latter is an unspecified and
114: // broken but unfortunately common extension of HTTP.
115: HeaderIterator hit = response
116: .headerIterator(HTTP.CONN_DIRECTIVE);
117: if (!hit.hasNext())
118: hit = response.headerIterator("Proxy-Connection");
119:
120: // Experimental usage of the "Connection" header in HTTP/1.0 is
121: // documented in RFC 2068, section 19.7.1. A token "keep-alive" is
122: // used to indicate that the connection should be persistent.
123: // Note that the final specification of HTTP/1.1 in RFC 2616 does not
124: // include this information. Neither is the "Connection" header
125: // mentioned in RFC 1945, which informally describes HTTP/1.0.
126: //
127: // RFC 2616 specifies "close" as the only connection token with a
128: // specific meaning: it disables persistent connections.
129: //
130: // The "Proxy-Connection" header is not formally specified anywhere,
131: // but is commonly used to carry one token, "close" or "keep-alive".
132: // The "Connection" header, on the other hand, is defined as a
133: // sequence of tokens, where each token is a header name, and the
134: // token "close" has the above-mentioned additional meaning.
135: //
136: // To get through this mess, we treat the "Proxy-Connection" header
137: // in exactly the same way as the "Connection" header, but only if
138: // the latter is missing. We scan the sequence of tokens for both
139: // "close" and "keep-alive". As "close" is specified by RFC 2068,
140: // it takes precedence and indicates a non-persistent connection.
141: // If there is no "close" but a "keep-alive", we take the hint.
142:
143: if (hit.hasNext()) {
144: try {
145: TokenIterator ti = createTokenIterator(hit);
146: boolean keepalive = false;
147: while (ti.hasNext()) {
148: final String token = ti.nextToken();
149: if (HTTP.CONN_CLOSE.equalsIgnoreCase(token)) {
150: return false;
151: } else if (HTTP.CONN_KEEP_ALIVE
152: .equalsIgnoreCase(token)) {
153: // continue the loop, there may be a "close" afterwards
154: keepalive = true;
155: }
156: }
157: if (keepalive)
158: return true;
159: // neither "close" nor "keep-alive", use default policy
160:
161: } catch (ParseException px) {
162: // invalid connection header means no persistent connection
163: // we don't have logging in HttpCore, so the exception is lost
164: return false;
165: }
166: }
167:
168: // default since HTTP/1.1 is persistent, before it was non-persistent
169: return !ver.lessEquals(HttpVersion.HTTP_1_0);
170: }
171:
172: /**
173: * Creates a token iterator from a header iterator.
174: * This method can be overridden to replace the implementation of
175: * the token iterator.
176: *
177: * @param hit the header iterator
178: *
179: * @return the token iterator
180: */
181: protected TokenIterator createTokenIterator(HeaderIterator hit) {
182: return new BasicTokenIterator(hit);
183: }
184: }
|