001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.impl; 029 030import org.apache.http.ConnectionReuseStrategy; 031import org.apache.http.Header; 032import org.apache.http.HeaderIterator; 033import org.apache.http.HttpHeaders; 034import org.apache.http.HttpRequest; 035import org.apache.http.HttpResponse; 036import org.apache.http.HttpStatus; 037import org.apache.http.HttpVersion; 038import org.apache.http.ParseException; 039import org.apache.http.ProtocolVersion; 040import org.apache.http.TokenIterator; 041import org.apache.http.annotation.Contract; 042import org.apache.http.annotation.ThreadingBehavior; 043import org.apache.http.message.BasicTokenIterator; 044import org.apache.http.protocol.HTTP; 045import org.apache.http.protocol.HttpContext; 046import org.apache.http.protocol.HttpCoreContext; 047import org.apache.http.util.Args; 048 049/** 050 * Default implementation of a strategy deciding about connection re-use. 051 * The default implementation first checks some basics, for example 052 * whether the connection is still open or whether the end of the 053 * request entity can be determined without closing the connection. 054 * If these checks pass, the tokens in the {@code Connection} header will 055 * be examined. In the absence of a {@code Connection} header, the 056 * non-standard but commonly used {@code Proxy-Connection} header takes 057 * it's role. A token {@code close} indicates that the connection cannot 058 * be reused. If there is no such token, a token {@code keep-alive} 059 * indicates that the connection should be re-used. If neither token is found, 060 * or if there are no {@code Connection} headers, the default policy for 061 * the HTTP version is applied. Since {@code HTTP/1.1}, connections are 062 * re-used by default. Up until {@code HTTP/1.0}, connections are not 063 * re-used by default. 064 * 065 * @since 4.0 066 */ 067@Contract(threading = ThreadingBehavior.IMMUTABLE) 068public class DefaultConnectionReuseStrategy implements ConnectionReuseStrategy { 069 070 public static final DefaultConnectionReuseStrategy INSTANCE = new DefaultConnectionReuseStrategy(); 071 072 public DefaultConnectionReuseStrategy() { 073 super(); 074 } 075 076 // see interface ConnectionReuseStrategy 077 @Override 078 public boolean keepAlive(final HttpResponse response, 079 final HttpContext context) { 080 Args.notNull(response, "HTTP response"); 081 Args.notNull(context, "HTTP context"); 082 083 final HttpRequest request = (HttpRequest) context.getAttribute(HttpCoreContext.HTTP_REQUEST); 084 if (request != null) { 085 try { 086 final TokenIterator ti = new BasicTokenIterator(request.headerIterator(HttpHeaders.CONNECTION)); 087 while (ti.hasNext()) { 088 final String token = ti.nextToken(); 089 if (HTTP.CONN_CLOSE.equalsIgnoreCase(token)) { 090 return false; 091 } 092 } 093 } catch (final ParseException px) { 094 // invalid connection header. do not re-use 095 return false; 096 } 097 } 098 099 // Check for a self-terminating entity. If the end of the entity will 100 // be indicated by closing the connection, there is no keep-alive. 101 final ProtocolVersion ver = response.getStatusLine().getProtocolVersion(); 102 final Header teh = response.getFirstHeader(HTTP.TRANSFER_ENCODING); 103 if (teh != null) { 104 if (!HTTP.CHUNK_CODING.equalsIgnoreCase(teh.getValue())) { 105 return false; 106 } 107 } else { 108 if (canResponseHaveBody(request, response)) { 109 final Header[] clhs = response.getHeaders(HTTP.CONTENT_LEN); 110 // Do not reuse if not properly content-length delimited 111 if (clhs.length == 1) { 112 final Header clh = clhs[0]; 113 try { 114 final int contentLen = Integer.parseInt(clh.getValue()); 115 if (contentLen < 0) { 116 return false; 117 } 118 } catch (final NumberFormatException ex) { 119 return false; 120 } 121 } else { 122 return false; 123 } 124 } 125 } 126 127 // Check for the "Connection" header. If that is absent, check for 128 // the "Proxy-Connection" header. The latter is an unspecified and 129 // broken but unfortunately common extension of HTTP. 130 HeaderIterator headerIterator = response.headerIterator(HTTP.CONN_DIRECTIVE); 131 if (!headerIterator.hasNext()) { 132 headerIterator = response.headerIterator("Proxy-Connection"); 133 } 134 135 // Experimental usage of the "Connection" header in HTTP/1.0 is 136 // documented in RFC 2068, section 19.7.1. A token "keep-alive" is 137 // used to indicate that the connection should be persistent. 138 // Note that the final specification of HTTP/1.1 in RFC 2616 does not 139 // include this information. Neither is the "Connection" header 140 // mentioned in RFC 1945, which informally describes HTTP/1.0. 141 // 142 // RFC 2616 specifies "close" as the only connection token with a 143 // specific meaning: it disables persistent connections. 144 // 145 // The "Proxy-Connection" header is not formally specified anywhere, 146 // but is commonly used to carry one token, "close" or "keep-alive". 147 // The "Connection" header, on the other hand, is defined as a 148 // sequence of tokens, where each token is a header name, and the 149 // token "close" has the above-mentioned additional meaning. 150 // 151 // To get through this mess, we treat the "Proxy-Connection" header 152 // in exactly the same way as the "Connection" header, but only if 153 // the latter is missing. We scan the sequence of tokens for both 154 // "close" and "keep-alive". As "close" is specified by RFC 2068, 155 // it takes precedence and indicates a non-persistent connection. 156 // If there is no "close" but a "keep-alive", we take the hint. 157 158 if (headerIterator.hasNext()) { 159 try { 160 final TokenIterator ti = new BasicTokenIterator(headerIterator); 161 boolean keepalive = false; 162 while (ti.hasNext()) { 163 final String token = ti.nextToken(); 164 if (HTTP.CONN_CLOSE.equalsIgnoreCase(token)) { 165 return false; 166 } else if (HTTP.CONN_KEEP_ALIVE.equalsIgnoreCase(token)) { 167 // continue the loop, there may be a "close" afterwards 168 keepalive = true; 169 } 170 } 171 if (keepalive) { 172 return true; 173 // neither "close" nor "keep-alive", use default policy 174 } 175 176 } catch (final ParseException px) { 177 // invalid connection header. do not re-use 178 return false; 179 } 180 } 181 182 // default since HTTP/1.1 is persistent, before it was non-persistent 183 return !ver.lessEquals(HttpVersion.HTTP_1_0); 184 } 185 186 187 /** 188 * Creates a token iterator from a header iterator. 189 * This method can be overridden to replace the implementation of 190 * the token iterator. 191 * 192 * @param hit the header iterator 193 * 194 * @return the token iterator 195 */ 196 protected TokenIterator createTokenIterator(final HeaderIterator hit) { 197 return new BasicTokenIterator(hit); 198 } 199 200 private boolean canResponseHaveBody(final HttpRequest request, final HttpResponse response) { 201 if (request != null && request.getRequestLine().getMethod().equalsIgnoreCase("HEAD")) { 202 return false; 203 } 204 final int status = response.getStatusLine().getStatusCode(); 205 return status >= HttpStatus.SC_OK 206 && status != HttpStatus.SC_NO_CONTENT 207 && status != HttpStatus.SC_NOT_MODIFIED 208 && status != HttpStatus.SC_RESET_CONTENT; 209 } 210 211}