* Added HttpVersion.isKeepAliveDefault() to handle the 'Connection' header in a more robust manner

* Added HttpVersion constructors with the default keep alive flag - old constructors were deprecated due to ambiguity
* Moved HttpMessage.is/setKeepAlive() to HttpHeaders and deprecated the original method
This commit is contained in:
Trustin Lee 2010-01-27 02:38:17 +00:00
parent aff9af5af2
commit a85635f74c
10 changed files with 100 additions and 87 deletions

View File

@ -96,7 +96,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler {
ChannelFuture writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192));
// Decide whether to close the connection or not.
if (!request.isKeepAlive()) {
if (!isKeepAlive(request)) {
// Close the connection when the whole content is written out.
writeFuture.addListener(ChannelFutureListener.CLOSE);
}

View File

@ -125,7 +125,7 @@ public class HttpRequestHandler extends SimpleChannelUpstreamHandler {
private void writeResponse(MessageEvent e) {
// Decide whether to close the connection or not.
boolean keepAlive = request.isKeepAlive();
boolean keepAlive = isKeepAlive(request);
// Build the response object.
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
@ -154,7 +154,7 @@ public class HttpRequestHandler extends SimpleChannelUpstreamHandler {
// Write the response.
ChannelFuture future = e.getChannel().write(response);
System.out.println(response.toString());
// Close the non-keep-alive connection after the write operation is done.
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);

View File

@ -140,7 +140,7 @@ public class WebSocketServerHandler extends SimpleChannelUpstreamHandler {
// Send the response and close the connection if necessary.
ChannelFuture f = ctx.getChannel().write(res);
if (!req.isKeepAlive() || res.getStatus().getCode() != 200) {
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
f.addListener(ChannelFutureListener.CLOSE);
}
}

View File

@ -21,8 +21,6 @@ import java.util.Set;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
import org.jboss.netty.util.internal.StringUtil;
/**
@ -88,43 +86,9 @@ public class DefaultHttpMessage implements HttpMessage {
}
}
@Deprecated
public boolean isKeepAlive() {
HttpVersion version = getProtocolVersion();
if (!version.getProtocolName().equals("HTTP")) {
return false;
}
String connection = getHeader(Names.CONNECTION);
if (HttpHeaders.Values.CLOSE.equalsIgnoreCase(connection)) {
return false;
}
if (version.equals(HttpVersion.HTTP_1_0) &&
!HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(connection)) {
return false;
}
return true;
}
public void setKeepAlive(boolean keepAlive) {
HttpVersion version = getProtocolVersion();
if (!version.getProtocolName().equals("HTTP")) {
return;
}
if (version.equals(HttpVersion.HTTP_1_0)) {
if (keepAlive) {
setHeader(Names.CONNECTION, Values.KEEP_ALIVE);
} else {
removeHeader(Names.CONNECTION);
}
} else {
if (keepAlive) {
removeHeader(Names.CONNECTION);
} else {
setHeader(Names.CONNECTION, Values.CLOSE);
}
}
return HttpHeaders.isKeepAlive(this);
}
public void clearHeaders() {

View File

@ -69,9 +69,7 @@ public class DefaultHttpRequest extends DefaultHttpMessage implements HttpReques
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append("(keepAlive: ");
buf.append(isKeepAlive());
buf.append(", chunked: ");
buf.append("(chunked: ");
buf.append(isChunked());
buf.append(')');
buf.append(StringUtil.NEWLINE);

View File

@ -55,9 +55,7 @@ public class DefaultHttpResponse extends DefaultHttpMessage implements HttpRespo
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append("(keepAlive: ");
buf.append(isKeepAlive());
buf.append(", chunked: ");
buf.append("(chunked: ");
buf.append(isChunked());
buf.append(')');
buf.append(StringUtil.NEWLINE);

View File

@ -441,6 +441,60 @@ public class HttpHeaders {
return defaultValue;
}
/**
* Returns {@code true} if and only if the connection can remain open and
* thus 'kept alive'. This methods respects the value of the
* {@code "Connection"} header first and then the return value of
* {@link HttpVersion#isKeepAliveDefault()}.
*/
public static boolean isKeepAlive(HttpMessage message) {
String connection = message.getHeader(Names.CONNECTION);
if (Values.CLOSE.equalsIgnoreCase(connection)) {
return false;
}
if (message.getProtocolVersion().isKeepAliveDefault()) {
return !Values.CLOSE.equalsIgnoreCase(connection);
} else {
return Values.KEEP_ALIVE.equalsIgnoreCase(connection);
}
}
/**
* Sets the value of the {@code "Connection"} header depending on the
* protocol version of the specified message. This method sets or removes
* the {@code "Connection"} header depending on what the default keep alive
* mode of the message's protocol version is, as specified by
* {@link HttpVersion#isKeepAliveDefault()}.
* <ul>
* <li>If the connection is kept alive by default:
* <ul>
* <li>set to {@code "close"} if {@code keepAlive} is {@code false}.</li>
* <li>remove otherwise.</li>
* </ul></li>
* <li>If the connection is closed by default:
* <ul>
* <li>set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.</li>
* <li>remove otherwise.</li>
* </ul></li>
* </ul>
*/
public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
if (message.getProtocolVersion().isKeepAliveDefault()) {
if (keepAlive) {
message.removeHeader(Names.CONNECTION);
} else {
message.setHeader(Names.CONNECTION, Values.CLOSE);
}
} else {
if (keepAlive) {
message.setHeader(Names.CONNECTION, Values.KEEP_ALIVE);
} else {
message.removeHeader(Names.CONNECTION);
}
}
}
// TODO Document me
public static void setContentLength(HttpMessage message, long value) {

View File

@ -164,38 +164,8 @@ public interface HttpMessage {
void setChunked(boolean chunked);
/**
* Returns {@code true} if and only if the connection can remain open and
* thus 'kept alive'. In HTTP, this property is determined by the value of the
* {@code "Connection"} header and the protocol version of this message.
* <p>
* Please note that the default implementation of this method only
* understands HTTP. If the protocol version of this message indicates
* other derived protocols such as RTSP and ICAP, it will return false by
* default.
* @deprecated Use {@link HttpHeaders#isKeepAlive(HttpMessage)} instead.
*/
@Deprecated
boolean isKeepAlive();
/**
* Sets the value of the {@code "Connection"} header depending on the
* protocol version of this message. The default implementation sets or
* removes the {@code "Connection"} header with the following rules:
* <ul>
* <li>If protocol version is HTTP/1.1 or later:
* <ul>
* <li>set to {@code "close"} if {@code keepAlive} is {@code false}.</li>
* <li>remove otherwise.</li>
* </ul></li>
* <li>If protocol version is HTTP/1.0:
* <ul>
* <li>set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.</li>
* <li>remove otherwise.</li>
* </ul></li>
* <li>do nothing if the protocol name is not {@code "HTTP"}.</li>
* </ul>
* Please note that the default implementation of this method only
* understands HTTP. If the protocol version of this message indicates
* other derived protocols such as RTSP and ICAP, it will do nothing by
* default.
*/
void setKeepAlive(boolean keepAlive);
}

View File

@ -38,12 +38,12 @@ public class HttpVersion implements Comparable<HttpVersion> {
/**
* HTTP/1.0
*/
public static final HttpVersion HTTP_1_0 = new HttpVersion("HTTP", 1, 0);
public static final HttpVersion HTTP_1_0 = new HttpVersion("HTTP", 1, 0, false);
/**
* HTTP/1.1
*/
public static final HttpVersion HTTP_1_1 = new HttpVersion("HTTP", 1, 1);
public static final HttpVersion HTTP_1_1 = new HttpVersion("HTTP", 1, 1, true);
/**
* Returns an existing or new {@link HttpVersion} instance which matches to
@ -65,13 +65,22 @@ public class HttpVersion implements Comparable<HttpVersion> {
if (text.equals("HTTP/1.0")) {
return HTTP_1_0;
}
return new HttpVersion(text);
return new HttpVersion(text, true);
}
private final String protocolName;
private final int majorVersion;
private final int minorVersion;
private final String text;
private final boolean keepAliveDefault;
/**
* @deprecated Use {@link #HttpVersion(String, boolean)} instead.
*/
@Deprecated
public HttpVersion(String text) {
this(text, true);
}
/**
* Creates a new HTTP version with the specified version string. You will
@ -80,7 +89,7 @@ public class HttpVersion implements Comparable<HttpVersion> {
* <a href="http://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol">RTSP</a> and
* <a href="http://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a>.
*/
public HttpVersion(String text) {
public HttpVersion(String text, boolean keepAliveDefault) {
if (text == null) {
throw new NullPointerException("text");
}
@ -99,6 +108,16 @@ public class HttpVersion implements Comparable<HttpVersion> {
majorVersion = Integer.parseInt(m.group(2));
minorVersion = Integer.parseInt(m.group(3));
this.text = protocolName + '/' + majorVersion + '.' + minorVersion;
this.keepAliveDefault = keepAliveDefault;
}
/**
* @deprecated Use {@link #HttpVersion(String, int, int, boolean)} instead.
*/
@Deprecated
public HttpVersion(
String protocolName, int majorVersion, int minorVersion) {
this(protocolName, majorVersion, minorVersion, true);
}
/**
@ -109,7 +128,8 @@ public class HttpVersion implements Comparable<HttpVersion> {
* <a href="http://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a>
*/
public HttpVersion(
String protocolName, int majorVersion, int minorVersion) {
String protocolName, int majorVersion, int minorVersion,
boolean keepAliveDefault) {
if (protocolName == null) {
throw new NullPointerException("protocolName");
}
@ -137,6 +157,7 @@ public class HttpVersion implements Comparable<HttpVersion> {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
text = protocolName + '/' + majorVersion + '.' + minorVersion;
this.keepAliveDefault = keepAliveDefault;
}
/**
@ -167,6 +188,14 @@ public class HttpVersion implements Comparable<HttpVersion> {
return text;
}
/**
* Returns {@code true} if and only if the connection is kept alive unless
* the {@code "Connection"} header is set to {@code "close"} explicitly.
*/
public boolean isKeepAliveDefault() {
return keepAliveDefault;
}
/**
* Returns the full protocol version text such as {@code "HTTP/1.0"}.
*/

View File

@ -30,7 +30,7 @@ public final class RtspVersions {
/**
* RTSP/1.0
*/
public static final HttpVersion RTSP_1_0 = new HttpVersion("RTSP", 1, 0);
public static final HttpVersion RTSP_1_0 = new HttpVersion("RTSP", 1, 0, true);
/**
* Returns an existing or new {@link HttpVersion} instance which matches to
@ -48,7 +48,7 @@ public final class RtspVersions {
return RTSP_1_0;
}
return new HttpVersion(text);
return new HttpVersion(text, true);
}
private RtspVersions() {