Resolved issues: NETTY-280 Add convenient getters and setters for HTTP headers

Resolved issues: NETTY-281 Allow non-string values for HTTP header values
This commit is contained in:
Trustin Lee 2010-01-26 04:31:54 +00:00
parent d87d81e398
commit d0e886c344
7 changed files with 162 additions and 65 deletions

View File

@ -48,15 +48,15 @@ public class DefaultHttpChunkTrailer implements HttpChunkTrailer {
return true;
}
public void addHeader(final String name, final String value) {
public void addHeader(final String name, final Object value) {
headers.addHeader(name, value);
}
public void setHeader(final String name, final String value) {
public void setHeader(final String name, final Object value) {
headers.setHeader(name, value);
}
public void setHeader(final String name, final Iterable<String> values) {
public void setHeader(final String name, final Iterable<?> values) {
headers.setHeader(name, values);
}

View File

@ -21,6 +21,8 @@ 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;
/**
* The default {@link HttpMessage} implementation.
@ -44,15 +46,15 @@ public class DefaultHttpMessage implements HttpMessage {
setProtocolVersion(version);
}
public void addHeader(final String name, final String value) {
public void addHeader(final String name, final Object value) {
headers.addHeader(name, value);
}
public void setHeader(final String name, final String value) {
public void setHeader(final String name, final Object value) {
headers.setHeader(name, value);
}
public void setHeader(final String name, final Iterable<String> values) {
public void setHeader(final String name, final Iterable<?> values) {
headers.setHeader(name, values);
}
@ -60,16 +62,14 @@ public class DefaultHttpMessage implements HttpMessage {
headers.removeHeader(name);
}
@Deprecated
public long getContentLength() {
return getContentLength(0);
return HttpHeaders.getContentLength(this);
}
@Deprecated
public long getContentLength(long defaultValue) {
List<String> contentLength = getHeaders(HttpHeaders.Names.CONTENT_LENGTH);
if (contentLength != null && contentLength.size() > 0) {
return Long.parseLong(contentLength.get(0));
}
return defaultValue;
return HttpHeaders.getContentLength(this, defaultValue);
}
public boolean isChunked() {
@ -88,17 +88,44 @@ public class DefaultHttpMessage implements HttpMessage {
}
public boolean isKeepAlive() {
if (HttpHeaders.Values.CLOSE.equalsIgnoreCase(getHeader(HttpHeaders.Names.CONNECTION))) {
HttpVersion version = getProtocolVersion();
if (!version.getProtocolName().equals("HTTP")) {
return false;
}
if (getProtocolVersion().equals(HttpVersion.HTTP_1_0) &&
!HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(getHeader(HttpHeaders.Names.CONNECTION))) {
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);
}
}
}
public void clearHeaders() {
headers.clearHeaders();
}

View File

@ -56,7 +56,7 @@ public interface HttpChunk {
return true;
}
public void addHeader(String name, String value) {
public void addHeader(String name, Object value) {
throw new IllegalStateException("read-only");
}
@ -88,11 +88,11 @@ public interface HttpChunk {
// NOOP
}
public void setHeader(String name, String value) {
public void setHeader(String name, Object value) {
throw new IllegalStateException("read-only");
}
public void setHeader(String name, Iterable<String> values) {
public void setHeader(String name, Iterable<?> values) {
throw new IllegalStateException("read-only");
}
};

View File

@ -74,21 +74,21 @@ public interface HttpChunkTrailer extends HttpChunk {
/**
* Adds a new trailing header with the specified name and value.
*/
void addHeader(String name, String value);
void addHeader(String name, Object value);
/**
* Sets a new trailing header with the specified name and value.
* If there is an existing trailing header with the same name, the existing
* one is removed.
*/
void setHeader(String name, String value);
void setHeader(String name, Object value);
/**
* Sets a new trailing header with the specified name and values.
* If there is an existing trailing header with the same name, the existing
* one is removed.
*/
void setHeader(String name, Iterable<String> values);
void setHeader(String name, Iterable<?> values);
/**
* Removes the trailing header with the specified name.

View File

@ -409,6 +409,57 @@ public class HttpHeaders {
}
}
/**
* Returns the length of the content. Please note that this value is
* not retrieved from {@link HttpMessage#getContent()} but from the
* {@code "Content-Length"} header, and thus they are independent from each
* other.
*
* @return the content length or {@code 0} if this message does not have
* the {@code "Content-Length"} header
*/
public static long getContentLength(HttpMessage message) {
return getContentLength(message, 0L);
}
/**
* Returns the length of the content. Please note that this value is
* not retrieved from {@link HttpMessage#getContent()} but from the
* {@code "Content-Length"} header, and thus they are independent from each
* other.
*
* @return the content length or {@code defaultValue} if this message does
* not have the {@code "Content-Length"} header
*/
public static long getContentLength(HttpMessage message, long defaultValue) {
String contentLength = message.getHeader(Names.CONTENT_LENGTH);
if (contentLength != null) {
return Long.parseLong(contentLength);
}
return defaultValue;
}
public static void setContentLength(HttpMessage message, long value) {
message.setHeader(Names.CONTENT_LENGTH, value);
}
public static String getHost(HttpMessage message) {
return message.getHeader(Names.HOST);
}
public static String getHost(HttpMessage message, String defaultValue) {
String host = getHost(message);
if (host == null) {
return defaultValue;
}
return host;
}
public static void setHost(HttpMessage message, String value) {
message.setHeader(Names.HOST, value);
}
private static final int BUCKET_SIZE = 17;
private static int hash(String name) {
@ -469,12 +520,13 @@ public class HttpHeaders {
HttpCodecUtil.validateHeaderName(name);
}
void addHeader(final String name, final String value) {
void addHeader(final String name, final Object value) {
validateHeaderName(name);
HttpCodecUtil.validateHeaderValue(value);
String strVal = toString(value);
HttpCodecUtil.validateHeaderValue(strVal);
int h = hash(name);
int i = index(h);
addHeader0(h, i, name, value);
addHeader0(h, i, name, strVal);
}
private void addHeader0(int h, int i, final String name, final String value) {
@ -536,43 +588,34 @@ public class HttpHeaders {
}
}
void setHeader(final String name, final String value) {
void setHeader(final String name, final Object value) {
validateHeaderName(name);
HttpCodecUtil.validateHeaderValue(value);
String strVal = toString(value);
HttpCodecUtil.validateHeaderValue(strVal);
int h = hash(name);
int i = index(h);
removeHeader0(h, i, name);
addHeader0(h, i, name, value);
addHeader0(h, i, name, strVal);
}
void setHeader(final String name, final Iterable<String> values) {
validateHeaderName(name);
void setHeader(final String name, final Iterable<?> values) {
if (values == null) {
throw new NullPointerException("values");
}
boolean empty = true;
for (String v: values) {
if (v == null) {
break;
}
HttpCodecUtil.validateHeaderValue(v);
empty = false;
}
validateHeaderName(name);
int h = hash(name);
int i = index(h);
removeHeader0(h, i, name);
if (empty) {
return;
}
for (String v: values) {
for (Object v: values) {
if (v == null) {
break;
}
addHeader0(h, i, name, v);
String strVal = toString(v);
HttpCodecUtil.validateHeaderValue(strVal);
addHeader0(h, i, name, strVal);
}
}
@ -648,6 +691,13 @@ public class HttpHeaders {
return names;
}
private static String toString(Object value) {
if (value == null) {
return null;
}
return value.toString();
}
private static final class Entry implements Map.Entry<String, String> {
final int hash;
final String key;

View File

@ -101,19 +101,19 @@ public interface HttpMessage {
/**
* Adds a new header with the specified name and value.
*/
void addHeader(String name, String value);
void addHeader(String name, Object value);
/**
* Sets a new header with the specified name and value. If there is an
* existing header with the same name, the existing header is removed.
*/
void setHeader(String name, String value);
void setHeader(String name, Object value);
/**
* Sets a new header with the specified name and values. If there is an
* existing header with the same name, the existing header is removed.
*/
void setHeader(String name, Iterable<String> values);
void setHeader(String name, Iterable<?> values);
/**
* Removes the header with the specified name.
@ -126,25 +126,15 @@ public interface HttpMessage {
void clearHeaders();
/**
* Returns the length of the content. Please note that this value is
* not retrieved from {@link #getContent()} but from the
* {@code "Content-Length"} header, and thus they are independent from each
* other.
*
* @return the content length or {@code 0} if this message does not have
* the {@code "Content-Length"} header
* @deprecated Use {@link HttpHeaders#getContentLength(HttpMessage)} instead.
*/
@Deprecated
long getContentLength();
/**
* Returns the length of the content. Please note that this value is
* not retrieved from {@link #getContent()} but from the
* {@code "Content-Length"} header, and thus they are independent from each
* other.
*
* @return the content length or {@code defaultValue} if this message does
* not have the {@code "Content-Length"} header
* @deprecated Use {@link HttpHeaders#getContentLength(HttpMessage, long)} instead.
*/
@Deprecated
long getContentLength(long defaultValue);
/**
@ -175,7 +165,37 @@ public interface HttpMessage {
/**
* Returns {@code true} if and only if the connection can remain open and
* thus 'kept alive'.
* 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.
*/
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

@ -214,7 +214,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
message.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
return message;
} else {
long contentLength = message.getContentLength(-1);
long contentLength = HttpHeaders.getContentLength(message, -1);
if (contentLength == 0 || contentLength == -1 && isDecodingRequest()) {
content = ChannelBuffers.EMPTY_BUFFER;
return reset();
@ -228,7 +228,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
message.setChunked(true);
// chunkSize will be decreased as the READ_FIXED_LENGTH_CONTENT_AS_CHUNKS
// state reads data chunk by chunk.
chunkSize = message.getContentLength(-1);
chunkSize = HttpHeaders.getContentLength(message, -1);
return message;
}
break;
@ -415,7 +415,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
}
private void readFixedLengthContent(ChannelBuffer buffer) {
long length = message.getContentLength(-1);
long length = HttpHeaders.getContentLength(message, -1);
assert length <= Integer.MAX_VALUE;
if (content == null) {
@ -461,7 +461,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
// yet, HttpMessage.isChunked() should return true only when
// 'Transfer-Encoding' is 'chunked'.
nextState = State.READ_CHUNK_SIZE;
} else if (message.getContentLength(-1) >= 0) {
} else if (HttpHeaders.getContentLength(message, -1) >= 0) {
nextState = State.READ_FIXED_LENGTH_CONTENT;
} else {
nextState = State.READ_VARIABLE_LENGTH_CONTENT;